@strkfarm/sdk 2.0.0-staging.23 → 2.0.0-staging.25
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/index.browser.global.js +846 -322
- package/dist/index.browser.mjs +770 -246
- package/dist/index.d.ts +1 -0
- package/dist/index.js +770 -246
- package/dist/index.mjs +770 -246
- package/package.json +1 -1
- package/src/interfaces/common.tsx +1 -0
- package/src/strategies/ekubo-cl-vault.tsx +2 -0
- package/src/strategies/universal-lst-muliplier-strategy.tsx +1317 -576
- package/src/strategies/universal-strategy.tsx +1 -1
|
@@ -1,12 +1,62 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
FAQ,
|
|
3
|
+
getNoRiskTags,
|
|
4
|
+
highlightTextWithLinks,
|
|
5
|
+
IConfig,
|
|
6
|
+
IStrategyMetadata,
|
|
7
|
+
Protocols,
|
|
8
|
+
RiskFactor,
|
|
9
|
+
RiskType,
|
|
10
|
+
StrategyTag,
|
|
11
|
+
TokenInfo,
|
|
12
|
+
AuditStatus,
|
|
13
|
+
SourceCodeType,
|
|
14
|
+
AccessControlType,
|
|
15
|
+
InstantWithdrawalVault,
|
|
16
|
+
StrategyLiveStatus,
|
|
17
|
+
StrategySettings,
|
|
18
|
+
VaultType,
|
|
19
|
+
RedemptionInfo,
|
|
20
|
+
} from "@/interfaces";
|
|
21
|
+
import {
|
|
22
|
+
AUMTypes,
|
|
23
|
+
getContractDetails,
|
|
24
|
+
UNIVERSAL_ADAPTERS,
|
|
25
|
+
UNIVERSAL_MANAGE_IDS,
|
|
26
|
+
UniversalManageCall,
|
|
27
|
+
UniversalStrategy,
|
|
28
|
+
UniversalStrategySettings,
|
|
29
|
+
} from "./universal-strategy";
|
|
3
30
|
import { PricerBase } from "@/modules/pricerBase";
|
|
4
31
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
5
32
|
import { Global } from "@/global";
|
|
6
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
ApproveCallParams,
|
|
35
|
+
AvnuSwapCallParams,
|
|
36
|
+
CommonAdapter,
|
|
37
|
+
getVesuSingletonAddress,
|
|
38
|
+
ManageCall,
|
|
39
|
+
Swap,
|
|
40
|
+
VesuAdapter,
|
|
41
|
+
VesuModifyDelegationCallParams,
|
|
42
|
+
VesuModifyPositionCallParams,
|
|
43
|
+
VesuMultiplyCallParams,
|
|
44
|
+
VesuPools,
|
|
45
|
+
} from "./universal-adapters";
|
|
7
46
|
import { AVNU_EXCHANGE } from "./universal-adapters/adapter-utils";
|
|
8
|
-
import {
|
|
9
|
-
|
|
47
|
+
import {
|
|
48
|
+
DepegRiskLevel,
|
|
49
|
+
LiquidationRiskLevel,
|
|
50
|
+
SmartContractRiskLevel,
|
|
51
|
+
TechnicalRiskLevel,
|
|
52
|
+
} from "@/interfaces/risks";
|
|
53
|
+
import {
|
|
54
|
+
AvnuWrapper,
|
|
55
|
+
EkuboQuoter,
|
|
56
|
+
ERC20,
|
|
57
|
+
LSTAPRService,
|
|
58
|
+
PricerLST,
|
|
59
|
+
} from "@/modules";
|
|
10
60
|
import { assert, logger } from "@/utils";
|
|
11
61
|
import { SingleTokenInfo } from "./base-strategy";
|
|
12
62
|
import { Call, Contract, uint256 } from "starknet";
|
|
@@ -16,30 +66,40 @@ import { findMaxInputWithSlippage } from "@/utils/math-utils";
|
|
|
16
66
|
import { LSTPriceType } from "./types";
|
|
17
67
|
|
|
18
68
|
export interface HyperLSTStrategySettings extends UniversalStrategySettings {
|
|
19
|
-
borrowable_assets: { token: TokenInfo
|
|
69
|
+
borrowable_assets: { token: TokenInfo; poolId: ContractAddr }[];
|
|
20
70
|
underlyingToken: TokenInfo;
|
|
21
71
|
defaultPoolId: ContractAddr;
|
|
22
72
|
}
|
|
23
73
|
|
|
24
74
|
export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTStrategySettings> {
|
|
25
|
-
|
|
26
75
|
private quoteAmountToFetchPrice = new Web3Number(1, 18);
|
|
27
|
-
|
|
28
|
-
constructor(
|
|
76
|
+
|
|
77
|
+
constructor(
|
|
78
|
+
config: IConfig,
|
|
79
|
+
pricer: PricerBase,
|
|
80
|
+
metadata: IStrategyMetadata<HyperLSTStrategySettings>,
|
|
81
|
+
) {
|
|
29
82
|
super(config, pricer, metadata);
|
|
30
83
|
|
|
31
|
-
const STRKToken = Global.getDefaultTokens().find(
|
|
84
|
+
const STRKToken = Global.getDefaultTokens().find(
|
|
85
|
+
(token) => token.symbol === "STRK",
|
|
86
|
+
)!;
|
|
32
87
|
const underlyingToken = this.getLSTUnderlyingTokenInfo();
|
|
33
88
|
if (underlyingToken.address.eq(STRKToken.address)) {
|
|
34
89
|
this.quoteAmountToFetchPrice = new Web3Number(100, 18);
|
|
35
90
|
} else {
|
|
36
91
|
// else this BTC
|
|
37
|
-
this.quoteAmountToFetchPrice = new Web3Number(
|
|
92
|
+
this.quoteAmountToFetchPrice = new Web3Number(
|
|
93
|
+
0.01,
|
|
94
|
+
this.asset().decimals,
|
|
95
|
+
);
|
|
38
96
|
}
|
|
39
97
|
}
|
|
40
98
|
|
|
41
99
|
asset() {
|
|
42
|
-
return this.getVesuSameTokenAdapter(
|
|
100
|
+
return this.getVesuSameTokenAdapter(
|
|
101
|
+
this.metadata.additionalInfo.defaultPoolId,
|
|
102
|
+
).config.collateral;
|
|
43
103
|
}
|
|
44
104
|
|
|
45
105
|
getTag() {
|
|
@@ -48,7 +108,13 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
48
108
|
|
|
49
109
|
// Vesu adapter with LST and base token match
|
|
50
110
|
getVesuSameTokenAdapter(poolId: ContractAddr) {
|
|
51
|
-
const baseAdapter = this.getAdapter(
|
|
111
|
+
const baseAdapter = this.getAdapter(
|
|
112
|
+
getVesuLegId(
|
|
113
|
+
UNIVERSAL_MANAGE_IDS.VESU_LEG1,
|
|
114
|
+
this.metadata.additionalInfo.underlyingToken.symbol,
|
|
115
|
+
poolId.toString(),
|
|
116
|
+
),
|
|
117
|
+
) as VesuAdapter;
|
|
52
118
|
baseAdapter.networkConfig = this.config;
|
|
53
119
|
baseAdapter.pricer = this.pricer;
|
|
54
120
|
return baseAdapter;
|
|
@@ -58,7 +124,8 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
58
124
|
// todo support lending assets of underlying as well (like if xSTRK looping is not viable, simply supply STRK)
|
|
59
125
|
getVesuAdapters() {
|
|
60
126
|
const adapters: VesuAdapter[] = [];
|
|
61
|
-
for (const borrowableInfo of this.metadata.additionalInfo
|
|
127
|
+
for (const borrowableInfo of this.metadata.additionalInfo
|
|
128
|
+
.borrowable_assets) {
|
|
62
129
|
// do not add adapter if the pool id does not match
|
|
63
130
|
const asset = borrowableInfo.token;
|
|
64
131
|
const poolId = borrowableInfo.poolId;
|
|
@@ -67,8 +134,8 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
67
134
|
collateral: this.asset(),
|
|
68
135
|
debt: asset,
|
|
69
136
|
vaultAllocator: this.metadata.additionalInfo.vaultAllocator,
|
|
70
|
-
id:
|
|
71
|
-
})
|
|
137
|
+
id: "",
|
|
138
|
+
});
|
|
72
139
|
vesuAdapter1.pricer = this.pricer;
|
|
73
140
|
vesuAdapter1.networkConfig = this.config;
|
|
74
141
|
adapters.push(vesuAdapter1);
|
|
@@ -80,7 +147,7 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
80
147
|
// No rewards on collateral or borrowing of LST assets
|
|
81
148
|
protected async getRewardsAUM(prevAum: Web3Number): Promise<Web3Number> {
|
|
82
149
|
const lstToken = this.asset();
|
|
83
|
-
if (lstToken.symbol ===
|
|
150
|
+
if (lstToken.symbol === "xSTRK") {
|
|
84
151
|
return super.getRewardsAUM(prevAum);
|
|
85
152
|
}
|
|
86
153
|
return Web3Number.fromWei("0", lstToken.decimals);
|
|
@@ -93,110 +160,163 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
93
160
|
const quote = await ekuboQuoter.getQuote(
|
|
94
161
|
lstTokenInfo.address.address,
|
|
95
162
|
lstUnderlyingTokenInfo.address.address,
|
|
96
|
-
this.quoteAmountToFetchPrice
|
|
163
|
+
this.quoteAmountToFetchPrice,
|
|
97
164
|
);
|
|
98
165
|
|
|
99
166
|
// in Underlying
|
|
100
|
-
const outputAmount = Web3Number.fromWei(
|
|
101
|
-
|
|
167
|
+
const outputAmount = Web3Number.fromWei(
|
|
168
|
+
quote.total_calculated,
|
|
169
|
+
lstUnderlyingTokenInfo.decimals,
|
|
170
|
+
);
|
|
171
|
+
const price =
|
|
172
|
+
outputAmount.toNumber() / this.quoteAmountToFetchPrice.toNumber();
|
|
102
173
|
logger.verbose(`${this.getTag()}:: LST Dex Price: ${price}`);
|
|
103
174
|
return price;
|
|
104
175
|
}
|
|
105
176
|
|
|
106
|
-
|
|
107
177
|
async getAvnuSwapMultiplyCall(params: {
|
|
108
|
-
isDeposit: boolean
|
|
109
|
-
leg1DepositAmount: Web3Number
|
|
178
|
+
isDeposit: boolean;
|
|
179
|
+
leg1DepositAmount: Web3Number;
|
|
110
180
|
}) {
|
|
111
|
-
assert(
|
|
181
|
+
assert(
|
|
182
|
+
params.isDeposit,
|
|
183
|
+
"Only deposit is supported in getAvnuSwapMultiplyCall",
|
|
184
|
+
);
|
|
112
185
|
// TODO use a varibale for 1.02
|
|
113
|
-
const maxBorrowableAmounts = await this.getMaxBorrowableAmount({
|
|
186
|
+
const maxBorrowableAmounts = await this.getMaxBorrowableAmount({
|
|
187
|
+
isAPYComputation: false,
|
|
188
|
+
});
|
|
114
189
|
const allVesuAdapters = this.getVesuAdapters();
|
|
115
190
|
let remainingAmount = params.leg1DepositAmount;
|
|
116
191
|
const lstExRate = await this.getLSTExchangeRate();
|
|
117
|
-
const baseAssetPrice = await this.pricer.getPrice(
|
|
192
|
+
const baseAssetPrice = await this.pricer.getPrice(
|
|
193
|
+
this.getLSTUnderlyingTokenInfo().symbol,
|
|
194
|
+
);
|
|
118
195
|
const lstPrice = baseAssetPrice.price * lstExRate;
|
|
119
196
|
for (let i = 0; i < maxBorrowableAmounts.maxBorrowables.length; i++) {
|
|
120
197
|
const maxBorrowable = maxBorrowableAmounts.maxBorrowables[i];
|
|
121
|
-
const vesuAdapter = allVesuAdapters.find(adapter =>
|
|
198
|
+
const vesuAdapter = allVesuAdapters.find((adapter) =>
|
|
199
|
+
adapter.config.debt.address.eq(maxBorrowable.borrowableAsset.address),
|
|
200
|
+
);
|
|
122
201
|
if (!vesuAdapter) {
|
|
123
|
-
throw new Error(
|
|
202
|
+
throw new Error(
|
|
203
|
+
`${this.getTag()}::getAvnuSwapMultiplyCall: vesuAdapter not found for borrowable asset: ${maxBorrowable.borrowableAsset.symbol}`,
|
|
204
|
+
);
|
|
124
205
|
}
|
|
125
206
|
const maxLTV = await vesuAdapter.getLTVConfig(this.config);
|
|
126
|
-
const debtPrice = await this.pricer.getPrice(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
207
|
+
const debtPrice = await this.pricer.getPrice(
|
|
208
|
+
maxBorrowable.borrowableAsset.symbol,
|
|
209
|
+
);
|
|
210
|
+
const maxAmountToDeposit =
|
|
211
|
+
HealthFactorMath.getMinCollateralRequiredOnLooping(
|
|
212
|
+
maxBorrowable.amount,
|
|
213
|
+
debtPrice.price,
|
|
214
|
+
this.metadata.additionalInfo.targetHealthFactor,
|
|
215
|
+
maxLTV,
|
|
216
|
+
lstPrice,
|
|
217
|
+
this.asset(),
|
|
218
|
+
);
|
|
135
219
|
const amountToDeposit = remainingAmount.minimum(maxAmountToDeposit);
|
|
136
|
-
logger.verbose(
|
|
220
|
+
logger.verbose(
|
|
221
|
+
`${this.getTag()}::getAvnuSwapMultiplyCall::${vesuAdapter.config.debt.symbol}:: remainingAmount: ${remainingAmount}, amountToDeposit: ${amountToDeposit}, depositAmount: ${amountToDeposit}, maxBorrowable: ${maxBorrowable.amount}`,
|
|
222
|
+
);
|
|
137
223
|
const call = await this._getAvnuDepositSwapLegCall({
|
|
138
224
|
isDeposit: params.isDeposit,
|
|
139
225
|
// adjust decimals of debt asset
|
|
140
226
|
leg1DepositAmount: amountToDeposit,
|
|
141
227
|
minHF: 1.1, // undo
|
|
142
|
-
vesuAdapter: vesuAdapter
|
|
228
|
+
vesuAdapter: vesuAdapter,
|
|
143
229
|
});
|
|
144
230
|
remainingAmount = remainingAmount.minus(amountToDeposit);
|
|
145
231
|
|
|
146
232
|
// return the first possible call because computing all calls at a time
|
|
147
233
|
// is not efficinet for swaps
|
|
148
|
-
return {call, vesuAdapter};
|
|
234
|
+
return { call, vesuAdapter };
|
|
149
235
|
}
|
|
150
|
-
throw new Error(
|
|
236
|
+
throw new Error(
|
|
237
|
+
`${this.getTag()}::getAvnuSwapMultiplyCall: no calls found`,
|
|
238
|
+
);
|
|
151
239
|
}
|
|
152
240
|
|
|
153
241
|
async _getAvnuDepositSwapLegCall(params: {
|
|
154
|
-
isDeposit: boolean
|
|
155
|
-
leg1DepositAmount: Web3Number
|
|
156
|
-
minHF: number
|
|
157
|
-
vesuAdapter: VesuAdapter
|
|
242
|
+
isDeposit: boolean;
|
|
243
|
+
leg1DepositAmount: Web3Number;
|
|
244
|
+
minHF: number; // e.g. 1.01
|
|
245
|
+
vesuAdapter: VesuAdapter;
|
|
158
246
|
}) {
|
|
159
247
|
const { vesuAdapter } = params;
|
|
160
|
-
logger.verbose(
|
|
161
|
-
|
|
248
|
+
logger.verbose(
|
|
249
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall params: ${JSON.stringify(params)}`,
|
|
250
|
+
);
|
|
251
|
+
assert(
|
|
252
|
+
params.isDeposit,
|
|
253
|
+
"Only deposit is supported in _getAvnuDepositSwapLegCall",
|
|
254
|
+
);
|
|
162
255
|
// add collateral
|
|
163
256
|
// borrow STRK (e.g.)
|
|
164
257
|
// approve and swap strk
|
|
165
258
|
// add collateral again
|
|
166
259
|
|
|
167
|
-
|
|
168
260
|
const legLTV = await vesuAdapter.getLTVConfig(this.config);
|
|
169
|
-
logger.verbose(
|
|
261
|
+
logger.verbose(
|
|
262
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall legLTV: ${legLTV}`,
|
|
263
|
+
);
|
|
170
264
|
const existingPositions = await vesuAdapter.getPositions(this.config);
|
|
171
|
-
const collateralisation = await vesuAdapter.getCollateralization(
|
|
265
|
+
const collateralisation = await vesuAdapter.getCollateralization(
|
|
266
|
+
this.config,
|
|
267
|
+
);
|
|
172
268
|
const existingCollateralInfo = existingPositions[0];
|
|
173
269
|
const existingDebtInfo = existingPositions[1];
|
|
174
270
|
logger.debug(`${this.getTag()}::_getAvnuDepositSwapLegCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
175
271
|
existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
176
272
|
|
|
177
273
|
// - Prices as seen by Vesu contracts, ideal for HF math
|
|
178
|
-
// Price 1 is ok as fallback bcz that would relatively price the
|
|
274
|
+
// Price 1 is ok as fallback bcz that would relatively price the
|
|
179
275
|
// collateral and debt as equal.
|
|
180
|
-
const collateralPrice =
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
276
|
+
const collateralPrice =
|
|
277
|
+
collateralisation[0].usdValue > 0
|
|
278
|
+
? collateralisation[0].usdValue /
|
|
279
|
+
existingCollateralInfo.amount.toNumber()
|
|
280
|
+
: 1;
|
|
281
|
+
const debtPrice =
|
|
282
|
+
collateralisation[1].usdValue > 0
|
|
283
|
+
? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber()
|
|
284
|
+
: 1;
|
|
285
|
+
logger.debug(
|
|
286
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`,
|
|
287
|
+
);
|
|
184
288
|
|
|
185
289
|
const debtTokenInfo = vesuAdapter.config.debt;
|
|
186
290
|
let newDepositAmount = params.leg1DepositAmount;
|
|
187
|
-
const totalCollateral = existingCollateralInfo.amount.plus(
|
|
188
|
-
|
|
291
|
+
const totalCollateral = existingCollateralInfo.amount.plus(
|
|
292
|
+
params.leg1DepositAmount,
|
|
293
|
+
);
|
|
294
|
+
logger.verbose(
|
|
295
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall totalCollateral: ${totalCollateral}`,
|
|
296
|
+
);
|
|
189
297
|
const totalDebtAmount = new Web3Number(
|
|
190
|
-
totalCollateral
|
|
191
|
-
|
|
298
|
+
totalCollateral
|
|
299
|
+
.multipliedBy(collateralPrice)
|
|
300
|
+
.multipliedBy(legLTV)
|
|
301
|
+
.dividedBy(debtPrice)
|
|
302
|
+
.dividedBy(params.minHF)
|
|
303
|
+
.toString(),
|
|
304
|
+
debtTokenInfo.decimals,
|
|
192
305
|
);
|
|
193
306
|
let debtAmount = totalDebtAmount.minus(existingDebtInfo.amount);
|
|
194
|
-
logger.verbose(
|
|
195
|
-
|
|
307
|
+
logger.verbose(
|
|
308
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall totalDebtAmount: ${totalDebtAmount}, initial computed debt: ${debtAmount}`,
|
|
309
|
+
);
|
|
310
|
+
const maxBorrowable = await this.getMaxBorrowableAmountByVesuAdapter(
|
|
311
|
+
vesuAdapter,
|
|
312
|
+
false,
|
|
313
|
+
);
|
|
196
314
|
|
|
197
315
|
// if the debt amount is greater than 0 and the max borrowable amount is 0, skip
|
|
198
316
|
if (debtAmount.gt(0) && maxBorrowable.amount.eq(0)) {
|
|
199
|
-
logger.verbose(
|
|
317
|
+
logger.verbose(
|
|
318
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall maxBorrowable is 0, skipping`,
|
|
319
|
+
);
|
|
200
320
|
return undefined;
|
|
201
321
|
} else if (debtAmount.gt(0) && maxBorrowable.amount.gt(0)) {
|
|
202
322
|
debtAmount = maxBorrowable.amount.minimum(debtAmount);
|
|
@@ -207,58 +327,78 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
207
327
|
params.minHF,
|
|
208
328
|
legLTV,
|
|
209
329
|
collateralPrice,
|
|
210
|
-
this.asset()
|
|
330
|
+
this.asset(),
|
|
331
|
+
);
|
|
332
|
+
newDepositAmount = totalCollateralRequired.minus(
|
|
333
|
+
existingCollateralInfo.amount,
|
|
211
334
|
);
|
|
212
|
-
newDepositAmount = totalCollateralRequired.minus(existingCollateralInfo.amount);
|
|
213
335
|
if (newDepositAmount.lt(0)) {
|
|
214
|
-
throw new Error(
|
|
336
|
+
throw new Error(
|
|
337
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall newDepositAmount is less than 0, newDepositAmount: ${newDepositAmount}, totalCollateralRequired: ${totalCollateralRequired}, existingCollateralInfo.amount: ${existingCollateralInfo.amount}`,
|
|
338
|
+
);
|
|
215
339
|
}
|
|
216
340
|
if (newDebtUSDValue.toNumber() < 100) {
|
|
217
341
|
// too less debt, skip
|
|
218
|
-
logger.verbose(
|
|
342
|
+
logger.verbose(
|
|
343
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall newDebtUSDValue is less than 100, skipping`,
|
|
344
|
+
);
|
|
219
345
|
return undefined;
|
|
220
346
|
}
|
|
221
347
|
}
|
|
222
348
|
|
|
223
|
-
logger.verbose(
|
|
349
|
+
logger.verbose(
|
|
350
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall debtAmount: ${debtAmount}`,
|
|
351
|
+
);
|
|
224
352
|
if (debtAmount.lt(0)) {
|
|
225
|
-
// this is to unwind the position to optimal HF.
|
|
353
|
+
// this is to unwind the position to optimal HF.
|
|
226
354
|
const lstDEXPrice = await this.getLSTDexPrice();
|
|
227
355
|
const debtAmountInLST = debtAmount.abs().dividedBy(lstDEXPrice);
|
|
228
356
|
const calls = await this.getVesuMultiplyCall({
|
|
229
357
|
isDeposit: false,
|
|
230
358
|
leg1DepositAmount: debtAmountInLST,
|
|
231
|
-
poolId: vesuAdapter.config.poolId
|
|
232
|
-
})
|
|
233
|
-
assert(
|
|
359
|
+
poolId: vesuAdapter.config.poolId,
|
|
360
|
+
});
|
|
361
|
+
assert(
|
|
362
|
+
calls.length == 1,
|
|
363
|
+
`Expected 1 call for unwind, got ${calls.length}`,
|
|
364
|
+
);
|
|
234
365
|
return calls[0];
|
|
235
366
|
}
|
|
236
367
|
// console.log(`debtAmount`, debtAmount.toWei(), params.leg1DepositAmount.toWei());
|
|
237
368
|
const STEP0 = UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1;
|
|
238
369
|
const manage0Info = this.getProofs<ApproveCallParams>(STEP0);
|
|
239
370
|
const manageCall0 = manage0Info.callConstructor({
|
|
240
|
-
amount: newDepositAmount
|
|
371
|
+
amount: newDepositAmount,
|
|
241
372
|
});
|
|
242
|
-
const STEP1 = getVesuLegId(
|
|
373
|
+
const STEP1 = getVesuLegId(
|
|
374
|
+
UNIVERSAL_MANAGE_IDS.VESU_LEG1,
|
|
375
|
+
vesuAdapter.config.debt.symbol,
|
|
376
|
+
vesuAdapter.config.poolId.toString(),
|
|
377
|
+
);
|
|
243
378
|
const manage1Info = this.getProofs<VesuModifyPositionCallParams>(STEP1);
|
|
244
|
-
const manageCall1 = manage1Info.callConstructor(
|
|
379
|
+
const manageCall1 = manage1Info.callConstructor(
|
|
380
|
+
VesuAdapter.getDefaultModifyPositionCallParams({
|
|
245
381
|
collateralAmount: newDepositAmount,
|
|
246
382
|
isAddCollateral: params.isDeposit,
|
|
247
383
|
debtAmount: debtAmount,
|
|
248
|
-
isBorrow: params.isDeposit
|
|
249
|
-
|
|
384
|
+
isBorrow: params.isDeposit,
|
|
385
|
+
}),
|
|
386
|
+
);
|
|
250
387
|
|
|
251
388
|
// console.log(`manageCall1`, manageCall1.call, debtAmount.toWei(), newDepositAmount.toWei());
|
|
252
|
-
|
|
389
|
+
|
|
253
390
|
const proofIds: string[] = [STEP0, STEP1];
|
|
254
391
|
const manageCalls: ManageCall[] = [manageCall0, manageCall1];
|
|
255
392
|
|
|
256
393
|
// approve and swap to LST using avnu
|
|
257
394
|
if (debtAmount.gt(0)) {
|
|
258
|
-
const STEP2 = getAvnuManageIDs(
|
|
395
|
+
const STEP2 = getAvnuManageIDs(
|
|
396
|
+
LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_APPROVE_DEPOSIT,
|
|
397
|
+
vesuAdapter.config.debt.symbol,
|
|
398
|
+
);
|
|
259
399
|
const manage2Info = this.getProofs<ApproveCallParams>(STEP2);
|
|
260
400
|
const manageCall2 = manage2Info.callConstructor({
|
|
261
|
-
amount: debtAmount
|
|
401
|
+
amount: debtAmount,
|
|
262
402
|
});
|
|
263
403
|
|
|
264
404
|
const debtTokenInfo = vesuAdapter.config.debt;
|
|
@@ -268,51 +408,75 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
268
408
|
debtTokenInfo.address.address,
|
|
269
409
|
lstTokenInfo.address.address,
|
|
270
410
|
debtAmount.toWei(),
|
|
271
|
-
this.metadata.additionalInfo.vaultAllocator.address
|
|
411
|
+
this.metadata.additionalInfo.vaultAllocator.address,
|
|
272
412
|
);
|
|
273
413
|
const minAmount = await this._getMinOutputAmountLSTBuy(debtAmount);
|
|
274
|
-
const minAmountWei =
|
|
275
|
-
logger.verbose(
|
|
414
|
+
const minAmountWei = minAmount.toWei();
|
|
415
|
+
logger.verbose(
|
|
416
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall minAmount: ${minAmount}`,
|
|
417
|
+
);
|
|
276
418
|
const swapInfo = await avnuModule.getSwapInfo(
|
|
277
|
-
quote,
|
|
278
|
-
this.metadata.additionalInfo.vaultAllocator.address,
|
|
279
|
-
0,
|
|
419
|
+
quote,
|
|
420
|
+
this.metadata.additionalInfo.vaultAllocator.address,
|
|
421
|
+
0,
|
|
280
422
|
this.address.address,
|
|
281
|
-
minAmountWei
|
|
423
|
+
minAmountWei,
|
|
424
|
+
);
|
|
425
|
+
logger.verbose(
|
|
426
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall swapInfo: ${JSON.stringify(swapInfo)}`,
|
|
427
|
+
);
|
|
428
|
+
const STEP3 = getAvnuManageIDs(
|
|
429
|
+
LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_DEPOSIT,
|
|
430
|
+
vesuAdapter.config.debt.symbol,
|
|
282
431
|
);
|
|
283
|
-
logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall swapInfo: ${JSON.stringify(swapInfo)}`);
|
|
284
|
-
const STEP3 = getAvnuManageIDs(LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_DEPOSIT, vesuAdapter.config.debt.symbol);
|
|
285
432
|
const manage3Info = this.getProofs<AvnuSwapCallParams>(STEP3);
|
|
286
433
|
const manageCall3 = manage3Info.callConstructor({
|
|
287
|
-
props: swapInfo
|
|
434
|
+
props: swapInfo,
|
|
288
435
|
});
|
|
289
436
|
proofIds.push(STEP2);
|
|
290
437
|
proofIds.push(STEP3);
|
|
291
438
|
manageCalls.push(manageCall2, manageCall3);
|
|
292
439
|
|
|
293
|
-
|
|
294
440
|
// if the created debt, when added is collateral will put the total HF above min, but below (target + 0.05),
|
|
295
|
-
// then lets close the looping cycle by adding this as collateral.
|
|
441
|
+
// then lets close the looping cycle by adding this as collateral.
|
|
296
442
|
const newCollateral = minAmount.plus(totalCollateral);
|
|
297
|
-
const newHF = newCollateral
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
443
|
+
const newHF = newCollateral
|
|
444
|
+
.multipliedBy(collateralPrice)
|
|
445
|
+
.multipliedBy(legLTV)
|
|
446
|
+
.dividedBy(totalDebtAmount)
|
|
447
|
+
.dividedBy(debtPrice)
|
|
448
|
+
.toNumber();
|
|
449
|
+
logger.verbose(
|
|
450
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall newHF: ${newHF}`,
|
|
451
|
+
);
|
|
452
|
+
if (
|
|
453
|
+
newHF > this.metadata.additionalInfo.minHealthFactor &&
|
|
454
|
+
newHF < this.metadata.additionalInfo.targetHealthFactor + 0.05
|
|
455
|
+
) {
|
|
456
|
+
logger.verbose(
|
|
457
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall newHF is above min and below target + 0.05, adding collateral on vesu`,
|
|
458
|
+
);
|
|
301
459
|
// approve and add collateral on vesu (modify position)
|
|
302
460
|
const STEP4 = UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1;
|
|
303
461
|
const manage4Info = this.getProofs<ApproveCallParams>(STEP4);
|
|
304
462
|
const manageCall4 = manage4Info.callConstructor({
|
|
305
|
-
amount: minAmount
|
|
463
|
+
amount: minAmount,
|
|
306
464
|
});
|
|
307
|
-
|
|
308
|
-
const STEP5 = getVesuLegId(
|
|
465
|
+
|
|
466
|
+
const STEP5 = getVesuLegId(
|
|
467
|
+
UNIVERSAL_MANAGE_IDS.VESU_LEG1,
|
|
468
|
+
vesuAdapter.config.debt.symbol,
|
|
469
|
+
vesuAdapter.config.poolId.toString(),
|
|
470
|
+
);
|
|
309
471
|
const manage5Info = this.getProofs<VesuModifyPositionCallParams>(STEP5);
|
|
310
|
-
const manageCall5 = manage5Info.callConstructor(
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
472
|
+
const manageCall5 = manage5Info.callConstructor(
|
|
473
|
+
VesuAdapter.getDefaultModifyPositionCallParams({
|
|
474
|
+
collateralAmount: minAmount,
|
|
475
|
+
isAddCollateral: true,
|
|
476
|
+
debtAmount: Web3Number.fromWei("0", this.asset().decimals),
|
|
477
|
+
isBorrow: params.isDeposit,
|
|
478
|
+
}),
|
|
479
|
+
);
|
|
316
480
|
proofIds.push(STEP4, STEP5);
|
|
317
481
|
manageCalls.push(manageCall4, manageCall5);
|
|
318
482
|
}
|
|
@@ -322,53 +486,78 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
322
486
|
return manageCall;
|
|
323
487
|
}
|
|
324
488
|
|
|
325
|
-
// todo unwind or not deposit when the yield is bad.
|
|
489
|
+
// todo unwind or not deposit when the yield is bad.
|
|
326
490
|
|
|
327
|
-
async getLSTMultiplierRebalanceCall(): Promise<{
|
|
491
|
+
async getLSTMultiplierRebalanceCall(): Promise<{
|
|
492
|
+
shouldRebalance: boolean;
|
|
493
|
+
manageCalls: { vesuAdapter: VesuAdapter; manageCall: Call }[];
|
|
494
|
+
}> {
|
|
328
495
|
let shouldRebalance = false;
|
|
329
|
-
const calls: {vesuAdapter: VesuAdapter
|
|
496
|
+
const calls: { vesuAdapter: VesuAdapter; manageCall: Call }[] = [];
|
|
330
497
|
// todo undo
|
|
331
|
-
const allVesuAdapters = this.getVesuAdapters().filter(
|
|
498
|
+
const allVesuAdapters = this.getVesuAdapters().filter(
|
|
499
|
+
(vesuAdapter) => vesuAdapter.config.debt.symbol === "LBTC",
|
|
500
|
+
);
|
|
332
501
|
for (const vesuAdapter of allVesuAdapters) {
|
|
333
502
|
const call = await this._getLSTMultiplierRebalanceCall(vesuAdapter);
|
|
334
503
|
if (call.shouldRebalance && call.manageCall) {
|
|
335
504
|
shouldRebalance = true;
|
|
336
|
-
calls.push({vesuAdapter, manageCall: call.manageCall});
|
|
505
|
+
calls.push({ vesuAdapter, manageCall: call.manageCall });
|
|
337
506
|
}
|
|
338
507
|
}
|
|
339
508
|
return { shouldRebalance, manageCalls: calls };
|
|
340
509
|
}
|
|
341
510
|
|
|
342
|
-
async _getLSTMultiplierRebalanceCall(
|
|
511
|
+
async _getLSTMultiplierRebalanceCall(
|
|
512
|
+
vesuAdapter: VesuAdapter,
|
|
513
|
+
): Promise<{ shouldRebalance: boolean; manageCall: Call | undefined }> {
|
|
343
514
|
const positions = await vesuAdapter.getPositions(this.config);
|
|
344
|
-
assert(
|
|
515
|
+
assert(
|
|
516
|
+
positions.length == 2,
|
|
517
|
+
"Rebalance call is only supported for 2 positions",
|
|
518
|
+
);
|
|
345
519
|
const existingCollateralInfo = positions[0];
|
|
346
520
|
const existingDebtInfo = positions[1];
|
|
347
521
|
const unusedBalance = await this.getUnusedBalance();
|
|
348
522
|
const healthFactor = await vesuAdapter.getHealthFactor();
|
|
349
523
|
|
|
350
|
-
const collateralisation = await vesuAdapter.getCollateralization(
|
|
524
|
+
const collateralisation = await vesuAdapter.getCollateralization(
|
|
525
|
+
this.config,
|
|
526
|
+
);
|
|
351
527
|
logger.debug(`${this.getTag()}::getVesuMultiplyCall::${vesuAdapter.config.debt.symbol} existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
352
528
|
existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
353
529
|
|
|
354
530
|
// - Prices as seen by Vesu contracts, ideal for HF math
|
|
355
|
-
// Price 1 is ok as fallback bcz that would relatively price the
|
|
531
|
+
// Price 1 is ok as fallback bcz that would relatively price the
|
|
356
532
|
// collateral and debt as equal.
|
|
357
|
-
const collateralPrice =
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
533
|
+
const collateralPrice =
|
|
534
|
+
collateralisation[0].usdValue > 0
|
|
535
|
+
? collateralisation[0].usdValue /
|
|
536
|
+
existingCollateralInfo.amount.toNumber()
|
|
537
|
+
: 1;
|
|
538
|
+
const debtPrice =
|
|
539
|
+
collateralisation[1].usdValue > 0
|
|
540
|
+
? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber()
|
|
541
|
+
: 1;
|
|
542
|
+
logger.debug(
|
|
543
|
+
`${this.getTag()}::getVesuMultiplyCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`,
|
|
544
|
+
);
|
|
545
|
+
logger.debug(
|
|
546
|
+
`${this.getTag()}::getVesuMultiplyCall healthFactor: ${healthFactor}`,
|
|
547
|
+
);
|
|
361
548
|
|
|
362
|
-
const isHFTooLow =
|
|
363
|
-
|
|
549
|
+
const isHFTooLow =
|
|
550
|
+
healthFactor < this.metadata.additionalInfo.minHealthFactor;
|
|
551
|
+
const isHFTooHigh =
|
|
552
|
+
healthFactor > this.metadata.additionalInfo.targetHealthFactor + 0.05;
|
|
364
553
|
if (isHFTooLow || isHFTooHigh || 1) {
|
|
365
|
-
// use unused collateral to target more.
|
|
554
|
+
// use unused collateral to target more.
|
|
366
555
|
const manageCall = await this._getAvnuDepositSwapLegCall({
|
|
367
556
|
isDeposit: true,
|
|
368
557
|
leg1DepositAmount: unusedBalance.amount,
|
|
369
|
-
minHF: 1.02, // todo, shouldnt use this 1.02 HF, if there isn;t more looping left.
|
|
370
|
-
vesuAdapter
|
|
371
|
-
})
|
|
558
|
+
minHF: 1.02, // todo, shouldnt use this 1.02 HF, if there isn;t more looping left.
|
|
559
|
+
vesuAdapter,
|
|
560
|
+
});
|
|
372
561
|
return { shouldRebalance: true, manageCall };
|
|
373
562
|
} else {
|
|
374
563
|
// do nothing
|
|
@@ -376,42 +565,61 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
376
565
|
}
|
|
377
566
|
}
|
|
378
567
|
|
|
379
|
-
protected async getVesuAUM(
|
|
568
|
+
protected async getVesuAUM(
|
|
569
|
+
adapter: VesuAdapter,
|
|
570
|
+
priceType: LSTPriceType = LSTPriceType.AVNU_PRICE,
|
|
571
|
+
) {
|
|
380
572
|
const legAUM = await adapter.getPositions(this.config);
|
|
381
573
|
const underlying = this.asset();
|
|
382
574
|
// assert its an LST of Endur
|
|
383
|
-
assert(
|
|
575
|
+
assert(
|
|
576
|
+
underlying.symbol.startsWith("x"),
|
|
577
|
+
"Underlying is not an LST of Endur",
|
|
578
|
+
);
|
|
384
579
|
|
|
385
580
|
let vesuAum = Web3Number.fromWei("0", underlying.decimals);
|
|
386
|
-
|
|
581
|
+
|
|
387
582
|
// Get the price based on the price type
|
|
388
583
|
let tokenUnderlyingPrice: number;
|
|
389
584
|
if (priceType === LSTPriceType.ENDUR_PRICE) {
|
|
390
585
|
tokenUnderlyingPrice = await this.getLSTExchangeRate();
|
|
391
586
|
if (tokenUnderlyingPrice === 0) {
|
|
392
|
-
throw new Error(
|
|
587
|
+
throw new Error(
|
|
588
|
+
`${this.getTag()}::getVesuAUM: tokenUnderlyingPrice (Endur) is 0`,
|
|
589
|
+
);
|
|
393
590
|
}
|
|
394
591
|
|
|
395
592
|
// For Endur price, also check against Avnu rate to ensure they're within 2%
|
|
396
593
|
const avnuRate = await this.getLSTAvnuRate();
|
|
397
594
|
if (avnuRate === 0) {
|
|
398
|
-
throw new Error(
|
|
595
|
+
throw new Error(
|
|
596
|
+
`${this.getTag()}::getVesuAUM: tokenUnderlyingPrice (Avnu) is 0`,
|
|
597
|
+
);
|
|
399
598
|
}
|
|
400
599
|
|
|
401
|
-
const diff =
|
|
600
|
+
const diff =
|
|
601
|
+
Math.abs(tokenUnderlyingPrice - avnuRate) / tokenUnderlyingPrice;
|
|
402
602
|
if (diff > 0.02) {
|
|
403
|
-
throw new Error(
|
|
603
|
+
throw new Error(
|
|
604
|
+
`${this.getTag()}::getVesuAUM: Endur and Avnu prices differ by more than 2% (Endur: ${tokenUnderlyingPrice}, Avnu: ${avnuRate})`,
|
|
605
|
+
);
|
|
404
606
|
}
|
|
405
607
|
|
|
406
|
-
logger.verbose(
|
|
608
|
+
logger.verbose(
|
|
609
|
+
`${this.getTag()} tokenUnderlyingPrice (Endur): ${tokenUnderlyingPrice}, avnuRate: ${avnuRate}, diff: ${diff}`,
|
|
610
|
+
);
|
|
407
611
|
} else {
|
|
408
612
|
tokenUnderlyingPrice = await this.getLSTAvnuRate();
|
|
409
613
|
if (tokenUnderlyingPrice === 0) {
|
|
410
|
-
throw new Error(
|
|
614
|
+
throw new Error(
|
|
615
|
+
`${this.getTag()}::getVesuAUM: tokenUnderlyingPrice (Avnu) is 0`,
|
|
616
|
+
);
|
|
411
617
|
}
|
|
412
|
-
logger.verbose(
|
|
618
|
+
logger.verbose(
|
|
619
|
+
`${this.getTag()} tokenUnderlyingPrice (Avnu): ${tokenUnderlyingPrice}`,
|
|
620
|
+
);
|
|
413
621
|
}
|
|
414
|
-
|
|
622
|
+
|
|
415
623
|
// handle collateral
|
|
416
624
|
if (legAUM[0].token.address.eq(underlying.address)) {
|
|
417
625
|
vesuAum = vesuAum.plus(legAUM[0].amount);
|
|
@@ -424,71 +632,82 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
424
632
|
vesuAum = vesuAum.minus(legAUM[1].amount);
|
|
425
633
|
} else {
|
|
426
634
|
vesuAum = vesuAum.minus(legAUM[1].amount.dividedBy(tokenUnderlyingPrice));
|
|
427
|
-
}
|
|
635
|
+
}
|
|
428
636
|
|
|
429
|
-
const priceTypeLabel =
|
|
430
|
-
|
|
637
|
+
const priceTypeLabel =
|
|
638
|
+
priceType === LSTPriceType.ENDUR_PRICE ? "Endur Price" : "Avnu Price";
|
|
639
|
+
logger.verbose(
|
|
640
|
+
`${this.getTag()} Vesu AUM (${priceTypeLabel}): ${vesuAum}, legCollateral: ${legAUM[0].amount.toNumber()}, legDebt: ${legAUM[1].amount.toNumber()}`,
|
|
641
|
+
);
|
|
431
642
|
return vesuAum;
|
|
432
643
|
}
|
|
433
644
|
|
|
434
|
-
async getTVLUnrealized(): Promise<{
|
|
645
|
+
async getTVLUnrealized(): Promise<{
|
|
646
|
+
net: SingleTokenInfo;
|
|
647
|
+
prevAum: Web3Number;
|
|
648
|
+
splits: { id: string; aum: Web3Number }[];
|
|
649
|
+
}> {
|
|
435
650
|
return await this.getAUM(true);
|
|
436
651
|
}
|
|
437
652
|
|
|
438
653
|
async getUserUnrealizedGains(user: ContractAddr) {
|
|
439
654
|
// Get total TVL (realized)
|
|
440
655
|
const tvl = await this.getTVL();
|
|
441
|
-
|
|
656
|
+
|
|
442
657
|
// Get unrealized TVL (using Endur prices)
|
|
443
658
|
const unrealizedTVL = await this.getTVLUnrealized();
|
|
444
|
-
|
|
659
|
+
|
|
445
660
|
// Calculate the difference between unrealized and realized TVL
|
|
446
661
|
const unrealizedDiff = unrealizedTVL.net.amount.minus(tvl.amount);
|
|
447
|
-
|
|
662
|
+
|
|
448
663
|
// Get user's TVL
|
|
449
664
|
const userTVL = await this.getUserTVL(user);
|
|
450
|
-
|
|
665
|
+
|
|
451
666
|
// Calculate user's share of the total TVL
|
|
452
667
|
const userShare = userTVL.amount.dividedBy(tvl.amount);
|
|
453
|
-
|
|
668
|
+
|
|
454
669
|
// Calculate user's unrealized gains (in token amount)
|
|
455
670
|
const unrealizedGains = unrealizedDiff.multipliedBy(userShare);
|
|
456
|
-
|
|
671
|
+
|
|
457
672
|
return {
|
|
458
673
|
unrealizedGains,
|
|
459
674
|
userShare: userShare.toNumber(),
|
|
460
|
-
tokenInfo: this.asset()
|
|
675
|
+
tokenInfo: this.asset(),
|
|
461
676
|
};
|
|
462
677
|
}
|
|
463
678
|
|
|
464
679
|
//
|
|
465
680
|
private async _getMinOutputAmountLSTBuy(amountInUnderlying: Web3Number) {
|
|
466
681
|
const lstTruePrice = await this.getLSTExchangeRate();
|
|
467
|
-
// during buy, the purchase should always be <= true LST price.
|
|
468
|
-
const minOutputAmount = amountInUnderlying
|
|
682
|
+
// during buy, the purchase should always be <= true LST price.
|
|
683
|
+
const minOutputAmount = amountInUnderlying
|
|
684
|
+
.dividedBy(lstTruePrice)
|
|
685
|
+
.multipliedBy(0.99979); // minus 0.021% to account for avnu fees
|
|
469
686
|
return new Web3Number(minOutputAmount.toString(), this.asset().decimals);
|
|
470
687
|
}
|
|
471
688
|
|
|
472
689
|
private async _getMinOutputAmountLSTSell(amountInLST: Web3Number) {
|
|
473
690
|
const lstTruePrice = await this.getLSTExchangeRate();
|
|
474
|
-
// during sell, the purchase should always be > 0.995 * true LST price.
|
|
475
|
-
const minOutputAmount = amountInLST
|
|
691
|
+
// during sell, the purchase should always be > 0.995 * true LST price.
|
|
692
|
+
const minOutputAmount = amountInLST
|
|
693
|
+
.multipliedBy(lstTruePrice)
|
|
694
|
+
.multipliedBy(0.995);
|
|
476
695
|
return minOutputAmount;
|
|
477
696
|
}
|
|
478
697
|
|
|
479
698
|
// todo add a function to findout max borrowable amount without fucking yield
|
|
480
|
-
// if the current net yield < LST yield, add a function to calculate how much to unwind.
|
|
699
|
+
// if the current net yield < LST yield, add a function to calculate how much to unwind.
|
|
481
700
|
|
|
482
701
|
/**
|
|
483
702
|
* Uses vesu's multiple call to create leverage on LST
|
|
484
703
|
* Deposit amount is in LST
|
|
485
|
-
* @param params
|
|
704
|
+
* @param params
|
|
486
705
|
*/
|
|
487
706
|
async getVesuMultiplyCall(params: {
|
|
488
|
-
isDeposit: boolean
|
|
489
|
-
leg1DepositAmount: Web3Number
|
|
490
|
-
maxEkuboPriceImpact?: number
|
|
491
|
-
poolId: ContractAddr
|
|
707
|
+
isDeposit: boolean;
|
|
708
|
+
leg1DepositAmount: Web3Number;
|
|
709
|
+
maxEkuboPriceImpact?: number;
|
|
710
|
+
poolId: ContractAddr;
|
|
492
711
|
}) {
|
|
493
712
|
const maxEkuboPriceImpact = params.maxEkuboPriceImpact || 0.01;
|
|
494
713
|
const vesuAdapter1 = this.getVesuSameTokenAdapter(params.poolId);
|
|
@@ -496,10 +715,12 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
496
715
|
logger.verbose(`${this.getTag()}::getVesuMultiplyCall legLTV: ${legLTV}`);
|
|
497
716
|
|
|
498
717
|
if (!params.isDeposit) {
|
|
499
|
-
// try using unused balance to unwind.
|
|
500
|
-
// no need to unwind.
|
|
718
|
+
// try using unused balance to unwind.
|
|
719
|
+
// no need to unwind.
|
|
501
720
|
const unusedBalance = await this.getUnusedBalance();
|
|
502
|
-
logger.verbose(
|
|
721
|
+
logger.verbose(
|
|
722
|
+
`${this.getTag()}::getVesuMultiplyCall unusedBalance: ${unusedBalance.amount.toString()}, required: ${params.leg1DepositAmount.toString()}`,
|
|
723
|
+
);
|
|
503
724
|
// undo
|
|
504
725
|
// if (unusedBalance.amount.gte(params.leg1DepositAmount)) {
|
|
505
726
|
// return [];
|
|
@@ -508,51 +729,85 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
508
729
|
}
|
|
509
730
|
|
|
510
731
|
const existingPositions = await vesuAdapter1.getPositions(this.config);
|
|
511
|
-
const collateralisation = await vesuAdapter1.getCollateralization(
|
|
732
|
+
const collateralisation = await vesuAdapter1.getCollateralization(
|
|
733
|
+
this.config,
|
|
734
|
+
);
|
|
512
735
|
const existingCollateralInfo = existingPositions[0];
|
|
513
736
|
const existingDebtInfo = existingPositions[1];
|
|
514
737
|
logger.debug(`${this.getTag()}::getVesuMultiplyCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
515
738
|
existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
516
739
|
|
|
517
740
|
// - Prices as seen by Vesu contracts, ideal for HF math
|
|
518
|
-
// Price 1 is ok as fallback bcz that would relatively price the
|
|
741
|
+
// Price 1 is ok as fallback bcz that would relatively price the
|
|
519
742
|
// collateral and debt as equal.
|
|
520
|
-
const collateralPrice =
|
|
521
|
-
|
|
522
|
-
|
|
743
|
+
const collateralPrice =
|
|
744
|
+
collateralisation[0].usdValue > 0
|
|
745
|
+
? collateralisation[0].usdValue /
|
|
746
|
+
existingCollateralInfo.amount.toNumber()
|
|
747
|
+
: 1;
|
|
748
|
+
const debtPrice =
|
|
749
|
+
collateralisation[1].usdValue > 0
|
|
750
|
+
? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber()
|
|
751
|
+
: 1;
|
|
752
|
+
logger.debug(
|
|
753
|
+
`${this.getTag()}::getVesuMultiplyCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`,
|
|
754
|
+
);
|
|
523
755
|
|
|
524
756
|
// - Prices as seen by actual swap price
|
|
525
757
|
const dexPrice = await this.getLSTDexPrice();
|
|
526
758
|
|
|
527
759
|
// compute optimal amount of collateral and debt post addition/removal
|
|
528
760
|
// target hf = collateral * collateralPrice * ltv / debt * debtPrice
|
|
529
|
-
// assuming X to be the usd amount of debt borrowed or repaied (negative).
|
|
761
|
+
// assuming X to be the usd amount of debt borrowed or repaied (negative).
|
|
530
762
|
// target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv / (debt * debtPrice + X)
|
|
531
763
|
// => X * target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv - (debt * debtPrice * target hf)
|
|
532
764
|
// => X * (target hf - ltv)= ((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)
|
|
533
765
|
// => X = (((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)) / (target hf - ltv)
|
|
534
|
-
const addedCollateral = params.leg1DepositAmount
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
logger.verbose(
|
|
545
|
-
|
|
766
|
+
const addedCollateral = params.leg1DepositAmount.multipliedBy(
|
|
767
|
+
params.isDeposit ? 1 : -1,
|
|
768
|
+
);
|
|
769
|
+
logger.verbose(
|
|
770
|
+
`${this.getTag()}::getVesuMultiplyCall addedCollateral: ${addedCollateral}`,
|
|
771
|
+
);
|
|
772
|
+
const numeratorPart1 = existingCollateralInfo.amount
|
|
773
|
+
.plus(addedCollateral)
|
|
774
|
+
.multipliedBy(collateralPrice)
|
|
775
|
+
.multipliedBy(legLTV);
|
|
776
|
+
logger.verbose(
|
|
777
|
+
`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}`,
|
|
778
|
+
);
|
|
779
|
+
const numeratorPart2 = existingDebtInfo.amount
|
|
780
|
+
.multipliedBy(debtPrice)
|
|
781
|
+
.multipliedBy(this.metadata.additionalInfo.targetHealthFactor);
|
|
782
|
+
logger.verbose(
|
|
783
|
+
`${this.getTag()}::getVesuMultiplyCall numeratorPart2: ${numeratorPart2}`,
|
|
784
|
+
);
|
|
785
|
+
const denominatorPart =
|
|
786
|
+
this.metadata.additionalInfo.targetHealthFactor - legLTV / dexPrice;
|
|
787
|
+
logger.verbose(
|
|
788
|
+
`${this.getTag()}::getVesuMultiplyCall denominatorPart: ${denominatorPart}`,
|
|
789
|
+
);
|
|
790
|
+
const x_debt_usd = numeratorPart1
|
|
791
|
+
.minus(numeratorPart2)
|
|
792
|
+
.dividedBy(denominatorPart);
|
|
793
|
+
logger.verbose(
|
|
794
|
+
`${this.getTag()}::getVesuMultiplyCall x_debt_usd: ${x_debt_usd}`,
|
|
795
|
+
);
|
|
796
|
+
logger.debug(
|
|
797
|
+
`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}, numeratorPart2: ${numeratorPart2}, denominatorPart: ${denominatorPart}`,
|
|
798
|
+
);
|
|
546
799
|
|
|
547
800
|
// both in underlying
|
|
548
801
|
let debtAmount = x_debt_usd.dividedBy(debtPrice);
|
|
549
802
|
const marginAmount = addedCollateral;
|
|
550
|
-
logger.verbose(
|
|
803
|
+
logger.verbose(
|
|
804
|
+
`${this.getTag()}::getVesuMultiplyCall debtAmount: ${debtAmount}, marginAmount: ${marginAmount}`,
|
|
805
|
+
);
|
|
551
806
|
|
|
552
807
|
if (marginAmount.lt(0) && debtAmount.gt(0)) {
|
|
553
808
|
// if we want to withdraw, but debt can go high, its conflicting between
|
|
554
|
-
// increasing and reducing lever. and in this case, the HF will go high,
|
|
555
|
-
// which is ok.
|
|
809
|
+
// increasing and reducing lever. and in this case, the HF will go high,
|
|
810
|
+
// which is ok.
|
|
556
811
|
debtAmount = Web3Number.fromWei(0, this.asset().decimals);
|
|
557
812
|
}
|
|
558
813
|
|
|
@@ -569,38 +824,69 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
569
824
|
lstDexPriceInUnderlying: dexPrice,
|
|
570
825
|
isIncrease: debtAmount.greaterThan(0),
|
|
571
826
|
maxEkuboPriceImpact,
|
|
572
|
-
poolId: params.poolId
|
|
827
|
+
poolId: params.poolId,
|
|
573
828
|
});
|
|
574
829
|
}
|
|
575
830
|
|
|
576
831
|
getLSTUnderlyingTokenInfo() {
|
|
577
|
-
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
832
|
+
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
833
|
+
this.metadata.additionalInfo.defaultPoolId,
|
|
834
|
+
);
|
|
578
835
|
return vesuAdapter1.config.debt;
|
|
579
836
|
}
|
|
580
837
|
|
|
581
|
-
async getMaxBorrowableAmount(
|
|
838
|
+
async getMaxBorrowableAmount(
|
|
839
|
+
params: { isAPYComputation: boolean } = { isAPYComputation: false },
|
|
840
|
+
) {
|
|
582
841
|
const vesuAdapters = this.getVesuAdapters();
|
|
583
|
-
let netMaxBorrowableAmount = Web3Number.fromWei(
|
|
584
|
-
|
|
842
|
+
let netMaxBorrowableAmount = Web3Number.fromWei(
|
|
843
|
+
"0",
|
|
844
|
+
this.getLSTUnderlyingTokenInfo().decimals,
|
|
845
|
+
);
|
|
846
|
+
const maxBorrowables: {
|
|
847
|
+
amount: Web3Number;
|
|
848
|
+
dexSwappableAmount: Web3Number;
|
|
849
|
+
maxBorrowableAmount: Web3Number;
|
|
850
|
+
borrowableAsset: TokenInfo;
|
|
851
|
+
ltv: number;
|
|
852
|
+
}[] = [];
|
|
585
853
|
for (const vesuAdapter of vesuAdapters) {
|
|
586
|
-
const output = await this.getMaxBorrowableAmountByVesuAdapter(
|
|
854
|
+
const output = await this.getMaxBorrowableAmountByVesuAdapter(
|
|
855
|
+
vesuAdapter,
|
|
856
|
+
params.isAPYComputation,
|
|
857
|
+
);
|
|
587
858
|
const ltv = await vesuAdapter.getLTVConfig(this.config);
|
|
588
|
-
maxBorrowables.push({...output, ltv});
|
|
859
|
+
maxBorrowables.push({ ...output, ltv });
|
|
589
860
|
}
|
|
590
861
|
maxBorrowables.sort((a, b) => b.amount.toNumber() - a.amount.toNumber());
|
|
591
|
-
netMaxBorrowableAmount = maxBorrowables.reduce(
|
|
592
|
-
|
|
862
|
+
netMaxBorrowableAmount = maxBorrowables.reduce(
|
|
863
|
+
(acc, curr) => acc.plus(curr.amount),
|
|
864
|
+
Web3Number.fromWei("0", this.getLSTUnderlyingTokenInfo().decimals),
|
|
865
|
+
);
|
|
866
|
+
return { netMaxBorrowableAmount, maxBorrowables };
|
|
593
867
|
}
|
|
594
868
|
|
|
595
|
-
// recursively, using binary search computes max swappable.
|
|
869
|
+
// recursively, using binary search computes max swappable.
|
|
596
870
|
// @dev assumes 1 token of from == 1 token of to
|
|
597
|
-
async getMaxSwappableWithMaxSlippage(
|
|
871
|
+
async getMaxSwappableWithMaxSlippage(
|
|
872
|
+
fromToken: TokenInfo,
|
|
873
|
+
toToken: TokenInfo,
|
|
874
|
+
maxSlippage: number,
|
|
875
|
+
maxAmount: Web3Number,
|
|
876
|
+
) {
|
|
598
877
|
const output = await findMaxInputWithSlippage({
|
|
599
878
|
apiGetOutput: async (inputAmount: number): Promise<number> => {
|
|
600
879
|
const ekuboQuoter = new EkuboQuoter(this.config);
|
|
601
|
-
await new Promise(resolve => setTimeout(resolve, 1000)); // artificial delay, to avoid rate limit
|
|
602
|
-
const quote = await ekuboQuoter.getQuote(
|
|
603
|
-
|
|
880
|
+
await new Promise((resolve) => setTimeout(resolve, 1000)); // artificial delay, to avoid rate limit
|
|
881
|
+
const quote = await ekuboQuoter.getQuote(
|
|
882
|
+
fromToken.address.address,
|
|
883
|
+
toToken.address.address,
|
|
884
|
+
new Web3Number(inputAmount.toFixed(9), fromToken.decimals),
|
|
885
|
+
);
|
|
886
|
+
return Web3Number.fromWei(
|
|
887
|
+
quote.total_calculated.toString(),
|
|
888
|
+
toToken.decimals,
|
|
889
|
+
).toNumber();
|
|
604
890
|
},
|
|
605
891
|
maxInput: maxAmount.toNumber(),
|
|
606
892
|
maxSlippagePercent: maxSlippage,
|
|
@@ -610,48 +896,103 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
610
896
|
return new Web3Number(output.optimalInput, fromToken.decimals);
|
|
611
897
|
}
|
|
612
898
|
|
|
613
|
-
async getMaxBorrowableAmountByVesuAdapter(
|
|
614
|
-
|
|
899
|
+
async getMaxBorrowableAmountByVesuAdapter(
|
|
900
|
+
vesuAdapter: VesuAdapter,
|
|
901
|
+
isAPYComputation: boolean,
|
|
902
|
+
) {
|
|
903
|
+
const lstAPY = await this.getLSTAPR(
|
|
904
|
+
this.getLSTUnderlyingTokenInfo().address,
|
|
905
|
+
);
|
|
615
906
|
const maxInterestRate = lstAPY * 0.8;
|
|
616
|
-
const {maxDebtToHave: maxBorrowableAmount, currentDebt} =
|
|
907
|
+
const { maxDebtToHave: maxBorrowableAmount, currentDebt } =
|
|
908
|
+
await vesuAdapter.getMaxBorrowableByInterestRate(
|
|
909
|
+
this.config,
|
|
910
|
+
vesuAdapter.config.debt,
|
|
911
|
+
maxInterestRate,
|
|
912
|
+
);
|
|
617
913
|
const debtCap = await vesuAdapter.getDebtCap(this.config);
|
|
618
914
|
|
|
619
915
|
if (currentDebt.gte(debtCap)) {
|
|
620
|
-
return {
|
|
916
|
+
return {
|
|
917
|
+
amount: Web3Number.fromWei("0", vesuAdapter.config.debt.decimals),
|
|
918
|
+
dexSwappableAmount: Web3Number.fromWei(
|
|
919
|
+
"0",
|
|
920
|
+
vesuAdapter.config.debt.decimals,
|
|
921
|
+
),
|
|
922
|
+
maxBorrowableAmount: Web3Number.fromWei(
|
|
923
|
+
"0",
|
|
924
|
+
vesuAdapter.config.debt.decimals,
|
|
925
|
+
),
|
|
926
|
+
borrowableAsset: vesuAdapter.config.debt,
|
|
927
|
+
};
|
|
621
928
|
}
|
|
622
929
|
|
|
623
930
|
const availableToBorrow = debtCap.minus(currentDebt);
|
|
624
931
|
|
|
625
|
-
const maxBorrowable = maxBorrowableAmount
|
|
932
|
+
const maxBorrowable = maxBorrowableAmount
|
|
933
|
+
.minimum(availableToBorrow)
|
|
934
|
+
.multipliedBy(0.999);
|
|
626
935
|
// Dont compute precise max swappable for APY computation
|
|
627
|
-
if (
|
|
628
|
-
|
|
936
|
+
if (
|
|
937
|
+
vesuAdapter.config.debt.address.eq(
|
|
938
|
+
this.getLSTUnderlyingTokenInfo().address,
|
|
939
|
+
) ||
|
|
940
|
+
isAPYComputation
|
|
941
|
+
) {
|
|
942
|
+
return {
|
|
943
|
+
amount: maxBorrowable,
|
|
944
|
+
dexSwappableAmount: maxBorrowable,
|
|
945
|
+
maxBorrowableAmount: maxBorrowable,
|
|
946
|
+
borrowableAsset: vesuAdapter.config.debt,
|
|
947
|
+
};
|
|
629
948
|
}
|
|
630
949
|
// Want < 0.02% slippage
|
|
631
950
|
try {
|
|
632
|
-
const maxSwappable = await this.getMaxSwappableWithMaxSlippage(
|
|
633
|
-
|
|
951
|
+
const maxSwappable = await this.getMaxSwappableWithMaxSlippage(
|
|
952
|
+
vesuAdapter.config.debt,
|
|
953
|
+
this.getLSTUnderlyingTokenInfo(),
|
|
954
|
+
0.0002,
|
|
955
|
+
maxBorrowable,
|
|
956
|
+
);
|
|
957
|
+
return {
|
|
958
|
+
amount: maxBorrowable.minimum(maxSwappable),
|
|
959
|
+
dexSwappableAmount: maxSwappable,
|
|
960
|
+
maxBorrowableAmount: maxBorrowable,
|
|
961
|
+
borrowableAsset: vesuAdapter.config.debt,
|
|
962
|
+
};
|
|
634
963
|
} catch (error) {
|
|
635
964
|
logger.warn(`${this.getTag()}: Failed to get max swappable: ${error}`);
|
|
636
|
-
const maxSwappable = Web3Number.fromWei(
|
|
637
|
-
|
|
638
|
-
|
|
965
|
+
const maxSwappable = Web3Number.fromWei(
|
|
966
|
+
"0",
|
|
967
|
+
vesuAdapter.config.debt.decimals,
|
|
968
|
+
);
|
|
969
|
+
return {
|
|
970
|
+
amount: maxBorrowable.minimum(maxSwappable),
|
|
971
|
+
dexSwappableAmount: maxSwappable,
|
|
972
|
+
maxBorrowableAmount: maxBorrowable,
|
|
973
|
+
borrowableAsset: vesuAdapter.config.debt,
|
|
974
|
+
};
|
|
975
|
+
}
|
|
639
976
|
}
|
|
640
977
|
|
|
641
978
|
// todo how much to unwind to get back healthy APY zone again
|
|
642
979
|
// if net APY < LST APR + 0.5%, we need to unwind to get back to LST APR + 1% atleast or 0 vesu position
|
|
643
|
-
// For xSTRK, simply deposit in Vesu if looping is not viable
|
|
644
|
-
|
|
980
|
+
// For xSTRK, simply deposit in Vesu if looping is not viable
|
|
981
|
+
|
|
645
982
|
/**
|
|
646
983
|
* Gets LST APR for the strategy's underlying asset from Endur API
|
|
647
984
|
* @returns Promise<number> The LST APR (not divided by 1e18)
|
|
648
985
|
*/
|
|
649
986
|
async getLSTAPR(_address: ContractAddr): Promise<number> {
|
|
650
987
|
try {
|
|
651
|
-
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
652
|
-
|
|
988
|
+
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
989
|
+
this.metadata.additionalInfo.defaultPoolId,
|
|
990
|
+
);
|
|
991
|
+
const apr = await LSTAPRService.getLSTAPR(
|
|
992
|
+
vesuAdapter1.config.debt.address,
|
|
993
|
+
);
|
|
653
994
|
if (!apr) {
|
|
654
|
-
throw new Error(
|
|
995
|
+
throw new Error("Failed to get LST APR");
|
|
655
996
|
}
|
|
656
997
|
return apr;
|
|
657
998
|
} catch (error) {
|
|
@@ -661,119 +1002,170 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
661
1002
|
}
|
|
662
1003
|
|
|
663
1004
|
// todo undo this
|
|
664
|
-
async netAPY(): Promise<{
|
|
1005
|
+
async netAPY(): Promise<{
|
|
1006
|
+
net: number;
|
|
1007
|
+
splits: { apy: number; id: string }[];
|
|
1008
|
+
}> {
|
|
665
1009
|
const unusedBalance = await this.getUnusedBalance();
|
|
666
|
-
const maxNewDeposits = await this.maxNewDeposits({
|
|
667
|
-
|
|
1010
|
+
const maxNewDeposits = await this.maxNewDeposits({
|
|
1011
|
+
isAPYComputation: true,
|
|
1012
|
+
});
|
|
1013
|
+
const lstAPY = await this.getLSTAPR(
|
|
1014
|
+
this.getLSTUnderlyingTokenInfo().address,
|
|
1015
|
+
);
|
|
668
1016
|
|
|
669
1017
|
// if unused balance is > max servicable from loan, we are limited by the max borrowing we can do
|
|
670
1018
|
// we also allow accepting little higher deposits (1.5x) to have room for future looping when theres more liquidity or debt cap rises
|
|
671
1019
|
if (maxNewDeposits * 1.5 < unusedBalance.amount.toNumber()) {
|
|
672
1020
|
// we have excess, just use real APY
|
|
673
|
-
logger.verbose(
|
|
1021
|
+
logger.verbose(
|
|
1022
|
+
`${this.getTag()}::netAPY: unused balance is > max servicable from loan, lstAPY: ${lstAPY}`,
|
|
1023
|
+
);
|
|
674
1024
|
const output = await super.netAPY();
|
|
675
|
-
output.splits.push({apy: lstAPY, id:
|
|
1025
|
+
output.splits.push({ apy: lstAPY, id: "lst_apy" });
|
|
676
1026
|
return output;
|
|
677
1027
|
} else {
|
|
678
1028
|
// we have little bit room to accept more deposits, we use theoretical max APY
|
|
679
|
-
logger.verbose(
|
|
1029
|
+
logger.verbose(
|
|
1030
|
+
`${this.getTag()}::netAPY: we can take more deposits, use theoretical max APY`,
|
|
1031
|
+
);
|
|
680
1032
|
const { positions, baseAPYs, rewardAPYs } = await this.getVesuAPYs();
|
|
681
|
-
const weights = positions.map(
|
|
1033
|
+
const weights = positions.map(
|
|
1034
|
+
(p, index) => p.usdValue * (index % 2 == 0 ? 1 : -1),
|
|
1035
|
+
);
|
|
682
1036
|
const aum = weights.reduce((acc, curr) => acc + curr, 0);
|
|
683
|
-
const output = await this.returnNetAPY(
|
|
684
|
-
|
|
1037
|
+
const output = await this.returnNetAPY(
|
|
1038
|
+
baseAPYs,
|
|
1039
|
+
rewardAPYs,
|
|
1040
|
+
weights,
|
|
1041
|
+
new Web3Number(
|
|
1042
|
+
aum.toFixed(9),
|
|
1043
|
+
this.getLSTUnderlyingTokenInfo().decimals,
|
|
1044
|
+
),
|
|
1045
|
+
);
|
|
1046
|
+
output.splits.push({ apy: lstAPY, id: "lst_apy" });
|
|
685
1047
|
return output;
|
|
686
1048
|
}
|
|
687
1049
|
}
|
|
688
1050
|
|
|
689
|
-
async maxNewDeposits(
|
|
1051
|
+
async maxNewDeposits(
|
|
1052
|
+
params: { isAPYComputation: boolean } = { isAPYComputation: false },
|
|
1053
|
+
) {
|
|
690
1054
|
const maxBorrowableAmounts = await this.getMaxBorrowableAmount(params);
|
|
691
1055
|
let numerator = 0;
|
|
692
1056
|
|
|
693
1057
|
let ltv: number | undefined = undefined;
|
|
694
1058
|
for (let adapter of this.getVesuAdapters()) {
|
|
695
|
-
const maxBorrowableAmountInfo = maxBorrowableAmounts.maxBorrowables.find(
|
|
1059
|
+
const maxBorrowableAmountInfo = maxBorrowableAmounts.maxBorrowables.find(
|
|
1060
|
+
(b) => b.borrowableAsset.address.eq(adapter.config.debt.address),
|
|
1061
|
+
);
|
|
696
1062
|
if (!maxBorrowableAmountInfo || !maxBorrowableAmountInfo?.amount) {
|
|
697
|
-
throw new Error(
|
|
1063
|
+
throw new Error(
|
|
1064
|
+
`Max borrowable amount not found for adapter: ${adapter.config.debt.symbol}`,
|
|
1065
|
+
);
|
|
698
1066
|
}
|
|
699
1067
|
|
|
700
|
-
numerator +=
|
|
1068
|
+
numerator +=
|
|
1069
|
+
(this.metadata.additionalInfo.targetHealthFactor *
|
|
1070
|
+
maxBorrowableAmountInfo.amount.toNumber()) /
|
|
1071
|
+
maxBorrowableAmountInfo.ltv;
|
|
701
1072
|
}
|
|
702
1073
|
|
|
703
|
-
return
|
|
1074
|
+
return numerator - maxBorrowableAmounts.netMaxBorrowableAmount.toNumber();
|
|
704
1075
|
}
|
|
705
1076
|
|
|
706
1077
|
// todo revisit cases where 0th adapters is used
|
|
707
1078
|
protected async getUnusedBalanceAPY() {
|
|
708
1079
|
const unusedBalance = await this.getUnusedBalance();
|
|
709
|
-
const vesuAdapter = this.getVesuSameTokenAdapter(
|
|
1080
|
+
const vesuAdapter = this.getVesuSameTokenAdapter(
|
|
1081
|
+
this.metadata.additionalInfo.defaultPoolId,
|
|
1082
|
+
);
|
|
710
1083
|
const underlying = vesuAdapter.config.debt;
|
|
711
1084
|
const lstAPY = await this.getLSTAPR(underlying.address);
|
|
712
1085
|
return {
|
|
713
|
-
apy: lstAPY,
|
|
714
|
-
|
|
1086
|
+
apy: lstAPY,
|
|
1087
|
+
weight: unusedBalance.usdValue,
|
|
1088
|
+
};
|
|
715
1089
|
}
|
|
716
1090
|
|
|
717
1091
|
async getLSTAvnuRate() {
|
|
718
|
-
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
1092
|
+
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
1093
|
+
this.metadata.additionalInfo.defaultPoolId,
|
|
1094
|
+
);
|
|
719
1095
|
const lstTokenInfo = vesuAdapter1.config.collateral;
|
|
720
1096
|
const underlyingTokenInfo = vesuAdapter1.config.debt;
|
|
721
1097
|
|
|
722
1098
|
const avnuModule = new AvnuWrapper();
|
|
723
1099
|
|
|
724
|
-
const sellAmount = lstTokenInfo.priceCheckAmount
|
|
725
|
-
|
|
726
|
-
|
|
1100
|
+
const sellAmount = lstTokenInfo.priceCheckAmount
|
|
1101
|
+
? new Web3Number(
|
|
1102
|
+
lstTokenInfo.priceCheckAmount,
|
|
1103
|
+
underlyingTokenInfo.decimals,
|
|
1104
|
+
)
|
|
1105
|
+
: new Web3Number(1, underlyingTokenInfo.decimals);
|
|
727
1106
|
|
|
728
1107
|
const quote = await avnuModule.getQuotes(
|
|
729
1108
|
underlyingTokenInfo.address.address,
|
|
730
1109
|
lstTokenInfo.address.address,
|
|
731
1110
|
sellAmount.toWei(),
|
|
732
|
-
this.metadata.additionalInfo.vaultAllocator.address
|
|
1111
|
+
this.metadata.additionalInfo.vaultAllocator.address,
|
|
733
1112
|
);
|
|
734
1113
|
|
|
735
1114
|
const underlyingAmountNumber = sellAmount.toNumber();
|
|
736
1115
|
const lstAmountNumber = Web3Number.fromWei(
|
|
737
1116
|
quote.buyAmount.toString(),
|
|
738
|
-
lstTokenInfo.decimals
|
|
1117
|
+
lstTokenInfo.decimals,
|
|
739
1118
|
).toNumber();
|
|
740
1119
|
|
|
741
|
-
assert(lstAmountNumber > 0,
|
|
1120
|
+
assert(lstAmountNumber > 0, "Avnu LST amount is zero");
|
|
742
1121
|
|
|
743
1122
|
const exchangeRate = underlyingAmountNumber / lstAmountNumber;
|
|
744
|
-
logger.verbose(
|
|
1123
|
+
logger.verbose(
|
|
1124
|
+
`${this.getTag()}:: LST Avnu Exchange Rate: ${exchangeRate}`,
|
|
1125
|
+
);
|
|
745
1126
|
return exchangeRate;
|
|
746
1127
|
}
|
|
747
1128
|
|
|
748
1129
|
async getLSTExchangeRate() {
|
|
749
|
-
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
1130
|
+
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
1131
|
+
this.metadata.additionalInfo.defaultPoolId,
|
|
1132
|
+
);
|
|
750
1133
|
const lstTokenInfo = vesuAdapter1.config.collateral;
|
|
751
1134
|
const lstABI = new Contract({
|
|
752
1135
|
abi: ERC4626Abi,
|
|
753
1136
|
address: lstTokenInfo.address.address,
|
|
754
|
-
providerOrAccount: this.config.provider
|
|
1137
|
+
providerOrAccount: this.config.provider,
|
|
755
1138
|
});
|
|
756
1139
|
|
|
757
|
-
const price: any = await lstABI.call(
|
|
758
|
-
|
|
1140
|
+
const price: any = await lstABI.call("convert_to_assets", [
|
|
1141
|
+
uint256.bnToUint256(new Web3Number(1, lstTokenInfo.decimals).toWei()),
|
|
1142
|
+
]);
|
|
1143
|
+
const exchangeRate =
|
|
1144
|
+
Number(uint256.uint256ToBN(price).toString()) /
|
|
1145
|
+
Math.pow(10, lstTokenInfo.decimals);
|
|
759
1146
|
logger.verbose(`${this.getTag()}:: LST Exchange Rate: ${exchangeRate}`);
|
|
760
1147
|
return exchangeRate;
|
|
761
|
-
|
|
1148
|
+
}
|
|
762
1149
|
|
|
763
1150
|
/**
|
|
764
|
-
*
|
|
1151
|
+
*
|
|
765
1152
|
* @param params marginAmount is in LST, debtAmount is in underlying
|
|
766
1153
|
*/
|
|
767
1154
|
async getModifyLeverCall(params: {
|
|
768
|
-
marginAmount: Web3Number
|
|
769
|
-
debtAmount: Web3Number
|
|
770
|
-
lstDexPriceInUnderlying: number
|
|
771
|
-
isIncrease: boolean
|
|
772
|
-
maxEkuboPriceImpact: number
|
|
773
|
-
poolId: ContractAddr
|
|
1155
|
+
marginAmount: Web3Number; // >0 during deposit
|
|
1156
|
+
debtAmount: Web3Number;
|
|
1157
|
+
lstDexPriceInUnderlying: number;
|
|
1158
|
+
isIncrease: boolean;
|
|
1159
|
+
maxEkuboPriceImpact: number;
|
|
1160
|
+
poolId: ContractAddr;
|
|
774
1161
|
}): Promise<Call[]> {
|
|
775
|
-
logger.verbose(
|
|
776
|
-
|
|
1162
|
+
logger.verbose(
|
|
1163
|
+
`${this.getTag()}::getModifyLeverCall marginAmount: ${params.marginAmount}, debtAmount: ${params.debtAmount}, lstDexPriceInUnderlying: ${params.lstDexPriceInUnderlying}, isIncrease: ${params.isIncrease}`,
|
|
1164
|
+
);
|
|
1165
|
+
assert(
|
|
1166
|
+
!params.marginAmount.isZero() || !params.debtAmount.isZero(),
|
|
1167
|
+
"Deposit/debt must be non-0",
|
|
1168
|
+
);
|
|
777
1169
|
|
|
778
1170
|
const vesuAdapter1 = this.getVesuSameTokenAdapter(params.poolId);
|
|
779
1171
|
const lstTokenInfo = this.asset();
|
|
@@ -781,9 +1173,11 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
781
1173
|
|
|
782
1174
|
// todo make it more general
|
|
783
1175
|
// 500k STRK (~75k$) or 0.5 BTC (~60k$)
|
|
784
|
-
const maxAmounts = lstTokenInfo.symbol ==
|
|
1176
|
+
const maxAmounts = lstTokenInfo.symbol == "xSTRK" ? 500000 : 0.5;
|
|
785
1177
|
if (params.marginAmount.greaterThan(maxAmounts)) {
|
|
786
|
-
throw new Error(
|
|
1178
|
+
throw new Error(
|
|
1179
|
+
`Margin amount is greater than max amount: ${params.marginAmount.toNumber()} > ${maxAmounts}`,
|
|
1180
|
+
);
|
|
787
1181
|
}
|
|
788
1182
|
|
|
789
1183
|
const proofsIDs: string[] = [];
|
|
@@ -791,12 +1185,15 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
791
1185
|
|
|
792
1186
|
// approve token
|
|
793
1187
|
if (params.marginAmount.greaterThan(0)) {
|
|
794
|
-
const STEP1_ID = getVesuGenericLegId(
|
|
1188
|
+
const STEP1_ID = getVesuGenericLegId(
|
|
1189
|
+
params.poolId.toString(),
|
|
1190
|
+
LST_MULTIPLIER_MANAGE_IDS.MULTIPLE_APPROVE,
|
|
1191
|
+
);
|
|
795
1192
|
const manage1Info = this.getProofs<ApproveCallParams>(STEP1_ID);
|
|
796
1193
|
const depositAmount = params.marginAmount;
|
|
797
1194
|
const manageCall1 = manage1Info.callConstructor({
|
|
798
|
-
|
|
799
|
-
})
|
|
1195
|
+
amount: depositAmount,
|
|
1196
|
+
});
|
|
800
1197
|
proofsIDs.push(STEP1_ID);
|
|
801
1198
|
manageCalls.push(manageCall1);
|
|
802
1199
|
}
|
|
@@ -813,75 +1210,119 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
813
1210
|
const fromToken = params.isIncrease ? lstUnderlyingTokenInfo : lstTokenInfo;
|
|
814
1211
|
const toToken = params.isIncrease ? lstTokenInfo : lstUnderlyingTokenInfo;
|
|
815
1212
|
const leverSwapQuote = await ekuboQuoter.getQuote(
|
|
816
|
-
fromToken.address.address,
|
|
817
|
-
toToken.address.address,
|
|
818
|
-
params.debtAmount // negative for exact amount out
|
|
1213
|
+
fromToken.address.address,
|
|
1214
|
+
toToken.address.address,
|
|
1215
|
+
params.debtAmount, // negative for exact amount out
|
|
1216
|
+
);
|
|
1217
|
+
logger.verbose(
|
|
1218
|
+
`${this.getTag()}::getModifyLeverCall leverSwapQuote: ${JSON.stringify(leverSwapQuote)}`,
|
|
1219
|
+
);
|
|
1220
|
+
// Ekubo's price impact can randomly show high numbers sometimes.
|
|
1221
|
+
assert(
|
|
1222
|
+
leverSwapQuote.price_impact <= params.maxEkuboPriceImpact,
|
|
1223
|
+
"getIncreaseLeverCall: Price impact is too high [Debt swap]",
|
|
1224
|
+
);
|
|
1225
|
+
const leverSwap = ekuboQuoter.getVesuMultiplyQuote(
|
|
1226
|
+
leverSwapQuote,
|
|
1227
|
+
fromToken,
|
|
1228
|
+
toToken,
|
|
1229
|
+
);
|
|
1230
|
+
logger.verbose(
|
|
1231
|
+
`${this.getTag()}::getModifyLeverCall leverSwap: ${JSON.stringify(leverSwap)}`,
|
|
819
1232
|
);
|
|
820
|
-
logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwapQuote: ${JSON.stringify(leverSwapQuote)}`);
|
|
821
|
-
// Ekubo's price impact can randomly show high numbers sometimes.
|
|
822
|
-
assert(leverSwapQuote.price_impact <= params.maxEkuboPriceImpact, 'getIncreaseLeverCall: Price impact is too high [Debt swap]');
|
|
823
|
-
const leverSwap = ekuboQuoter.getVesuMultiplyQuote(leverSwapQuote, fromToken, toToken);
|
|
824
|
-
logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwap: ${JSON.stringify(leverSwap)}`);
|
|
825
1233
|
|
|
826
1234
|
// todo double check this logic
|
|
827
1235
|
// is Deposit
|
|
828
|
-
let minLSTReceived = params.debtAmount
|
|
829
|
-
|
|
1236
|
+
let minLSTReceived = params.debtAmount
|
|
1237
|
+
.dividedBy(lstDexPriceInUnderlying)
|
|
1238
|
+
.multipliedBy(1 - MAX_SLIPPAGE); // used for increase
|
|
1239
|
+
const minLSTReceivedAsPerTruePrice =
|
|
1240
|
+
params.debtAmount.dividedBy(lstTrueExchangeRate); // execution output to be <= True LST price
|
|
830
1241
|
// if (minLSTReceived < minLSTReceivedAsPerTruePrice) {
|
|
831
1242
|
// minLSTReceived = minLSTReceivedAsPerTruePrice; // the execution shouldn't be bad than True price logi
|
|
832
1243
|
// }
|
|
833
1244
|
minLSTReceived = minLSTReceivedAsPerTruePrice; // in any case, we are ok with this, bcz the BTC LST spread shouldnt be high
|
|
834
|
-
logger.verbose(
|
|
1245
|
+
logger.verbose(
|
|
1246
|
+
`${this.getTag()}::getModifyLeverCall minLSTReceivedAsPerTruePrice: ${minLSTReceivedAsPerTruePrice}, minLSTReceived: ${minLSTReceived}`,
|
|
1247
|
+
);
|
|
835
1248
|
|
|
836
1249
|
// is withdraw
|
|
837
|
-
let maxUsedCollateral = params.debtAmount
|
|
838
|
-
|
|
839
|
-
|
|
1250
|
+
let maxUsedCollateral = params.debtAmount
|
|
1251
|
+
.abs()
|
|
1252
|
+
.dividedBy(lstDexPriceInUnderlying)
|
|
1253
|
+
.multipliedBy(1 + MAX_SLIPPAGE); // +ve for exact amount out, used for decrease
|
|
1254
|
+
const maxUsedCollateralInLST = params.debtAmount
|
|
1255
|
+
.abs()
|
|
1256
|
+
.dividedBy(lstTrueExchangeRate)
|
|
1257
|
+
.multipliedBy(1.005); // 0.5% slippage, worst case based on true price
|
|
1258
|
+
logger.verbose(
|
|
1259
|
+
`${this.getTag()}::getModifyLeverCall maxUsedCollateralInLST: ${maxUsedCollateralInLST}, maxUsedCollateral: ${maxUsedCollateral}`,
|
|
1260
|
+
);
|
|
840
1261
|
// if (maxUsedCollateralInLST > maxUsedCollateral) {
|
|
841
1262
|
// maxUsedCollateral = maxUsedCollateralInLST;
|
|
842
1263
|
// }
|
|
843
1264
|
maxUsedCollateral = maxUsedCollateralInLST; // in any case, we are ok with this, bcz the BTC LST spread shouldnt be high
|
|
844
1265
|
|
|
845
|
-
const STEP2_ID = getVesuGenericLegId(
|
|
846
|
-
|
|
1266
|
+
const STEP2_ID = getVesuGenericLegId(
|
|
1267
|
+
params.poolId.toString(),
|
|
1268
|
+
LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_ON,
|
|
1269
|
+
);
|
|
1270
|
+
const manage2Info =
|
|
1271
|
+
this.getProofs<VesuModifyDelegationCallParams>(STEP2_ID);
|
|
847
1272
|
const manageCall2 = manage2Info.callConstructor({
|
|
848
1273
|
delegation: true,
|
|
849
1274
|
});
|
|
850
1275
|
|
|
851
1276
|
// deposit and borrow or repay and withdraw
|
|
852
|
-
const STEP3_ID = getVesuLegId(
|
|
1277
|
+
const STEP3_ID = getVesuLegId(
|
|
1278
|
+
LST_MULTIPLIER_MANAGE_IDS.MULTIPLY_VESU,
|
|
1279
|
+
vesuAdapter1.config.debt.symbol,
|
|
1280
|
+
vesuAdapter1.config.poolId.toString(),
|
|
1281
|
+
);
|
|
853
1282
|
const manage3Info = this.getProofs<VesuMultiplyCallParams>(STEP3_ID);
|
|
854
|
-
const multiplyParams: VesuMultiplyCallParams = params.isIncrease
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1283
|
+
const multiplyParams: VesuMultiplyCallParams = params.isIncrease
|
|
1284
|
+
? {
|
|
1285
|
+
isIncrease: true,
|
|
1286
|
+
increaseParams: {
|
|
1287
|
+
add_margin: params.marginAmount,
|
|
1288
|
+
margin_swap: [],
|
|
1289
|
+
margin_swap_limit_amount: Web3Number.fromWei(
|
|
1290
|
+
0,
|
|
1291
|
+
this.asset().decimals,
|
|
1292
|
+
),
|
|
1293
|
+
lever_swap: leverSwap,
|
|
1294
|
+
lever_swap_limit_amount: minLSTReceived,
|
|
1295
|
+
},
|
|
1296
|
+
}
|
|
1297
|
+
: {
|
|
1298
|
+
isIncrease: false,
|
|
1299
|
+
decreaseParams: {
|
|
1300
|
+
sub_margin: params.marginAmount.multipliedBy(-1),
|
|
1301
|
+
lever_swap: leverSwap,
|
|
1302
|
+
lever_swap_limit_amount: maxUsedCollateral,
|
|
1303
|
+
// only required for close position
|
|
1304
|
+
lever_swap_weights: [],
|
|
1305
|
+
// no need to swap collateral to anything, and any residuals return our contract anyways.
|
|
1306
|
+
withdraw_swap: [],
|
|
1307
|
+
withdraw_swap_limit_amount: Web3Number.fromWei(
|
|
1308
|
+
0,
|
|
1309
|
+
this.asset().decimals,
|
|
1310
|
+
),
|
|
1311
|
+
withdraw_swap_weights: [],
|
|
1312
|
+
close_position: false,
|
|
1313
|
+
},
|
|
1314
|
+
};
|
|
878
1315
|
const manageCall3 = manage3Info.callConstructor(multiplyParams);
|
|
879
1316
|
|
|
880
1317
|
// switch delegation off
|
|
881
|
-
const STEP4_ID = getVesuGenericLegId(
|
|
882
|
-
|
|
1318
|
+
const STEP4_ID = getVesuGenericLegId(
|
|
1319
|
+
params.poolId.toString(),
|
|
1320
|
+
LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_OFF,
|
|
1321
|
+
);
|
|
1322
|
+
const manage4Info =
|
|
1323
|
+
this.getProofs<VesuModifyDelegationCallParams>(STEP4_ID);
|
|
883
1324
|
const manageCall4 = manage4Info.callConstructor({
|
|
884
|
-
delegation: false
|
|
1325
|
+
delegation: false,
|
|
885
1326
|
});
|
|
886
1327
|
|
|
887
1328
|
proofsIDs.push(STEP2_ID, STEP3_ID, STEP4_ID);
|
|
@@ -893,12 +1334,11 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
893
1334
|
|
|
894
1335
|
export default function VaultDescription(
|
|
895
1336
|
lstSymbol: string,
|
|
896
|
-
underlyingSymbol: string
|
|
1337
|
+
underlyingSymbol: string,
|
|
897
1338
|
) {
|
|
898
1339
|
const containerStyle = {
|
|
899
1340
|
maxWidth: "800px",
|
|
900
1341
|
margin: "0 auto",
|
|
901
|
-
backgroundColor: "#111",
|
|
902
1342
|
color: "#eee",
|
|
903
1343
|
fontFamily: "Arial, sans-serif",
|
|
904
1344
|
borderRadius: "12px",
|
|
@@ -906,26 +1346,64 @@ export default function VaultDescription(
|
|
|
906
1346
|
|
|
907
1347
|
return (
|
|
908
1348
|
<div style={containerStyle}>
|
|
909
|
-
<h1 style={{ fontSize: "18px", marginBottom: "10px" }}>
|
|
1349
|
+
<h1 style={{ fontSize: "18px", marginBottom: "10px" }}>
|
|
1350
|
+
Liquidation risk managed leverged {lstSymbol} Vault
|
|
1351
|
+
</h1>
|
|
910
1352
|
<p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
|
|
911
|
-
This Levered Endur {lstSymbol} vault is a tokenized leveraged Vault,
|
|
912
|
-
|
|
913
|
-
|
|
1353
|
+
This Levered Endur {lstSymbol} vault is a tokenized leveraged Vault,
|
|
1354
|
+
auto-compounding strategy that takes upto 5x leverage on {lstSymbol} by
|
|
1355
|
+
borrow {underlyingSymbol}. Borrowed amount is swapped to {lstSymbol} to
|
|
1356
|
+
create leverage. Depositors receive vault shares that represent a
|
|
1357
|
+
proportional claim on the underlying assets and accrued yield.
|
|
914
1358
|
</p>
|
|
915
1359
|
|
|
916
1360
|
<p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
|
|
917
|
-
This vault uses Vesu for lending and borrowing. The oracle used by this
|
|
918
|
-
{" "}
|
|
1361
|
+
This vault uses Vesu for lending and borrowing. The oracle used by this
|
|
1362
|
+
pool is a{" "}
|
|
1363
|
+
{highlightTextWithLinks("conversion rate oracle", [
|
|
1364
|
+
{
|
|
1365
|
+
highlight: "conversion rate oracle",
|
|
1366
|
+
link: "https://docs.pragma.build/starknet/development#conversion-rate",
|
|
1367
|
+
},
|
|
1368
|
+
])}{" "}
|
|
1369
|
+
which is resilient to liquidity issues and price volatility, hence
|
|
1370
|
+
reducing the risk of liquidation. However, overtime, if left
|
|
1371
|
+
un-monitored, debt can increase enough to trigger a liquidation. But no
|
|
1372
|
+
worries, our continuous monitoring systems look for situations with
|
|
1373
|
+
reduced health factor and balance collateral/debt to bring it back to
|
|
1374
|
+
safe levels. With Troves, you can have a peaceful sleep.
|
|
919
1375
|
</p>
|
|
920
1376
|
|
|
921
|
-
<div
|
|
1377
|
+
<div
|
|
1378
|
+
style={{
|
|
1379
|
+
backgroundColor: "#222",
|
|
1380
|
+
padding: "10px",
|
|
1381
|
+
borderRadius: "8px",
|
|
1382
|
+
marginBottom: "20px",
|
|
1383
|
+
border: "1px solid #444",
|
|
1384
|
+
}}
|
|
1385
|
+
>
|
|
922
1386
|
<p style={{ fontSize: "13px", color: "#ccc" }}>
|
|
923
|
-
<strong>Withdrawals:</strong> Requests can take up to
|
|
1387
|
+
<strong>Withdrawals:</strong> Requests can take up to{" "}
|
|
1388
|
+
<strong>1-2 hours</strong> to process as the vault unwinds and settles
|
|
1389
|
+
routing.
|
|
924
1390
|
</p>
|
|
925
1391
|
</div>
|
|
926
|
-
<div
|
|
1392
|
+
<div
|
|
1393
|
+
style={{
|
|
1394
|
+
backgroundColor: "#222",
|
|
1395
|
+
padding: "10px",
|
|
1396
|
+
borderRadius: "8px",
|
|
1397
|
+
marginBottom: "20px",
|
|
1398
|
+
border: "1px solid #444",
|
|
1399
|
+
}}
|
|
1400
|
+
>
|
|
927
1401
|
<p style={{ fontSize: "13px", color: "#ccc" }}>
|
|
928
|
-
<strong>Debt limits:</strong> Pools on Vesu have debt caps that are
|
|
1402
|
+
<strong>Debt limits:</strong> Pools on Vesu have debt caps that are
|
|
1403
|
+
gradually increased over time. Until caps are raised, deposited LSTs
|
|
1404
|
+
remain in the vault, generating a shared net return for all
|
|
1405
|
+
depositors. There is no additional fee taken by Troves on LST APY, its
|
|
1406
|
+
only on added gain.
|
|
929
1407
|
</p>
|
|
930
1408
|
</div>
|
|
931
1409
|
{/* <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
|
|
@@ -937,24 +1415,25 @@ export default function VaultDescription(
|
|
|
937
1415
|
);
|
|
938
1416
|
}
|
|
939
1417
|
|
|
940
|
-
|
|
941
1418
|
function getDescription(tokenSymbol: string, underlyingSymbol: string) {
|
|
942
1419
|
return VaultDescription(tokenSymbol, underlyingSymbol);
|
|
943
1420
|
}
|
|
944
1421
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
AVNU_MULTIPLY_SWAP_WITHDRAW = 'avnu_mul_swap_withdr',
|
|
1422
|
+
enum LST_MULTIPLIER_MANAGE_IDS {
|
|
1423
|
+
MULTIPLE_APPROVE = "multiple_approve",
|
|
1424
|
+
MULTIPLY_VESU = "multiply_vesu",
|
|
1425
|
+
SWITCH_DELEGATION_ON = "switch_delegation_on",
|
|
1426
|
+
SWITCH_DELEGATION_OFF = "switch_delegation_off",
|
|
1427
|
+
AVNU_MULTIPLY_APPROVE_DEPOSIT = "avnu_mul_approve_dep",
|
|
1428
|
+
AVNU_MULTIPLY_SWAP_DEPOSIT = "avnu_mul_swap_dep",
|
|
1429
|
+
AVNU_MULTIPLY_APPROVE_WITHDRAW = "avnu_mul_approve_withdr",
|
|
1430
|
+
AVNU_MULTIPLY_SWAP_WITHDRAW = "avnu_mul_swap_withdr",
|
|
955
1431
|
}
|
|
956
1432
|
|
|
957
|
-
function getAvnuManageIDs(
|
|
1433
|
+
function getAvnuManageIDs(
|
|
1434
|
+
baseID: LST_MULTIPLIER_MANAGE_IDS,
|
|
1435
|
+
debtTokenSymbol: string,
|
|
1436
|
+
) {
|
|
958
1437
|
return `${baseID}_${debtTokenSymbol.toLowerCase()}`;
|
|
959
1438
|
}
|
|
960
1439
|
|
|
@@ -973,45 +1452,100 @@ function addVesuLeaves(
|
|
|
973
1452
|
vaultSettings: HyperLSTStrategySettings,
|
|
974
1453
|
commonAdapter: CommonAdapter,
|
|
975
1454
|
) {
|
|
976
|
-
const lstToken = Global.getDefaultTokens().find(
|
|
977
|
-
|
|
1455
|
+
const lstToken = Global.getDefaultTokens().find(
|
|
1456
|
+
(token) => token.symbol === lstSymbol,
|
|
1457
|
+
)!;
|
|
1458
|
+
const underlyingToken = Global.getDefaultTokens().find(
|
|
1459
|
+
(token) => token.symbol === underlyingSymbol,
|
|
1460
|
+
)!;
|
|
978
1461
|
|
|
979
1462
|
const vesuAdapterLST = new VesuAdapter({
|
|
980
1463
|
poolId: poolId,
|
|
981
1464
|
collateral: lstToken,
|
|
982
1465
|
debt: underlyingToken,
|
|
983
1466
|
vaultAllocator: vaultSettings.vaultAllocator,
|
|
984
|
-
id: getVesuLegId(
|
|
1467
|
+
id: getVesuLegId(
|
|
1468
|
+
UNIVERSAL_MANAGE_IDS.VESU_LEG1,
|
|
1469
|
+
underlyingToken.symbol,
|
|
1470
|
+
poolId.toString(),
|
|
1471
|
+
),
|
|
985
1472
|
});
|
|
986
1473
|
|
|
987
1474
|
// Useful for returning adapter class objects that can compute
|
|
988
1475
|
// certain things for us (e.g. positions, hfs)
|
|
989
|
-
vaultSettings.adapters.push(
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1476
|
+
vaultSettings.adapters.push(
|
|
1477
|
+
...[
|
|
1478
|
+
{
|
|
1479
|
+
id: getVesuLegId(
|
|
1480
|
+
UNIVERSAL_MANAGE_IDS.VESU_LEG1,
|
|
1481
|
+
underlyingToken.symbol,
|
|
1482
|
+
poolId.toString(),
|
|
1483
|
+
),
|
|
1484
|
+
adapter: vesuAdapterLST,
|
|
1485
|
+
},
|
|
1486
|
+
],
|
|
1487
|
+
);
|
|
993
1488
|
|
|
994
1489
|
// avnu multiply
|
|
995
1490
|
const { isV2, addr: poolAddr } = getVesuSingletonAddress(poolId);
|
|
996
1491
|
|
|
997
1492
|
// vesu multiply looping
|
|
998
|
-
const VESU_MULTIPLY = isV2
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1493
|
+
const VESU_MULTIPLY = isV2
|
|
1494
|
+
? vesuAdapterLST.VESU_MULTIPLY
|
|
1495
|
+
: vesuAdapterLST.VESU_MULTIPLY_V1;
|
|
1496
|
+
|
|
1497
|
+
const leafIdApprove = getVesuGenericLegId(
|
|
1498
|
+
poolId.toString(),
|
|
1499
|
+
LST_MULTIPLIER_MANAGE_IDS.MULTIPLE_APPROVE,
|
|
1500
|
+
);
|
|
1501
|
+
vaultSettings.leafAdapters.push(
|
|
1502
|
+
commonAdapter
|
|
1503
|
+
.getApproveAdapter(lstToken.address, VESU_MULTIPLY, leafIdApprove)
|
|
1504
|
+
.bind(commonAdapter),
|
|
1505
|
+
);
|
|
1506
|
+
|
|
1507
|
+
const leafIdDelegationOn = getVesuGenericLegId(
|
|
1508
|
+
poolId.toString(),
|
|
1509
|
+
LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_ON,
|
|
1510
|
+
);
|
|
1511
|
+
vaultSettings.leafAdapters.push(
|
|
1512
|
+
vesuAdapterLST
|
|
1513
|
+
.getVesuModifyDelegationAdapter(leafIdDelegationOn, VESU_MULTIPLY)
|
|
1514
|
+
.bind(vesuAdapterLST),
|
|
1515
|
+
);
|
|
1516
|
+
|
|
1517
|
+
const leafIdDelegationOff = getVesuGenericLegId(
|
|
1518
|
+
poolId.toString(),
|
|
1519
|
+
LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_OFF,
|
|
1520
|
+
);
|
|
1521
|
+
vaultSettings.leafAdapters.push(
|
|
1522
|
+
vesuAdapterLST
|
|
1523
|
+
.getVesuModifyDelegationAdapter(leafIdDelegationOff, VESU_MULTIPLY)
|
|
1524
|
+
.bind(vesuAdapterLST),
|
|
1525
|
+
);
|
|
1526
|
+
|
|
1527
|
+
const multiplID = getVesuLegId(
|
|
1528
|
+
LST_MULTIPLIER_MANAGE_IDS.MULTIPLY_VESU,
|
|
1529
|
+
underlyingToken.symbol,
|
|
1530
|
+
poolId.toString(),
|
|
1531
|
+
);
|
|
1532
|
+
vaultSettings.leafAdapters.push(
|
|
1533
|
+
vesuAdapterLST.getMultiplyAdapter(multiplID).bind(vesuAdapterLST),
|
|
1534
|
+
);
|
|
1535
|
+
|
|
1012
1536
|
// direct modify position stuff
|
|
1013
|
-
vaultSettings.leafAdapters.push(
|
|
1014
|
-
|
|
1537
|
+
vaultSettings.leafAdapters.push(
|
|
1538
|
+
commonAdapter
|
|
1539
|
+
.getApproveAdapter(
|
|
1540
|
+
lstToken.address,
|
|
1541
|
+
poolAddr,
|
|
1542
|
+
UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1,
|
|
1543
|
+
)
|
|
1544
|
+
.bind(commonAdapter),
|
|
1545
|
+
);
|
|
1546
|
+
vaultSettings.leafAdapters.push(
|
|
1547
|
+
vesuAdapterLST.getModifyPosition.bind(vesuAdapterLST),
|
|
1548
|
+
);
|
|
1015
1549
|
|
|
1016
1550
|
return vesuAdapterLST;
|
|
1017
1551
|
}
|
|
@@ -1020,7 +1554,7 @@ function getLooperSettings(
|
|
|
1020
1554
|
lstSymbol: string,
|
|
1021
1555
|
underlyingSymbol: string,
|
|
1022
1556
|
vaultSettings: HyperLSTStrategySettings,
|
|
1023
|
-
defaultPoolId: ContractAddr
|
|
1557
|
+
defaultPoolId: ContractAddr,
|
|
1024
1558
|
) {
|
|
1025
1559
|
vaultSettings.leafAdapters = [];
|
|
1026
1560
|
const pool1 = vaultSettings.defaultPoolId;
|
|
@@ -1028,82 +1562,179 @@ function getLooperSettings(
|
|
|
1028
1562
|
throw new Error(`Dont include default pool id in supported pool ids`);
|
|
1029
1563
|
}
|
|
1030
1564
|
|
|
1031
|
-
const lstToken = Global.getDefaultTokens().find(
|
|
1032
|
-
|
|
1565
|
+
const lstToken = Global.getDefaultTokens().find(
|
|
1566
|
+
(token) => token.symbol === lstSymbol,
|
|
1567
|
+
)!;
|
|
1568
|
+
const underlyingToken = Global.getDefaultTokens().find(
|
|
1569
|
+
(token) => token.symbol === underlyingSymbol,
|
|
1570
|
+
)!;
|
|
1033
1571
|
|
|
1034
1572
|
const commonAdapter = new CommonAdapter({
|
|
1035
1573
|
manager: vaultSettings.manager,
|
|
1036
1574
|
asset: lstToken.address,
|
|
1037
|
-
id:
|
|
1575
|
+
id: "",
|
|
1038
1576
|
vaultAddress: vaultSettings.vaultAddress,
|
|
1039
1577
|
vaultAllocator: vaultSettings.vaultAllocator,
|
|
1040
1578
|
});
|
|
1041
1579
|
|
|
1042
1580
|
// add common adapter to adapters
|
|
1043
|
-
vaultSettings.adapters.push(
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1581
|
+
vaultSettings.adapters.push(
|
|
1582
|
+
...[
|
|
1583
|
+
{
|
|
1584
|
+
id: UNIVERSAL_ADAPTERS.COMMON,
|
|
1585
|
+
adapter: commonAdapter,
|
|
1586
|
+
},
|
|
1587
|
+
],
|
|
1588
|
+
);
|
|
1047
1589
|
|
|
1048
1590
|
// add vesu leaves for all supported pool ids
|
|
1049
|
-
vaultSettings.borrowable_assets.map(borrowableAsset =>
|
|
1050
|
-
|
|
1591
|
+
vaultSettings.borrowable_assets.map((borrowableAsset) =>
|
|
1592
|
+
addVesuLeaves(
|
|
1593
|
+
borrowableAsset.poolId,
|
|
1594
|
+
lstSymbol,
|
|
1595
|
+
underlyingSymbol,
|
|
1596
|
+
vaultSettings,
|
|
1597
|
+
commonAdapter,
|
|
1598
|
+
),
|
|
1599
|
+
);
|
|
1600
|
+
|
|
1051
1601
|
// approve lst once to avnu
|
|
1052
|
-
vaultSettings.leafAdapters.push(
|
|
1053
|
-
|
|
1054
|
-
|
|
1602
|
+
vaultSettings.leafAdapters.push(
|
|
1603
|
+
commonAdapter
|
|
1604
|
+
.getApproveAdapter(
|
|
1605
|
+
lstToken.address,
|
|
1606
|
+
AVNU_EXCHANGE,
|
|
1607
|
+
LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_APPROVE_WITHDRAW,
|
|
1608
|
+
)
|
|
1609
|
+
.bind(commonAdapter),
|
|
1610
|
+
);
|
|
1611
|
+
|
|
1612
|
+
const uniqueBorrowableAssets = [
|
|
1613
|
+
...new Set(
|
|
1614
|
+
vaultSettings.borrowable_assets.map(
|
|
1615
|
+
(borrowableAsset) => borrowableAsset.token.symbol,
|
|
1616
|
+
),
|
|
1617
|
+
),
|
|
1618
|
+
];
|
|
1055
1619
|
for (let borrowableAssetSymbol of uniqueBorrowableAssets) {
|
|
1056
|
-
const borrowableAsset = Global.getDefaultTokens().find(
|
|
1620
|
+
const borrowableAsset = Global.getDefaultTokens().find(
|
|
1621
|
+
(token) => token.symbol === borrowableAssetSymbol,
|
|
1622
|
+
)!;
|
|
1057
1623
|
// in-efficient avnu swap looping (but good with endur integration)
|
|
1058
1624
|
const debtAsset = borrowableAsset;
|
|
1059
|
-
const approve_debt_token_id = getAvnuManageIDs(
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1625
|
+
const approve_debt_token_id = getAvnuManageIDs(
|
|
1626
|
+
LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_APPROVE_DEPOSIT,
|
|
1627
|
+
debtAsset.symbol,
|
|
1628
|
+
);
|
|
1629
|
+
const swap_debt_token_id = getAvnuManageIDs(
|
|
1630
|
+
LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_DEPOSIT,
|
|
1631
|
+
debtAsset.symbol,
|
|
1632
|
+
);
|
|
1633
|
+
const swap_lst_token_id = getAvnuManageIDs(
|
|
1634
|
+
LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_WITHDRAW,
|
|
1635
|
+
debtAsset.symbol,
|
|
1636
|
+
);
|
|
1637
|
+
vaultSettings.leafAdapters.push(
|
|
1638
|
+
commonAdapter
|
|
1639
|
+
.getApproveAdapter(
|
|
1640
|
+
debtAsset.address,
|
|
1641
|
+
AVNU_EXCHANGE,
|
|
1642
|
+
approve_debt_token_id,
|
|
1643
|
+
)
|
|
1644
|
+
.bind(commonAdapter),
|
|
1645
|
+
);
|
|
1646
|
+
vaultSettings.leafAdapters.push(
|
|
1647
|
+
commonAdapter
|
|
1648
|
+
.getAvnuAdapter(
|
|
1649
|
+
debtAsset.address,
|
|
1650
|
+
lstToken.address,
|
|
1651
|
+
swap_debt_token_id,
|
|
1652
|
+
false,
|
|
1653
|
+
)
|
|
1654
|
+
.bind(commonAdapter),
|
|
1655
|
+
);
|
|
1656
|
+
vaultSettings.leafAdapters.push(
|
|
1657
|
+
commonAdapter
|
|
1658
|
+
.getAvnuAdapter(
|
|
1659
|
+
lstToken.address,
|
|
1660
|
+
debtAsset.address,
|
|
1661
|
+
swap_lst_token_id,
|
|
1662
|
+
false,
|
|
1663
|
+
)
|
|
1664
|
+
.bind(commonAdapter),
|
|
1665
|
+
);
|
|
1065
1666
|
}
|
|
1066
1667
|
|
|
1067
|
-
|
|
1068
1668
|
// to bridge liquidity back to vault (used by bring_liquidity)
|
|
1069
|
-
vaultSettings.leafAdapters.push(
|
|
1070
|
-
|
|
1071
|
-
|
|
1669
|
+
vaultSettings.leafAdapters.push(
|
|
1670
|
+
commonAdapter
|
|
1671
|
+
.getApproveAdapter(
|
|
1672
|
+
lstToken.address,
|
|
1673
|
+
vaultSettings.vaultAddress,
|
|
1674
|
+
UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY,
|
|
1675
|
+
)
|
|
1676
|
+
.bind(commonAdapter),
|
|
1677
|
+
);
|
|
1678
|
+
vaultSettings.leafAdapters.push(
|
|
1679
|
+
commonAdapter
|
|
1680
|
+
.getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY)
|
|
1681
|
+
.bind(commonAdapter),
|
|
1682
|
+
);
|
|
1683
|
+
|
|
1072
1684
|
// claim rewards (defi spring ended)
|
|
1073
1685
|
// vaultSettings.leafAdapters.push(vesuAdapterLST.getDefispringRewardsAdapter(UNIVERSAL_MANAGE_IDS.DEFISPRING_REWARDS).bind(vesuAdapterLST));
|
|
1074
1686
|
|
|
1075
1687
|
// avnu swap for claims rewards
|
|
1076
|
-
const STRKToken = Global.getDefaultTokens().find(
|
|
1077
|
-
|
|
1078
|
-
|
|
1688
|
+
const STRKToken = Global.getDefaultTokens().find(
|
|
1689
|
+
(token) => token.symbol === "STRK",
|
|
1690
|
+
)!;
|
|
1691
|
+
vaultSettings.leafAdapters.push(
|
|
1692
|
+
commonAdapter
|
|
1693
|
+
.getApproveAdapter(
|
|
1694
|
+
STRKToken.address,
|
|
1695
|
+
AVNU_EXCHANGE,
|
|
1696
|
+
UNIVERSAL_MANAGE_IDS.APPROVE_SWAP_TOKEN1,
|
|
1697
|
+
)
|
|
1698
|
+
.bind(commonAdapter),
|
|
1699
|
+
);
|
|
1700
|
+
vaultSettings.leafAdapters.push(
|
|
1701
|
+
commonAdapter
|
|
1702
|
+
.getAvnuAdapter(
|
|
1703
|
+
STRKToken.address,
|
|
1704
|
+
lstToken.address,
|
|
1705
|
+
UNIVERSAL_MANAGE_IDS.AVNU_SWAP_REWARDS,
|
|
1706
|
+
false,
|
|
1707
|
+
)
|
|
1708
|
+
.bind(commonAdapter),
|
|
1709
|
+
);
|
|
1079
1710
|
return vaultSettings;
|
|
1080
1711
|
}
|
|
1081
1712
|
|
|
1082
|
-
const AUDIT_URL =
|
|
1713
|
+
const AUDIT_URL = "https://docs.troves.fi/p/security#starknet-vault-kit";
|
|
1083
1714
|
|
|
1084
1715
|
function getFAQs(lstSymbol: string, underlyingSymbol: string): FAQ[] {
|
|
1085
1716
|
return [
|
|
1086
1717
|
{
|
|
1087
1718
|
question: `What is the Hyper ${lstSymbol} Vault?`,
|
|
1088
|
-
answer:
|
|
1089
|
-
`The Hyper ${lstSymbol} Vault is a tokenized strategy that automatically loops your ${lstSymbol} to create up to 5x leverage to hence yield in a very low risk manner.`,
|
|
1719
|
+
answer: `The Hyper ${lstSymbol} Vault is a tokenized strategy that automatically loops your ${lstSymbol} to create up to 5x leverage to hence yield in a very low risk manner.`,
|
|
1090
1720
|
},
|
|
1091
1721
|
{
|
|
1092
1722
|
question: "How does yield allocation work?",
|
|
1093
|
-
answer:
|
|
1094
|
-
`The strategy uses deposited ${lstSymbol} to collateralize it on Vesu, borrow more ${underlyingSymbol} to loop further. Instead of manually doing this, using flash loan, this leverage is created in a single gas efficient step. Our continuous monitoring systems gauge current yield and available liquidity in real time to make sure yield is optimal. For instance, if the looping becomes in-efficient in future, the strategy will rebalance to reduce leverage or simply hold ${lstSymbol} to continue earning yield.`,
|
|
1723
|
+
answer: `The strategy uses deposited ${lstSymbol} to collateralize it on Vesu, borrow more ${underlyingSymbol} to loop further. Instead of manually doing this, using flash loan, this leverage is created in a single gas efficient step. Our continuous monitoring systems gauge current yield and available liquidity in real time to make sure yield is optimal. For instance, if the looping becomes in-efficient in future, the strategy will rebalance to reduce leverage or simply hold ${lstSymbol} to continue earning yield.`,
|
|
1095
1724
|
},
|
|
1096
1725
|
{
|
|
1097
1726
|
question: "Which protocols/dApp are used??",
|
|
1098
1727
|
answer: (
|
|
1099
1728
|
<span>
|
|
1100
|
-
Currently, the LST is from <strong>Endur</strong> while
|
|
1729
|
+
Currently, the LST is from <strong>Endur</strong> while{" "}
|
|
1730
|
+
<strong>Vesu</strong> is used to collateralize the looped position.
|
|
1101
1731
|
</span>
|
|
1102
1732
|
),
|
|
1103
1733
|
},
|
|
1104
1734
|
{
|
|
1105
1735
|
question: "Can I get liquidated?",
|
|
1106
|
-
answer:
|
|
1736
|
+
answer:
|
|
1737
|
+
"The strategy uses highly correlated assets which drastically reduces the risk of liquidation. However, overtime, if left un-monitored, debt can increase enough to trigger a liquidation. But no worries, our continuous monitoring systems look for situations with reduced health factor and balance collateral/debt to bring it back to safe levels. With Troves, you can have a peaceful sleep.",
|
|
1107
1738
|
},
|
|
1108
1739
|
{
|
|
1109
1740
|
question: "What do I receive when I deposit?",
|
|
@@ -1134,107 +1765,209 @@ function getFAQs(lstSymbol: string, underlyingSymbol: string): FAQ[] {
|
|
|
1134
1765
|
}
|
|
1135
1766
|
|
|
1136
1767
|
const _riskFactor: RiskFactor[] = [
|
|
1137
|
-
{
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1768
|
+
{
|
|
1769
|
+
type: RiskType.SMART_CONTRACT_RISK,
|
|
1770
|
+
value: SmartContractRiskLevel.WELL_AUDITED,
|
|
1771
|
+
weight: 25,
|
|
1772
|
+
reason: "Audited by Zellic",
|
|
1773
|
+
},
|
|
1774
|
+
{
|
|
1775
|
+
type: RiskType.LIQUIDATION_RISK,
|
|
1776
|
+
value: LiquidationRiskLevel.VERY_LOW_PROBABILITY,
|
|
1777
|
+
weight: 25,
|
|
1778
|
+
reason: "The collateral and debt are highly correlated",
|
|
1779
|
+
},
|
|
1780
|
+
{
|
|
1781
|
+
type: RiskType.TECHNICAL_RISK,
|
|
1782
|
+
value: TechnicalRiskLevel.STABLE_INFRASTRUCTURE,
|
|
1783
|
+
weight: 25,
|
|
1784
|
+
reason:
|
|
1785
|
+
"Liquidation can only happen if vault is left un-monitored for weeks, which is highly unlikely. We actively monitor all services on a daily basis.",
|
|
1786
|
+
},
|
|
1787
|
+
{
|
|
1788
|
+
type: RiskType.DEPEG_RISK,
|
|
1789
|
+
value: DepegRiskLevel.GENERALLY_STABLE,
|
|
1790
|
+
weight: 25,
|
|
1791
|
+
reason: "Generally stable pegged assets",
|
|
1792
|
+
},
|
|
1141
1793
|
];
|
|
1142
1794
|
|
|
1143
|
-
const btcBorrowableAssets = [
|
|
1144
|
-
'WBTC', 'tBTC', 'LBTC', 'solvBTC'
|
|
1145
|
-
]
|
|
1795
|
+
const btcBorrowableAssets = ["WBTC", "tBTC", "LBTC", "solvBTC"];
|
|
1146
1796
|
|
|
1147
1797
|
const hyperxSTRK: HyperLSTStrategySettings = {
|
|
1148
|
-
vaultAddress: ContractAddr.from(
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1798
|
+
vaultAddress: ContractAddr.from(
|
|
1799
|
+
"0x46c7a54c82b1fe374353859f554a40b8bd31d3e30f742901579e7b57b1b5960",
|
|
1800
|
+
),
|
|
1801
|
+
manager: ContractAddr.from(
|
|
1802
|
+
"0x5d499cd333757f461a0bedaca3dfc4d77320c773037e0aa299f22a6dbfdc03a",
|
|
1803
|
+
),
|
|
1804
|
+
vaultAllocator: ContractAddr.from(
|
|
1805
|
+
"0x511d07953a09bc7c505970891507c5a2486d2ea22752601a14db092186d7caa",
|
|
1806
|
+
),
|
|
1807
|
+
redeemRequestNFT: ContractAddr.from(
|
|
1808
|
+
"0x51e40b839dc0c2feca923f863072673b94abfa2483345be3b30b457a90d095",
|
|
1809
|
+
),
|
|
1810
|
+
aumOracle: ContractAddr.from(
|
|
1811
|
+
"0x48cf709870a1a0d453d37de108e0c41b8b89819ef54f95abc0e2e1f98bbe937",
|
|
1812
|
+
),
|
|
1153
1813
|
leafAdapters: [],
|
|
1154
1814
|
adapters: [],
|
|
1155
1815
|
targetHealthFactor: 1.1,
|
|
1156
1816
|
minHealthFactor: 1.05,
|
|
1157
1817
|
borrowable_assets: [
|
|
1158
|
-
...Global.getDefaultTokens()
|
|
1159
|
-
|
|
1160
|
-
|
|
1818
|
+
...Global.getDefaultTokens()
|
|
1819
|
+
.filter((token) => token.symbol === "STRK")
|
|
1820
|
+
.map((token) => ({ token, poolId: VesuPools.Re7xSTRK })),
|
|
1821
|
+
...Global.getDefaultTokens()
|
|
1822
|
+
.filter((token) => token.symbol === "STRK")
|
|
1823
|
+
.map((token) => ({ token, poolId: VesuPools.Prime })),
|
|
1824
|
+
...Global.getDefaultTokens()
|
|
1825
|
+
.filter((token) => token.symbol === "STRK")
|
|
1826
|
+
.map((token) => ({ token, poolId: VesuPools.Re7STRK })), // v2, new alt for Re7xSTRK
|
|
1161
1827
|
],
|
|
1162
|
-
underlyingToken: Global.getDefaultTokens().find(
|
|
1828
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
1829
|
+
(token) => token.symbol === "STRK",
|
|
1830
|
+
)!,
|
|
1163
1831
|
defaultPoolId: VesuPools.Re7xSTRK,
|
|
1164
|
-
}
|
|
1832
|
+
};
|
|
1165
1833
|
|
|
1166
1834
|
const hyperxWBTC: HyperLSTStrategySettings = {
|
|
1167
|
-
vaultAddress: ContractAddr.from(
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1835
|
+
vaultAddress: ContractAddr.from(
|
|
1836
|
+
"0x2da9d0f96a46b453f55604313785dc866424240b1c6811d13bef594343db818",
|
|
1837
|
+
),
|
|
1838
|
+
manager: ContractAddr.from(
|
|
1839
|
+
"0x75866db44c81e6986f06035206ee9c7d15833ddb22d6a22c016cfb5c866a491",
|
|
1840
|
+
),
|
|
1841
|
+
vaultAllocator: ContractAddr.from(
|
|
1842
|
+
"0x57b5c1bb457b5e840a2714ae53ada87d77be2f3fd33a59b4fe709ef20c020c1",
|
|
1843
|
+
),
|
|
1844
|
+
redeemRequestNFT: ContractAddr.from(
|
|
1845
|
+
"0x7a5dc288325456f05e70e9616e16bc02ffbe448f4b89f80b47c0970b989c7c",
|
|
1846
|
+
),
|
|
1847
|
+
aumOracle: ContractAddr.from(
|
|
1848
|
+
"0x258f8a0ca0d21f542e48ad89d00e92dc4d9db4999084f50ef9c22dfb1e83023",
|
|
1849
|
+
),
|
|
1172
1850
|
leafAdapters: [],
|
|
1173
1851
|
adapters: [],
|
|
1174
1852
|
targetHealthFactor: 1.1,
|
|
1175
1853
|
minHealthFactor: 1.05,
|
|
1176
1854
|
borrowable_assets: [
|
|
1177
1855
|
// allow all BTC flavours borrowing on Re7xBTC pool
|
|
1178
|
-
...btcBorrowableAssets
|
|
1856
|
+
...btcBorrowableAssets
|
|
1857
|
+
.map(
|
|
1858
|
+
(asset) =>
|
|
1859
|
+
Global.getDefaultTokens().find((token) => token.symbol === asset)!,
|
|
1860
|
+
)
|
|
1861
|
+
.map((token) => ({ token, poolId: VesuPools.Re7xBTC })),
|
|
1179
1862
|
// allow only WBTC borrowing on Prime pool
|
|
1180
|
-
...Global.getDefaultTokens()
|
|
1863
|
+
...Global.getDefaultTokens()
|
|
1864
|
+
.filter((token) => token.symbol === "WBTC")
|
|
1865
|
+
.map((token) => ({ token, poolId: VesuPools.Prime })),
|
|
1181
1866
|
],
|
|
1182
|
-
underlyingToken: Global.getDefaultTokens().find(
|
|
1867
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
1868
|
+
(token) => token.symbol === "WBTC",
|
|
1869
|
+
)!,
|
|
1183
1870
|
defaultPoolId: VesuPools.Re7xBTC,
|
|
1184
|
-
redemptionRouter: ContractAddr.from(
|
|
1185
|
-
|
|
1871
|
+
redemptionRouter: ContractAddr.from(
|
|
1872
|
+
"0x6ea649f402898f69baf775c1afdd08522c071c640b9c4460192070ec2b96417",
|
|
1873
|
+
),
|
|
1874
|
+
};
|
|
1186
1875
|
|
|
1187
1876
|
const hyperxtBTC: HyperLSTStrategySettings = {
|
|
1188
|
-
vaultAddress: ContractAddr.from(
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1877
|
+
vaultAddress: ContractAddr.from(
|
|
1878
|
+
"0x47d5f68477e5637ce0e56436c6b5eee5a354e6828995dae106b11a48679328",
|
|
1879
|
+
),
|
|
1880
|
+
manager: ContractAddr.from(
|
|
1881
|
+
"0xc4cc3e08029a0ae076f5fdfca70575abb78d23c5cd1c49a957f7e697885401",
|
|
1882
|
+
),
|
|
1883
|
+
vaultAllocator: ContractAddr.from(
|
|
1884
|
+
"0x50bbd4fe69f841ecb13b2619fe50ebfa4e8944671b5d0ebf7868fd80c61b31e",
|
|
1885
|
+
),
|
|
1886
|
+
redeemRequestNFT: ContractAddr.from(
|
|
1887
|
+
"0xeac9032f02057779816e38a6cb9185d12d86b3aacc9949b96b36de359c1e3",
|
|
1888
|
+
),
|
|
1889
|
+
aumOracle: ContractAddr.from(
|
|
1890
|
+
"0x7e0d05cb7ba3f7db77a36c21c21583b5a524c2e685c08c24b3554911fb4a039",
|
|
1891
|
+
),
|
|
1193
1892
|
leafAdapters: [],
|
|
1194
1893
|
adapters: [],
|
|
1195
1894
|
targetHealthFactor: 1.1,
|
|
1196
1895
|
minHealthFactor: 1.05,
|
|
1197
1896
|
borrowable_assets: [
|
|
1198
|
-
...Global.getDefaultTokens()
|
|
1897
|
+
...Global.getDefaultTokens()
|
|
1898
|
+
.filter((token) => token.symbol === "tBTC" || token.symbol === "WBTC")
|
|
1899
|
+
.map((token) => ({ token, poolId: VesuPools.Re7xBTC })),
|
|
1199
1900
|
],
|
|
1200
|
-
underlyingToken: Global.getDefaultTokens().find(
|
|
1901
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
1902
|
+
(token) => token.symbol === "tBTC",
|
|
1903
|
+
)!,
|
|
1201
1904
|
defaultPoolId: VesuPools.Re7xBTC,
|
|
1202
|
-
redemptionRouter: ContractAddr.from(
|
|
1203
|
-
|
|
1905
|
+
redemptionRouter: ContractAddr.from(
|
|
1906
|
+
"0x3de9c409d1e357e25778fb7a3e2e2393666956846a5c2caa607296fa8e76b5d",
|
|
1907
|
+
),
|
|
1908
|
+
};
|
|
1204
1909
|
|
|
1205
1910
|
const hyperxsBTC: HyperLSTStrategySettings = {
|
|
1206
|
-
vaultAddress: ContractAddr.from(
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1911
|
+
vaultAddress: ContractAddr.from(
|
|
1912
|
+
"0x437ef1e7d0f100b2e070b7a65cafec0b2be31b0290776da8b4112f5473d8d9",
|
|
1913
|
+
),
|
|
1914
|
+
manager: ContractAddr.from(
|
|
1915
|
+
"0xc9ac023090625b0be3f6532ca353f086746f9c09f939dbc1b2613f09e5f821",
|
|
1916
|
+
),
|
|
1917
|
+
vaultAllocator: ContractAddr.from(
|
|
1918
|
+
"0x60c2d856936b975459a5b4eb28b8672d91f757bd76cebb6241f8d670185dc01",
|
|
1919
|
+
),
|
|
1920
|
+
redeemRequestNFT: ContractAddr.from(
|
|
1921
|
+
"0x429e8ee8bc7ecd1ade72630d350a2e0f10f9a2507c45f188ba17fe8f2ab4cf3",
|
|
1922
|
+
),
|
|
1923
|
+
aumOracle: ContractAddr.from(
|
|
1924
|
+
"0x149298ade3e79ec6cbdac6cfad289c57504eaf54e590939136ed1ceca60c345",
|
|
1925
|
+
),
|
|
1211
1926
|
leafAdapters: [],
|
|
1212
1927
|
adapters: [],
|
|
1213
1928
|
targetHealthFactor: 1.1,
|
|
1214
1929
|
minHealthFactor: 1.05,
|
|
1215
1930
|
borrowable_assets: [
|
|
1216
|
-
...Global.getDefaultTokens()
|
|
1931
|
+
...Global.getDefaultTokens()
|
|
1932
|
+
.filter((token) => token.symbol === "solvBTC")
|
|
1933
|
+
.map((token) => ({ token, poolId: VesuPools.Re7xBTC })),
|
|
1217
1934
|
],
|
|
1218
|
-
underlyingToken: Global.getDefaultTokens().find(
|
|
1935
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
1936
|
+
(token) => token.symbol === "solvBTC",
|
|
1937
|
+
)!,
|
|
1219
1938
|
defaultPoolId: VesuPools.Re7xBTC,
|
|
1220
|
-
}
|
|
1939
|
+
};
|
|
1221
1940
|
|
|
1222
1941
|
const hyperxLBTC: HyperLSTStrategySettings = {
|
|
1223
|
-
vaultAddress: ContractAddr.from(
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1942
|
+
vaultAddress: ContractAddr.from(
|
|
1943
|
+
"0x64cf24d4883fe569926419a0569ab34497c6956a1a308fa883257f7486d7030",
|
|
1944
|
+
),
|
|
1945
|
+
manager: ContractAddr.from(
|
|
1946
|
+
"0x203530a4022a99b8f4b406aaf33b0849d43ad7422c1d5cc14ff8c667abec6c0",
|
|
1947
|
+
),
|
|
1948
|
+
vaultAllocator: ContractAddr.from(
|
|
1949
|
+
"0x7dbc8ccd4eabce6ea6c19e0e5c9ccca3a93bd510303b9e071cbe25fc508546e",
|
|
1950
|
+
),
|
|
1951
|
+
redeemRequestNFT: ContractAddr.from(
|
|
1952
|
+
"0x5ee66a39af9aef3d0d48982b4a63e8bd2a5bad021916bd87fb0eae3a26800b8",
|
|
1953
|
+
),
|
|
1954
|
+
aumOracle: ContractAddr.from(
|
|
1955
|
+
"0x23d69e4391fa72d10e625e7575d8bddbb4aff96f04503f83fdde23123bf41d0",
|
|
1956
|
+
),
|
|
1228
1957
|
leafAdapters: [],
|
|
1229
1958
|
adapters: [],
|
|
1230
1959
|
targetHealthFactor: 1.1,
|
|
1231
1960
|
minHealthFactor: 1.05,
|
|
1232
1961
|
borrowable_assets: [
|
|
1233
|
-
...Global.getDefaultTokens()
|
|
1962
|
+
...Global.getDefaultTokens()
|
|
1963
|
+
.filter((token) => token.symbol === "LBTC")
|
|
1964
|
+
.map((token) => ({ token, poolId: VesuPools.Re7xBTC })),
|
|
1234
1965
|
],
|
|
1235
|
-
underlyingToken: Global.getDefaultTokens().find(
|
|
1966
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
1967
|
+
(token) => token.symbol === "LBTC",
|
|
1968
|
+
)!,
|
|
1236
1969
|
defaultPoolId: VesuPools.Re7xBTC,
|
|
1237
|
-
}
|
|
1970
|
+
};
|
|
1238
1971
|
|
|
1239
1972
|
function getInvestmentSteps(lstSymbol: string, underlyingSymbol: string) {
|
|
1240
1973
|
return [
|
|
@@ -1242,172 +1975,180 @@ function getInvestmentSteps(lstSymbol: string, underlyingSymbol: string) {
|
|
|
1242
1975
|
`The vault manager loops the ${underlyingSymbol} to buy ${lstSymbol}`,
|
|
1243
1976
|
`The vault manager collateralizes the ${lstSymbol} on Vesu`,
|
|
1244
1977
|
`The vault manager borrows more ${underlyingSymbol} to loop further`,
|
|
1245
|
-
`If required, adjust leverage or re-allocate assets within LST pool on Vesu to optimize yield
|
|
1246
|
-
]
|
|
1978
|
+
`If required, adjust leverage or re-allocate assets within LST pool on Vesu to optimize yield`,
|
|
1979
|
+
];
|
|
1247
1980
|
}
|
|
1248
1981
|
|
|
1249
1982
|
// Helper to get maxTVL based on LST symbol (matching client values)
|
|
1250
1983
|
function getMaxTVL(lstSymbol: string): Web3Number {
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1984
|
+
const lstMaxTVLs: Record<string, number> = {
|
|
1985
|
+
xWBTC: 5,
|
|
1986
|
+
xLBTC: 5,
|
|
1987
|
+
xtBTC: 5,
|
|
1988
|
+
xsBTC: 5,
|
|
1989
|
+
xSTRK: 7000000,
|
|
1990
|
+
};
|
|
1258
1991
|
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1992
|
+
const maxTVLValue = lstMaxTVLs[lstSymbol] || 0;
|
|
1993
|
+
const token = Global.getDefaultTokens().find(
|
|
1994
|
+
(token) => token.symbol === lstSymbol,
|
|
1995
|
+
);
|
|
1996
|
+
if (!token) {
|
|
1997
|
+
return Web3Number.fromWei(0, 18);
|
|
1998
|
+
}
|
|
1999
|
+
return Web3Number.fromWei(maxTVLValue, token.decimals);
|
|
1267
2000
|
}
|
|
1268
2001
|
|
|
1269
2002
|
// Helper to create Hyper LST strategy settings
|
|
1270
2003
|
function createHyperLSTSettings(
|
|
1271
|
-
|
|
1272
|
-
|
|
2004
|
+
lstSymbol: string,
|
|
2005
|
+
underlyingSymbol: string,
|
|
1273
2006
|
): StrategySettings {
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
2007
|
+
const depositToken = Global.getDefaultTokens().find(
|
|
2008
|
+
(token) => token.symbol === lstSymbol,
|
|
2009
|
+
)!;
|
|
2010
|
+
return {
|
|
2011
|
+
maxTVL: getMaxTVL(lstSymbol),
|
|
2012
|
+
isPaused: false,
|
|
2013
|
+
liveStatus: StrategyLiveStatus.HOT,
|
|
2014
|
+
isAudited: true,
|
|
2015
|
+
isInstantWithdrawal: false,
|
|
2016
|
+
hideHarvestInfo: true,
|
|
2017
|
+
quoteToken: depositToken,
|
|
2018
|
+
showWithdrawalWarningModal: false,
|
|
2019
|
+
alerts: [
|
|
2020
|
+
{
|
|
2021
|
+
tab: "withdraw" as const,
|
|
2022
|
+
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.",
|
|
2023
|
+
type: "info" as const,
|
|
2024
|
+
},
|
|
2025
|
+
{
|
|
2026
|
+
tab: "deposit" as const,
|
|
2027
|
+
text: (
|
|
2028
|
+
<>
|
|
2029
|
+
To acquire the LST, please visit{" "}
|
|
2030
|
+
<a
|
|
2031
|
+
href="https://app.endur.fi"
|
|
2032
|
+
target="_blank"
|
|
2033
|
+
rel="noopener noreferrer"
|
|
2034
|
+
>
|
|
2035
|
+
endur.fi
|
|
2036
|
+
</a>
|
|
2037
|
+
</>
|
|
2038
|
+
),
|
|
2039
|
+
type: "info" as const,
|
|
2040
|
+
},
|
|
2041
|
+
{
|
|
2042
|
+
tab: "deposit" as const,
|
|
2043
|
+
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.",
|
|
2044
|
+
type: "info" as const,
|
|
2045
|
+
},
|
|
2046
|
+
],
|
|
2047
|
+
};
|
|
1315
2048
|
}
|
|
1316
2049
|
|
|
1317
2050
|
const HYPER_LST_SECURITY = {
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
2051
|
+
auditStatus: AuditStatus.AUDITED,
|
|
2052
|
+
sourceCode: {
|
|
2053
|
+
type: SourceCodeType.CLOSED_SOURCE,
|
|
2054
|
+
contractLink: "https://github.com/trovesfi/troves-contracts",
|
|
2055
|
+
},
|
|
2056
|
+
accessControl: {
|
|
2057
|
+
type: AccessControlType.STANDARD_ACCOUNT,
|
|
2058
|
+
addresses: [ContractAddr.from("0x0")],
|
|
2059
|
+
timeLock: "2 Days",
|
|
2060
|
+
},
|
|
1328
2061
|
};
|
|
1329
2062
|
|
|
1330
2063
|
const HYPER_LST_REDEMPTION_INFO: RedemptionInfo = {
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
2064
|
+
instantWithdrawalVault: InstantWithdrawalVault.NO,
|
|
2065
|
+
redemptionsInfo: [
|
|
2066
|
+
{
|
|
2067
|
+
title: "Typical Duration",
|
|
2068
|
+
description: "1-2 hours",
|
|
2069
|
+
},
|
|
2070
|
+
],
|
|
2071
|
+
alerts: [
|
|
2072
|
+
{
|
|
2073
|
+
type: "info",
|
|
2074
|
+
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.",
|
|
2075
|
+
tab: "withdraw",
|
|
2076
|
+
},
|
|
2077
|
+
],
|
|
1341
2078
|
};
|
|
1342
2079
|
|
|
1343
2080
|
function getStrategySettings(
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
2081
|
+
lstSymbol: string,
|
|
2082
|
+
underlyingSymbol: string,
|
|
2083
|
+
settings: HyperLSTStrategySettings,
|
|
2084
|
+
isPreview: boolean = false,
|
|
1348
2085
|
): IStrategyMetadata<HyperLSTStrategySettings> {
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
2086
|
+
return {
|
|
2087
|
+
id: `hyper_${lstSymbol.toLowerCase()}`,
|
|
2088
|
+
name: `Hyper ${lstSymbol}`,
|
|
2089
|
+
description: getDescription(lstSymbol, underlyingSymbol),
|
|
2090
|
+
address: settings.vaultAddress,
|
|
2091
|
+
launchBlock: 0,
|
|
2092
|
+
type: "Other",
|
|
2093
|
+
vaultType: {
|
|
2094
|
+
type: VaultType.LOOPING,
|
|
2095
|
+
description: `Creates leveraged looping position on ${lstSymbol} by borrowing ${underlyingSymbol} to increase yield`,
|
|
2096
|
+
},
|
|
2097
|
+
depositTokens: [
|
|
2098
|
+
Global.getDefaultTokens().find((token) => token.symbol === lstSymbol)!,
|
|
2099
|
+
],
|
|
2100
|
+
additionalInfo: getLooperSettings(
|
|
2101
|
+
lstSymbol,
|
|
2102
|
+
underlyingSymbol,
|
|
2103
|
+
settings,
|
|
2104
|
+
lstSymbol === "xSTRK" ? VesuPools.Re7xSTRK : VesuPools.Re7xBTC,
|
|
2105
|
+
),
|
|
2106
|
+
risk: {
|
|
2107
|
+
riskFactor: _riskFactor,
|
|
2108
|
+
netRisk:
|
|
2109
|
+
_riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
|
|
2110
|
+
_riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
2111
|
+
notARisks: getNoRiskTags(_riskFactor),
|
|
2112
|
+
},
|
|
2113
|
+
auditUrl: AUDIT_URL,
|
|
2114
|
+
protocols: [Protocols.ENDUR, Protocols.VESU],
|
|
2115
|
+
curator: {
|
|
2116
|
+
name: "Unwrap Labs",
|
|
2117
|
+
logo: "https://assets.troves.fi/integrations/unwraplabs/white.png",
|
|
2118
|
+
},
|
|
2119
|
+
settings: createHyperLSTSettings(lstSymbol, underlyingSymbol),
|
|
2120
|
+
contractDetails: getContractDetails(settings),
|
|
2121
|
+
faqs: getFAQs(lstSymbol, underlyingSymbol),
|
|
2122
|
+
investmentSteps: getInvestmentSteps(lstSymbol, underlyingSymbol),
|
|
2123
|
+
isPreview: isPreview,
|
|
2124
|
+
apyMethodology:
|
|
2125
|
+
"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.",
|
|
2126
|
+
realizedAPYMethodology:
|
|
2127
|
+
"The realizedAPY is based on past 14 days performance by the vault",
|
|
2128
|
+
tags: lstSymbol.includes("BTC")
|
|
2129
|
+
? [StrategyTag.BTC, StrategyTag.LEVERED]
|
|
2130
|
+
: [StrategyTag.LEVERED],
|
|
2131
|
+
security: HYPER_LST_SECURITY,
|
|
2132
|
+
redemptionInfo: HYPER_LST_REDEMPTION_INFO,
|
|
2133
|
+
usualTimeToEarnings: "2 weeks",
|
|
2134
|
+
usualTimeToEarningsDescription:
|
|
2135
|
+
"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.",
|
|
2136
|
+
points: [
|
|
2137
|
+
{
|
|
2138
|
+
multiplier: 4,
|
|
2139
|
+
logo: "https://endur.fi/favicon.ico",
|
|
2140
|
+
toolTip:
|
|
2141
|
+
"This strategy holds xSTRK. Earn 3-4x Endur points on your xSTRK due to the leverage. Points can be found on endur.fi.",
|
|
2142
|
+
},
|
|
2143
|
+
],
|
|
2144
|
+
};
|
|
1404
2145
|
}
|
|
1405
2146
|
|
|
1406
2147
|
export const HyperLSTStrategies: IStrategyMetadata<HyperLSTStrategySettings>[] =
|
|
1407
2148
|
[
|
|
1408
|
-
getStrategySettings(
|
|
1409
|
-
getStrategySettings(
|
|
1410
|
-
getStrategySettings(
|
|
1411
|
-
getStrategySettings(
|
|
1412
|
-
getStrategySettings(
|
|
1413
|
-
]
|
|
2149
|
+
getStrategySettings("xSTRK", "STRK", hyperxSTRK, false),
|
|
2150
|
+
getStrategySettings("xWBTC", "WBTC", hyperxWBTC, false),
|
|
2151
|
+
getStrategySettings("xtBTC", "tBTC", hyperxtBTC, false),
|
|
2152
|
+
getStrategySettings("xsBTC", "solvBTC", hyperxsBTC, false),
|
|
2153
|
+
getStrategySettings("xLBTC", "LBTC", hyperxLBTC, false),
|
|
2154
|
+
];
|