@strkfarm/sdk 2.0.0-staging.22 → 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 +912 -322
- package/dist/index.browser.mjs +836 -246
- package/dist/index.d.ts +24 -4
- package/dist/index.js +837 -246
- package/dist/index.mjs +836 -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/index.ts +1 -0
- package/src/strategies/types.ts +4 -0
- package/src/strategies/universal-lst-muliplier-strategy.tsx +1385 -558
- package/src/strategies/universal-strategy.tsx +7 -4
|
@@ -1,44 +1,105 @@
|
|
|
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";
|
|
13
63
|
import ERC4626Abi from "@/data/erc4626.abi.json";
|
|
14
64
|
import { HealthFactorMath } from "@/utils/health-factor-math";
|
|
15
65
|
import { findMaxInputWithSlippage } from "@/utils/math-utils";
|
|
66
|
+
import { LSTPriceType } from "./types";
|
|
16
67
|
|
|
17
68
|
export interface HyperLSTStrategySettings extends UniversalStrategySettings {
|
|
18
|
-
borrowable_assets: { token: TokenInfo
|
|
69
|
+
borrowable_assets: { token: TokenInfo; poolId: ContractAddr }[];
|
|
19
70
|
underlyingToken: TokenInfo;
|
|
20
71
|
defaultPoolId: ContractAddr;
|
|
21
72
|
}
|
|
22
73
|
|
|
23
74
|
export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTStrategySettings> {
|
|
24
|
-
|
|
25
75
|
private quoteAmountToFetchPrice = new Web3Number(1, 18);
|
|
26
|
-
|
|
27
|
-
constructor(
|
|
76
|
+
|
|
77
|
+
constructor(
|
|
78
|
+
config: IConfig,
|
|
79
|
+
pricer: PricerBase,
|
|
80
|
+
metadata: IStrategyMetadata<HyperLSTStrategySettings>,
|
|
81
|
+
) {
|
|
28
82
|
super(config, pricer, metadata);
|
|
29
83
|
|
|
30
|
-
const STRKToken = Global.getDefaultTokens().find(
|
|
84
|
+
const STRKToken = Global.getDefaultTokens().find(
|
|
85
|
+
(token) => token.symbol === "STRK",
|
|
86
|
+
)!;
|
|
31
87
|
const underlyingToken = this.getLSTUnderlyingTokenInfo();
|
|
32
88
|
if (underlyingToken.address.eq(STRKToken.address)) {
|
|
33
89
|
this.quoteAmountToFetchPrice = new Web3Number(100, 18);
|
|
34
90
|
} else {
|
|
35
91
|
// else this BTC
|
|
36
|
-
this.quoteAmountToFetchPrice = new Web3Number(
|
|
92
|
+
this.quoteAmountToFetchPrice = new Web3Number(
|
|
93
|
+
0.01,
|
|
94
|
+
this.asset().decimals,
|
|
95
|
+
);
|
|
37
96
|
}
|
|
38
97
|
}
|
|
39
98
|
|
|
40
99
|
asset() {
|
|
41
|
-
return this.getVesuSameTokenAdapter(
|
|
100
|
+
return this.getVesuSameTokenAdapter(
|
|
101
|
+
this.metadata.additionalInfo.defaultPoolId,
|
|
102
|
+
).config.collateral;
|
|
42
103
|
}
|
|
43
104
|
|
|
44
105
|
getTag() {
|
|
@@ -47,7 +108,13 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
47
108
|
|
|
48
109
|
// Vesu adapter with LST and base token match
|
|
49
110
|
getVesuSameTokenAdapter(poolId: ContractAddr) {
|
|
50
|
-
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;
|
|
51
118
|
baseAdapter.networkConfig = this.config;
|
|
52
119
|
baseAdapter.pricer = this.pricer;
|
|
53
120
|
return baseAdapter;
|
|
@@ -57,7 +124,8 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
57
124
|
// todo support lending assets of underlying as well (like if xSTRK looping is not viable, simply supply STRK)
|
|
58
125
|
getVesuAdapters() {
|
|
59
126
|
const adapters: VesuAdapter[] = [];
|
|
60
|
-
for (const borrowableInfo of this.metadata.additionalInfo
|
|
127
|
+
for (const borrowableInfo of this.metadata.additionalInfo
|
|
128
|
+
.borrowable_assets) {
|
|
61
129
|
// do not add adapter if the pool id does not match
|
|
62
130
|
const asset = borrowableInfo.token;
|
|
63
131
|
const poolId = borrowableInfo.poolId;
|
|
@@ -66,8 +134,8 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
66
134
|
collateral: this.asset(),
|
|
67
135
|
debt: asset,
|
|
68
136
|
vaultAllocator: this.metadata.additionalInfo.vaultAllocator,
|
|
69
|
-
id:
|
|
70
|
-
})
|
|
137
|
+
id: "",
|
|
138
|
+
});
|
|
71
139
|
vesuAdapter1.pricer = this.pricer;
|
|
72
140
|
vesuAdapter1.networkConfig = this.config;
|
|
73
141
|
adapters.push(vesuAdapter1);
|
|
@@ -79,7 +147,7 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
79
147
|
// No rewards on collateral or borrowing of LST assets
|
|
80
148
|
protected async getRewardsAUM(prevAum: Web3Number): Promise<Web3Number> {
|
|
81
149
|
const lstToken = this.asset();
|
|
82
|
-
if (lstToken.symbol ===
|
|
150
|
+
if (lstToken.symbol === "xSTRK") {
|
|
83
151
|
return super.getRewardsAUM(prevAum);
|
|
84
152
|
}
|
|
85
153
|
return Web3Number.fromWei("0", lstToken.decimals);
|
|
@@ -92,110 +160,163 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
92
160
|
const quote = await ekuboQuoter.getQuote(
|
|
93
161
|
lstTokenInfo.address.address,
|
|
94
162
|
lstUnderlyingTokenInfo.address.address,
|
|
95
|
-
this.quoteAmountToFetchPrice
|
|
163
|
+
this.quoteAmountToFetchPrice,
|
|
96
164
|
);
|
|
97
165
|
|
|
98
166
|
// in Underlying
|
|
99
|
-
const outputAmount = Web3Number.fromWei(
|
|
100
|
-
|
|
167
|
+
const outputAmount = Web3Number.fromWei(
|
|
168
|
+
quote.total_calculated,
|
|
169
|
+
lstUnderlyingTokenInfo.decimals,
|
|
170
|
+
);
|
|
171
|
+
const price =
|
|
172
|
+
outputAmount.toNumber() / this.quoteAmountToFetchPrice.toNumber();
|
|
101
173
|
logger.verbose(`${this.getTag()}:: LST Dex Price: ${price}`);
|
|
102
174
|
return price;
|
|
103
175
|
}
|
|
104
176
|
|
|
105
|
-
|
|
106
177
|
async getAvnuSwapMultiplyCall(params: {
|
|
107
|
-
isDeposit: boolean
|
|
108
|
-
leg1DepositAmount: Web3Number
|
|
178
|
+
isDeposit: boolean;
|
|
179
|
+
leg1DepositAmount: Web3Number;
|
|
109
180
|
}) {
|
|
110
|
-
assert(
|
|
181
|
+
assert(
|
|
182
|
+
params.isDeposit,
|
|
183
|
+
"Only deposit is supported in getAvnuSwapMultiplyCall",
|
|
184
|
+
);
|
|
111
185
|
// TODO use a varibale for 1.02
|
|
112
|
-
const maxBorrowableAmounts = await this.getMaxBorrowableAmount({
|
|
186
|
+
const maxBorrowableAmounts = await this.getMaxBorrowableAmount({
|
|
187
|
+
isAPYComputation: false,
|
|
188
|
+
});
|
|
113
189
|
const allVesuAdapters = this.getVesuAdapters();
|
|
114
190
|
let remainingAmount = params.leg1DepositAmount;
|
|
115
191
|
const lstExRate = await this.getLSTExchangeRate();
|
|
116
|
-
const baseAssetPrice = await this.pricer.getPrice(
|
|
192
|
+
const baseAssetPrice = await this.pricer.getPrice(
|
|
193
|
+
this.getLSTUnderlyingTokenInfo().symbol,
|
|
194
|
+
);
|
|
117
195
|
const lstPrice = baseAssetPrice.price * lstExRate;
|
|
118
196
|
for (let i = 0; i < maxBorrowableAmounts.maxBorrowables.length; i++) {
|
|
119
197
|
const maxBorrowable = maxBorrowableAmounts.maxBorrowables[i];
|
|
120
|
-
const vesuAdapter = allVesuAdapters.find(adapter =>
|
|
198
|
+
const vesuAdapter = allVesuAdapters.find((adapter) =>
|
|
199
|
+
adapter.config.debt.address.eq(maxBorrowable.borrowableAsset.address),
|
|
200
|
+
);
|
|
121
201
|
if (!vesuAdapter) {
|
|
122
|
-
throw new Error(
|
|
202
|
+
throw new Error(
|
|
203
|
+
`${this.getTag()}::getAvnuSwapMultiplyCall: vesuAdapter not found for borrowable asset: ${maxBorrowable.borrowableAsset.symbol}`,
|
|
204
|
+
);
|
|
123
205
|
}
|
|
124
206
|
const maxLTV = await vesuAdapter.getLTVConfig(this.config);
|
|
125
|
-
const debtPrice = await this.pricer.getPrice(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
+
);
|
|
134
219
|
const amountToDeposit = remainingAmount.minimum(maxAmountToDeposit);
|
|
135
|
-
logger.verbose(
|
|
220
|
+
logger.verbose(
|
|
221
|
+
`${this.getTag()}::getAvnuSwapMultiplyCall::${vesuAdapter.config.debt.symbol}:: remainingAmount: ${remainingAmount}, amountToDeposit: ${amountToDeposit}, depositAmount: ${amountToDeposit}, maxBorrowable: ${maxBorrowable.amount}`,
|
|
222
|
+
);
|
|
136
223
|
const call = await this._getAvnuDepositSwapLegCall({
|
|
137
224
|
isDeposit: params.isDeposit,
|
|
138
225
|
// adjust decimals of debt asset
|
|
139
226
|
leg1DepositAmount: amountToDeposit,
|
|
140
227
|
minHF: 1.1, // undo
|
|
141
|
-
vesuAdapter: vesuAdapter
|
|
228
|
+
vesuAdapter: vesuAdapter,
|
|
142
229
|
});
|
|
143
230
|
remainingAmount = remainingAmount.minus(amountToDeposit);
|
|
144
231
|
|
|
145
232
|
// return the first possible call because computing all calls at a time
|
|
146
233
|
// is not efficinet for swaps
|
|
147
|
-
return {call, vesuAdapter};
|
|
234
|
+
return { call, vesuAdapter };
|
|
148
235
|
}
|
|
149
|
-
throw new Error(
|
|
236
|
+
throw new Error(
|
|
237
|
+
`${this.getTag()}::getAvnuSwapMultiplyCall: no calls found`,
|
|
238
|
+
);
|
|
150
239
|
}
|
|
151
240
|
|
|
152
241
|
async _getAvnuDepositSwapLegCall(params: {
|
|
153
|
-
isDeposit: boolean
|
|
154
|
-
leg1DepositAmount: Web3Number
|
|
155
|
-
minHF: number
|
|
156
|
-
vesuAdapter: VesuAdapter
|
|
242
|
+
isDeposit: boolean;
|
|
243
|
+
leg1DepositAmount: Web3Number;
|
|
244
|
+
minHF: number; // e.g. 1.01
|
|
245
|
+
vesuAdapter: VesuAdapter;
|
|
157
246
|
}) {
|
|
158
247
|
const { vesuAdapter } = params;
|
|
159
|
-
logger.verbose(
|
|
160
|
-
|
|
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
|
+
);
|
|
161
255
|
// add collateral
|
|
162
256
|
// borrow STRK (e.g.)
|
|
163
257
|
// approve and swap strk
|
|
164
258
|
// add collateral again
|
|
165
259
|
|
|
166
|
-
|
|
167
260
|
const legLTV = await vesuAdapter.getLTVConfig(this.config);
|
|
168
|
-
logger.verbose(
|
|
261
|
+
logger.verbose(
|
|
262
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall legLTV: ${legLTV}`,
|
|
263
|
+
);
|
|
169
264
|
const existingPositions = await vesuAdapter.getPositions(this.config);
|
|
170
|
-
const collateralisation = await vesuAdapter.getCollateralization(
|
|
265
|
+
const collateralisation = await vesuAdapter.getCollateralization(
|
|
266
|
+
this.config,
|
|
267
|
+
);
|
|
171
268
|
const existingCollateralInfo = existingPositions[0];
|
|
172
269
|
const existingDebtInfo = existingPositions[1];
|
|
173
270
|
logger.debug(`${this.getTag()}::_getAvnuDepositSwapLegCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
174
271
|
existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
175
272
|
|
|
176
273
|
// - Prices as seen by Vesu contracts, ideal for HF math
|
|
177
|
-
// 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
|
|
178
275
|
// collateral and debt as equal.
|
|
179
|
-
const collateralPrice =
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
+
);
|
|
183
288
|
|
|
184
289
|
const debtTokenInfo = vesuAdapter.config.debt;
|
|
185
290
|
let newDepositAmount = params.leg1DepositAmount;
|
|
186
|
-
const totalCollateral = existingCollateralInfo.amount.plus(
|
|
187
|
-
|
|
291
|
+
const totalCollateral = existingCollateralInfo.amount.plus(
|
|
292
|
+
params.leg1DepositAmount,
|
|
293
|
+
);
|
|
294
|
+
logger.verbose(
|
|
295
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall totalCollateral: ${totalCollateral}`,
|
|
296
|
+
);
|
|
188
297
|
const totalDebtAmount = new Web3Number(
|
|
189
|
-
totalCollateral
|
|
190
|
-
|
|
298
|
+
totalCollateral
|
|
299
|
+
.multipliedBy(collateralPrice)
|
|
300
|
+
.multipliedBy(legLTV)
|
|
301
|
+
.dividedBy(debtPrice)
|
|
302
|
+
.dividedBy(params.minHF)
|
|
303
|
+
.toString(),
|
|
304
|
+
debtTokenInfo.decimals,
|
|
191
305
|
);
|
|
192
306
|
let debtAmount = totalDebtAmount.minus(existingDebtInfo.amount);
|
|
193
|
-
logger.verbose(
|
|
194
|
-
|
|
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
|
+
);
|
|
195
314
|
|
|
196
315
|
// if the debt amount is greater than 0 and the max borrowable amount is 0, skip
|
|
197
316
|
if (debtAmount.gt(0) && maxBorrowable.amount.eq(0)) {
|
|
198
|
-
logger.verbose(
|
|
317
|
+
logger.verbose(
|
|
318
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall maxBorrowable is 0, skipping`,
|
|
319
|
+
);
|
|
199
320
|
return undefined;
|
|
200
321
|
} else if (debtAmount.gt(0) && maxBorrowable.amount.gt(0)) {
|
|
201
322
|
debtAmount = maxBorrowable.amount.minimum(debtAmount);
|
|
@@ -206,58 +327,78 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
206
327
|
params.minHF,
|
|
207
328
|
legLTV,
|
|
208
329
|
collateralPrice,
|
|
209
|
-
this.asset()
|
|
330
|
+
this.asset(),
|
|
331
|
+
);
|
|
332
|
+
newDepositAmount = totalCollateralRequired.minus(
|
|
333
|
+
existingCollateralInfo.amount,
|
|
210
334
|
);
|
|
211
|
-
newDepositAmount = totalCollateralRequired.minus(existingCollateralInfo.amount);
|
|
212
335
|
if (newDepositAmount.lt(0)) {
|
|
213
|
-
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
|
+
);
|
|
214
339
|
}
|
|
215
340
|
if (newDebtUSDValue.toNumber() < 100) {
|
|
216
341
|
// too less debt, skip
|
|
217
|
-
logger.verbose(
|
|
342
|
+
logger.verbose(
|
|
343
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall newDebtUSDValue is less than 100, skipping`,
|
|
344
|
+
);
|
|
218
345
|
return undefined;
|
|
219
346
|
}
|
|
220
347
|
}
|
|
221
348
|
|
|
222
|
-
logger.verbose(
|
|
349
|
+
logger.verbose(
|
|
350
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall debtAmount: ${debtAmount}`,
|
|
351
|
+
);
|
|
223
352
|
if (debtAmount.lt(0)) {
|
|
224
|
-
// this is to unwind the position to optimal HF.
|
|
353
|
+
// this is to unwind the position to optimal HF.
|
|
225
354
|
const lstDEXPrice = await this.getLSTDexPrice();
|
|
226
355
|
const debtAmountInLST = debtAmount.abs().dividedBy(lstDEXPrice);
|
|
227
356
|
const calls = await this.getVesuMultiplyCall({
|
|
228
357
|
isDeposit: false,
|
|
229
358
|
leg1DepositAmount: debtAmountInLST,
|
|
230
|
-
poolId: vesuAdapter.config.poolId
|
|
231
|
-
})
|
|
232
|
-
assert(
|
|
359
|
+
poolId: vesuAdapter.config.poolId,
|
|
360
|
+
});
|
|
361
|
+
assert(
|
|
362
|
+
calls.length == 1,
|
|
363
|
+
`Expected 1 call for unwind, got ${calls.length}`,
|
|
364
|
+
);
|
|
233
365
|
return calls[0];
|
|
234
366
|
}
|
|
235
|
-
console.log(`debtAmount`, debtAmount.toWei(), params.leg1DepositAmount.toWei());
|
|
367
|
+
// console.log(`debtAmount`, debtAmount.toWei(), params.leg1DepositAmount.toWei());
|
|
236
368
|
const STEP0 = UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1;
|
|
237
369
|
const manage0Info = this.getProofs<ApproveCallParams>(STEP0);
|
|
238
370
|
const manageCall0 = manage0Info.callConstructor({
|
|
239
|
-
amount: newDepositAmount
|
|
371
|
+
amount: newDepositAmount,
|
|
240
372
|
});
|
|
241
|
-
const STEP1 = getVesuLegId(
|
|
373
|
+
const STEP1 = getVesuLegId(
|
|
374
|
+
UNIVERSAL_MANAGE_IDS.VESU_LEG1,
|
|
375
|
+
vesuAdapter.config.debt.symbol,
|
|
376
|
+
vesuAdapter.config.poolId.toString(),
|
|
377
|
+
);
|
|
242
378
|
const manage1Info = this.getProofs<VesuModifyPositionCallParams>(STEP1);
|
|
243
|
-
const manageCall1 = manage1Info.callConstructor(
|
|
379
|
+
const manageCall1 = manage1Info.callConstructor(
|
|
380
|
+
VesuAdapter.getDefaultModifyPositionCallParams({
|
|
244
381
|
collateralAmount: newDepositAmount,
|
|
245
382
|
isAddCollateral: params.isDeposit,
|
|
246
383
|
debtAmount: debtAmount,
|
|
247
|
-
isBorrow: params.isDeposit
|
|
248
|
-
|
|
384
|
+
isBorrow: params.isDeposit,
|
|
385
|
+
}),
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
// console.log(`manageCall1`, manageCall1.call, debtAmount.toWei(), newDepositAmount.toWei());
|
|
249
389
|
|
|
250
|
-
console.log(`manageCall1`, manageCall1.call, debtAmount.toWei(), newDepositAmount.toWei());
|
|
251
|
-
|
|
252
390
|
const proofIds: string[] = [STEP0, STEP1];
|
|
253
391
|
const manageCalls: ManageCall[] = [manageCall0, manageCall1];
|
|
254
392
|
|
|
255
393
|
// approve and swap to LST using avnu
|
|
256
394
|
if (debtAmount.gt(0)) {
|
|
257
|
-
const STEP2 = getAvnuManageIDs(
|
|
395
|
+
const STEP2 = getAvnuManageIDs(
|
|
396
|
+
LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_APPROVE_DEPOSIT,
|
|
397
|
+
vesuAdapter.config.debt.symbol,
|
|
398
|
+
);
|
|
258
399
|
const manage2Info = this.getProofs<ApproveCallParams>(STEP2);
|
|
259
400
|
const manageCall2 = manage2Info.callConstructor({
|
|
260
|
-
amount: debtAmount
|
|
401
|
+
amount: debtAmount,
|
|
261
402
|
});
|
|
262
403
|
|
|
263
404
|
const debtTokenInfo = vesuAdapter.config.debt;
|
|
@@ -267,51 +408,75 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
267
408
|
debtTokenInfo.address.address,
|
|
268
409
|
lstTokenInfo.address.address,
|
|
269
410
|
debtAmount.toWei(),
|
|
270
|
-
this.metadata.additionalInfo.vaultAllocator.address
|
|
411
|
+
this.metadata.additionalInfo.vaultAllocator.address,
|
|
271
412
|
);
|
|
272
413
|
const minAmount = await this._getMinOutputAmountLSTBuy(debtAmount);
|
|
273
|
-
const minAmountWei =
|
|
274
|
-
logger.verbose(
|
|
414
|
+
const minAmountWei = minAmount.toWei();
|
|
415
|
+
logger.verbose(
|
|
416
|
+
`${this.getTag()}::_getAvnuDepositSwapLegCall minAmount: ${minAmount}`,
|
|
417
|
+
);
|
|
275
418
|
const swapInfo = await avnuModule.getSwapInfo(
|
|
276
|
-
quote,
|
|
277
|
-
this.metadata.additionalInfo.vaultAllocator.address,
|
|
278
|
-
0,
|
|
419
|
+
quote,
|
|
420
|
+
this.metadata.additionalInfo.vaultAllocator.address,
|
|
421
|
+
0,
|
|
279
422
|
this.address.address,
|
|
280
|
-
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,
|
|
281
431
|
);
|
|
282
|
-
logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall swapInfo: ${JSON.stringify(swapInfo)}`);
|
|
283
|
-
const STEP3 = getAvnuManageIDs(LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_DEPOSIT, vesuAdapter.config.debt.symbol);
|
|
284
432
|
const manage3Info = this.getProofs<AvnuSwapCallParams>(STEP3);
|
|
285
433
|
const manageCall3 = manage3Info.callConstructor({
|
|
286
|
-
props: swapInfo
|
|
434
|
+
props: swapInfo,
|
|
287
435
|
});
|
|
288
436
|
proofIds.push(STEP2);
|
|
289
437
|
proofIds.push(STEP3);
|
|
290
438
|
manageCalls.push(manageCall2, manageCall3);
|
|
291
439
|
|
|
292
|
-
|
|
293
440
|
// if the created debt, when added is collateral will put the total HF above min, but below (target + 0.05),
|
|
294
|
-
// then lets close the looping cycle by adding this as collateral.
|
|
441
|
+
// then lets close the looping cycle by adding this as collateral.
|
|
295
442
|
const newCollateral = minAmount.plus(totalCollateral);
|
|
296
|
-
const newHF = newCollateral
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
+
);
|
|
300
459
|
// approve and add collateral on vesu (modify position)
|
|
301
460
|
const STEP4 = UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1;
|
|
302
461
|
const manage4Info = this.getProofs<ApproveCallParams>(STEP4);
|
|
303
462
|
const manageCall4 = manage4Info.callConstructor({
|
|
304
|
-
amount: minAmount
|
|
463
|
+
amount: minAmount,
|
|
305
464
|
});
|
|
306
|
-
|
|
307
|
-
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
|
+
);
|
|
308
471
|
const manage5Info = this.getProofs<VesuModifyPositionCallParams>(STEP5);
|
|
309
|
-
const manageCall5 = manage5Info.callConstructor(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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
|
+
);
|
|
315
480
|
proofIds.push(STEP4, STEP5);
|
|
316
481
|
manageCalls.push(manageCall4, manageCall5);
|
|
317
482
|
}
|
|
@@ -321,53 +486,78 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
321
486
|
return manageCall;
|
|
322
487
|
}
|
|
323
488
|
|
|
324
|
-
// todo unwind or not deposit when the yield is bad.
|
|
489
|
+
// todo unwind or not deposit when the yield is bad.
|
|
325
490
|
|
|
326
|
-
async getLSTMultiplierRebalanceCall(): Promise<{
|
|
491
|
+
async getLSTMultiplierRebalanceCall(): Promise<{
|
|
492
|
+
shouldRebalance: boolean;
|
|
493
|
+
manageCalls: { vesuAdapter: VesuAdapter; manageCall: Call }[];
|
|
494
|
+
}> {
|
|
327
495
|
let shouldRebalance = false;
|
|
328
|
-
const calls: {vesuAdapter: VesuAdapter
|
|
496
|
+
const calls: { vesuAdapter: VesuAdapter; manageCall: Call }[] = [];
|
|
329
497
|
// todo undo
|
|
330
|
-
const allVesuAdapters = this.getVesuAdapters().filter(
|
|
498
|
+
const allVesuAdapters = this.getVesuAdapters().filter(
|
|
499
|
+
(vesuAdapter) => vesuAdapter.config.debt.symbol === "LBTC",
|
|
500
|
+
);
|
|
331
501
|
for (const vesuAdapter of allVesuAdapters) {
|
|
332
502
|
const call = await this._getLSTMultiplierRebalanceCall(vesuAdapter);
|
|
333
503
|
if (call.shouldRebalance && call.manageCall) {
|
|
334
504
|
shouldRebalance = true;
|
|
335
|
-
calls.push({vesuAdapter, manageCall: call.manageCall});
|
|
505
|
+
calls.push({ vesuAdapter, manageCall: call.manageCall });
|
|
336
506
|
}
|
|
337
507
|
}
|
|
338
508
|
return { shouldRebalance, manageCalls: calls };
|
|
339
509
|
}
|
|
340
510
|
|
|
341
|
-
async _getLSTMultiplierRebalanceCall(
|
|
511
|
+
async _getLSTMultiplierRebalanceCall(
|
|
512
|
+
vesuAdapter: VesuAdapter,
|
|
513
|
+
): Promise<{ shouldRebalance: boolean; manageCall: Call | undefined }> {
|
|
342
514
|
const positions = await vesuAdapter.getPositions(this.config);
|
|
343
|
-
assert(
|
|
515
|
+
assert(
|
|
516
|
+
positions.length == 2,
|
|
517
|
+
"Rebalance call is only supported for 2 positions",
|
|
518
|
+
);
|
|
344
519
|
const existingCollateralInfo = positions[0];
|
|
345
520
|
const existingDebtInfo = positions[1];
|
|
346
521
|
const unusedBalance = await this.getUnusedBalance();
|
|
347
522
|
const healthFactor = await vesuAdapter.getHealthFactor();
|
|
348
523
|
|
|
349
|
-
const collateralisation = await vesuAdapter.getCollateralization(
|
|
524
|
+
const collateralisation = await vesuAdapter.getCollateralization(
|
|
525
|
+
this.config,
|
|
526
|
+
);
|
|
350
527
|
logger.debug(`${this.getTag()}::getVesuMultiplyCall::${vesuAdapter.config.debt.symbol} existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
351
528
|
existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
352
529
|
|
|
353
530
|
// - Prices as seen by Vesu contracts, ideal for HF math
|
|
354
|
-
// 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
|
|
355
532
|
// collateral and debt as equal.
|
|
356
|
-
const collateralPrice =
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|
+
);
|
|
360
548
|
|
|
361
|
-
const isHFTooLow =
|
|
362
|
-
|
|
549
|
+
const isHFTooLow =
|
|
550
|
+
healthFactor < this.metadata.additionalInfo.minHealthFactor;
|
|
551
|
+
const isHFTooHigh =
|
|
552
|
+
healthFactor > this.metadata.additionalInfo.targetHealthFactor + 0.05;
|
|
363
553
|
if (isHFTooLow || isHFTooHigh || 1) {
|
|
364
|
-
// use unused collateral to target more.
|
|
554
|
+
// use unused collateral to target more.
|
|
365
555
|
const manageCall = await this._getAvnuDepositSwapLegCall({
|
|
366
556
|
isDeposit: true,
|
|
367
557
|
leg1DepositAmount: unusedBalance.amount,
|
|
368
|
-
minHF: 1.02, // todo, shouldnt use this 1.02 HF, if there isn;t more looping left.
|
|
369
|
-
vesuAdapter
|
|
370
|
-
})
|
|
558
|
+
minHF: 1.02, // todo, shouldnt use this 1.02 HF, if there isn;t more looping left.
|
|
559
|
+
vesuAdapter,
|
|
560
|
+
});
|
|
371
561
|
return { shouldRebalance: true, manageCall };
|
|
372
562
|
} else {
|
|
373
563
|
// do nothing
|
|
@@ -375,19 +565,61 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
375
565
|
}
|
|
376
566
|
}
|
|
377
567
|
|
|
378
|
-
protected async getVesuAUM(
|
|
568
|
+
protected async getVesuAUM(
|
|
569
|
+
adapter: VesuAdapter,
|
|
570
|
+
priceType: LSTPriceType = LSTPriceType.AVNU_PRICE,
|
|
571
|
+
) {
|
|
379
572
|
const legAUM = await adapter.getPositions(this.config);
|
|
380
573
|
const underlying = this.asset();
|
|
381
574
|
// assert its an LST of Endur
|
|
382
|
-
assert(
|
|
575
|
+
assert(
|
|
576
|
+
underlying.symbol.startsWith("x"),
|
|
577
|
+
"Underlying is not an LST of Endur",
|
|
578
|
+
);
|
|
383
579
|
|
|
384
580
|
let vesuAum = Web3Number.fromWei("0", underlying.decimals);
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
581
|
+
|
|
582
|
+
// Get the price based on the price type
|
|
583
|
+
let tokenUnderlyingPrice: number;
|
|
584
|
+
if (priceType === LSTPriceType.ENDUR_PRICE) {
|
|
585
|
+
tokenUnderlyingPrice = await this.getLSTExchangeRate();
|
|
586
|
+
if (tokenUnderlyingPrice === 0) {
|
|
587
|
+
throw new Error(
|
|
588
|
+
`${this.getTag()}::getVesuAUM: tokenUnderlyingPrice (Endur) is 0`,
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// For Endur price, also check against Avnu rate to ensure they're within 2%
|
|
593
|
+
const avnuRate = await this.getLSTAvnuRate();
|
|
594
|
+
if (avnuRate === 0) {
|
|
595
|
+
throw new Error(
|
|
596
|
+
`${this.getTag()}::getVesuAUM: tokenUnderlyingPrice (Avnu) is 0`,
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
const diff =
|
|
601
|
+
Math.abs(tokenUnderlyingPrice - avnuRate) / tokenUnderlyingPrice;
|
|
602
|
+
if (diff > 0.02) {
|
|
603
|
+
throw new Error(
|
|
604
|
+
`${this.getTag()}::getVesuAUM: Endur and Avnu prices differ by more than 2% (Endur: ${tokenUnderlyingPrice}, Avnu: ${avnuRate})`,
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
logger.verbose(
|
|
609
|
+
`${this.getTag()} tokenUnderlyingPrice (Endur): ${tokenUnderlyingPrice}, avnuRate: ${avnuRate}, diff: ${diff}`,
|
|
610
|
+
);
|
|
611
|
+
} else {
|
|
612
|
+
tokenUnderlyingPrice = await this.getLSTAvnuRate();
|
|
613
|
+
if (tokenUnderlyingPrice === 0) {
|
|
614
|
+
throw new Error(
|
|
615
|
+
`${this.getTag()}::getVesuAUM: tokenUnderlyingPrice (Avnu) is 0`,
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
logger.verbose(
|
|
619
|
+
`${this.getTag()} tokenUnderlyingPrice (Avnu): ${tokenUnderlyingPrice}`,
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
|
|
391
623
|
// handle collateral
|
|
392
624
|
if (legAUM[0].token.address.eq(underlying.address)) {
|
|
393
625
|
vesuAum = vesuAum.plus(legAUM[0].amount);
|
|
@@ -400,40 +632,82 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
400
632
|
vesuAum = vesuAum.minus(legAUM[1].amount);
|
|
401
633
|
} else {
|
|
402
634
|
vesuAum = vesuAum.minus(legAUM[1].amount.dividedBy(tokenUnderlyingPrice));
|
|
403
|
-
}
|
|
635
|
+
}
|
|
404
636
|
|
|
405
|
-
|
|
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
|
+
);
|
|
406
642
|
return vesuAum;
|
|
407
643
|
}
|
|
408
644
|
|
|
645
|
+
async getTVLUnrealized(): Promise<{
|
|
646
|
+
net: SingleTokenInfo;
|
|
647
|
+
prevAum: Web3Number;
|
|
648
|
+
splits: { id: string; aum: Web3Number }[];
|
|
649
|
+
}> {
|
|
650
|
+
return await this.getAUM(true);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
async getUserUnrealizedGains(user: ContractAddr) {
|
|
654
|
+
// Get total TVL (realized)
|
|
655
|
+
const tvl = await this.getTVL();
|
|
656
|
+
|
|
657
|
+
// Get unrealized TVL (using Endur prices)
|
|
658
|
+
const unrealizedTVL = await this.getTVLUnrealized();
|
|
659
|
+
|
|
660
|
+
// Calculate the difference between unrealized and realized TVL
|
|
661
|
+
const unrealizedDiff = unrealizedTVL.net.amount.minus(tvl.amount);
|
|
662
|
+
|
|
663
|
+
// Get user's TVL
|
|
664
|
+
const userTVL = await this.getUserTVL(user);
|
|
665
|
+
|
|
666
|
+
// Calculate user's share of the total TVL
|
|
667
|
+
const userShare = userTVL.amount.dividedBy(tvl.amount);
|
|
668
|
+
|
|
669
|
+
// Calculate user's unrealized gains (in token amount)
|
|
670
|
+
const unrealizedGains = unrealizedDiff.multipliedBy(userShare);
|
|
671
|
+
|
|
672
|
+
return {
|
|
673
|
+
unrealizedGains,
|
|
674
|
+
userShare: userShare.toNumber(),
|
|
675
|
+
tokenInfo: this.asset(),
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
|
|
409
679
|
//
|
|
410
680
|
private async _getMinOutputAmountLSTBuy(amountInUnderlying: Web3Number) {
|
|
411
681
|
const lstTruePrice = await this.getLSTExchangeRate();
|
|
412
|
-
// during buy, the purchase should always be <= true LST price.
|
|
413
|
-
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
|
|
414
686
|
return new Web3Number(minOutputAmount.toString(), this.asset().decimals);
|
|
415
687
|
}
|
|
416
688
|
|
|
417
689
|
private async _getMinOutputAmountLSTSell(amountInLST: Web3Number) {
|
|
418
690
|
const lstTruePrice = await this.getLSTExchangeRate();
|
|
419
|
-
// during sell, the purchase should always be > 0.995 * true LST price.
|
|
420
|
-
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);
|
|
421
695
|
return minOutputAmount;
|
|
422
696
|
}
|
|
423
697
|
|
|
424
698
|
// todo add a function to findout max borrowable amount without fucking yield
|
|
425
|
-
// 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.
|
|
426
700
|
|
|
427
701
|
/**
|
|
428
702
|
* Uses vesu's multiple call to create leverage on LST
|
|
429
703
|
* Deposit amount is in LST
|
|
430
|
-
* @param params
|
|
704
|
+
* @param params
|
|
431
705
|
*/
|
|
432
706
|
async getVesuMultiplyCall(params: {
|
|
433
|
-
isDeposit: boolean
|
|
434
|
-
leg1DepositAmount: Web3Number
|
|
435
|
-
maxEkuboPriceImpact?: number
|
|
436
|
-
poolId: ContractAddr
|
|
707
|
+
isDeposit: boolean;
|
|
708
|
+
leg1DepositAmount: Web3Number;
|
|
709
|
+
maxEkuboPriceImpact?: number;
|
|
710
|
+
poolId: ContractAddr;
|
|
437
711
|
}) {
|
|
438
712
|
const maxEkuboPriceImpact = params.maxEkuboPriceImpact || 0.01;
|
|
439
713
|
const vesuAdapter1 = this.getVesuSameTokenAdapter(params.poolId);
|
|
@@ -441,10 +715,12 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
441
715
|
logger.verbose(`${this.getTag()}::getVesuMultiplyCall legLTV: ${legLTV}`);
|
|
442
716
|
|
|
443
717
|
if (!params.isDeposit) {
|
|
444
|
-
// try using unused balance to unwind.
|
|
445
|
-
// no need to unwind.
|
|
718
|
+
// try using unused balance to unwind.
|
|
719
|
+
// no need to unwind.
|
|
446
720
|
const unusedBalance = await this.getUnusedBalance();
|
|
447
|
-
logger.verbose(
|
|
721
|
+
logger.verbose(
|
|
722
|
+
`${this.getTag()}::getVesuMultiplyCall unusedBalance: ${unusedBalance.amount.toString()}, required: ${params.leg1DepositAmount.toString()}`,
|
|
723
|
+
);
|
|
448
724
|
// undo
|
|
449
725
|
// if (unusedBalance.amount.gte(params.leg1DepositAmount)) {
|
|
450
726
|
// return [];
|
|
@@ -453,51 +729,85 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
453
729
|
}
|
|
454
730
|
|
|
455
731
|
const existingPositions = await vesuAdapter1.getPositions(this.config);
|
|
456
|
-
const collateralisation = await vesuAdapter1.getCollateralization(
|
|
732
|
+
const collateralisation = await vesuAdapter1.getCollateralization(
|
|
733
|
+
this.config,
|
|
734
|
+
);
|
|
457
735
|
const existingCollateralInfo = existingPositions[0];
|
|
458
736
|
const existingDebtInfo = existingPositions[1];
|
|
459
737
|
logger.debug(`${this.getTag()}::getVesuMultiplyCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
460
738
|
existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
461
739
|
|
|
462
740
|
// - Prices as seen by Vesu contracts, ideal for HF math
|
|
463
|
-
// 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
|
|
464
742
|
// collateral and debt as equal.
|
|
465
|
-
const collateralPrice =
|
|
466
|
-
|
|
467
|
-
|
|
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
|
+
);
|
|
468
755
|
|
|
469
756
|
// - Prices as seen by actual swap price
|
|
470
757
|
const dexPrice = await this.getLSTDexPrice();
|
|
471
758
|
|
|
472
759
|
// compute optimal amount of collateral and debt post addition/removal
|
|
473
760
|
// target hf = collateral * collateralPrice * ltv / debt * debtPrice
|
|
474
|
-
// 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).
|
|
475
762
|
// target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv / (debt * debtPrice + X)
|
|
476
763
|
// => X * target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv - (debt * debtPrice * target hf)
|
|
477
764
|
// => X * (target hf - ltv)= ((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)
|
|
478
765
|
// => X = (((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)) / (target hf - ltv)
|
|
479
|
-
const addedCollateral = params.leg1DepositAmount
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
logger.verbose(
|
|
490
|
-
|
|
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
|
+
);
|
|
491
799
|
|
|
492
800
|
// both in underlying
|
|
493
801
|
let debtAmount = x_debt_usd.dividedBy(debtPrice);
|
|
494
802
|
const marginAmount = addedCollateral;
|
|
495
|
-
logger.verbose(
|
|
803
|
+
logger.verbose(
|
|
804
|
+
`${this.getTag()}::getVesuMultiplyCall debtAmount: ${debtAmount}, marginAmount: ${marginAmount}`,
|
|
805
|
+
);
|
|
496
806
|
|
|
497
807
|
if (marginAmount.lt(0) && debtAmount.gt(0)) {
|
|
498
808
|
// if we want to withdraw, but debt can go high, its conflicting between
|
|
499
|
-
// increasing and reducing lever. and in this case, the HF will go high,
|
|
500
|
-
// which is ok.
|
|
809
|
+
// increasing and reducing lever. and in this case, the HF will go high,
|
|
810
|
+
// which is ok.
|
|
501
811
|
debtAmount = Web3Number.fromWei(0, this.asset().decimals);
|
|
502
812
|
}
|
|
503
813
|
|
|
@@ -514,38 +824,69 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
514
824
|
lstDexPriceInUnderlying: dexPrice,
|
|
515
825
|
isIncrease: debtAmount.greaterThan(0),
|
|
516
826
|
maxEkuboPriceImpact,
|
|
517
|
-
poolId: params.poolId
|
|
827
|
+
poolId: params.poolId,
|
|
518
828
|
});
|
|
519
829
|
}
|
|
520
830
|
|
|
521
831
|
getLSTUnderlyingTokenInfo() {
|
|
522
|
-
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
832
|
+
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
833
|
+
this.metadata.additionalInfo.defaultPoolId,
|
|
834
|
+
);
|
|
523
835
|
return vesuAdapter1.config.debt;
|
|
524
836
|
}
|
|
525
837
|
|
|
526
|
-
async getMaxBorrowableAmount(
|
|
838
|
+
async getMaxBorrowableAmount(
|
|
839
|
+
params: { isAPYComputation: boolean } = { isAPYComputation: false },
|
|
840
|
+
) {
|
|
527
841
|
const vesuAdapters = this.getVesuAdapters();
|
|
528
|
-
let netMaxBorrowableAmount = Web3Number.fromWei(
|
|
529
|
-
|
|
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
|
+
}[] = [];
|
|
530
853
|
for (const vesuAdapter of vesuAdapters) {
|
|
531
|
-
const output = await this.getMaxBorrowableAmountByVesuAdapter(
|
|
854
|
+
const output = await this.getMaxBorrowableAmountByVesuAdapter(
|
|
855
|
+
vesuAdapter,
|
|
856
|
+
params.isAPYComputation,
|
|
857
|
+
);
|
|
532
858
|
const ltv = await vesuAdapter.getLTVConfig(this.config);
|
|
533
|
-
maxBorrowables.push({...output, ltv});
|
|
859
|
+
maxBorrowables.push({ ...output, ltv });
|
|
534
860
|
}
|
|
535
861
|
maxBorrowables.sort((a, b) => b.amount.toNumber() - a.amount.toNumber());
|
|
536
|
-
netMaxBorrowableAmount = maxBorrowables.reduce(
|
|
537
|
-
|
|
862
|
+
netMaxBorrowableAmount = maxBorrowables.reduce(
|
|
863
|
+
(acc, curr) => acc.plus(curr.amount),
|
|
864
|
+
Web3Number.fromWei("0", this.getLSTUnderlyingTokenInfo().decimals),
|
|
865
|
+
);
|
|
866
|
+
return { netMaxBorrowableAmount, maxBorrowables };
|
|
538
867
|
}
|
|
539
868
|
|
|
540
|
-
// recursively, using binary search computes max swappable.
|
|
869
|
+
// recursively, using binary search computes max swappable.
|
|
541
870
|
// @dev assumes 1 token of from == 1 token of to
|
|
542
|
-
async getMaxSwappableWithMaxSlippage(
|
|
871
|
+
async getMaxSwappableWithMaxSlippage(
|
|
872
|
+
fromToken: TokenInfo,
|
|
873
|
+
toToken: TokenInfo,
|
|
874
|
+
maxSlippage: number,
|
|
875
|
+
maxAmount: Web3Number,
|
|
876
|
+
) {
|
|
543
877
|
const output = await findMaxInputWithSlippage({
|
|
544
878
|
apiGetOutput: async (inputAmount: number): Promise<number> => {
|
|
545
879
|
const ekuboQuoter = new EkuboQuoter(this.config);
|
|
546
|
-
await new Promise(resolve => setTimeout(resolve, 1000)); // artificial delay, to avoid rate limit
|
|
547
|
-
const quote = await ekuboQuoter.getQuote(
|
|
548
|
-
|
|
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();
|
|
549
890
|
},
|
|
550
891
|
maxInput: maxAmount.toNumber(),
|
|
551
892
|
maxSlippagePercent: maxSlippage,
|
|
@@ -555,48 +896,103 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
555
896
|
return new Web3Number(output.optimalInput, fromToken.decimals);
|
|
556
897
|
}
|
|
557
898
|
|
|
558
|
-
async getMaxBorrowableAmountByVesuAdapter(
|
|
559
|
-
|
|
899
|
+
async getMaxBorrowableAmountByVesuAdapter(
|
|
900
|
+
vesuAdapter: VesuAdapter,
|
|
901
|
+
isAPYComputation: boolean,
|
|
902
|
+
) {
|
|
903
|
+
const lstAPY = await this.getLSTAPR(
|
|
904
|
+
this.getLSTUnderlyingTokenInfo().address,
|
|
905
|
+
);
|
|
560
906
|
const maxInterestRate = lstAPY * 0.8;
|
|
561
|
-
const {maxDebtToHave: maxBorrowableAmount, currentDebt} =
|
|
907
|
+
const { maxDebtToHave: maxBorrowableAmount, currentDebt } =
|
|
908
|
+
await vesuAdapter.getMaxBorrowableByInterestRate(
|
|
909
|
+
this.config,
|
|
910
|
+
vesuAdapter.config.debt,
|
|
911
|
+
maxInterestRate,
|
|
912
|
+
);
|
|
562
913
|
const debtCap = await vesuAdapter.getDebtCap(this.config);
|
|
563
914
|
|
|
564
915
|
if (currentDebt.gte(debtCap)) {
|
|
565
|
-
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
|
+
};
|
|
566
928
|
}
|
|
567
929
|
|
|
568
930
|
const availableToBorrow = debtCap.minus(currentDebt);
|
|
569
931
|
|
|
570
|
-
const maxBorrowable = maxBorrowableAmount
|
|
932
|
+
const maxBorrowable = maxBorrowableAmount
|
|
933
|
+
.minimum(availableToBorrow)
|
|
934
|
+
.multipliedBy(0.999);
|
|
571
935
|
// Dont compute precise max swappable for APY computation
|
|
572
|
-
if (
|
|
573
|
-
|
|
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
|
+
};
|
|
574
948
|
}
|
|
575
949
|
// Want < 0.02% slippage
|
|
576
950
|
try {
|
|
577
|
-
const maxSwappable = await this.getMaxSwappableWithMaxSlippage(
|
|
578
|
-
|
|
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
|
+
};
|
|
579
963
|
} catch (error) {
|
|
580
964
|
logger.warn(`${this.getTag()}: Failed to get max swappable: ${error}`);
|
|
581
|
-
const maxSwappable = Web3Number.fromWei(
|
|
582
|
-
|
|
583
|
-
|
|
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
|
+
}
|
|
584
976
|
}
|
|
585
977
|
|
|
586
978
|
// todo how much to unwind to get back healthy APY zone again
|
|
587
979
|
// if net APY < LST APR + 0.5%, we need to unwind to get back to LST APR + 1% atleast or 0 vesu position
|
|
588
|
-
// For xSTRK, simply deposit in Vesu if looping is not viable
|
|
589
|
-
|
|
980
|
+
// For xSTRK, simply deposit in Vesu if looping is not viable
|
|
981
|
+
|
|
590
982
|
/**
|
|
591
983
|
* Gets LST APR for the strategy's underlying asset from Endur API
|
|
592
984
|
* @returns Promise<number> The LST APR (not divided by 1e18)
|
|
593
985
|
*/
|
|
594
986
|
async getLSTAPR(_address: ContractAddr): Promise<number> {
|
|
595
987
|
try {
|
|
596
|
-
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
597
|
-
|
|
988
|
+
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
989
|
+
this.metadata.additionalInfo.defaultPoolId,
|
|
990
|
+
);
|
|
991
|
+
const apr = await LSTAPRService.getLSTAPR(
|
|
992
|
+
vesuAdapter1.config.debt.address,
|
|
993
|
+
);
|
|
598
994
|
if (!apr) {
|
|
599
|
-
throw new Error(
|
|
995
|
+
throw new Error("Failed to get LST APR");
|
|
600
996
|
}
|
|
601
997
|
return apr;
|
|
602
998
|
} catch (error) {
|
|
@@ -606,88 +1002,170 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
606
1002
|
}
|
|
607
1003
|
|
|
608
1004
|
// todo undo this
|
|
609
|
-
async netAPY(): Promise<{
|
|
1005
|
+
async netAPY(): Promise<{
|
|
1006
|
+
net: number;
|
|
1007
|
+
splits: { apy: number; id: string }[];
|
|
1008
|
+
}> {
|
|
610
1009
|
const unusedBalance = await this.getUnusedBalance();
|
|
611
|
-
const maxNewDeposits = await this.maxNewDeposits({
|
|
612
|
-
|
|
1010
|
+
const maxNewDeposits = await this.maxNewDeposits({
|
|
1011
|
+
isAPYComputation: true,
|
|
1012
|
+
});
|
|
1013
|
+
const lstAPY = await this.getLSTAPR(
|
|
1014
|
+
this.getLSTUnderlyingTokenInfo().address,
|
|
1015
|
+
);
|
|
613
1016
|
|
|
614
1017
|
// if unused balance is > max servicable from loan, we are limited by the max borrowing we can do
|
|
615
1018
|
// we also allow accepting little higher deposits (1.5x) to have room for future looping when theres more liquidity or debt cap rises
|
|
616
1019
|
if (maxNewDeposits * 1.5 < unusedBalance.amount.toNumber()) {
|
|
617
1020
|
// we have excess, just use real APY
|
|
618
|
-
logger.verbose(
|
|
1021
|
+
logger.verbose(
|
|
1022
|
+
`${this.getTag()}::netAPY: unused balance is > max servicable from loan, lstAPY: ${lstAPY}`,
|
|
1023
|
+
);
|
|
619
1024
|
const output = await super.netAPY();
|
|
620
|
-
output.splits.push({apy: lstAPY, id:
|
|
1025
|
+
output.splits.push({ apy: lstAPY, id: "lst_apy" });
|
|
621
1026
|
return output;
|
|
622
1027
|
} else {
|
|
623
1028
|
// we have little bit room to accept more deposits, we use theoretical max APY
|
|
624
|
-
logger.verbose(
|
|
1029
|
+
logger.verbose(
|
|
1030
|
+
`${this.getTag()}::netAPY: we can take more deposits, use theoretical max APY`,
|
|
1031
|
+
);
|
|
625
1032
|
const { positions, baseAPYs, rewardAPYs } = await this.getVesuAPYs();
|
|
626
|
-
const weights = positions.map(
|
|
1033
|
+
const weights = positions.map(
|
|
1034
|
+
(p, index) => p.usdValue * (index % 2 == 0 ? 1 : -1),
|
|
1035
|
+
);
|
|
627
1036
|
const aum = weights.reduce((acc, curr) => acc + curr, 0);
|
|
628
|
-
const output = await this.returnNetAPY(
|
|
629
|
-
|
|
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" });
|
|
630
1047
|
return output;
|
|
631
1048
|
}
|
|
632
1049
|
}
|
|
633
1050
|
|
|
634
|
-
async maxNewDeposits(
|
|
1051
|
+
async maxNewDeposits(
|
|
1052
|
+
params: { isAPYComputation: boolean } = { isAPYComputation: false },
|
|
1053
|
+
) {
|
|
635
1054
|
const maxBorrowableAmounts = await this.getMaxBorrowableAmount(params);
|
|
636
1055
|
let numerator = 0;
|
|
637
1056
|
|
|
638
1057
|
let ltv: number | undefined = undefined;
|
|
639
1058
|
for (let adapter of this.getVesuAdapters()) {
|
|
640
|
-
const maxBorrowableAmountInfo = maxBorrowableAmounts.maxBorrowables.find(
|
|
1059
|
+
const maxBorrowableAmountInfo = maxBorrowableAmounts.maxBorrowables.find(
|
|
1060
|
+
(b) => b.borrowableAsset.address.eq(adapter.config.debt.address),
|
|
1061
|
+
);
|
|
641
1062
|
if (!maxBorrowableAmountInfo || !maxBorrowableAmountInfo?.amount) {
|
|
642
|
-
throw new Error(
|
|
1063
|
+
throw new Error(
|
|
1064
|
+
`Max borrowable amount not found for adapter: ${adapter.config.debt.symbol}`,
|
|
1065
|
+
);
|
|
643
1066
|
}
|
|
644
1067
|
|
|
645
|
-
numerator +=
|
|
1068
|
+
numerator +=
|
|
1069
|
+
(this.metadata.additionalInfo.targetHealthFactor *
|
|
1070
|
+
maxBorrowableAmountInfo.amount.toNumber()) /
|
|
1071
|
+
maxBorrowableAmountInfo.ltv;
|
|
646
1072
|
}
|
|
647
1073
|
|
|
648
|
-
return
|
|
1074
|
+
return numerator - maxBorrowableAmounts.netMaxBorrowableAmount.toNumber();
|
|
649
1075
|
}
|
|
650
1076
|
|
|
651
1077
|
// todo revisit cases where 0th adapters is used
|
|
652
1078
|
protected async getUnusedBalanceAPY() {
|
|
653
1079
|
const unusedBalance = await this.getUnusedBalance();
|
|
654
|
-
const vesuAdapter = this.getVesuSameTokenAdapter(
|
|
1080
|
+
const vesuAdapter = this.getVesuSameTokenAdapter(
|
|
1081
|
+
this.metadata.additionalInfo.defaultPoolId,
|
|
1082
|
+
);
|
|
655
1083
|
const underlying = vesuAdapter.config.debt;
|
|
656
1084
|
const lstAPY = await this.getLSTAPR(underlying.address);
|
|
657
1085
|
return {
|
|
658
|
-
apy: lstAPY,
|
|
659
|
-
|
|
1086
|
+
apy: lstAPY,
|
|
1087
|
+
weight: unusedBalance.usdValue,
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
async getLSTAvnuRate() {
|
|
1092
|
+
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
1093
|
+
this.metadata.additionalInfo.defaultPoolId,
|
|
1094
|
+
);
|
|
1095
|
+
const lstTokenInfo = vesuAdapter1.config.collateral;
|
|
1096
|
+
const underlyingTokenInfo = vesuAdapter1.config.debt;
|
|
1097
|
+
|
|
1098
|
+
const avnuModule = new AvnuWrapper();
|
|
1099
|
+
|
|
1100
|
+
const sellAmount = lstTokenInfo.priceCheckAmount
|
|
1101
|
+
? new Web3Number(
|
|
1102
|
+
lstTokenInfo.priceCheckAmount,
|
|
1103
|
+
underlyingTokenInfo.decimals,
|
|
1104
|
+
)
|
|
1105
|
+
: new Web3Number(1, underlyingTokenInfo.decimals);
|
|
1106
|
+
|
|
1107
|
+
const quote = await avnuModule.getQuotes(
|
|
1108
|
+
underlyingTokenInfo.address.address,
|
|
1109
|
+
lstTokenInfo.address.address,
|
|
1110
|
+
sellAmount.toWei(),
|
|
1111
|
+
this.metadata.additionalInfo.vaultAllocator.address,
|
|
1112
|
+
);
|
|
1113
|
+
|
|
1114
|
+
const underlyingAmountNumber = sellAmount.toNumber();
|
|
1115
|
+
const lstAmountNumber = Web3Number.fromWei(
|
|
1116
|
+
quote.buyAmount.toString(),
|
|
1117
|
+
lstTokenInfo.decimals,
|
|
1118
|
+
).toNumber();
|
|
1119
|
+
|
|
1120
|
+
assert(lstAmountNumber > 0, "Avnu LST amount is zero");
|
|
1121
|
+
|
|
1122
|
+
const exchangeRate = underlyingAmountNumber / lstAmountNumber;
|
|
1123
|
+
logger.verbose(
|
|
1124
|
+
`${this.getTag()}:: LST Avnu Exchange Rate: ${exchangeRate}`,
|
|
1125
|
+
);
|
|
1126
|
+
return exchangeRate;
|
|
660
1127
|
}
|
|
661
1128
|
|
|
662
1129
|
async getLSTExchangeRate() {
|
|
663
|
-
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
1130
|
+
const vesuAdapter1 = this.getVesuSameTokenAdapter(
|
|
1131
|
+
this.metadata.additionalInfo.defaultPoolId,
|
|
1132
|
+
);
|
|
664
1133
|
const lstTokenInfo = vesuAdapter1.config.collateral;
|
|
665
1134
|
const lstABI = new Contract({
|
|
666
1135
|
abi: ERC4626Abi,
|
|
667
1136
|
address: lstTokenInfo.address.address,
|
|
668
|
-
providerOrAccount: this.config.provider
|
|
1137
|
+
providerOrAccount: this.config.provider,
|
|
669
1138
|
});
|
|
670
1139
|
|
|
671
|
-
const price: any = await lstABI.call(
|
|
672
|
-
|
|
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);
|
|
673
1146
|
logger.verbose(`${this.getTag()}:: LST Exchange Rate: ${exchangeRate}`);
|
|
674
1147
|
return exchangeRate;
|
|
675
|
-
|
|
1148
|
+
}
|
|
676
1149
|
|
|
677
1150
|
/**
|
|
678
|
-
*
|
|
1151
|
+
*
|
|
679
1152
|
* @param params marginAmount is in LST, debtAmount is in underlying
|
|
680
1153
|
*/
|
|
681
1154
|
async getModifyLeverCall(params: {
|
|
682
|
-
marginAmount: Web3Number
|
|
683
|
-
debtAmount: Web3Number
|
|
684
|
-
lstDexPriceInUnderlying: number
|
|
685
|
-
isIncrease: boolean
|
|
686
|
-
maxEkuboPriceImpact: number
|
|
687
|
-
poolId: ContractAddr
|
|
1155
|
+
marginAmount: Web3Number; // >0 during deposit
|
|
1156
|
+
debtAmount: Web3Number;
|
|
1157
|
+
lstDexPriceInUnderlying: number;
|
|
1158
|
+
isIncrease: boolean;
|
|
1159
|
+
maxEkuboPriceImpact: number;
|
|
1160
|
+
poolId: ContractAddr;
|
|
688
1161
|
}): Promise<Call[]> {
|
|
689
|
-
logger.verbose(
|
|
690
|
-
|
|
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
|
+
);
|
|
691
1169
|
|
|
692
1170
|
const vesuAdapter1 = this.getVesuSameTokenAdapter(params.poolId);
|
|
693
1171
|
const lstTokenInfo = this.asset();
|
|
@@ -695,9 +1173,11 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
695
1173
|
|
|
696
1174
|
// todo make it more general
|
|
697
1175
|
// 500k STRK (~75k$) or 0.5 BTC (~60k$)
|
|
698
|
-
const maxAmounts = lstTokenInfo.symbol ==
|
|
1176
|
+
const maxAmounts = lstTokenInfo.symbol == "xSTRK" ? 500000 : 0.5;
|
|
699
1177
|
if (params.marginAmount.greaterThan(maxAmounts)) {
|
|
700
|
-
throw new Error(
|
|
1178
|
+
throw new Error(
|
|
1179
|
+
`Margin amount is greater than max amount: ${params.marginAmount.toNumber()} > ${maxAmounts}`,
|
|
1180
|
+
);
|
|
701
1181
|
}
|
|
702
1182
|
|
|
703
1183
|
const proofsIDs: string[] = [];
|
|
@@ -705,12 +1185,15 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
705
1185
|
|
|
706
1186
|
// approve token
|
|
707
1187
|
if (params.marginAmount.greaterThan(0)) {
|
|
708
|
-
const STEP1_ID = getVesuGenericLegId(
|
|
1188
|
+
const STEP1_ID = getVesuGenericLegId(
|
|
1189
|
+
params.poolId.toString(),
|
|
1190
|
+
LST_MULTIPLIER_MANAGE_IDS.MULTIPLE_APPROVE,
|
|
1191
|
+
);
|
|
709
1192
|
const manage1Info = this.getProofs<ApproveCallParams>(STEP1_ID);
|
|
710
1193
|
const depositAmount = params.marginAmount;
|
|
711
1194
|
const manageCall1 = manage1Info.callConstructor({
|
|
712
|
-
|
|
713
|
-
})
|
|
1195
|
+
amount: depositAmount,
|
|
1196
|
+
});
|
|
714
1197
|
proofsIDs.push(STEP1_ID);
|
|
715
1198
|
manageCalls.push(manageCall1);
|
|
716
1199
|
}
|
|
@@ -727,75 +1210,119 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
727
1210
|
const fromToken = params.isIncrease ? lstUnderlyingTokenInfo : lstTokenInfo;
|
|
728
1211
|
const toToken = params.isIncrease ? lstTokenInfo : lstUnderlyingTokenInfo;
|
|
729
1212
|
const leverSwapQuote = await ekuboQuoter.getQuote(
|
|
730
|
-
fromToken.address.address,
|
|
731
|
-
toToken.address.address,
|
|
732
|
-
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)}`,
|
|
733
1232
|
);
|
|
734
|
-
logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwapQuote: ${JSON.stringify(leverSwapQuote)}`);
|
|
735
|
-
// Ekubo's price impact can randomly show high numbers sometimes.
|
|
736
|
-
assert(leverSwapQuote.price_impact <= params.maxEkuboPriceImpact, 'getIncreaseLeverCall: Price impact is too high [Debt swap]');
|
|
737
|
-
const leverSwap = ekuboQuoter.getVesuMultiplyQuote(leverSwapQuote, fromToken, toToken);
|
|
738
|
-
logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwap: ${JSON.stringify(leverSwap)}`);
|
|
739
1233
|
|
|
740
1234
|
// todo double check this logic
|
|
741
1235
|
// is Deposit
|
|
742
|
-
let minLSTReceived = params.debtAmount
|
|
743
|
-
|
|
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
|
|
744
1241
|
// if (minLSTReceived < minLSTReceivedAsPerTruePrice) {
|
|
745
1242
|
// minLSTReceived = minLSTReceivedAsPerTruePrice; // the execution shouldn't be bad than True price logi
|
|
746
1243
|
// }
|
|
747
1244
|
minLSTReceived = minLSTReceivedAsPerTruePrice; // in any case, we are ok with this, bcz the BTC LST spread shouldnt be high
|
|
748
|
-
logger.verbose(
|
|
1245
|
+
logger.verbose(
|
|
1246
|
+
`${this.getTag()}::getModifyLeverCall minLSTReceivedAsPerTruePrice: ${minLSTReceivedAsPerTruePrice}, minLSTReceived: ${minLSTReceived}`,
|
|
1247
|
+
);
|
|
749
1248
|
|
|
750
1249
|
// is withdraw
|
|
751
|
-
let maxUsedCollateral = params.debtAmount
|
|
752
|
-
|
|
753
|
-
|
|
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
|
+
);
|
|
754
1261
|
// if (maxUsedCollateralInLST > maxUsedCollateral) {
|
|
755
1262
|
// maxUsedCollateral = maxUsedCollateralInLST;
|
|
756
1263
|
// }
|
|
757
1264
|
maxUsedCollateral = maxUsedCollateralInLST; // in any case, we are ok with this, bcz the BTC LST spread shouldnt be high
|
|
758
1265
|
|
|
759
|
-
const STEP2_ID = getVesuGenericLegId(
|
|
760
|
-
|
|
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);
|
|
761
1272
|
const manageCall2 = manage2Info.callConstructor({
|
|
762
1273
|
delegation: true,
|
|
763
1274
|
});
|
|
764
1275
|
|
|
765
1276
|
// deposit and borrow or repay and withdraw
|
|
766
|
-
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
|
+
);
|
|
767
1282
|
const manage3Info = this.getProofs<VesuMultiplyCallParams>(STEP3_ID);
|
|
768
|
-
const multiplyParams: VesuMultiplyCallParams = params.isIncrease
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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
|
+
};
|
|
792
1315
|
const manageCall3 = manage3Info.callConstructor(multiplyParams);
|
|
793
1316
|
|
|
794
1317
|
// switch delegation off
|
|
795
|
-
const STEP4_ID = getVesuGenericLegId(
|
|
796
|
-
|
|
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);
|
|
797
1324
|
const manageCall4 = manage4Info.callConstructor({
|
|
798
|
-
delegation: false
|
|
1325
|
+
delegation: false,
|
|
799
1326
|
});
|
|
800
1327
|
|
|
801
1328
|
proofsIDs.push(STEP2_ID, STEP3_ID, STEP4_ID);
|
|
@@ -807,12 +1334,11 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
807
1334
|
|
|
808
1335
|
export default function VaultDescription(
|
|
809
1336
|
lstSymbol: string,
|
|
810
|
-
underlyingSymbol: string
|
|
1337
|
+
underlyingSymbol: string,
|
|
811
1338
|
) {
|
|
812
1339
|
const containerStyle = {
|
|
813
1340
|
maxWidth: "800px",
|
|
814
1341
|
margin: "0 auto",
|
|
815
|
-
backgroundColor: "#111",
|
|
816
1342
|
color: "#eee",
|
|
817
1343
|
fontFamily: "Arial, sans-serif",
|
|
818
1344
|
borderRadius: "12px",
|
|
@@ -820,26 +1346,64 @@ export default function VaultDescription(
|
|
|
820
1346
|
|
|
821
1347
|
return (
|
|
822
1348
|
<div style={containerStyle}>
|
|
823
|
-
<h1 style={{ fontSize: "18px", marginBottom: "10px" }}>
|
|
1349
|
+
<h1 style={{ fontSize: "18px", marginBottom: "10px" }}>
|
|
1350
|
+
Liquidation risk managed leverged {lstSymbol} Vault
|
|
1351
|
+
</h1>
|
|
824
1352
|
<p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
|
|
825
|
-
This Levered Endur {lstSymbol} vault is a tokenized leveraged Vault,
|
|
826
|
-
|
|
827
|
-
|
|
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.
|
|
828
1358
|
</p>
|
|
829
1359
|
|
|
830
1360
|
<p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
|
|
831
|
-
This vault uses Vesu for lending and borrowing. The oracle used by this
|
|
832
|
-
{" "}
|
|
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.
|
|
833
1375
|
</p>
|
|
834
1376
|
|
|
835
|
-
<div
|
|
1377
|
+
<div
|
|
1378
|
+
style={{
|
|
1379
|
+
backgroundColor: "#222",
|
|
1380
|
+
padding: "10px",
|
|
1381
|
+
borderRadius: "8px",
|
|
1382
|
+
marginBottom: "20px",
|
|
1383
|
+
border: "1px solid #444",
|
|
1384
|
+
}}
|
|
1385
|
+
>
|
|
836
1386
|
<p style={{ fontSize: "13px", color: "#ccc" }}>
|
|
837
|
-
<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.
|
|
838
1390
|
</p>
|
|
839
1391
|
</div>
|
|
840
|
-
<div
|
|
1392
|
+
<div
|
|
1393
|
+
style={{
|
|
1394
|
+
backgroundColor: "#222",
|
|
1395
|
+
padding: "10px",
|
|
1396
|
+
borderRadius: "8px",
|
|
1397
|
+
marginBottom: "20px",
|
|
1398
|
+
border: "1px solid #444",
|
|
1399
|
+
}}
|
|
1400
|
+
>
|
|
841
1401
|
<p style={{ fontSize: "13px", color: "#ccc" }}>
|
|
842
|
-
<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.
|
|
843
1407
|
</p>
|
|
844
1408
|
</div>
|
|
845
1409
|
{/* <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
|
|
@@ -851,24 +1415,25 @@ export default function VaultDescription(
|
|
|
851
1415
|
);
|
|
852
1416
|
}
|
|
853
1417
|
|
|
854
|
-
|
|
855
1418
|
function getDescription(tokenSymbol: string, underlyingSymbol: string) {
|
|
856
1419
|
return VaultDescription(tokenSymbol, underlyingSymbol);
|
|
857
1420
|
}
|
|
858
1421
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
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",
|
|
869
1431
|
}
|
|
870
1432
|
|
|
871
|
-
function getAvnuManageIDs(
|
|
1433
|
+
function getAvnuManageIDs(
|
|
1434
|
+
baseID: LST_MULTIPLIER_MANAGE_IDS,
|
|
1435
|
+
debtTokenSymbol: string,
|
|
1436
|
+
) {
|
|
872
1437
|
return `${baseID}_${debtTokenSymbol.toLowerCase()}`;
|
|
873
1438
|
}
|
|
874
1439
|
|
|
@@ -887,45 +1452,100 @@ function addVesuLeaves(
|
|
|
887
1452
|
vaultSettings: HyperLSTStrategySettings,
|
|
888
1453
|
commonAdapter: CommonAdapter,
|
|
889
1454
|
) {
|
|
890
|
-
const lstToken = Global.getDefaultTokens().find(
|
|
891
|
-
|
|
1455
|
+
const lstToken = Global.getDefaultTokens().find(
|
|
1456
|
+
(token) => token.symbol === lstSymbol,
|
|
1457
|
+
)!;
|
|
1458
|
+
const underlyingToken = Global.getDefaultTokens().find(
|
|
1459
|
+
(token) => token.symbol === underlyingSymbol,
|
|
1460
|
+
)!;
|
|
892
1461
|
|
|
893
1462
|
const vesuAdapterLST = new VesuAdapter({
|
|
894
1463
|
poolId: poolId,
|
|
895
1464
|
collateral: lstToken,
|
|
896
1465
|
debt: underlyingToken,
|
|
897
1466
|
vaultAllocator: vaultSettings.vaultAllocator,
|
|
898
|
-
id: getVesuLegId(
|
|
1467
|
+
id: getVesuLegId(
|
|
1468
|
+
UNIVERSAL_MANAGE_IDS.VESU_LEG1,
|
|
1469
|
+
underlyingToken.symbol,
|
|
1470
|
+
poolId.toString(),
|
|
1471
|
+
),
|
|
899
1472
|
});
|
|
900
1473
|
|
|
901
1474
|
// Useful for returning adapter class objects that can compute
|
|
902
1475
|
// certain things for us (e.g. positions, hfs)
|
|
903
|
-
vaultSettings.adapters.push(
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
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
|
+
);
|
|
907
1488
|
|
|
908
1489
|
// avnu multiply
|
|
909
1490
|
const { isV2, addr: poolAddr } = getVesuSingletonAddress(poolId);
|
|
910
1491
|
|
|
911
1492
|
// vesu multiply looping
|
|
912
|
-
const VESU_MULTIPLY = isV2
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
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
|
+
|
|
926
1536
|
// direct modify position stuff
|
|
927
|
-
vaultSettings.leafAdapters.push(
|
|
928
|
-
|
|
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
|
+
);
|
|
929
1549
|
|
|
930
1550
|
return vesuAdapterLST;
|
|
931
1551
|
}
|
|
@@ -934,7 +1554,7 @@ function getLooperSettings(
|
|
|
934
1554
|
lstSymbol: string,
|
|
935
1555
|
underlyingSymbol: string,
|
|
936
1556
|
vaultSettings: HyperLSTStrategySettings,
|
|
937
|
-
defaultPoolId: ContractAddr
|
|
1557
|
+
defaultPoolId: ContractAddr,
|
|
938
1558
|
) {
|
|
939
1559
|
vaultSettings.leafAdapters = [];
|
|
940
1560
|
const pool1 = vaultSettings.defaultPoolId;
|
|
@@ -942,82 +1562,179 @@ function getLooperSettings(
|
|
|
942
1562
|
throw new Error(`Dont include default pool id in supported pool ids`);
|
|
943
1563
|
}
|
|
944
1564
|
|
|
945
|
-
const lstToken = Global.getDefaultTokens().find(
|
|
946
|
-
|
|
1565
|
+
const lstToken = Global.getDefaultTokens().find(
|
|
1566
|
+
(token) => token.symbol === lstSymbol,
|
|
1567
|
+
)!;
|
|
1568
|
+
const underlyingToken = Global.getDefaultTokens().find(
|
|
1569
|
+
(token) => token.symbol === underlyingSymbol,
|
|
1570
|
+
)!;
|
|
947
1571
|
|
|
948
1572
|
const commonAdapter = new CommonAdapter({
|
|
949
1573
|
manager: vaultSettings.manager,
|
|
950
1574
|
asset: lstToken.address,
|
|
951
|
-
id:
|
|
1575
|
+
id: "",
|
|
952
1576
|
vaultAddress: vaultSettings.vaultAddress,
|
|
953
1577
|
vaultAllocator: vaultSettings.vaultAllocator,
|
|
954
1578
|
});
|
|
955
1579
|
|
|
956
1580
|
// add common adapter to adapters
|
|
957
|
-
vaultSettings.adapters.push(
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1581
|
+
vaultSettings.adapters.push(
|
|
1582
|
+
...[
|
|
1583
|
+
{
|
|
1584
|
+
id: UNIVERSAL_ADAPTERS.COMMON,
|
|
1585
|
+
adapter: commonAdapter,
|
|
1586
|
+
},
|
|
1587
|
+
],
|
|
1588
|
+
);
|
|
961
1589
|
|
|
962
1590
|
// add vesu leaves for all supported pool ids
|
|
963
|
-
vaultSettings.borrowable_assets.map(borrowableAsset =>
|
|
964
|
-
|
|
1591
|
+
vaultSettings.borrowable_assets.map((borrowableAsset) =>
|
|
1592
|
+
addVesuLeaves(
|
|
1593
|
+
borrowableAsset.poolId,
|
|
1594
|
+
lstSymbol,
|
|
1595
|
+
underlyingSymbol,
|
|
1596
|
+
vaultSettings,
|
|
1597
|
+
commonAdapter,
|
|
1598
|
+
),
|
|
1599
|
+
);
|
|
1600
|
+
|
|
965
1601
|
// approve lst once to avnu
|
|
966
|
-
vaultSettings.leafAdapters.push(
|
|
967
|
-
|
|
968
|
-
|
|
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
|
+
];
|
|
969
1619
|
for (let borrowableAssetSymbol of uniqueBorrowableAssets) {
|
|
970
|
-
const borrowableAsset = Global.getDefaultTokens().find(
|
|
1620
|
+
const borrowableAsset = Global.getDefaultTokens().find(
|
|
1621
|
+
(token) => token.symbol === borrowableAssetSymbol,
|
|
1622
|
+
)!;
|
|
971
1623
|
// in-efficient avnu swap looping (but good with endur integration)
|
|
972
1624
|
const debtAsset = borrowableAsset;
|
|
973
|
-
const approve_debt_token_id = getAvnuManageIDs(
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
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
|
+
);
|
|
979
1666
|
}
|
|
980
1667
|
|
|
981
|
-
|
|
982
1668
|
// to bridge liquidity back to vault (used by bring_liquidity)
|
|
983
|
-
vaultSettings.leafAdapters.push(
|
|
984
|
-
|
|
985
|
-
|
|
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
|
+
|
|
986
1684
|
// claim rewards (defi spring ended)
|
|
987
1685
|
// vaultSettings.leafAdapters.push(vesuAdapterLST.getDefispringRewardsAdapter(UNIVERSAL_MANAGE_IDS.DEFISPRING_REWARDS).bind(vesuAdapterLST));
|
|
988
1686
|
|
|
989
1687
|
// avnu swap for claims rewards
|
|
990
|
-
const STRKToken = Global.getDefaultTokens().find(
|
|
991
|
-
|
|
992
|
-
|
|
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
|
+
);
|
|
993
1710
|
return vaultSettings;
|
|
994
1711
|
}
|
|
995
1712
|
|
|
996
|
-
const AUDIT_URL =
|
|
1713
|
+
const AUDIT_URL = "https://docs.troves.fi/p/security#starknet-vault-kit";
|
|
997
1714
|
|
|
998
1715
|
function getFAQs(lstSymbol: string, underlyingSymbol: string): FAQ[] {
|
|
999
1716
|
return [
|
|
1000
1717
|
{
|
|
1001
1718
|
question: `What is the Hyper ${lstSymbol} Vault?`,
|
|
1002
|
-
answer:
|
|
1003
|
-
`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.`,
|
|
1004
1720
|
},
|
|
1005
1721
|
{
|
|
1006
1722
|
question: "How does yield allocation work?",
|
|
1007
|
-
answer:
|
|
1008
|
-
`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.`,
|
|
1009
1724
|
},
|
|
1010
1725
|
{
|
|
1011
1726
|
question: "Which protocols/dApp are used??",
|
|
1012
1727
|
answer: (
|
|
1013
1728
|
<span>
|
|
1014
|
-
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.
|
|
1015
1731
|
</span>
|
|
1016
1732
|
),
|
|
1017
1733
|
},
|
|
1018
1734
|
{
|
|
1019
1735
|
question: "Can I get liquidated?",
|
|
1020
|
-
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.",
|
|
1021
1738
|
},
|
|
1022
1739
|
{
|
|
1023
1740
|
question: "What do I receive when I deposit?",
|
|
@@ -1048,107 +1765,209 @@ function getFAQs(lstSymbol: string, underlyingSymbol: string): FAQ[] {
|
|
|
1048
1765
|
}
|
|
1049
1766
|
|
|
1050
1767
|
const _riskFactor: RiskFactor[] = [
|
|
1051
|
-
{
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
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
|
+
},
|
|
1055
1793
|
];
|
|
1056
1794
|
|
|
1057
|
-
const btcBorrowableAssets = [
|
|
1058
|
-
'WBTC', 'tBTC', 'LBTC', 'solvBTC'
|
|
1059
|
-
]
|
|
1795
|
+
const btcBorrowableAssets = ["WBTC", "tBTC", "LBTC", "solvBTC"];
|
|
1060
1796
|
|
|
1061
1797
|
const hyperxSTRK: HyperLSTStrategySettings = {
|
|
1062
|
-
vaultAddress: ContractAddr.from(
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
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
|
+
),
|
|
1067
1813
|
leafAdapters: [],
|
|
1068
1814
|
adapters: [],
|
|
1069
1815
|
targetHealthFactor: 1.1,
|
|
1070
1816
|
minHealthFactor: 1.05,
|
|
1071
1817
|
borrowable_assets: [
|
|
1072
|
-
...Global.getDefaultTokens()
|
|
1073
|
-
|
|
1074
|
-
|
|
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
|
|
1075
1827
|
],
|
|
1076
|
-
underlyingToken: Global.getDefaultTokens().find(
|
|
1828
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
1829
|
+
(token) => token.symbol === "STRK",
|
|
1830
|
+
)!,
|
|
1077
1831
|
defaultPoolId: VesuPools.Re7xSTRK,
|
|
1078
|
-
}
|
|
1832
|
+
};
|
|
1079
1833
|
|
|
1080
1834
|
const hyperxWBTC: HyperLSTStrategySettings = {
|
|
1081
|
-
vaultAddress: ContractAddr.from(
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
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
|
+
),
|
|
1086
1850
|
leafAdapters: [],
|
|
1087
1851
|
adapters: [],
|
|
1088
1852
|
targetHealthFactor: 1.1,
|
|
1089
1853
|
minHealthFactor: 1.05,
|
|
1090
1854
|
borrowable_assets: [
|
|
1091
1855
|
// allow all BTC flavours borrowing on Re7xBTC pool
|
|
1092
|
-
...btcBorrowableAssets
|
|
1856
|
+
...btcBorrowableAssets
|
|
1857
|
+
.map(
|
|
1858
|
+
(asset) =>
|
|
1859
|
+
Global.getDefaultTokens().find((token) => token.symbol === asset)!,
|
|
1860
|
+
)
|
|
1861
|
+
.map((token) => ({ token, poolId: VesuPools.Re7xBTC })),
|
|
1093
1862
|
// allow only WBTC borrowing on Prime pool
|
|
1094
|
-
...Global.getDefaultTokens()
|
|
1863
|
+
...Global.getDefaultTokens()
|
|
1864
|
+
.filter((token) => token.symbol === "WBTC")
|
|
1865
|
+
.map((token) => ({ token, poolId: VesuPools.Prime })),
|
|
1095
1866
|
],
|
|
1096
|
-
underlyingToken: Global.getDefaultTokens().find(
|
|
1867
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
1868
|
+
(token) => token.symbol === "WBTC",
|
|
1869
|
+
)!,
|
|
1097
1870
|
defaultPoolId: VesuPools.Re7xBTC,
|
|
1098
|
-
redemptionRouter: ContractAddr.from(
|
|
1099
|
-
|
|
1871
|
+
redemptionRouter: ContractAddr.from(
|
|
1872
|
+
"0x6ea649f402898f69baf775c1afdd08522c071c640b9c4460192070ec2b96417",
|
|
1873
|
+
),
|
|
1874
|
+
};
|
|
1100
1875
|
|
|
1101
1876
|
const hyperxtBTC: HyperLSTStrategySettings = {
|
|
1102
|
-
vaultAddress: ContractAddr.from(
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
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
|
+
),
|
|
1107
1892
|
leafAdapters: [],
|
|
1108
1893
|
adapters: [],
|
|
1109
1894
|
targetHealthFactor: 1.1,
|
|
1110
1895
|
minHealthFactor: 1.05,
|
|
1111
1896
|
borrowable_assets: [
|
|
1112
|
-
...Global.getDefaultTokens()
|
|
1897
|
+
...Global.getDefaultTokens()
|
|
1898
|
+
.filter((token) => token.symbol === "tBTC" || token.symbol === "WBTC")
|
|
1899
|
+
.map((token) => ({ token, poolId: VesuPools.Re7xBTC })),
|
|
1113
1900
|
],
|
|
1114
|
-
underlyingToken: Global.getDefaultTokens().find(
|
|
1901
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
1902
|
+
(token) => token.symbol === "tBTC",
|
|
1903
|
+
)!,
|
|
1115
1904
|
defaultPoolId: VesuPools.Re7xBTC,
|
|
1116
|
-
redemptionRouter: ContractAddr.from(
|
|
1117
|
-
|
|
1905
|
+
redemptionRouter: ContractAddr.from(
|
|
1906
|
+
"0x3de9c409d1e357e25778fb7a3e2e2393666956846a5c2caa607296fa8e76b5d",
|
|
1907
|
+
),
|
|
1908
|
+
};
|
|
1118
1909
|
|
|
1119
1910
|
const hyperxsBTC: HyperLSTStrategySettings = {
|
|
1120
|
-
vaultAddress: ContractAddr.from(
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
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
|
+
),
|
|
1125
1926
|
leafAdapters: [],
|
|
1126
1927
|
adapters: [],
|
|
1127
1928
|
targetHealthFactor: 1.1,
|
|
1128
1929
|
minHealthFactor: 1.05,
|
|
1129
1930
|
borrowable_assets: [
|
|
1130
|
-
...Global.getDefaultTokens()
|
|
1931
|
+
...Global.getDefaultTokens()
|
|
1932
|
+
.filter((token) => token.symbol === "solvBTC")
|
|
1933
|
+
.map((token) => ({ token, poolId: VesuPools.Re7xBTC })),
|
|
1131
1934
|
],
|
|
1132
|
-
underlyingToken: Global.getDefaultTokens().find(
|
|
1935
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
1936
|
+
(token) => token.symbol === "solvBTC",
|
|
1937
|
+
)!,
|
|
1133
1938
|
defaultPoolId: VesuPools.Re7xBTC,
|
|
1134
|
-
}
|
|
1939
|
+
};
|
|
1135
1940
|
|
|
1136
1941
|
const hyperxLBTC: HyperLSTStrategySettings = {
|
|
1137
|
-
vaultAddress: ContractAddr.from(
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
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
|
+
),
|
|
1142
1957
|
leafAdapters: [],
|
|
1143
1958
|
adapters: [],
|
|
1144
1959
|
targetHealthFactor: 1.1,
|
|
1145
1960
|
minHealthFactor: 1.05,
|
|
1146
1961
|
borrowable_assets: [
|
|
1147
|
-
...Global.getDefaultTokens()
|
|
1962
|
+
...Global.getDefaultTokens()
|
|
1963
|
+
.filter((token) => token.symbol === "LBTC")
|
|
1964
|
+
.map((token) => ({ token, poolId: VesuPools.Re7xBTC })),
|
|
1148
1965
|
],
|
|
1149
|
-
underlyingToken: Global.getDefaultTokens().find(
|
|
1966
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
1967
|
+
(token) => token.symbol === "LBTC",
|
|
1968
|
+
)!,
|
|
1150
1969
|
defaultPoolId: VesuPools.Re7xBTC,
|
|
1151
|
-
}
|
|
1970
|
+
};
|
|
1152
1971
|
|
|
1153
1972
|
function getInvestmentSteps(lstSymbol: string, underlyingSymbol: string) {
|
|
1154
1973
|
return [
|
|
@@ -1156,172 +1975,180 @@ function getInvestmentSteps(lstSymbol: string, underlyingSymbol: string) {
|
|
|
1156
1975
|
`The vault manager loops the ${underlyingSymbol} to buy ${lstSymbol}`,
|
|
1157
1976
|
`The vault manager collateralizes the ${lstSymbol} on Vesu`,
|
|
1158
1977
|
`The vault manager borrows more ${underlyingSymbol} to loop further`,
|
|
1159
|
-
`If required, adjust leverage or re-allocate assets within LST pool on Vesu to optimize yield
|
|
1160
|
-
]
|
|
1978
|
+
`If required, adjust leverage or re-allocate assets within LST pool on Vesu to optimize yield`,
|
|
1979
|
+
];
|
|
1161
1980
|
}
|
|
1162
1981
|
|
|
1163
1982
|
// Helper to get maxTVL based on LST symbol (matching client values)
|
|
1164
1983
|
function getMaxTVL(lstSymbol: string): Web3Number {
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1984
|
+
const lstMaxTVLs: Record<string, number> = {
|
|
1985
|
+
xWBTC: 5,
|
|
1986
|
+
xLBTC: 5,
|
|
1987
|
+
xtBTC: 5,
|
|
1988
|
+
xsBTC: 5,
|
|
1989
|
+
xSTRK: 7000000,
|
|
1990
|
+
};
|
|
1172
1991
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
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);
|
|
1181
2000
|
}
|
|
1182
2001
|
|
|
1183
2002
|
// Helper to create Hyper LST strategy settings
|
|
1184
2003
|
function createHyperLSTSettings(
|
|
1185
|
-
|
|
1186
|
-
|
|
2004
|
+
lstSymbol: string,
|
|
2005
|
+
underlyingSymbol: string,
|
|
1187
2006
|
): StrategySettings {
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
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
|
+
};
|
|
1229
2048
|
}
|
|
1230
2049
|
|
|
1231
2050
|
const HYPER_LST_SECURITY = {
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
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
|
+
},
|
|
1242
2061
|
};
|
|
1243
2062
|
|
|
1244
2063
|
const HYPER_LST_REDEMPTION_INFO: RedemptionInfo = {
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
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
|
+
],
|
|
1255
2078
|
};
|
|
1256
2079
|
|
|
1257
2080
|
function getStrategySettings(
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
2081
|
+
lstSymbol: string,
|
|
2082
|
+
underlyingSymbol: string,
|
|
2083
|
+
settings: HyperLSTStrategySettings,
|
|
2084
|
+
isPreview: boolean = false,
|
|
1262
2085
|
): IStrategyMetadata<HyperLSTStrategySettings> {
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
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
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
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
|
+
};
|
|
1318
2145
|
}
|
|
1319
2146
|
|
|
1320
2147
|
export const HyperLSTStrategies: IStrategyMetadata<HyperLSTStrategySettings>[] =
|
|
1321
2148
|
[
|
|
1322
|
-
getStrategySettings(
|
|
1323
|
-
getStrategySettings(
|
|
1324
|
-
getStrategySettings(
|
|
1325
|
-
getStrategySettings(
|
|
1326
|
-
getStrategySettings(
|
|
1327
|
-
]
|
|
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
|
+
];
|