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