@strkfarm/sdk 1.1.8 → 1.1.9
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 +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.browser.global.js +1195 -74
- package/dist/index.browser.mjs +1350 -227
- package/dist/index.d.ts +168 -30
- package/dist/index.js +1366 -239
- package/dist/index.mjs +1351 -228
- package/package.json +1 -1
- package/src/data/vesu-multiple.abi.json +475 -0
- package/src/dataTypes/_bignumber.ts +13 -0
- package/src/modules/ekubo-quoter.ts +127 -0
- package/src/modules/index.ts +1 -0
- package/src/strategies/ekubo-cl-vault.tsx +25 -11
- package/src/strategies/index.ts +2 -1
- package/src/strategies/universal-adapters/adapter-utils.ts +3 -0
- package/src/strategies/universal-adapters/baseAdapter.ts +3 -3
- package/src/strategies/universal-adapters/vesu-adapter.ts +244 -3
- package/src/strategies/universal-lst-muliplier-strategy.tsx +457 -0
- package/src/strategies/universal-strategy.tsx +134 -60
- package/src/utils/index.ts +3 -1
- package/src/utils/logger.node.ts +1 -1
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { FAQ, getNoRiskTags, IConfig, IStrategyMetadata, Protocols, RiskFactor, RiskType } from "@/interfaces";
|
|
2
|
+
import { AUMTypes, getContractDetails, UNIVERSAL_ADAPTERS, UNIVERSAL_MANAGE_IDS, UniversalManageCall, UniversalStrategy, UniversalStrategySettings } from "./universal-strategy";
|
|
3
|
+
import { PricerBase } from "@/modules/pricerBase";
|
|
4
|
+
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
5
|
+
import { Global } from "@/global";
|
|
6
|
+
import { ApproveCallParams, CommonAdapter, ManageCall, Swap, VesuAdapter, VesuModifyDelegationCallParams, VesuMultiplyCallParams, VesuPools } from "./universal-adapters";
|
|
7
|
+
import { AVNU_MIDDLEWARE } from "./universal-adapters/adapter-utils";
|
|
8
|
+
import { LiquidationRiskLevel, SmartContractRiskLevel, TechnicalRiskLevel } from "@/interfaces/risks";
|
|
9
|
+
import { EkuboQuoter, ERC20 } from "@/modules";
|
|
10
|
+
import { assert, logger } from "@/utils";
|
|
11
|
+
import { SingleTokenInfo } from "./base-strategy";
|
|
12
|
+
import { Call, Contract, uint256 } from "starknet";
|
|
13
|
+
import ERC4626Abi from "@/data/erc4626.abi.json";
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalStrategySettings> {
|
|
17
|
+
|
|
18
|
+
private quoteAmountToFetchPrice = new Web3Number(1, 18);
|
|
19
|
+
|
|
20
|
+
constructor(config: IConfig, pricer: PricerBase, metadata: IStrategyMetadata<UniversalStrategySettings>) {
|
|
21
|
+
super(config, pricer, metadata);
|
|
22
|
+
|
|
23
|
+
const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
|
|
24
|
+
const underlyingToken = this.getLSTUnderlyingTokenInfo();
|
|
25
|
+
if (underlyingToken.address.eq(STRKToken.address)) {
|
|
26
|
+
this.quoteAmountToFetchPrice = new Web3Number(100, 18);
|
|
27
|
+
} else {
|
|
28
|
+
// else this BTC
|
|
29
|
+
this.quoteAmountToFetchPrice = new Web3Number(0.01, this.asset().decimals);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// only one leg is used
|
|
34
|
+
// todo support lending assets of underlying as well
|
|
35
|
+
getVesuAdapters() {
|
|
36
|
+
const vesuAdapter1 = this.getAdapter(UNIVERSAL_ADAPTERS.VESU_LEG1) as VesuAdapter;
|
|
37
|
+
vesuAdapter1.pricer = this.pricer;
|
|
38
|
+
vesuAdapter1.networkConfig = this.config;
|
|
39
|
+
|
|
40
|
+
return [vesuAdapter1];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// not applicable for this strategy
|
|
44
|
+
// No rewards on collateral or borrowing of LST assets
|
|
45
|
+
protected async getRewardsAUM(prevAum: Web3Number): Promise<Web3Number> {
|
|
46
|
+
return Web3Number.fromWei("0", this.asset().decimals);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async getLSTDexPrice() {
|
|
50
|
+
const ekuboQuoter = new EkuboQuoter(this.config);
|
|
51
|
+
const lstTokenInfo = this.asset();
|
|
52
|
+
const lstUnderlyingTokenInfo = this.getLSTUnderlyingTokenInfo();
|
|
53
|
+
const quote = await ekuboQuoter.getQuote(
|
|
54
|
+
lstTokenInfo.address.address,
|
|
55
|
+
lstUnderlyingTokenInfo.address.address,
|
|
56
|
+
this.quoteAmountToFetchPrice
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// in Underlying
|
|
60
|
+
const outputAmount = Web3Number.fromWei(quote.total_calculated, lstUnderlyingTokenInfo.decimals);
|
|
61
|
+
const price = outputAmount.toNumber() / this.quoteAmountToFetchPrice.toNumber();
|
|
62
|
+
logger.verbose(`${this.getTag()}:: LST Dex Price: ${price}`);
|
|
63
|
+
return price;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Uses vesu's multiple call to create leverage on LST
|
|
68
|
+
* Deposit amount is in LST
|
|
69
|
+
* @param params
|
|
70
|
+
*/
|
|
71
|
+
async getVesuMultiplyCall(params: {
|
|
72
|
+
isDeposit: boolean,
|
|
73
|
+
leg1DepositAmount: Web3Number
|
|
74
|
+
}) {
|
|
75
|
+
const [vesuAdapter1] = this.getVesuAdapters();
|
|
76
|
+
const legLTV = await vesuAdapter1.getLTVConfig(this.config);
|
|
77
|
+
|
|
78
|
+
const existingPositions = await vesuAdapter1.getPositions(this.config);
|
|
79
|
+
const collateralisation = await vesuAdapter1.getCollateralization(this.config);
|
|
80
|
+
const existingCollateralInfo = existingPositions[0];
|
|
81
|
+
const existingDebtInfo = existingPositions[1];
|
|
82
|
+
logger.debug(`${this.getTag()}::getVesuMultiplyCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
83
|
+
existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
84
|
+
|
|
85
|
+
// Price 1 is ok as fallback bcz that would relatively price the
|
|
86
|
+
// collateral and debt as equal.
|
|
87
|
+
const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
|
|
88
|
+
const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
|
|
89
|
+
logger.debug(`${this.getTag()}::getVesuMultiplyCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
|
|
90
|
+
|
|
91
|
+
// compute optimal amount of collateral and debt post addition/removal
|
|
92
|
+
// target hf = collateral * collateralPrice * ltv / debt * debtPrice
|
|
93
|
+
// assuming X to be the usd amount of debt borrowed or repaied (negative).
|
|
94
|
+
// target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv / (debt * debtPrice + X)
|
|
95
|
+
// => X * target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv - (debt * debtPrice * target hf)
|
|
96
|
+
// => X * (target hf - ltv)= ((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)
|
|
97
|
+
// => X = (((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)) / (target hf - ltv)
|
|
98
|
+
const addedCollateral = params.leg1DepositAmount
|
|
99
|
+
.multipliedBy(params.isDeposit ? 1 : -1)
|
|
100
|
+
const DEXPrice = await this.getLSTDexPrice();
|
|
101
|
+
logger.verbose(`${this.getTag()}::getVesuMultiplyCall addedCollateral: ${addedCollateral}`);
|
|
102
|
+
const numeratorPart1 = (existingCollateralInfo.amount.plus((addedCollateral))).multipliedBy(collateralPrice).multipliedBy(legLTV);
|
|
103
|
+
const numeratorPart2 = existingDebtInfo.amount.multipliedBy(debtPrice).multipliedBy(this.metadata.additionalInfo.targetHealthFactor);
|
|
104
|
+
const denominatorPart = this.metadata.additionalInfo.targetHealthFactor - (legLTV / DEXPrice);
|
|
105
|
+
const x_debt_usd = numeratorPart1.minus(numeratorPart2).dividedBy(denominatorPart);
|
|
106
|
+
logger.verbose(`${this.getTag()}::getVesuMultiplyCall x_debt_usd: ${x_debt_usd}`);
|
|
107
|
+
logger.debug(`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}, numeratorPart2: ${numeratorPart2}, denominatorPart: ${denominatorPart}`);
|
|
108
|
+
|
|
109
|
+
// both in underlying
|
|
110
|
+
const debtAmount = x_debt_usd.dividedBy(debtPrice);
|
|
111
|
+
const marginAmount = addedCollateral;
|
|
112
|
+
logger.verbose(`${this.getTag()}::getVesuMultiplyCall debtAmount: ${debtAmount}, marginAmount: ${marginAmount}`);
|
|
113
|
+
|
|
114
|
+
// Cases of lever increase (within the scopr of this function)
|
|
115
|
+
// 1. debtAmount > 0 and marginAmount > 0
|
|
116
|
+
// 2. debtAmount > 0 and marginAmount < 0
|
|
117
|
+
|
|
118
|
+
// Cases of lever decrease
|
|
119
|
+
// 3. debtAmount < 0 and marginAmount > 0
|
|
120
|
+
// 4. debtAmount < 0 and marginAmount < 0
|
|
121
|
+
return this.getModifyLeverCall({
|
|
122
|
+
marginAmount,
|
|
123
|
+
debtAmount,
|
|
124
|
+
isIncrease: debtAmount.greaterThan(0)
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getLSTUnderlyingTokenInfo() {
|
|
129
|
+
const [vesuAdapter1] = this.getVesuAdapters();
|
|
130
|
+
return vesuAdapter1.config.debt;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async getLSTExchangeRate() {
|
|
134
|
+
const [vesuAdapter1] = this.getVesuAdapters();
|
|
135
|
+
const lstTokenInfo = vesuAdapter1.config.collateral;
|
|
136
|
+
const lstABI = new Contract({
|
|
137
|
+
abi: ERC4626Abi,
|
|
138
|
+
address: lstTokenInfo.address.address,
|
|
139
|
+
providerOrAccount: this.config.provider
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const price: any = await lstABI.call('convert_to_assets', [uint256.bnToUint256((new Web3Number(1, lstTokenInfo.decimals)).toWei())]);
|
|
143
|
+
const exchangeRate = Number(uint256.uint256ToBN(price).toString()) / Math.pow(10, lstTokenInfo.decimals);
|
|
144
|
+
logger.verbose(`${this.getTag()}:: LST Exchange Rate: ${exchangeRate}`);
|
|
145
|
+
return exchangeRate;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
*
|
|
150
|
+
* @param params marginAmount is in LST, debtAmount is in underlying
|
|
151
|
+
*/
|
|
152
|
+
async getModifyLeverCall(params: {
|
|
153
|
+
marginAmount: Web3Number, // >0 during deposit
|
|
154
|
+
debtAmount: Web3Number,
|
|
155
|
+
isIncrease: boolean
|
|
156
|
+
}): Promise<Call[]> {
|
|
157
|
+
assert(!params.marginAmount.isZero() || !params.debtAmount.isZero(), 'Deposit/debt must be non-0');
|
|
158
|
+
|
|
159
|
+
const [vesuAdapter1] = this.getVesuAdapters();
|
|
160
|
+
const lstTokenInfo = this.asset();
|
|
161
|
+
const lstUnderlyingTokenInfo = vesuAdapter1.config.debt;
|
|
162
|
+
|
|
163
|
+
const proofsIDs: string[] = [];
|
|
164
|
+
const manageCalls: ManageCall[] = [];
|
|
165
|
+
|
|
166
|
+
// approve token
|
|
167
|
+
if (params.marginAmount.greaterThan(0)) {
|
|
168
|
+
const STEP1_ID = UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1;
|
|
169
|
+
const manage1Info = this.getProofs<ApproveCallParams>(STEP1_ID);
|
|
170
|
+
const depositAmount = params.marginAmount;
|
|
171
|
+
const manageCall1 = manage1Info.callConstructor({
|
|
172
|
+
amount: depositAmount
|
|
173
|
+
})
|
|
174
|
+
proofsIDs.push(STEP1_ID);
|
|
175
|
+
manageCalls.push(manageCall1);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const ekuboQuoter = new EkuboQuoter(this.config);
|
|
179
|
+
|
|
180
|
+
// compute quotes for margin swap
|
|
181
|
+
const lstPrice = await this.getLSTExchangeRate();
|
|
182
|
+
const marginSwap: Swap[] = [];
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
// compute quotes for lever swap
|
|
186
|
+
const MAX_SLIPPAGE = 0.01;
|
|
187
|
+
// when increasing, debt is swapped to collateral (LST)
|
|
188
|
+
// when decreasing, collateral is swapped to debt (underlying)
|
|
189
|
+
// but both cases, we denominate amount in underlying. negative for decrease (exact amount out)
|
|
190
|
+
const fromToken = params.isIncrease ? lstUnderlyingTokenInfo : lstTokenInfo;
|
|
191
|
+
const toToken = params.isIncrease ? lstTokenInfo : lstUnderlyingTokenInfo;
|
|
192
|
+
const leverSwapQuote = await ekuboQuoter.getQuote(
|
|
193
|
+
fromToken.address.address,
|
|
194
|
+
toToken.address.address,
|
|
195
|
+
params.debtAmount // negative for exact amount out
|
|
196
|
+
);
|
|
197
|
+
assert(leverSwapQuote.price_impact < MAX_SLIPPAGE, 'getIncreaseLeverCall: Price impact is too high [Debt swap]');
|
|
198
|
+
const leverSwap = ekuboQuoter.getVesuMultiplyQuote(leverSwapQuote, fromToken, toToken);
|
|
199
|
+
const minExpectedDebt = params.debtAmount.dividedBy(lstPrice).multipliedBy(1 - MAX_SLIPPAGE); // used for increase
|
|
200
|
+
const maxUsedCollateral = params.debtAmount.abs().dividedBy(lstPrice).multipliedBy(1 + MAX_SLIPPAGE); // +ve for exact amount out, used for decrease
|
|
201
|
+
|
|
202
|
+
const STEP2_ID = LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_ON;
|
|
203
|
+
const manage2Info = this.getProofs<VesuModifyDelegationCallParams>(STEP2_ID);
|
|
204
|
+
const manageCall2 = manage2Info.callConstructor({
|
|
205
|
+
delegation: true
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// deposit and borrow or repay and withdraw
|
|
209
|
+
const STEP3_ID = LST_MULTIPLIER_MANAGE_IDS.MULTIPLY_VESU;
|
|
210
|
+
const manage3Info = this.getProofs<VesuMultiplyCallParams>(STEP3_ID);
|
|
211
|
+
const multiplyParams: VesuMultiplyCallParams = params.isIncrease ? {
|
|
212
|
+
isIncrease: true,
|
|
213
|
+
increaseParams: {
|
|
214
|
+
add_margin: params.marginAmount,
|
|
215
|
+
margin_swap: marginSwap,
|
|
216
|
+
margin_swap_limit_amount: Web3Number.fromWei(0, this.asset().decimals),
|
|
217
|
+
lever_swap: leverSwap,
|
|
218
|
+
lever_swap_limit_amount: minExpectedDebt
|
|
219
|
+
}
|
|
220
|
+
} : {
|
|
221
|
+
isIncrease: false,
|
|
222
|
+
decreaseParams: {
|
|
223
|
+
sub_margin: params.marginAmount.multipliedBy(-1),
|
|
224
|
+
lever_swap: leverSwap,
|
|
225
|
+
lever_swap_limit_amount: maxUsedCollateral,
|
|
226
|
+
// only required for close position
|
|
227
|
+
lever_swap_weights: [],
|
|
228
|
+
// no need to swap collateral to anything, and any residuals return our contract anyways.
|
|
229
|
+
withdraw_swap: [],
|
|
230
|
+
withdraw_swap_limit_amount: Web3Number.fromWei(0, this.asset().decimals),
|
|
231
|
+
withdraw_swap_weights: [],
|
|
232
|
+
close_position: false
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const manageCall3 = manage3Info.callConstructor(multiplyParams);
|
|
236
|
+
|
|
237
|
+
// switch delegation off
|
|
238
|
+
const STEP4_ID = LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_OFF;
|
|
239
|
+
const manage4Info = this.getProofs<VesuModifyDelegationCallParams>(STEP4_ID);
|
|
240
|
+
const manageCall4 = manage4Info.callConstructor({
|
|
241
|
+
delegation: false
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
proofsIDs.push(STEP2_ID, STEP3_ID, STEP4_ID);
|
|
245
|
+
manageCalls.push(manageCall2, manageCall3, manageCall4);
|
|
246
|
+
|
|
247
|
+
return [this.getManageCall(proofsIDs, manageCalls)];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export default function VaultDescription() {
|
|
252
|
+
const containerStyle = {
|
|
253
|
+
maxWidth: "800px",
|
|
254
|
+
margin: "0 auto",
|
|
255
|
+
backgroundColor: "#111",
|
|
256
|
+
color: "#eee",
|
|
257
|
+
fontFamily: "Arial, sans-serif",
|
|
258
|
+
borderRadius: "12px",
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
return (
|
|
262
|
+
<div style={containerStyle}>
|
|
263
|
+
<h1 style={{ fontSize: "18px", marginBottom: "10px" }}>Meta Vault — Automated Yield Router</h1>
|
|
264
|
+
<p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
|
|
265
|
+
This Levered Endur LST vault is a tokenized leveraged Vault, auto-compounding strategy that continuously allocates your deposited
|
|
266
|
+
asset to the best available yield source in the ecosystem. Depositors receive vault shares that
|
|
267
|
+
represent a proportional claim on the underlying assets and accrued yield. Allocation shifts are
|
|
268
|
+
handled programmatically based on on-chain signals and risk filters, minimizing idle capital and
|
|
269
|
+
maximizing net APY.
|
|
270
|
+
</p>
|
|
271
|
+
|
|
272
|
+
<div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
|
|
273
|
+
<p style={{ fontSize: "13px", color: "#ccc" }}>
|
|
274
|
+
<strong>Withdrawals:</strong> Requests can take up to <strong>1-2 hours</strong> to process as the vault unwinds and settles routing.
|
|
275
|
+
</p>
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
function getDescription(tokenSymbol: string) {
|
|
283
|
+
return VaultDescription();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
enum LST_MULTIPLIER_MANAGE_IDS {
|
|
287
|
+
MULTIPLY_VESU = 'multiply_vesu',
|
|
288
|
+
SWITCH_DELEGATION_ON = 'switch_delegation_on',
|
|
289
|
+
SWITCH_DELEGATION_OFF = 'switch_delegation_off'
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function getLooperSettings(
|
|
293
|
+
lstSymbol: string,
|
|
294
|
+
underlyingSymbol: string,
|
|
295
|
+
vaultSettings: UniversalStrategySettings,
|
|
296
|
+
pool1: ContractAddr,
|
|
297
|
+
) {
|
|
298
|
+
vaultSettings.leafAdapters = [];
|
|
299
|
+
|
|
300
|
+
const lstToken = Global.getDefaultTokens().find(token => token.symbol === lstSymbol)!;
|
|
301
|
+
const underlyingToken = Global.getDefaultTokens().find(token => token.symbol === underlyingSymbol)!;
|
|
302
|
+
|
|
303
|
+
const vesuAdapterLST = new VesuAdapter({
|
|
304
|
+
poolId: pool1,
|
|
305
|
+
collateral: lstToken,
|
|
306
|
+
debt: underlyingToken,
|
|
307
|
+
vaultAllocator: vaultSettings.vaultAllocator,
|
|
308
|
+
id: LST_MULTIPLIER_MANAGE_IDS.MULTIPLY_VESU
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
const commonAdapter = new CommonAdapter({
|
|
312
|
+
manager: vaultSettings.manager,
|
|
313
|
+
asset: lstToken.address,
|
|
314
|
+
id: '',
|
|
315
|
+
vaultAddress: vaultSettings.vaultAddress,
|
|
316
|
+
vaultAllocator: vaultSettings.vaultAllocator,
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
// vesu looping
|
|
320
|
+
vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, vesuAdapterLST.VESU_MULTIPLY, UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1).bind(commonAdapter));
|
|
321
|
+
vaultSettings.leafAdapters.push(vesuAdapterLST.getMultiplyAdapter.bind(vesuAdapterLST));
|
|
322
|
+
vaultSettings.leafAdapters.push(vesuAdapterLST.getVesuModifyDelegationAdapter(LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_ON).bind(vesuAdapterLST));
|
|
323
|
+
vaultSettings.leafAdapters.push(vesuAdapterLST.getVesuModifyDelegationAdapter(LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_OFF).bind(vesuAdapterLST));
|
|
324
|
+
|
|
325
|
+
// Useful for returning adapter class objects that can compute
|
|
326
|
+
// certain things for us (e.g. positions, hfs)
|
|
327
|
+
vaultSettings.adapters.push(...[{
|
|
328
|
+
id: UNIVERSAL_ADAPTERS.VESU_LEG1,
|
|
329
|
+
adapter: vesuAdapterLST
|
|
330
|
+
},{
|
|
331
|
+
id: UNIVERSAL_ADAPTERS.COMMON,
|
|
332
|
+
adapter: commonAdapter
|
|
333
|
+
}])
|
|
334
|
+
|
|
335
|
+
// to bridge liquidity back to vault (used by bring_liquidity)
|
|
336
|
+
vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, vaultSettings.vaultAddress, UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY).bind(commonAdapter));
|
|
337
|
+
vaultSettings.leafAdapters.push(commonAdapter.getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY).bind(commonAdapter));
|
|
338
|
+
|
|
339
|
+
// claim rewards
|
|
340
|
+
vaultSettings.leafAdapters.push(vesuAdapterLST.getDefispringRewardsAdapter(UNIVERSAL_MANAGE_IDS.DEFISPRING_REWARDS).bind(vesuAdapterLST));
|
|
341
|
+
|
|
342
|
+
// avnu swap for claims rewards
|
|
343
|
+
const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
|
|
344
|
+
vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(STRKToken.address, AVNU_MIDDLEWARE, UNIVERSAL_MANAGE_IDS.APPROVE_SWAP_TOKEN1).bind(commonAdapter));
|
|
345
|
+
vaultSettings.leafAdapters.push(commonAdapter.getAvnuAdapter(STRKToken.address, lstToken.address, UNIVERSAL_MANAGE_IDS.AVNU_SWAP_REWARDS).bind(commonAdapter));
|
|
346
|
+
return vaultSettings;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function getFAQs(lstSymbol: string, underlyingSymbol: string): FAQ[] {
|
|
350
|
+
return [
|
|
351
|
+
{
|
|
352
|
+
question: `What is the Hyper ${lstSymbol} Vault?`,
|
|
353
|
+
answer:
|
|
354
|
+
`The Hyper ${lstSymbol} Vault is a tokenized strategy that automatically loops your ${underlyingSymbol} or ${lstSymbol} to create up to 4x leverage to hence yield in a very low risk manner.`,
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
question: "How does yield allocation work?",
|
|
358
|
+
answer:
|
|
359
|
+
`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.`,
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
question: "Which protocols/dApp are used??",
|
|
363
|
+
answer: (
|
|
364
|
+
<span>
|
|
365
|
+
Currently, the LST is from <strong>Endur</strong> while <strong>Vesu</strong> is used to collateralize the looped position.
|
|
366
|
+
</span>
|
|
367
|
+
),
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
question: "Can I get liquidated?",
|
|
371
|
+
answer: "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.",
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
question: "What do I receive when I deposit?",
|
|
375
|
+
answer:
|
|
376
|
+
"Depositors receive vault tokens representing their proportional share of the vault. These tokens entitle holders to both the principal and accrued yield.",
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
question: "How long do withdrawals take?",
|
|
380
|
+
answer:
|
|
381
|
+
"Withdrawals may take up to 1-2 hours to process, as the vault unwinds and settles liquidity routing across integrated protocols. In case of large withdrawals, to avoid slippage, we may slowly unwind the position, which could make the withdrawals longer.",
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
question: "Is the Hyper xSTRK Vault non-custodial?",
|
|
385
|
+
answer:
|
|
386
|
+
"Yes. The Hyper xSTRK Vault operates entirely on-chain. Users always maintain control of their vault tokens, and the strategy is fully transparent.",
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
question: "Is the Vault audited?",
|
|
390
|
+
answer:
|
|
391
|
+
"Yes. The Hyper xSTRK Vault is audited by Zellic. Look for safety icon beside the strategy name for more details.",
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
question: "Are there any fees?",
|
|
395
|
+
answer:
|
|
396
|
+
"Troves charges a performance of 10% on the yield generated. The APY shown is net of this fee. This fee is only applied to the profits earned, ensuring that users retain their initial capital.",
|
|
397
|
+
},
|
|
398
|
+
];
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const _riskFactor: RiskFactor[] = [
|
|
402
|
+
{ type: RiskType.SMART_CONTRACT_RISK, value: SmartContractRiskLevel.WELL_AUDITED, weight: 25, reason: "Audited by Zellic" },
|
|
403
|
+
{ type: RiskType.LIQUIDATION_RISK, value: LiquidationRiskLevel.VERY_LOW_PROBABILITY, weight: 50, reason: "The collateral and debt are highly correlated" },
|
|
404
|
+
{ type: RiskType.TECHNICAL_RISK, value: TechnicalRiskLevel.STABLE_INFRASTRUCTURE, weight: 50, reason: "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." }
|
|
405
|
+
];
|
|
406
|
+
|
|
407
|
+
const hyperxSTRK: UniversalStrategySettings = {
|
|
408
|
+
vaultAddress: ContractAddr.from('0x46c7a54c82b1fe374353859f554a40b8bd31d3e30f742901579e7b57b1b5960'),
|
|
409
|
+
manager: ContractAddr.from('0x5d499cd333757f461a0bedaca3dfc4d77320c773037e0aa299f22a6dbfdc03a'),
|
|
410
|
+
vaultAllocator: ContractAddr.from('0x511d07953a09bc7c505970891507c5a2486d2ea22752601a14db092186d7caa'),
|
|
411
|
+
redeemRequestNFT: ContractAddr.from('0x51e40b839dc0c2feca923f863072673b94abfa2483345be3b30b457a90d095'),
|
|
412
|
+
aumOracle: ContractAddr.from('0x48cf709870a1a0d453d37de108e0c41b8b89819ef54f95abc0e2e1f98bbe937'),
|
|
413
|
+
leafAdapters: [],
|
|
414
|
+
adapters: [],
|
|
415
|
+
targetHealthFactor: 1.1,
|
|
416
|
+
minHealthFactor: 1.05
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function getInvestmentSteps(lstSymbol: string, underlyingSymbol: string) {
|
|
420
|
+
return [
|
|
421
|
+
`Deposit ${underlyingSymbol} into the vault`,
|
|
422
|
+
`The vault manager loops the ${underlyingSymbol} to buy ${lstSymbol}`,
|
|
423
|
+
`The vault manager collateralizes the ${lstSymbol} on Vesu`,
|
|
424
|
+
`The vault manager borrows more ${underlyingSymbol} to loop further`,
|
|
425
|
+
`Claim BTCFi STRK rewards weekly to swap to ${lstSymbol} and reinvest`,
|
|
426
|
+
`If required, adjust leverage or re-allocate assets within LST pool on Vesu to optimize yield`
|
|
427
|
+
]
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function getStrategySettings(lstSymbol: string, underlyingSymbol: string, addresses: UniversalStrategySettings): IStrategyMetadata<UniversalStrategySettings> {
|
|
431
|
+
return {
|
|
432
|
+
name: `Hyper ${lstSymbol}`,
|
|
433
|
+
description: getDescription(lstSymbol),
|
|
434
|
+
address: addresses.vaultAddress,
|
|
435
|
+
launchBlock: 0,
|
|
436
|
+
type: 'Other',
|
|
437
|
+
depositTokens: [Global.getDefaultTokens().find(token => token.symbol === lstSymbol)!],
|
|
438
|
+
additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, addresses, VesuPools.Re7xSTRK),
|
|
439
|
+
risk: {
|
|
440
|
+
riskFactor: _riskFactor,
|
|
441
|
+
netRisk:
|
|
442
|
+
_riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
|
|
443
|
+
_riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
444
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
445
|
+
},
|
|
446
|
+
protocols: [Protocols.ENDUR, Protocols.VESU],
|
|
447
|
+
maxTVL: Web3Number.fromWei(0, 18),
|
|
448
|
+
contractDetails: getContractDetails(addresses),
|
|
449
|
+
faqs: getFAQs(lstSymbol, underlyingSymbol),
|
|
450
|
+
investmentSteps: getInvestmentSteps(lstSymbol, underlyingSymbol),
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export const HyperLSTStrategies: IStrategyMetadata<UniversalStrategySettings>[] =
|
|
455
|
+
[
|
|
456
|
+
getStrategySettings('xSTRK', 'STRK', hyperxSTRK),
|
|
457
|
+
]
|