@strkfarm/sdk 1.1.70 → 2.0.0-dev.1
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 +2 -2
- package/dist/cli.mjs +2 -2
- package/dist/index.browser.global.js +66861 -59746
- package/dist/index.browser.mjs +24970 -18579
- package/dist/index.d.ts +1969 -776
- package/dist/index.js +25264 -18850
- package/dist/index.mjs +25463 -19089
- package/package.json +80 -76
- package/src/data/extended-deposit.abi.json +3613 -0
- package/src/data/universal-vault.abi.json +135 -20
- package/src/dataTypes/address.ts +8 -1
- package/src/global.ts +240 -193
- package/src/interfaces/common.tsx +26 -2
- package/src/modules/ExtendedWrapperSDk/index.ts +62 -0
- package/src/modules/ExtendedWrapperSDk/types.ts +311 -0
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +395 -0
- package/src/modules/avnu.ts +17 -4
- package/src/modules/ekubo-quoter.ts +98 -10
- package/src/modules/erc20.ts +67 -21
- package/src/modules/harvests.ts +16 -29
- package/src/modules/index.ts +5 -1
- package/src/modules/lst-apr.ts +36 -0
- package/src/modules/midas.ts +159 -0
- package/src/modules/pricer-from-api.ts +2 -2
- package/src/modules/pricer-lst.ts +1 -1
- package/src/modules/pricer.ts +3 -38
- package/src/modules/token-market-data.ts +202 -0
- package/src/node/deployer.ts +1 -36
- package/src/strategies/autoCompounderStrk.ts +1 -1
- package/src/strategies/base-strategy.ts +20 -3
- package/src/strategies/ekubo-cl-vault.tsx +123 -306
- package/src/strategies/index.ts +4 -1
- package/src/strategies/svk-strategy.ts +247 -0
- package/src/strategies/universal-adapters/adapter-optimizer.ts +65 -0
- package/src/strategies/universal-adapters/adapter-utils.ts +5 -1
- package/src/strategies/universal-adapters/avnu-adapter.ts +418 -0
- package/src/strategies/universal-adapters/baseAdapter.ts +181 -153
- package/src/strategies/universal-adapters/common-adapter.ts +98 -77
- package/src/strategies/universal-adapters/extended-adapter.ts +544 -0
- package/src/strategies/universal-adapters/index.ts +5 -1
- package/src/strategies/universal-adapters/unused-balance-adapter.ts +109 -0
- package/src/strategies/universal-adapters/vesu-adapter.ts +220 -218
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +924 -0
- package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +58 -51
- package/src/strategies/universal-lst-muliplier-strategy.tsx +707 -774
- package/src/strategies/universal-strategy.tsx +1098 -1180
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +28 -0
- package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +77 -0
- package/src/strategies/vesu-extended-strategy/utils/constants.ts +48 -0
- package/src/strategies/vesu-extended-strategy/utils/helper.ts +374 -0
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +992 -0
- package/src/strategies/vesu-rebalance.tsx +16 -19
- package/src/utils/health-factor-math.ts +11 -5
|
@@ -1,31 +1,36 @@
|
|
|
1
|
-
import { FAQ, getNoRiskTags, highlightTextWithLinks, IConfig, IStrategyMetadata, Protocols, RiskFactor, RiskType, TokenInfo } from "@/interfaces";
|
|
2
|
-
import { AUMTypes, getContractDetails,
|
|
1
|
+
import { FAQ, getMainnetConfig, getNoRiskTags, highlightTextWithLinks, IConfig, IStrategyMetadata, Protocols, RiskFactor, RiskType, TokenInfo, VaultPosition } from "@/interfaces";
|
|
2
|
+
import { AUMTypes, getContractDetails, UNIVERSAL_MANAGE_IDS, UniversalManageCall, UniversalStrategySettings } from "./universal-strategy";
|
|
3
3
|
import { PricerBase } from "@/modules/pricerBase";
|
|
4
4
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
5
5
|
import { Global } from "@/global";
|
|
6
|
-
import { ApproveCallParams, AvnuSwapCallParams,
|
|
6
|
+
import { ApproveCallParams, APYType, AvnuSwapCallParams, BaseAdapterConfig, CommonAdapter, ManageCall, PositionInfo, UnusedBalanceAdapter, VesuMultiplyAdapter, VesuPools, VesuSupplyOnlyAdapter } from "./universal-adapters";
|
|
7
7
|
import { AVNU_EXCHANGE } from "./universal-adapters/adapter-utils";
|
|
8
8
|
import { DepegRiskLevel, LiquidationRiskLevel, SmartContractRiskLevel, TechnicalRiskLevel } from "@/interfaces/risks";
|
|
9
|
-
import { AvnuWrapper, EkuboQuoter, ERC20,
|
|
9
|
+
import { AvnuWrapper, EkuboQuoter, ERC20, PricerFromApi, PricerLST } from "@/modules";
|
|
10
10
|
import { assert, logger } from "@/utils";
|
|
11
|
-
import { SingleTokenInfo } from "./base-strategy";
|
|
12
|
-
import {
|
|
11
|
+
import { SingleActionAmount, SingleTokenInfo } from "./base-strategy";
|
|
12
|
+
import { SVKStrategy } from "./svk-strategy";
|
|
13
|
+
import { Call, uint256 } from "starknet";
|
|
13
14
|
import ERC4626Abi from "@/data/erc4626.abi.json";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
15
|
+
import { AdapterOptimizer } from "./universal-adapters/adapter-optimizer";
|
|
16
|
+
import { MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP } from "./vesu-extended-strategy/utils/constants";
|
|
17
|
+
import { VesuExtendedStrategySettings } from "./vesu-extended-strategy/vesu-extended-strategy";
|
|
16
18
|
|
|
17
19
|
export interface HyperLSTStrategySettings extends UniversalStrategySettings {
|
|
18
20
|
borrowable_assets: TokenInfo[];
|
|
19
21
|
underlyingToken: TokenInfo;
|
|
22
|
+
quoteAmountToFetchPrice: Web3Number;
|
|
23
|
+
targetHealthFactor: number;
|
|
24
|
+
minHealthFactor: number;
|
|
25
|
+
aumOracle: ContractAddr;
|
|
20
26
|
}
|
|
21
27
|
|
|
22
|
-
export class UniversalLstMultiplierStrategy extends
|
|
23
|
-
|
|
28
|
+
export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings> extends SVKStrategy<S> {
|
|
29
|
+
|
|
24
30
|
private quoteAmountToFetchPrice = new Web3Number(1, 18);
|
|
25
31
|
|
|
26
|
-
constructor(config: IConfig, pricer: PricerBase, metadata: IStrategyMetadata<
|
|
32
|
+
constructor(config: IConfig, pricer: PricerBase, metadata: IStrategyMetadata<S>) {
|
|
27
33
|
super(config, pricer, metadata);
|
|
28
|
-
|
|
29
34
|
const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
|
|
30
35
|
const underlyingToken = this.getLSTUnderlyingTokenInfo();
|
|
31
36
|
if (underlyingToken.address.eq(STRKToken.address)) {
|
|
@@ -34,10 +39,15 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
34
39
|
// else this BTC
|
|
35
40
|
this.quoteAmountToFetchPrice = new Web3Number(0.01, this.asset().decimals);
|
|
36
41
|
}
|
|
37
|
-
}
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
this.metadata.additionalInfo.adapters.forEach(adapter => {
|
|
44
|
+
adapter.adapter.config.networkConfig = this.config;
|
|
45
|
+
adapter.adapter.config.pricer = this.pricer;
|
|
46
|
+
if ((adapter.adapter as VesuMultiplyAdapter).vesuAdapter) {
|
|
47
|
+
(adapter.adapter as VesuMultiplyAdapter).vesuAdapter.networkConfig = this.config;
|
|
48
|
+
(adapter.adapter as VesuMultiplyAdapter).vesuAdapter.pricer = this.pricer;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
getTag() {
|
|
@@ -46,376 +56,352 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
46
56
|
|
|
47
57
|
// Vesu adapter with LST and base token match
|
|
48
58
|
getVesuSameTokenAdapter() {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
baseAdapter
|
|
59
|
+
const vesuMultipleAdapters = this.getVesuAdapters();
|
|
60
|
+
console.log(vesuMultipleAdapters.map(adapter => adapter.config.debt.symbol));
|
|
61
|
+
const baseAdapter = vesuMultipleAdapters.find((adapter) => {
|
|
62
|
+
return adapter.config.debt.address.eq(this.metadata.additionalInfo.underlyingToken.address);
|
|
63
|
+
})
|
|
64
|
+
if (!baseAdapter) {
|
|
65
|
+
throw new Error(`${this.getTag()}::getVesuSameTokenAdapter: base adapter not found`);
|
|
66
|
+
}
|
|
67
|
+
baseAdapter.config.networkConfig = this.config;
|
|
68
|
+
baseAdapter.config.pricer = this.pricer;
|
|
52
69
|
return baseAdapter;
|
|
53
70
|
}
|
|
54
71
|
|
|
55
72
|
// only one leg is used
|
|
56
73
|
// todo support lending assets of underlying as well (like if xSTRK looping is not viable, simply supply STRK)
|
|
57
74
|
getVesuAdapters() {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const vesuAdapter1 = new VesuAdapter({
|
|
62
|
-
poolId: baseAdapter.config.poolId,
|
|
63
|
-
collateral: this.asset(),
|
|
64
|
-
debt: asset,
|
|
65
|
-
vaultAllocator: this.metadata.additionalInfo.vaultAllocator,
|
|
66
|
-
id: ''
|
|
67
|
-
})
|
|
68
|
-
vesuAdapter1.pricer = this.pricer;
|
|
69
|
-
vesuAdapter1.networkConfig = this.config;
|
|
70
|
-
adapters.push(vesuAdapter1);
|
|
71
|
-
}
|
|
72
|
-
return adapters;
|
|
73
|
-
}
|
|
75
|
+
const vesuMultipleAdapters = this.metadata.additionalInfo.adapters
|
|
76
|
+
.filter(adapter => adapter.adapter.name === VesuMultiplyAdapter.name)
|
|
77
|
+
.map(adapter => adapter.adapter) as VesuMultiplyAdapter[]
|
|
74
78
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const lstToken = this.asset();
|
|
79
|
-
if (lstToken.symbol === 'xSTRK') {
|
|
80
|
-
return super.getRewardsAUM(prevAum);
|
|
79
|
+
for (const vesuAdapter of vesuMultipleAdapters) {
|
|
80
|
+
vesuAdapter.config.pricer = this.pricer;
|
|
81
|
+
vesuAdapter.config.networkConfig = this.config;
|
|
81
82
|
}
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async getLSTDexPrice() {
|
|
86
|
-
const ekuboQuoter = new EkuboQuoter(this.config);
|
|
87
|
-
const lstTokenInfo = this.asset();
|
|
88
|
-
const lstUnderlyingTokenInfo = this.getLSTUnderlyingTokenInfo();
|
|
89
|
-
const quote = await ekuboQuoter.getQuote(
|
|
90
|
-
lstTokenInfo.address.address,
|
|
91
|
-
lstUnderlyingTokenInfo.address.address,
|
|
92
|
-
this.quoteAmountToFetchPrice
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
// in Underlying
|
|
96
|
-
const outputAmount = Web3Number.fromWei(quote.total_calculated, lstUnderlyingTokenInfo.decimals);
|
|
97
|
-
const price = outputAmount.toNumber() / this.quoteAmountToFetchPrice.toNumber();
|
|
98
|
-
logger.verbose(`${this.getTag()}:: LST Dex Price: ${price}`);
|
|
99
|
-
return price;
|
|
83
|
+
return vesuMultipleAdapters;
|
|
100
84
|
}
|
|
101
85
|
|
|
102
86
|
|
|
103
|
-
async getAvnuSwapMultiplyCall(params: {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
async _getAvnuDepositSwapLegCall(params: {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
87
|
+
// async getAvnuSwapMultiplyCall(params: {
|
|
88
|
+
// isDeposit: boolean,
|
|
89
|
+
// leg1DepositAmount: Web3Number
|
|
90
|
+
// }) {
|
|
91
|
+
// assert(params.isDeposit, 'Only deposit is supported in getAvnuSwapMultiplyCall')
|
|
92
|
+
// // TODO use a varibale for 1.02
|
|
93
|
+
// const maxBorrowableAmounts = await this.getMaxBorrowableAmount({ isAPYComputation: false });
|
|
94
|
+
// const allVesuAdapters = this.getVesuAdapters();
|
|
95
|
+
// let remainingAmount = params.leg1DepositAmount;
|
|
96
|
+
// const lstExRate = await this.getLSTExchangeRate();
|
|
97
|
+
// const baseAssetPrice = await this.pricer.getPrice(this.getLSTUnderlyingTokenInfo().symbol);
|
|
98
|
+
// const lstPrice = baseAssetPrice.price * lstExRate;
|
|
99
|
+
// for (let i = 0; i < maxBorrowableAmounts.maxBorrowables.length; i++) {
|
|
100
|
+
// const maxBorrowable = maxBorrowableAmounts.maxBorrowables[i];
|
|
101
|
+
// const vesuAdapter = allVesuAdapters.find(adapter => adapter.config.debt.address.eq(maxBorrowable.borrowableAsset.address));
|
|
102
|
+
// if (!vesuAdapter) {
|
|
103
|
+
// throw new Error(`${this.getTag()}::getAvnuSwapMultiplyCall: vesuAdapter not found for borrowable asset: ${maxBorrowable.borrowableAsset.symbol}`);
|
|
104
|
+
// }
|
|
105
|
+
// const maxLTV = await vesuAdapter.getLTVConfig(this.config);
|
|
106
|
+
// const debtPrice = await this.pricer.getPrice(maxBorrowable.borrowableAsset.symbol);
|
|
107
|
+
// const maxAmountToDeposit = HealthFactorMath.getMinCollateralRequiredOnLooping(
|
|
108
|
+
// maxBorrowable.amount,
|
|
109
|
+
// debtPrice.price,
|
|
110
|
+
// this.metadata.additionalInfo.targetHealthFactor,
|
|
111
|
+
// maxLTV,
|
|
112
|
+
// lstPrice,
|
|
113
|
+
// this.asset()
|
|
114
|
+
// )
|
|
115
|
+
// const amountToDeposit = remainingAmount.minimum(maxAmountToDeposit);
|
|
116
|
+
// logger.verbose(`${this.getTag()}::getAvnuSwapMultiplyCall::${vesuAdapter.config.debt.symbol}:: remainingAmount: ${remainingAmount}, amountToDeposit: ${amountToDeposit}, depositAmount: ${amountToDeposit}, maxBorrowable: ${maxBorrowable.amount}`);
|
|
117
|
+
// const call = await this._getAvnuDepositSwapLegCall({
|
|
118
|
+
// isDeposit: params.isDeposit,
|
|
119
|
+
// // adjust decimals of debt asset
|
|
120
|
+
// leg1DepositAmount: amountToDeposit,
|
|
121
|
+
// minHF: 1.1, // undo
|
|
122
|
+
// vesuAdapter: vesuAdapter
|
|
123
|
+
// });
|
|
124
|
+
// remainingAmount = remainingAmount.minus(amountToDeposit);
|
|
125
|
+
|
|
126
|
+
// // return the first possible call because computing all calls at a time
|
|
127
|
+
// // is not efficinet for swaps
|
|
128
|
+
// return {call, vesuAdapter};
|
|
129
|
+
// }
|
|
130
|
+
// throw new Error(`${this.getTag()}::getAvnuSwapMultiplyCall: no calls found`);
|
|
131
|
+
// }
|
|
132
|
+
|
|
133
|
+
// async _getAvnuDepositSwapLegCall(params: {
|
|
134
|
+
// isDeposit: boolean,
|
|
135
|
+
// leg1DepositAmount: Web3Number,
|
|
136
|
+
// minHF: number, // e.g. 1.01
|
|
137
|
+
// vesuAdapter: VesuAdapter
|
|
138
|
+
// }) {
|
|
139
|
+
// const { vesuAdapter } = params;
|
|
140
|
+
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall params: ${JSON.stringify(params)}`);
|
|
141
|
+
// assert(params.isDeposit, 'Only deposit is supported in _getAvnuDepositSwapLegCall')
|
|
142
|
+
// // add collateral
|
|
143
|
+
// // borrow STRK (e.g.)
|
|
144
|
+
// // approve and swap strk
|
|
145
|
+
// // add collateral again
|
|
162
146
|
|
|
163
147
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
148
|
+
// const legLTV = await vesuAdapter.getLTVConfig(this.config);
|
|
149
|
+
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall legLTV: ${legLTV}`);
|
|
150
|
+
// const existingPositions = await vesuAdapter.getPositions(this.config);
|
|
151
|
+
// const collateralisation = await vesuAdapter.getCollateralization(this.config);
|
|
152
|
+
// const existingCollateralInfo = existingPositions[0];
|
|
153
|
+
// const existingDebtInfo = existingPositions[1];
|
|
154
|
+
// logger.debug(`${this.getTag()}::_getAvnuDepositSwapLegCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
155
|
+
// existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
156
|
+
|
|
157
|
+
// // - Prices as seen by Vesu contracts, ideal for HF math
|
|
158
|
+
// // Price 1 is ok as fallback bcz that would relatively price the
|
|
159
|
+
// // collateral and debt as equal.
|
|
160
|
+
// const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
|
|
161
|
+
// const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
|
|
162
|
+
// logger.debug(`${this.getTag()}::_getAvnuDepositSwapLegCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
// const debtTokenInfo = vesuAdapter.config.debt;
|
|
166
|
+
// let newDepositAmount = params.leg1DepositAmount;
|
|
167
|
+
// const totalCollateral = existingCollateralInfo.amount.plus(params.leg1DepositAmount);
|
|
168
|
+
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall totalCollateral: ${totalCollateral}`);
|
|
169
|
+
// const totalDebtAmount = new Web3Number(
|
|
170
|
+
// totalCollateral.multipliedBy(collateralPrice).multipliedBy(legLTV).dividedBy(debtPrice).dividedBy(params.minHF).toString(),
|
|
171
|
+
// debtTokenInfo.decimals
|
|
172
|
+
// );
|
|
173
|
+
// let debtAmount = totalDebtAmount.minus(existingDebtInfo.amount);
|
|
174
|
+
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall totalDebtAmount: ${totalDebtAmount}, initial computed debt: ${debtAmount}`);
|
|
175
|
+
// const maxBorrowable = await this.getMaxBorrowableAmountByVesuAdapter(vesuAdapter, false);
|
|
176
|
+
|
|
177
|
+
// // if the debt amount is greater than 0 and the max borrowable amount is 0, skip
|
|
178
|
+
// if (debtAmount.gt(0) && maxBorrowable.amount.eq(0)) {
|
|
179
|
+
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall maxBorrowable is 0, skipping`);
|
|
180
|
+
// return undefined;
|
|
181
|
+
// } else if (debtAmount.gt(0) && maxBorrowable.amount.gt(0)) {
|
|
182
|
+
// debtAmount = maxBorrowable.amount.minimum(debtAmount);
|
|
183
|
+
// const newDebtUSDValue = debtAmount.multipliedBy(debtPrice);
|
|
184
|
+
// const totalCollateralRequired = HealthFactorMath.getCollateralRequired(
|
|
185
|
+
// debtAmount.plus(existingDebtInfo.amount),
|
|
186
|
+
// debtPrice,
|
|
187
|
+
// params.minHF,
|
|
188
|
+
// legLTV,
|
|
189
|
+
// collateralPrice,
|
|
190
|
+
// this.asset()
|
|
191
|
+
// );
|
|
192
|
+
// newDepositAmount = totalCollateralRequired.minus(existingCollateralInfo.amount);
|
|
193
|
+
// if (newDepositAmount.lt(0)) {
|
|
194
|
+
// throw new Error(`${this.getTag()}::_getAvnuDepositSwapLegCall newDepositAmount is less than 0, newDepositAmount: ${newDepositAmount}, totalCollateralRequired: ${totalCollateralRequired}, existingCollateralInfo.amount: ${existingCollateralInfo.amount}`);
|
|
195
|
+
// }
|
|
196
|
+
// if (newDebtUSDValue.toNumber() < 100) {
|
|
197
|
+
// // too less debt, skip
|
|
198
|
+
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall newDebtUSDValue is less than 100, skipping`);
|
|
199
|
+
// return undefined;
|
|
200
|
+
// }
|
|
201
|
+
// }
|
|
202
|
+
|
|
203
|
+
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall debtAmount: ${debtAmount}`);
|
|
204
|
+
// if (debtAmount.lt(0)) {
|
|
205
|
+
// // this is to unwind the position to optimal HF.
|
|
206
|
+
// const lstDEXPrice = await this.getLSTDexPrice();
|
|
207
|
+
// const debtAmountInLST = debtAmount.abs().dividedBy(lstDEXPrice);
|
|
208
|
+
// const calls = await this.getVesuMultiplyCall({
|
|
209
|
+
// isDeposit: false,
|
|
210
|
+
// leg1DepositAmount: debtAmountInLST
|
|
211
|
+
// })
|
|
212
|
+
// assert(calls.length == 1, `Expected 1 call for unwind, got ${calls.length}`);
|
|
213
|
+
// return calls[0];
|
|
214
|
+
// }
|
|
215
|
+
// console.log(`debtAmount`, debtAmount.toWei(), params.leg1DepositAmount.toWei());
|
|
216
|
+
// const STEP0 = UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1;
|
|
217
|
+
// const manage0Info = this.getProofs<ApproveCallParams>(STEP0);
|
|
218
|
+
// const manageCall0 = manage0Info.callConstructor({
|
|
219
|
+
// amount: newDepositAmount
|
|
220
|
+
// });
|
|
221
|
+
// const STEP1 = getVesuLegId(UNIVERSAL_MANAGE_IDS.VESU_LEG1, vesuAdapter.config.debt.symbol);
|
|
222
|
+
// const manage1Info = this.getProofs<VesuModifyPositionCallParams>(STEP1);
|
|
223
|
+
// const manageCall1 = manage1Info.callConstructor(VesuAdapter.getDefaultModifyPositionCallParams({
|
|
224
|
+
// collateralAmount: newDepositAmount,
|
|
225
|
+
// isAddCollateral: params.isDeposit,
|
|
226
|
+
// debtAmount: debtAmount,
|
|
227
|
+
// isBorrow: params.isDeposit
|
|
228
|
+
// }));
|
|
229
|
+
|
|
230
|
+
// console.log(`manageCall1`, manageCall1.call, debtAmount.toWei(), newDepositAmount.toWei());
|
|
247
231
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
232
|
+
// const proofIds: string[] = [STEP0, STEP1];
|
|
233
|
+
// const manageCalls: ManageCall[] = [manageCall0, manageCall1];
|
|
234
|
+
|
|
235
|
+
// // approve and swap to LST using avnu
|
|
236
|
+
// if (debtAmount.gt(0)) {
|
|
237
|
+
// const STEP2 = getAvnuManageIDs(LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_APPROVE_DEPOSIT, vesuAdapter.config.debt.symbol);
|
|
238
|
+
// const manage2Info = this.getProofs<ApproveCallParams>(STEP2);
|
|
239
|
+
// const manageCall2 = manage2Info.callConstructor({
|
|
240
|
+
// amount: debtAmount
|
|
241
|
+
// });
|
|
242
|
+
|
|
243
|
+
// const debtTokenInfo = vesuAdapter.config.debt;
|
|
244
|
+
// const lstTokenInfo = this.asset();
|
|
245
|
+
// const avnuModule = new AvnuWrapper();
|
|
246
|
+
// const quote = await avnuModule.getQuotes(
|
|
247
|
+
// debtTokenInfo.address.address,
|
|
248
|
+
// lstTokenInfo.address.address,
|
|
249
|
+
// debtAmount.toWei(),
|
|
250
|
+
// this.metadata.additionalInfo.vaultAllocator.address
|
|
251
|
+
// );
|
|
252
|
+
// const minAmount = await this._getMinOutputAmountLSTBuy(debtAmount);
|
|
253
|
+
// const minAmountWei = (minAmount).toWei();
|
|
254
|
+
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall minAmount: ${minAmount}`);
|
|
255
|
+
// const swapInfo = await avnuModule.getSwapInfo(
|
|
256
|
+
// quote,
|
|
257
|
+
// this.metadata.additionalInfo.vaultAllocator.address,
|
|
258
|
+
// 0,
|
|
259
|
+
// this.address.address,
|
|
260
|
+
// minAmountWei
|
|
261
|
+
// );
|
|
262
|
+
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall swapInfo: ${JSON.stringify(swapInfo)}`);
|
|
263
|
+
// const STEP3 = getAvnuManageIDs(LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_DEPOSIT, vesuAdapter.config.debt.symbol);
|
|
264
|
+
// const manage3Info = this.getProofs<AvnuSwapCallParams>(STEP3);
|
|
265
|
+
// const manageCall3 = manage3Info.callConstructor({
|
|
266
|
+
// props: swapInfo
|
|
267
|
+
// });
|
|
268
|
+
// proofIds.push(STEP2);
|
|
269
|
+
// proofIds.push(STEP3);
|
|
270
|
+
// manageCalls.push(manageCall2, manageCall3);
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
// // if the created debt, when added is collateral will put the total HF above min, but below (target + 0.05),
|
|
274
|
+
// // then lets close the looping cycle by adding this as collateral.
|
|
275
|
+
// const newCollateral = minAmount.plus(totalCollateral);
|
|
276
|
+
// const newHF = newCollateral.multipliedBy(collateralPrice).multipliedBy(legLTV).dividedBy(totalDebtAmount).dividedBy(debtPrice).toNumber();
|
|
277
|
+
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall newHF: ${newHF}`);
|
|
278
|
+
// if (newHF > this.metadata.additionalInfo.minHealthFactor && newHF < this.metadata.additionalInfo.targetHealthFactor + 0.05) {
|
|
279
|
+
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall newHF is above min and below target + 0.05, adding collateral on vesu`);
|
|
280
|
+
// // approve and add collateral on vesu (modify position)
|
|
281
|
+
// const STEP4 = UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1;
|
|
282
|
+
// const manage4Info = this.getProofs<ApproveCallParams>(STEP4);
|
|
283
|
+
// const manageCall4 = manage4Info.callConstructor({
|
|
284
|
+
// amount: minAmount
|
|
285
|
+
// });
|
|
302
286
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
287
|
+
// const STEP5 = getVesuLegId(UNIVERSAL_MANAGE_IDS.VESU_LEG1, vesuAdapter.config.debt.symbol);
|
|
288
|
+
// const manage5Info = this.getProofs<VesuModifyPositionCallParams>(STEP5);
|
|
289
|
+
// const manageCall5 = manage5Info.callConstructor(VesuAdapter.getDefaultModifyPositionCallParams({
|
|
290
|
+
// collateralAmount: minAmount,
|
|
291
|
+
// isAddCollateral: true,
|
|
292
|
+
// debtAmount: Web3Number.fromWei('0', this.asset().decimals),
|
|
293
|
+
// isBorrow: params.isDeposit
|
|
294
|
+
// }));
|
|
295
|
+
// proofIds.push(STEP4, STEP5);
|
|
296
|
+
// manageCalls.push(manageCall4, manageCall5);
|
|
297
|
+
// }
|
|
298
|
+
// }
|
|
299
|
+
|
|
300
|
+
// const manageCall = this.getManageCall(proofIds, manageCalls);
|
|
301
|
+
// return manageCall;
|
|
302
|
+
// }
|
|
319
303
|
|
|
320
304
|
// todo unwind or not deposit when the yield is bad.
|
|
321
305
|
|
|
322
|
-
async getLSTMultiplierRebalanceCall(): Promise<{ shouldRebalance: boolean, manageCalls: {vesuAdapter: VesuAdapter, manageCall: Call}[] }> {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
async _getLSTMultiplierRebalanceCall(vesuAdapter: VesuAdapter): Promise<{ shouldRebalance: boolean, manageCall: Call | undefined }> {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
protected async getVesuAUM(adapter: VesuAdapter) {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
306
|
+
// async getLSTMultiplierRebalanceCall(): Promise<{ shouldRebalance: boolean, manageCalls: {vesuAdapter: VesuAdapter, manageCall: Call}[] }> {
|
|
307
|
+
// let shouldRebalance = false;
|
|
308
|
+
// const calls: {vesuAdapter: VesuAdapter, manageCall: Call}[] = [];
|
|
309
|
+
// // todo undo
|
|
310
|
+
// const allVesuAdapters = this.getVesuAdapters().filter(vesuAdapter => vesuAdapter.config.debt.symbol === 'LBTC');
|
|
311
|
+
// for (const vesuAdapter of allVesuAdapters) {
|
|
312
|
+
// const call = await this._getLSTMultiplierRebalanceCall(vesuAdapter);
|
|
313
|
+
// if (call.shouldRebalance && call.manageCall) {
|
|
314
|
+
// shouldRebalance = true;
|
|
315
|
+
// calls.push({vesuAdapter, manageCall: call.manageCall});
|
|
316
|
+
// }
|
|
317
|
+
// }
|
|
318
|
+
// return { shouldRebalance, manageCalls: calls };
|
|
319
|
+
// }
|
|
320
|
+
|
|
321
|
+
// async _getLSTMultiplierRebalanceCall(vesuAdapter: VesuAdapter): Promise<{ shouldRebalance: boolean, manageCall: Call | undefined }> {
|
|
322
|
+
// const positions = await vesuAdapter.getPositions(this.config);
|
|
323
|
+
// assert(positions.length == 2, 'Rebalance call is only supported for 2 positions');
|
|
324
|
+
// const existingCollateralInfo = positions[0];
|
|
325
|
+
// const existingDebtInfo = positions[1];
|
|
326
|
+
// const unusedBalance = await this.getUnusedBalance();
|
|
327
|
+
// const healthFactor = await vesuAdapter.getHealthFactor();
|
|
328
|
+
|
|
329
|
+
// const collateralisation = await vesuAdapter.getCollateralization(this.config);
|
|
330
|
+
// logger.debug(`${this.getTag()}::getVesuMultiplyCall::${vesuAdapter.config.debt.symbol} existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
331
|
+
// existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
332
|
+
|
|
333
|
+
// // - Prices as seen by Vesu contracts, ideal for HF math
|
|
334
|
+
// // Price 1 is ok as fallback bcz that would relatively price the
|
|
335
|
+
// // collateral and debt as equal.
|
|
336
|
+
// const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
|
|
337
|
+
// const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
|
|
338
|
+
// logger.debug(`${this.getTag()}::getVesuMultiplyCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
|
|
339
|
+
// logger.debug(`${this.getTag()}::getVesuMultiplyCall healthFactor: ${healthFactor}`);
|
|
340
|
+
|
|
341
|
+
// const isHFTooLow = healthFactor < this.metadata.additionalInfo.minHealthFactor;
|
|
342
|
+
// const isHFTooHigh = healthFactor > this.metadata.additionalInfo.targetHealthFactor + 0.05;
|
|
343
|
+
// if (isHFTooLow || isHFTooHigh || 1) {
|
|
344
|
+
// // use unused collateral to target more.
|
|
345
|
+
// const manageCall = await this._getAvnuDepositSwapLegCall({
|
|
346
|
+
// isDeposit: true,
|
|
347
|
+
// leg1DepositAmount: unusedBalance.amount,
|
|
348
|
+
// minHF: 1.02, // todo, shouldnt use this 1.02 HF, if there isn;t more looping left.
|
|
349
|
+
// vesuAdapter
|
|
350
|
+
// })
|
|
351
|
+
// return { shouldRebalance: true, manageCall };
|
|
352
|
+
// } else {
|
|
353
|
+
// // do nothing
|
|
354
|
+
// return { shouldRebalance: false, manageCall: undefined };
|
|
355
|
+
// }
|
|
356
|
+
// }
|
|
357
|
+
|
|
358
|
+
// protected async getVesuAUM(adapter: VesuAdapter) {
|
|
359
|
+
// const legAUM = await adapter.getPositions(this.config);
|
|
360
|
+
// const underlying = this.asset();
|
|
361
|
+
// // assert its an LST of Endur
|
|
362
|
+
// assert(underlying.symbol.startsWith('x'), 'Underlying is not an LST of Endur');
|
|
363
|
+
|
|
364
|
+
// let vesuAum = Web3Number.fromWei("0", underlying.decimals);
|
|
381
365
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
366
|
+
// let tokenUnderlyingPrice = await this.getLSTExchangeRate();
|
|
367
|
+
// // to offset for usual DEX lag, we multiply by 0.998 (i.e. 0.2% loss)
|
|
368
|
+
// tokenUnderlyingPrice = tokenUnderlyingPrice * 0.998;
|
|
369
|
+
// logger.verbose(`${this.getTag()} tokenUnderlyingPrice: ${tokenUnderlyingPrice}`);
|
|
386
370
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
371
|
+
// // handle collateral
|
|
372
|
+
// if (legAUM[0].token.address.eq(underlying.address)) {
|
|
373
|
+
// vesuAum = vesuAum.plus(legAUM[0].amount);
|
|
374
|
+
// } else {
|
|
375
|
+
// vesuAum = vesuAum.plus(legAUM[0].amount.dividedBy(tokenUnderlyingPrice));
|
|
376
|
+
// }
|
|
377
|
+
|
|
378
|
+
// // handle debt
|
|
379
|
+
// if (legAUM[1].token.address.eq(underlying.address)) {
|
|
380
|
+
// vesuAum = vesuAum.minus(legAUM[1].amount);
|
|
381
|
+
// } else {
|
|
382
|
+
// vesuAum = vesuAum.minus(legAUM[1].amount.dividedBy(tokenUnderlyingPrice));
|
|
383
|
+
// };
|
|
384
|
+
|
|
385
|
+
// logger.verbose(`${this.getTag()} Vesu AUM: ${vesuAum}, legCollateral: ${legAUM[0].amount.toNumber()}, legDebt: ${legAUM[1].amount.toNumber()}`);
|
|
386
|
+
// return vesuAum;
|
|
387
|
+
// }
|
|
393
388
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
389
|
+
//
|
|
390
|
+
// private async _getMinOutputAmountLSTBuy(amountInUnderlying: Web3Number) {
|
|
391
|
+
// const lstTruePrice = await this.getLSTExchangeRate();
|
|
392
|
+
// // during buy, the purchase should always be <= true LST price.
|
|
393
|
+
// const minOutputAmount = amountInUnderlying.dividedBy(lstTruePrice).multipliedBy(0.99979); // minus 0.021% to account for avnu fees
|
|
394
|
+
// return new Web3Number(minOutputAmount.toString(), this.asset().decimals);
|
|
395
|
+
// }
|
|
400
396
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
397
|
+
// private async _getMinOutputAmountLSTSell(amountInLST: Web3Number) {
|
|
398
|
+
// const lstTruePrice = await this.getLSTExchangeRate();
|
|
399
|
+
// // during sell, the purchase should always be > 0.995 * true LST price.
|
|
400
|
+
// const minOutputAmount = amountInLST.multipliedBy(lstTruePrice).multipliedBy(0.995);
|
|
401
|
+
// return minOutputAmount;
|
|
402
|
+
// }
|
|
404
403
|
|
|
405
|
-
//
|
|
406
|
-
private async _getMinOutputAmountLSTBuy(amountInUnderlying: Web3Number) {
|
|
407
|
-
const lstTruePrice = await this.getLSTExchangeRate();
|
|
408
|
-
// during buy, the purchase should always be <= true LST price.
|
|
409
|
-
const minOutputAmount = amountInUnderlying.dividedBy(lstTruePrice).multipliedBy(0.99979); // minus 0.021% to account for avnu fees
|
|
410
|
-
return new Web3Number(minOutputAmount.toString(), this.asset().decimals);
|
|
411
|
-
}
|
|
412
404
|
|
|
413
|
-
private async _getMinOutputAmountLSTSell(amountInLST: Web3Number) {
|
|
414
|
-
const lstTruePrice = await this.getLSTExchangeRate();
|
|
415
|
-
// during sell, the purchase should always be > 0.995 * true LST price.
|
|
416
|
-
const minOutputAmount = amountInLST.multipliedBy(lstTruePrice).multipliedBy(0.995);
|
|
417
|
-
return minOutputAmount;
|
|
418
|
-
}
|
|
419
405
|
|
|
420
406
|
// todo add a function to findout max borrowable amount without fucking yield
|
|
421
407
|
// if the current net yield < LST yield, add a function to calculate how much to unwind.
|
|
@@ -425,376 +411,275 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTSt
|
|
|
425
411
|
* Deposit amount is in LST
|
|
426
412
|
* @param params
|
|
427
413
|
*/
|
|
428
|
-
async
|
|
414
|
+
async getFundManagementCall(params: {
|
|
429
415
|
isDeposit: boolean,
|
|
430
|
-
leg1DepositAmount: Web3Number
|
|
431
|
-
maxEkuboPriceImpact?: number
|
|
416
|
+
leg1DepositAmount: Web3Number
|
|
432
417
|
}) {
|
|
433
|
-
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
418
|
+
logger.verbose(`${this.getTag()}::getFundManagementCall params: ${JSON.stringify(params)}`);
|
|
419
|
+
// const legLTV = await vesuAdapter1.getLTVConfig(this.config);
|
|
420
|
+
// logger.verbose(`${this.getTag()}::getVesuMultiplyCall legLTV: ${legLTV}`);
|
|
421
|
+
const allAdapters = this.metadata.additionalInfo.adapters.map(adapter => adapter.adapter);
|
|
438
422
|
if (!params.isDeposit) {
|
|
439
423
|
// try using unused balance to unwind.
|
|
440
424
|
// no need to unwind.
|
|
441
425
|
const unusedBalance = await this.getUnusedBalance();
|
|
442
426
|
logger.verbose(`${this.getTag()}::getVesuMultiplyCall unusedBalance: ${unusedBalance.amount.toString()}, required: ${params.leg1DepositAmount.toString()}`);
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
427
|
+
if (unusedBalance.amount.gte(params.leg1DepositAmount)) {
|
|
428
|
+
return null;
|
|
429
|
+
} else {
|
|
430
|
+
const adapters = await AdapterOptimizer.getAdapterToUse(allAdapters, false, params.leg1DepositAmount);
|
|
431
|
+
if (adapters.length > 0) {
|
|
432
|
+
const proofsInfo = adapters.map(adapter => adapter.getProofs(false, this.getMerkleTree()));
|
|
433
|
+
const calls: Call[] = [];
|
|
434
|
+
for (const info of proofsInfo) {
|
|
435
|
+
const proofGroups = info.proofs;
|
|
436
|
+
const call = this.getManageCall(proofGroups, await info.callConstructor({amount: params.leg1DepositAmount}));
|
|
437
|
+
calls.push(call);
|
|
438
|
+
}
|
|
439
|
+
return calls;
|
|
440
|
+
} else {
|
|
441
|
+
throw new Error(`${this.getTag()}::getVesuMultiplyCall: no adapters to use for unused balance: ${unusedBalance.amount.toString()}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
448
444
|
}
|
|
449
445
|
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
logger.debug(`${this.getTag()}::getVesuMultiplyCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
|
|
463
|
-
|
|
464
|
-
// - Prices as seen by actual swap price
|
|
465
|
-
const dexPrice = await this.getLSTDexPrice();
|
|
466
|
-
|
|
467
|
-
// compute optimal amount of collateral and debt post addition/removal
|
|
468
|
-
// target hf = collateral * collateralPrice * ltv / debt * debtPrice
|
|
469
|
-
// assuming X to be the usd amount of debt borrowed or repaied (negative).
|
|
470
|
-
// target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv / (debt * debtPrice + X)
|
|
471
|
-
// => X * target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv - (debt * debtPrice * target hf)
|
|
472
|
-
// => X * (target hf - ltv)= ((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)
|
|
473
|
-
// => X = (((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)) / (target hf - ltv)
|
|
474
|
-
const addedCollateral = params.leg1DepositAmount
|
|
475
|
-
.multipliedBy(params.isDeposit ? 1 : -1)
|
|
476
|
-
logger.verbose(`${this.getTag()}::getVesuMultiplyCall addedCollateral: ${addedCollateral}`);
|
|
477
|
-
const numeratorPart1 = (existingCollateralInfo.amount.plus((addedCollateral))).multipliedBy(collateralPrice).multipliedBy(legLTV);
|
|
478
|
-
logger.verbose(`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}`);
|
|
479
|
-
const numeratorPart2 = existingDebtInfo.amount.multipliedBy(debtPrice).multipliedBy(this.metadata.additionalInfo.targetHealthFactor);
|
|
480
|
-
logger.verbose(`${this.getTag()}::getVesuMultiplyCall numeratorPart2: ${numeratorPart2}`);
|
|
481
|
-
const denominatorPart = this.metadata.additionalInfo.targetHealthFactor - (legLTV / dexPrice);
|
|
482
|
-
logger.verbose(`${this.getTag()}::getVesuMultiplyCall denominatorPart: ${denominatorPart}`);
|
|
483
|
-
const x_debt_usd = numeratorPart1.minus(numeratorPart2).dividedBy(denominatorPart);
|
|
484
|
-
logger.verbose(`${this.getTag()}::getVesuMultiplyCall x_debt_usd: ${x_debt_usd}`);
|
|
485
|
-
logger.debug(`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}, numeratorPart2: ${numeratorPart2}, denominatorPart: ${denominatorPart}`);
|
|
486
|
-
|
|
487
|
-
// both in underlying
|
|
488
|
-
let debtAmount = x_debt_usd.dividedBy(debtPrice);
|
|
489
|
-
const marginAmount = addedCollateral;
|
|
490
|
-
logger.verbose(`${this.getTag()}::getVesuMultiplyCall debtAmount: ${debtAmount}, marginAmount: ${marginAmount}`);
|
|
491
|
-
|
|
492
|
-
if (marginAmount.lt(0) && debtAmount.gt(0)) {
|
|
493
|
-
// if we want to withdraw, but debt can go high, its conflicting between
|
|
494
|
-
// increasing and reducing lever. and in this case, the HF will go high,
|
|
495
|
-
// which is ok.
|
|
496
|
-
debtAmount = Web3Number.fromWei(0, this.asset().decimals);
|
|
446
|
+
const adapters = await AdapterOptimizer.getAdapterToUse(allAdapters, true, params.leg1DepositAmount);
|
|
447
|
+
if (adapters.length > 0) {
|
|
448
|
+
const proofsInfo = adapters.map(adapter => adapter.getProofs(true, this.getMerkleTree()));
|
|
449
|
+
const calls: Call[] = [];
|
|
450
|
+
for (const info of proofsInfo) {
|
|
451
|
+
const proofGroups = info.proofs;
|
|
452
|
+
const call = this.getManageCall(proofGroups, await info.callConstructor({amount: params.leg1DepositAmount}));
|
|
453
|
+
calls.push(call);
|
|
454
|
+
}
|
|
455
|
+
return calls;
|
|
456
|
+
} else {
|
|
457
|
+
throw new Error(`${this.getTag()}::getVesuMultiplyCall: no adapters to use for deposit: ${params.leg1DepositAmount.toString()}`);
|
|
497
458
|
}
|
|
498
459
|
|
|
499
|
-
//
|
|
500
|
-
//
|
|
501
|
-
//
|
|
502
|
-
|
|
503
|
-
//
|
|
504
|
-
//
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
460
|
+
// const existingPositions = await vesuAdapter1.getPositions(this.config);
|
|
461
|
+
// const collateralisation = await vesuAdapter1.getCollateralization(this.config);
|
|
462
|
+
// const existingCollateralInfo = existingPositions[0];
|
|
463
|
+
// const existingDebtInfo = existingPositions[1];
|
|
464
|
+
// logger.debug(`${this.getTag()}::getVesuMultiplyCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
465
|
+
// existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
466
|
+
|
|
467
|
+
// // - Prices as seen by Vesu contracts, ideal for HF math
|
|
468
|
+
// // Price 1 is ok as fallback bcz that would relatively price the
|
|
469
|
+
// // collateral and debt as equal.
|
|
470
|
+
// const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
|
|
471
|
+
// const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
|
|
472
|
+
// logger.debug(`${this.getTag()}::getVesuMultiplyCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`);
|
|
473
|
+
|
|
474
|
+
// // - Prices as seen by actual swap price
|
|
475
|
+
// const dexPrice = await this.getLSTDexPrice();
|
|
476
|
+
|
|
477
|
+
// // compute optimal amount of collateral and debt post addition/removal
|
|
478
|
+
// // target hf = collateral * collateralPrice * ltv / debt * debtPrice
|
|
479
|
+
// // assuming X to be the usd amount of debt borrowed or repaied (negative).
|
|
480
|
+
// // target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv / (debt * debtPrice + X)
|
|
481
|
+
// // => X * target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv - (debt * debtPrice * target hf)
|
|
482
|
+
// // => X * (target hf - ltv)= ((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)
|
|
483
|
+
// // => X = (((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)) / (target hf - ltv)
|
|
484
|
+
// const addedCollateral = params.leg1DepositAmount
|
|
485
|
+
// .multipliedBy(params.isDeposit ? 1 : -1)
|
|
486
|
+
// logger.verbose(`${this.getTag()}::getVesuMultiplyCall addedCollateral: ${addedCollateral}`);
|
|
487
|
+
// const numeratorPart1 = (existingCollateralInfo.amount.plus((addedCollateral))).multipliedBy(collateralPrice).multipliedBy(legLTV);
|
|
488
|
+
// logger.verbose(`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}`);
|
|
489
|
+
// const numeratorPart2 = existingDebtInfo.amount.multipliedBy(debtPrice).multipliedBy(this.metadata.additionalInfo.targetHealthFactor);
|
|
490
|
+
// logger.verbose(`${this.getTag()}::getVesuMultiplyCall numeratorPart2: ${numeratorPart2}`);
|
|
491
|
+
// const denominatorPart = this.metadata.additionalInfo.targetHealthFactor - (legLTV / dexPrice);
|
|
492
|
+
// logger.verbose(`${this.getTag()}::getVesuMultiplyCall denominatorPart: ${denominatorPart}`);
|
|
493
|
+
// const x_debt_usd = numeratorPart1.minus(numeratorPart2).dividedBy(denominatorPart);
|
|
494
|
+
// logger.verbose(`${this.getTag()}::getVesuMultiplyCall x_debt_usd: ${x_debt_usd}`);
|
|
495
|
+
// logger.debug(`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}, numeratorPart2: ${numeratorPart2}, denominatorPart: ${denominatorPart}`);
|
|
496
|
+
|
|
497
|
+
// // both in underlying
|
|
498
|
+
// const debtAmount = x_debt_usd.dividedBy(debtPrice);
|
|
499
|
+
// const marginAmount = addedCollateral;
|
|
500
|
+
// logger.verbose(`${this.getTag()}::getVesuMultiplyCall debtAmount: ${debtAmount}, marginAmount: ${marginAmount}`);
|
|
501
|
+
|
|
502
|
+
// // Cases of lever increase (within the scopr of this function)
|
|
503
|
+
// // 1. debtAmount > 0 and marginAmount > 0
|
|
504
|
+
// // 2. debtAmount > 0 and marginAmount < 0
|
|
505
|
+
|
|
506
|
+
// // Cases of lever decrease
|
|
507
|
+
// // 3. debtAmount < 0 and marginAmount > 0
|
|
508
|
+
// // 4. debtAmount < 0 and marginAmount < 0
|
|
509
|
+
// return this.getModifyLeverCall({
|
|
510
|
+
// marginAmount,
|
|
511
|
+
// debtAmount,
|
|
512
|
+
// lstDexPriceInUnderlying: dexPrice,
|
|
513
|
+
// isIncrease: debtAmount.greaterThan(0)
|
|
514
|
+
// });
|
|
513
515
|
}
|
|
514
516
|
|
|
517
|
+
|
|
515
518
|
getLSTUnderlyingTokenInfo() {
|
|
516
|
-
|
|
517
|
-
return vesuAdapter1.config.debt;
|
|
519
|
+
return this.metadata.additionalInfo.underlyingToken;
|
|
518
520
|
}
|
|
519
521
|
|
|
520
|
-
async getMaxBorrowableAmount(params: { isAPYComputation: boolean } = { isAPYComputation: false }) {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
}
|
|
522
|
+
// async getMaxBorrowableAmount(params: { isAPYComputation: boolean } = { isAPYComputation: false }) {
|
|
523
|
+
// const vesuAdapters = this.getVesuAdapters();
|
|
524
|
+
// let netMaxBorrowableAmount = Web3Number.fromWei("0", this.getLSTUnderlyingTokenInfo().decimals);
|
|
525
|
+
// const maxBorrowables: {amount: Web3Number, dexSwappableAmount: Web3Number, maxBorrowableAmount: Web3Number, borrowableAsset: TokenInfo}[] = [];
|
|
526
|
+
// for (const vesuAdapter of vesuAdapters) {
|
|
527
|
+
// maxBorrowables.push(await this.getMaxBorrowableAmountByVesuAdapter(vesuAdapter, params.isAPYComputation));
|
|
528
|
+
// }
|
|
529
|
+
// maxBorrowables.sort((a, b) => b.amount.toNumber() - a.amount.toNumber());
|
|
530
|
+
// netMaxBorrowableAmount = maxBorrowables.reduce((acc, curr) => acc.plus(curr.amount), Web3Number.fromWei("0", this.getLSTUnderlyingTokenInfo().decimals));
|
|
531
|
+
// return {netMaxBorrowableAmount, maxBorrowables};
|
|
532
|
+
// }
|
|
531
533
|
|
|
532
534
|
// recursively, using binary search computes max swappable.
|
|
533
535
|
// @dev assumes 1 token of from == 1 token of to
|
|
534
|
-
async getMaxSwappableWithMaxSlippage(fromToken: TokenInfo, toToken: TokenInfo, maxSlippage: number, maxAmount: Web3Number) {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
async getMaxBorrowableAmountByVesuAdapter(vesuAdapter: VesuAdapter, isAPYComputation: boolean) {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
}
|
|
536
|
+
// async getMaxSwappableWithMaxSlippage(fromToken: TokenInfo, toToken: TokenInfo, maxSlippage: number, maxAmount: Web3Number) {
|
|
537
|
+
// const output = await findMaxInputWithSlippage({
|
|
538
|
+
// apiGetOutput: async (inputAmount: number): Promise<number> => {
|
|
539
|
+
// const ekuboQuoter = new EkuboQuoter(this.config);
|
|
540
|
+
// await new Promise(resolve => setTimeout(resolve, 1000)); // artificial delay, to avoid rate limit
|
|
541
|
+
// const quote = await ekuboQuoter.getQuote(fromToken.address.address, toToken.address.address, new Web3Number(inputAmount.toFixed(9), fromToken.decimals));
|
|
542
|
+
// return Web3Number.fromWei(quote.total_calculated.toString(), toToken.decimals).toNumber();
|
|
543
|
+
// },
|
|
544
|
+
// maxInput: maxAmount.toNumber(),
|
|
545
|
+
// maxSlippagePercent: maxSlippage,
|
|
546
|
+
// tolerance: 0.001,
|
|
547
|
+
// referenceRate: 1,
|
|
548
|
+
// });
|
|
549
|
+
// return new Web3Number(output.optimalInput, fromToken.decimals);
|
|
550
|
+
// }
|
|
551
|
+
|
|
552
|
+
// async getMaxBorrowableAmountByVesuAdapter(vesuAdapter: VesuAdapter, isAPYComputation: boolean) {
|
|
553
|
+
// const lstAPY = await this.getLSTAPR(this.getLSTUnderlyingTokenInfo().address);
|
|
554
|
+
// const maxInterestRate = lstAPY * 0.8;
|
|
555
|
+
// const maxBorrowableAmount = await vesuAdapter.getMaxBorrowableByInterestRate(this.config, vesuAdapter.config.debt, maxInterestRate);
|
|
556
|
+
// const debtCap = await vesuAdapter.getDebtCap(this.config);
|
|
557
|
+
|
|
558
|
+
// const maxBorrowable = maxBorrowableAmount.minimum(debtCap).multipliedBy(0.999);
|
|
559
|
+
// // Dont compute precise max swappable for APY computation
|
|
560
|
+
// if (vesuAdapter.config.debt.address.eq(this.getLSTUnderlyingTokenInfo().address) || isAPYComputation) {
|
|
561
|
+
// return {amount: maxBorrowable, dexSwappableAmount: maxBorrowable, maxBorrowableAmount: maxBorrowable, borrowableAsset: vesuAdapter.config.debt};
|
|
562
|
+
// }
|
|
563
|
+
// // Want < 0.02% slippage
|
|
564
|
+
// try {
|
|
565
|
+
// const maxSwappable = await this.getMaxSwappableWithMaxSlippage(vesuAdapter.config.debt, this.getLSTUnderlyingTokenInfo(), 0.0002, maxBorrowable);
|
|
566
|
+
// return {amount: maxBorrowable.minimum(maxSwappable), dexSwappableAmount: maxSwappable, maxBorrowableAmount: maxBorrowable, borrowableAsset: vesuAdapter.config.debt};
|
|
567
|
+
// } catch (error) {
|
|
568
|
+
// logger.warn(`${this.getTag()}: Failed to get max swappable: ${error}`);
|
|
569
|
+
// const maxSwappable = Web3Number.fromWei("0", vesuAdapter.config.debt.decimals);
|
|
570
|
+
// return {amount: maxBorrowable.minimum(maxSwappable), dexSwappableAmount: maxSwappable, maxBorrowableAmount: maxBorrowable, borrowableAsset: vesuAdapter.config.debt};
|
|
571
|
+
// }
|
|
572
|
+
// }
|
|
571
573
|
|
|
572
574
|
// todo how much to unwind to get back healthy APY zone again
|
|
573
575
|
// if net APY < LST APR + 0.5%, we need to unwind to get back to LST APR + 1% atleast or 0 vesu position
|
|
574
576
|
// For xSTRK, simply deposit in Vesu if looping is not viable
|
|
575
577
|
|
|
576
|
-
/**
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
async getLSTAPR(_address: ContractAddr): Promise<number> {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
//
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
578
|
+
// /**
|
|
579
|
+
// * Gets LST APR for the strategy's underlying asset from Endur API
|
|
580
|
+
// * @returns Promise<number> The LST APR (not divided by 1e18)
|
|
581
|
+
// */
|
|
582
|
+
// async getLSTAPR(_address: ContractAddr): Promise<number> {
|
|
583
|
+
// try {
|
|
584
|
+
// const vesuAdapter1 = this.getVesuSameTokenAdapter();
|
|
585
|
+
// const apr = await LSTAPRService.getLSTAPR(vesuAdapter1.config.debt.address);
|
|
586
|
+
// if (!apr) {
|
|
587
|
+
// throw new Error('Failed to get LST APR');
|
|
588
|
+
// }
|
|
589
|
+
// return apr;
|
|
590
|
+
// } catch (error) {
|
|
591
|
+
// logger.warn(`${this.getTag()}: Failed to get LST APR: ${error}`);
|
|
592
|
+
// return 0;
|
|
593
|
+
// }
|
|
594
|
+
// }
|
|
595
|
+
|
|
596
|
+
// async maxNewDeposits(params: { isAPYComputation: boolean } = { isAPYComputation: false }) {
|
|
597
|
+
// const maxBorrowableAmounts = await this.getMaxBorrowableAmount(params);
|
|
598
|
+
|
|
599
|
+
// let ltv: number | undefined = undefined;
|
|
600
|
+
// for (let adapter of this.getVesuAdapters()) {
|
|
601
|
+
// const maxBorrowableAmount = maxBorrowableAmounts.maxBorrowables.find(b => b.borrowableAsset.address.eq(adapter.config.debt.address))?.amount;
|
|
602
|
+
// if (!maxBorrowableAmount) {
|
|
603
|
+
// throw new Error(`Max borrowable amount not found for adapter: ${adapter.config.debt.symbol}`);
|
|
604
|
+
// }
|
|
605
|
+
// const maxLTV = await adapter.getLTVConfig(this.config);
|
|
606
|
+
// if (!ltv) {
|
|
607
|
+
// ltv = maxLTV;
|
|
608
|
+
// } else if (ltv != maxLTV) {
|
|
609
|
+
// throw new Error(`LTV mismatch for adapter: ${adapter.config.debt.symbol}`);
|
|
610
|
+
// }
|
|
611
|
+
// }
|
|
612
|
+
// if (!ltv) {
|
|
613
|
+
// throw new Error('LTV not found');
|
|
614
|
+
// }
|
|
615
|
+
// // for simplicity, we assume 1 underlying = 1 LST
|
|
616
|
+
// const numerator = this.metadata.additionalInfo.targetHealthFactor * maxBorrowableAmounts.netMaxBorrowableAmount.toNumber() / (ltv)
|
|
617
|
+
// return numerator - maxBorrowableAmounts.netMaxBorrowableAmount.toNumber();
|
|
618
|
+
// }
|
|
619
|
+
|
|
620
|
+
async getAUM(): Promise<{net: SingleTokenInfo, prevAum: Web3Number, splits: PositionInfo[]}> {
|
|
621
|
+
const allPositions: PositionInfo[] = [];
|
|
622
|
+
for (let adapter of this.metadata.additionalInfo.adapters) {
|
|
623
|
+
const positions = await adapter.adapter.getPositions();
|
|
624
|
+
allPositions.push(...positions);
|
|
617
625
|
}
|
|
618
|
-
}
|
|
619
626
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
throw new Error(`Max borrowable amount not found for adapter: ${adapter.config.debt.symbol}`);
|
|
627
|
+
const assetPrice = await this.pricer.getPrice(this.asset().symbol);
|
|
628
|
+
let netAUM = new Web3Number(0, this.asset().decimals);
|
|
629
|
+
for (let position of allPositions) {
|
|
630
|
+
if (position.tokenInfo.address.eq(this.asset().address)) {
|
|
631
|
+
netAUM = netAUM.plus(position.amount);
|
|
632
|
+
} else {
|
|
633
|
+
netAUM = netAUM.plus(position.usdValue / assetPrice.price);
|
|
628
634
|
}
|
|
629
|
-
const maxLTV = await adapter.getLTVConfig(this.config);
|
|
630
|
-
if (!ltv) {
|
|
631
|
-
ltv = maxLTV;
|
|
632
|
-
} else if (ltv != maxLTV) {
|
|
633
|
-
throw new Error(`LTV mismatch for adapter: ${adapter.config.debt.symbol}`);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
if (!ltv) {
|
|
637
|
-
throw new Error('LTV not found');
|
|
638
635
|
}
|
|
639
|
-
// for simplicity, we assume 1 underlying = 1 LST
|
|
640
|
-
const numerator = this.metadata.additionalInfo.targetHealthFactor * maxBorrowableAmounts.netMaxBorrowableAmount.toNumber() / (ltv)
|
|
641
|
-
return numerator - maxBorrowableAmounts.netMaxBorrowableAmount.toNumber();
|
|
642
|
-
}
|
|
643
636
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
}
|
|
653
|
-
}
|
|
637
|
+
const prevAum = await this.getPrevAUM();
|
|
638
|
+
const realAUM: PositionInfo = {
|
|
639
|
+
tokenInfo: this.asset(),
|
|
640
|
+
amount: netAUM,
|
|
641
|
+
usdValue: netAUM.toNumber() * assetPrice.price,
|
|
642
|
+
apy: {apy: netAUM.toNumber() * assetPrice.price, type: APYType.BASE},
|
|
643
|
+
remarks: AUMTypes.FINALISED,
|
|
644
|
+
protocol: Protocols.NONE // just placeholder
|
|
645
|
+
};
|
|
654
646
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
}
|
|
647
|
+
const estimatedAUMDelta: PositionInfo = {
|
|
648
|
+
tokenInfo: this.asset(),
|
|
649
|
+
amount: Web3Number.fromWei('0', this.asset().decimals),
|
|
650
|
+
usdValue: 0,
|
|
651
|
+
apy: {apy: 0, type: APYType.BASE},
|
|
652
|
+
remarks: AUMTypes.DEFISPRING,
|
|
653
|
+
protocol: Protocols.NONE // just placeholder
|
|
654
|
+
};
|
|
663
655
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
656
|
+
return {net: {
|
|
657
|
+
tokenInfo: this.asset(),
|
|
658
|
+
amount: netAUM,
|
|
659
|
+
usdValue: netAUM.toNumber() * assetPrice.price
|
|
660
|
+
}, prevAum: prevAum, splits: [realAUM, estimatedAUMDelta]};
|
|
661
|
+
}
|
|
669
662
|
|
|
670
663
|
/**
|
|
671
664
|
*
|
|
672
665
|
* @param params marginAmount is in LST, debtAmount is in underlying
|
|
673
666
|
*/
|
|
674
|
-
async getModifyLeverCall(params: {
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
const maxAmounts = lstTokenInfo.symbol == 'xSTRK' ? 500000 : 0.5;
|
|
691
|
-
if (params.marginAmount.greaterThan(maxAmounts)) {
|
|
692
|
-
throw new Error(`Margin amount is greater than max amount: ${params.marginAmount.toNumber()} > ${maxAmounts}`);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
const proofsIDs: string[] = [];
|
|
696
|
-
const manageCalls: ManageCall[] = [];
|
|
697
|
-
|
|
698
|
-
// approve token
|
|
699
|
-
if (params.marginAmount.greaterThan(0)) {
|
|
700
|
-
const STEP1_ID = LST_MULTIPLIER_MANAGE_IDS.MULTIPLE_APPROVE;
|
|
701
|
-
const manage1Info = this.getProofs<ApproveCallParams>(STEP1_ID);
|
|
702
|
-
const depositAmount = params.marginAmount;
|
|
703
|
-
const manageCall1 = manage1Info.callConstructor({
|
|
704
|
-
amount: depositAmount
|
|
705
|
-
})
|
|
706
|
-
proofsIDs.push(STEP1_ID);
|
|
707
|
-
manageCalls.push(manageCall1);
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
const lstDexPriceInUnderlying = params.lstDexPriceInUnderlying;
|
|
711
|
-
const lstTrueExchangeRate = await this.getLSTExchangeRate();
|
|
712
|
-
const ekuboQuoter = new EkuboQuoter(this.config);
|
|
713
|
-
|
|
714
|
-
// compute quotes for lever swap
|
|
715
|
-
const MAX_SLIPPAGE = 0.002;
|
|
716
|
-
// when increasing, debt is swapped to collateral (LST)
|
|
717
|
-
// when decreasing, collateral is swapped to debt (underlying)
|
|
718
|
-
// but both cases, we denominate amount in underlying. negative for decrease (exact amount out)
|
|
719
|
-
const fromToken = params.isIncrease ? lstUnderlyingTokenInfo : lstTokenInfo;
|
|
720
|
-
const toToken = params.isIncrease ? lstTokenInfo : lstUnderlyingTokenInfo;
|
|
721
|
-
const leverSwapQuote = await ekuboQuoter.getQuote(
|
|
722
|
-
fromToken.address.address,
|
|
723
|
-
toToken.address.address,
|
|
724
|
-
params.debtAmount // negative for exact amount out
|
|
725
|
-
);
|
|
726
|
-
logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwapQuote: ${JSON.stringify(leverSwapQuote)}`);
|
|
727
|
-
// Ekubo's price impact can randomly show high numbers sometimes.
|
|
728
|
-
assert(leverSwapQuote.price_impact <= params.maxEkuboPriceImpact, 'getIncreaseLeverCall: Price impact is too high [Debt swap]');
|
|
729
|
-
const leverSwap = ekuboQuoter.getVesuMultiplyQuote(leverSwapQuote, fromToken, toToken);
|
|
730
|
-
logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwap: ${JSON.stringify(leverSwap)}`);
|
|
731
|
-
|
|
732
|
-
// todo double check this logic
|
|
733
|
-
// is Deposit
|
|
734
|
-
let minLSTReceived = params.debtAmount.dividedBy(lstDexPriceInUnderlying).multipliedBy(1 - MAX_SLIPPAGE); // used for increase
|
|
735
|
-
const minLSTReceivedAsPerTruePrice = params.debtAmount.dividedBy(lstTrueExchangeRate); // execution output to be <= True LST price
|
|
736
|
-
// if (minLSTReceived < minLSTReceivedAsPerTruePrice) {
|
|
737
|
-
// minLSTReceived = minLSTReceivedAsPerTruePrice; // the execution shouldn't be bad than True price logi
|
|
738
|
-
// }
|
|
739
|
-
minLSTReceived = minLSTReceivedAsPerTruePrice; // in any case, we are ok with this, bcz the BTC LST spread shouldnt be high
|
|
740
|
-
logger.verbose(`${this.getTag()}::getModifyLeverCall minLSTReceivedAsPerTruePrice: ${minLSTReceivedAsPerTruePrice}, minLSTReceived: ${minLSTReceived}`);
|
|
741
|
-
|
|
742
|
-
// is withdraw
|
|
743
|
-
let maxUsedCollateral = params.debtAmount.abs().dividedBy(lstDexPriceInUnderlying).multipliedBy(1 + MAX_SLIPPAGE); // +ve for exact amount out, used for decrease
|
|
744
|
-
const maxUsedCollateralInLST = params.debtAmount.abs().dividedBy(lstTrueExchangeRate).multipliedBy(1.005); // 0.5% slippage, worst case based on true price
|
|
745
|
-
logger.verbose(`${this.getTag()}::getModifyLeverCall maxUsedCollateralInLST: ${maxUsedCollateralInLST}, maxUsedCollateral: ${maxUsedCollateral}`);
|
|
746
|
-
// if (maxUsedCollateralInLST > maxUsedCollateral) {
|
|
747
|
-
// maxUsedCollateral = maxUsedCollateralInLST;
|
|
748
|
-
// }
|
|
749
|
-
maxUsedCollateral = maxUsedCollateralInLST; // in any case, we are ok with this, bcz the BTC LST spread shouldnt be high
|
|
750
|
-
|
|
751
|
-
const STEP2_ID = LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_ON;
|
|
752
|
-
const manage2Info = this.getProofs<VesuModifyDelegationCallParams>(STEP2_ID);
|
|
753
|
-
const manageCall2 = manage2Info.callConstructor({
|
|
754
|
-
delegation: true
|
|
755
|
-
});
|
|
756
|
-
|
|
757
|
-
// deposit and borrow or repay and withdraw
|
|
758
|
-
const STEP3_ID = getVesuLegId(LST_MULTIPLIER_MANAGE_IDS.MULTIPLY_VESU, vesuAdapter1.config.debt.symbol);
|
|
759
|
-
const manage3Info = this.getProofs<VesuMultiplyCallParams>(STEP3_ID);
|
|
760
|
-
const multiplyParams: VesuMultiplyCallParams = params.isIncrease ? {
|
|
761
|
-
isIncrease: true,
|
|
762
|
-
increaseParams: {
|
|
763
|
-
add_margin: params.marginAmount,
|
|
764
|
-
margin_swap: [],
|
|
765
|
-
margin_swap_limit_amount: Web3Number.fromWei(0, this.asset().decimals),
|
|
766
|
-
lever_swap: leverSwap,
|
|
767
|
-
lever_swap_limit_amount: minLSTReceived
|
|
768
|
-
}
|
|
769
|
-
} : {
|
|
770
|
-
isIncrease: false,
|
|
771
|
-
decreaseParams: {
|
|
772
|
-
sub_margin: params.marginAmount.multipliedBy(-1),
|
|
773
|
-
lever_swap: leverSwap,
|
|
774
|
-
lever_swap_limit_amount: maxUsedCollateral,
|
|
775
|
-
// only required for close position
|
|
776
|
-
lever_swap_weights: [],
|
|
777
|
-
// no need to swap collateral to anything, and any residuals return our contract anyways.
|
|
778
|
-
withdraw_swap: [],
|
|
779
|
-
withdraw_swap_limit_amount: Web3Number.fromWei(0, this.asset().decimals),
|
|
780
|
-
withdraw_swap_weights: [],
|
|
781
|
-
close_position: false
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
const manageCall3 = manage3Info.callConstructor(multiplyParams);
|
|
785
|
-
|
|
786
|
-
// switch delegation off
|
|
787
|
-
const STEP4_ID = LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_OFF;
|
|
788
|
-
const manage4Info = this.getProofs<VesuModifyDelegationCallParams>(STEP4_ID);
|
|
789
|
-
const manageCall4 = manage4Info.callConstructor({
|
|
790
|
-
delegation: false
|
|
791
|
-
});
|
|
792
|
-
|
|
793
|
-
proofsIDs.push(STEP2_ID, STEP3_ID, STEP4_ID);
|
|
794
|
-
manageCalls.push(manageCall2, manageCall3, manageCall4);
|
|
795
|
-
|
|
796
|
-
return [this.getManageCall(proofsIDs, manageCalls)];
|
|
797
|
-
}
|
|
667
|
+
// async getModifyLeverCall(params: {
|
|
668
|
+
// marginAmount: Web3Number, // >0 during deposit
|
|
669
|
+
// debtAmount: Web3Number,
|
|
670
|
+
// lstDexPriceInUnderlying: number,
|
|
671
|
+
// isIncrease: boolean
|
|
672
|
+
// }): Promise<Call[]> {
|
|
673
|
+
// logger.verbose(`${this.getTag()}::getModifyLeverCall marginAmount: ${params.marginAmount}, debtAmount: ${params.debtAmount}, lstDexPriceInUnderlying: ${params.lstDexPriceInUnderlying}, isIncrease: ${params.isIncrease}`);
|
|
674
|
+
|
|
675
|
+
// const vesuAdapter = this.getVesuSameTokenAdapter();
|
|
676
|
+
// const manage0Info = vesuAdapter.
|
|
677
|
+
// const manageCall0 = manage0Info.callConstructor({
|
|
678
|
+
// amount: newDepositAmount
|
|
679
|
+
// });
|
|
680
|
+
// const manageCalls = await vesu
|
|
681
|
+
// return [this.getManageCall(proofsIDs, manageCalls)];
|
|
682
|
+
// }
|
|
798
683
|
}
|
|
799
684
|
|
|
800
685
|
export default function VaultDescription(
|
|
@@ -831,7 +716,7 @@ export default function VaultDescription(
|
|
|
831
716
|
</div>
|
|
832
717
|
<div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
|
|
833
718
|
<p style={{ fontSize: "13px", color: "#ccc" }}>
|
|
834
|
-
<strong>Debt limits:</strong> Pools on Vesu have debt caps that are gradually increased over time. Until caps are raised, deposited
|
|
719
|
+
<strong>Debt limits:</strong> Pools on Vesu have debt caps that are gradually increased over time. Until caps are raised, deposited Tokens remain in the vault, generating a shared net return for all depositors. There is no additional fee taken by Troves on Yield token's APY, its only on added gain.
|
|
835
720
|
</p>
|
|
836
721
|
</div>
|
|
837
722
|
{/* <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
|
|
@@ -879,86 +764,72 @@ function getLooperSettings(
|
|
|
879
764
|
const lstToken = Global.getDefaultTokens().find(token => token.symbol === lstSymbol)!;
|
|
880
765
|
const underlyingToken = Global.getDefaultTokens().find(token => token.symbol === underlyingSymbol)!;
|
|
881
766
|
|
|
882
|
-
const
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
767
|
+
const baseAdapterConfig: BaseAdapterConfig = {
|
|
768
|
+
baseToken: lstToken,
|
|
769
|
+
supportedPositions: [{asset: lstToken, isDebt: false}],
|
|
770
|
+
networkConfig: getMainnetConfig(),
|
|
771
|
+
pricer: new PricerFromApi(getMainnetConfig(), Global.getDefaultTokens()),
|
|
886
772
|
vaultAllocator: vaultSettings.vaultAllocator,
|
|
887
|
-
|
|
773
|
+
vaultAddress: vaultSettings.vaultAddress
|
|
774
|
+
}
|
|
775
|
+
const vesuAdapterLST = new VesuSupplyOnlyAdapter({
|
|
776
|
+
// xWBTC vToken on re7 xBTC pool
|
|
777
|
+
vTokenContract: ContractAddr.from('0x062a162d0827db6f43ebb850cbef3c99fc7969e3070b83a2236c9f3713c89fd8'),
|
|
778
|
+
...baseAdapterConfig,
|
|
888
779
|
})
|
|
889
780
|
|
|
781
|
+
const vesuMultiplyAdapters = borrowableAssets.map(position => new VesuMultiplyAdapter({
|
|
782
|
+
poolId: pool1,
|
|
783
|
+
collateral: lstToken,
|
|
784
|
+
debt: Global.getDefaultTokens().find(token => token.symbol === position)!,
|
|
785
|
+
targetHealthFactor: vaultSettings.targetHealthFactor,
|
|
786
|
+
minHealthFactor: vaultSettings.minHealthFactor,
|
|
787
|
+
quoteAmountToFetchPrice: vaultSettings.quoteAmountToFetchPrice,
|
|
788
|
+
...baseAdapterConfig,
|
|
789
|
+
supportedPositions: [{asset: lstToken, isDebt: false}, {asset: Global.getDefaultTokens().find(token => token.symbol === position)!, isDebt: true}]
|
|
790
|
+
}));
|
|
791
|
+
|
|
792
|
+
const unusedBalanceAdapter = new UnusedBalanceAdapter({
|
|
793
|
+
...baseAdapterConfig,
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
vaultSettings.adapters.push({id: `${vesuAdapterLST.name}_${lstToken.symbol}_${underlyingToken.symbol}`, adapter: vesuAdapterLST});
|
|
797
|
+
vesuMultiplyAdapters.map(adapter => vaultSettings.adapters.push({id: `${adapter.name}_${lstToken.symbol}_${underlyingToken.symbol}`, adapter: adapter}));
|
|
798
|
+
vaultSettings.adapters.push({id: `${unusedBalanceAdapter.name}_${lstToken.symbol}`, adapter: unusedBalanceAdapter});
|
|
799
|
+
|
|
890
800
|
const commonAdapter = new CommonAdapter({
|
|
891
|
-
|
|
892
|
-
asset: lstToken.address,
|
|
893
|
-
id: '',
|
|
801
|
+
id: UNIVERSAL_MANAGE_IDS.FLASH_LOAN,
|
|
894
802
|
vaultAddress: vaultSettings.vaultAddress,
|
|
895
803
|
vaultAllocator: vaultSettings.vaultAllocator,
|
|
804
|
+
manager: vaultSettings.manager,
|
|
805
|
+
asset: lstToken.address
|
|
896
806
|
})
|
|
897
807
|
|
|
898
|
-
//
|
|
899
|
-
|
|
900
|
-
vaultSettings.
|
|
901
|
-
id: getVesuLegId(UNIVERSAL_MANAGE_IDS.VESU_LEG1, underlyingToken.symbol),
|
|
902
|
-
adapter: vesuAdapterLST
|
|
903
|
-
},{
|
|
904
|
-
id: UNIVERSAL_ADAPTERS.COMMON,
|
|
905
|
-
adapter: commonAdapter
|
|
906
|
-
}])
|
|
907
|
-
|
|
908
|
-
// avnu multiply
|
|
909
|
-
const { isV2, addr:poolAddr } = getVesuSingletonAddress(pool1);
|
|
910
|
-
// vesu multiply looping
|
|
911
|
-
const VESU_MULTIPLY = isV2 ? vesuAdapterLST.VESU_MULTIPLY : vesuAdapterLST.VESU_MULTIPLY_V1;
|
|
912
|
-
vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, VESU_MULTIPLY, LST_MULTIPLIER_MANAGE_IDS.MULTIPLE_APPROVE).bind(commonAdapter));
|
|
913
|
-
vaultSettings.leafAdapters.push(vesuAdapterLST.getVesuModifyDelegationAdapter(LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_ON).bind(vesuAdapterLST));
|
|
914
|
-
vaultSettings.leafAdapters.push(vesuAdapterLST.getVesuModifyDelegationAdapter(LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_OFF).bind(vesuAdapterLST));
|
|
915
|
-
|
|
916
|
-
// approve lst once to avnu
|
|
917
|
-
vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, AVNU_EXCHANGE, LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_APPROVE_WITHDRAW).bind(commonAdapter));
|
|
918
|
-
for (let borrowableAsset of vaultSettings.borrowable_assets) {
|
|
919
|
-
// in-efficient avnu swap looping (but good with endur integration)
|
|
920
|
-
const debtAsset = borrowableAsset;
|
|
921
|
-
const approve_debt_token_id = getAvnuManageIDs(LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_APPROVE_DEPOSIT, debtAsset.symbol);
|
|
922
|
-
const swap_debt_token_id = getAvnuManageIDs(LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_DEPOSIT, debtAsset.symbol);
|
|
923
|
-
const swap_lst_token_id = getAvnuManageIDs(LST_MULTIPLIER_MANAGE_IDS.AVNU_MULTIPLY_SWAP_WITHDRAW, debtAsset.symbol);
|
|
924
|
-
vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(debtAsset.address, AVNU_EXCHANGE, approve_debt_token_id).bind(commonAdapter));
|
|
925
|
-
vaultSettings.leafAdapters.push(commonAdapter.getAvnuAdapter(debtAsset.address, lstToken.address, swap_debt_token_id, false).bind(commonAdapter));
|
|
926
|
-
vaultSettings.leafAdapters.push(commonAdapter.getAvnuAdapter(lstToken.address, debtAsset.address, swap_lst_token_id, false).bind(commonAdapter));
|
|
927
|
-
|
|
928
|
-
// approve LST to add collateral
|
|
929
|
-
const vesuAdapter = new VesuAdapter({
|
|
930
|
-
poolId: pool1,
|
|
931
|
-
collateral: lstToken,
|
|
932
|
-
debt: debtAsset,
|
|
933
|
-
vaultAllocator: vaultSettings.vaultAllocator,
|
|
934
|
-
id: getVesuLegId(UNIVERSAL_MANAGE_IDS.VESU_LEG1, debtAsset.symbol)
|
|
935
|
-
});
|
|
936
|
-
vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, poolAddr, UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1).bind(commonAdapter));
|
|
937
|
-
vaultSettings.leafAdapters.push(vesuAdapter.getModifyPosition.bind(vesuAdapter));
|
|
938
|
-
|
|
939
|
-
// Vesu multiply
|
|
940
|
-
const multiplID = getVesuLegId(LST_MULTIPLIER_MANAGE_IDS.MULTIPLY_VESU, debtAsset.symbol);
|
|
941
|
-
vaultSettings.leafAdapters.push(vesuAdapter.getMultiplyAdapter(multiplID).bind(vesuAdapter));
|
|
942
|
-
}
|
|
808
|
+
// push vesu adapter to leaf adapters
|
|
809
|
+
vaultSettings.leafAdapters.push(() => vesuAdapterLST.getDepositLeaf());
|
|
810
|
+
vaultSettings.leafAdapters.push(() => vesuAdapterLST.getWithdrawLeaf());
|
|
943
811
|
|
|
812
|
+
// push vesu multiply adapter to leaf adapters
|
|
813
|
+
vesuMultiplyAdapters.map(adapter => vaultSettings.leafAdapters.push(() => adapter.getDepositLeaf()));
|
|
814
|
+
vesuMultiplyAdapters.map(adapter => vaultSettings.leafAdapters.push(() => adapter.getWithdrawLeaf()));
|
|
944
815
|
|
|
945
816
|
// to bridge liquidity back to vault (used by bring_liquidity)
|
|
946
817
|
vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, vaultSettings.vaultAddress, UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY).bind(commonAdapter));
|
|
947
818
|
vaultSettings.leafAdapters.push(commonAdapter.getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY).bind(commonAdapter));
|
|
948
819
|
|
|
949
820
|
// claim rewards
|
|
950
|
-
vaultSettings.leafAdapters.push(vesuAdapterLST.getDefispringRewardsAdapter(UNIVERSAL_MANAGE_IDS.DEFISPRING_REWARDS).bind(vesuAdapterLST));
|
|
821
|
+
// vaultSettings.leafAdapters.push(vesuAdapterLST.getDefispringRewardsAdapter(UNIVERSAL_MANAGE_IDS.DEFISPRING_REWARDS).bind(vesuAdapterLST));
|
|
951
822
|
|
|
952
|
-
// avnu swap for claims rewards
|
|
953
|
-
const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
|
|
954
|
-
vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(STRKToken.address, AVNU_EXCHANGE, UNIVERSAL_MANAGE_IDS.APPROVE_SWAP_TOKEN1).bind(commonAdapter));
|
|
955
|
-
|
|
823
|
+
// // avnu swap for claims rewards
|
|
824
|
+
// const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
|
|
825
|
+
// vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(STRKToken.address, AVNU_EXCHANGE, UNIVERSAL_MANAGE_IDS.APPROVE_SWAP_TOKEN1).bind(commonAdapter));
|
|
826
|
+
// vabaseAdapterConfigultSettings.leafAdapters.push(commonAdapter.getAvnuAdapter(STRKToken.address, lstToken.address, UNIVERSAL_MANAGE_IDS.AVNU_SWAP_REWARDS, false).bind(commonAdapter));
|
|
956
827
|
return vaultSettings;
|
|
957
828
|
}
|
|
958
829
|
|
|
959
|
-
const AUDIT_URL = 'https://docs.troves.fi/p/security#starknet-vault-kit'
|
|
830
|
+
export const AUDIT_URL = 'https://docs.troves.fi/p/security#starknet-vault-kit'
|
|
960
831
|
|
|
961
|
-
function getFAQs(lstSymbol: string, underlyingSymbol: string): FAQ[] {
|
|
832
|
+
export function getFAQs(lstSymbol: string, underlyingSymbol: string, isLST: boolean): FAQ[] {
|
|
962
833
|
return [
|
|
963
834
|
{
|
|
964
835
|
question: `What is the Hyper ${lstSymbol} Vault?`,
|
|
@@ -972,10 +843,14 @@ function getFAQs(lstSymbol: string, underlyingSymbol: string): FAQ[] {
|
|
|
972
843
|
},
|
|
973
844
|
{
|
|
974
845
|
question: "Which protocols/dApp are used??",
|
|
975
|
-
answer: (
|
|
846
|
+
answer: isLST ? (
|
|
976
847
|
<span>
|
|
977
848
|
Currently, the LST is from <strong>Endur</strong> while <strong>Vesu</strong> is used to collateralize the looped position.
|
|
978
849
|
</span>
|
|
850
|
+
) : (
|
|
851
|
+
<span>
|
|
852
|
+
Currently, the Yield Token is from <strong>Re7 Labs (Midas)</strong> while <strong>Vesu</strong> is used to collateralize the looped position.
|
|
853
|
+
</span>
|
|
979
854
|
),
|
|
980
855
|
},
|
|
981
856
|
{
|
|
@@ -993,14 +868,14 @@ function getFAQs(lstSymbol: string, underlyingSymbol: string): FAQ[] {
|
|
|
993
868
|
"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.",
|
|
994
869
|
},
|
|
995
870
|
{
|
|
996
|
-
question:
|
|
871
|
+
question: `Is the Hyper ${lstSymbol} Vault non-custodial?`,
|
|
997
872
|
answer:
|
|
998
|
-
|
|
873
|
+
`Yes. The Hyper ${lstSymbol} Vault operates entirely on-chain. Users always maintain control of their vault tokens, and the strategy is fully transparent.`,
|
|
999
874
|
},
|
|
1000
875
|
{
|
|
1001
876
|
question: "Is the Vault audited?",
|
|
1002
877
|
answer:
|
|
1003
|
-
|
|
878
|
+
`Yes. The Hyper ${lstSymbol} Vault is audited by Zellic. Look for safety icon beside the strategy name for more details.`,
|
|
1004
879
|
},
|
|
1005
880
|
{
|
|
1006
881
|
question: "Are there any fees?",
|
|
@@ -1010,7 +885,7 @@ function getFAQs(lstSymbol: string, underlyingSymbol: string): FAQ[] {
|
|
|
1010
885
|
];
|
|
1011
886
|
}
|
|
1012
887
|
|
|
1013
|
-
const _riskFactor: RiskFactor[] = [
|
|
888
|
+
export const _riskFactor: RiskFactor[] = [
|
|
1014
889
|
{ type: RiskType.SMART_CONTRACT_RISK, value: SmartContractRiskLevel.WELL_AUDITED, weight: 25, reason: "Audited by Zellic" },
|
|
1015
890
|
{ type: RiskType.LIQUIDATION_RISK, value: LiquidationRiskLevel.VERY_LOW_PROBABILITY, weight: 25, reason: "The collateral and debt are highly correlated" },
|
|
1016
891
|
{ type: RiskType.TECHNICAL_RISK, value: TechnicalRiskLevel.STABLE_INFRASTRUCTURE, weight: 25, 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." },
|
|
@@ -1033,6 +908,7 @@ const hyperxSTRK: HyperLSTStrategySettings = {
|
|
|
1033
908
|
minHealthFactor: 1.05,
|
|
1034
909
|
borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'STRK'),
|
|
1035
910
|
underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'STRK')!,
|
|
911
|
+
quoteAmountToFetchPrice: new Web3Number('100', Global.getDefaultTokens().find(token => token.symbol === 'STRK')!.decimals),
|
|
1036
912
|
}
|
|
1037
913
|
|
|
1038
914
|
const hyperxWBTC: HyperLSTStrategySettings = {
|
|
@@ -1047,6 +923,7 @@ const hyperxWBTC: HyperLSTStrategySettings = {
|
|
|
1047
923
|
minHealthFactor: 1.05,
|
|
1048
924
|
borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
|
|
1049
925
|
underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
|
|
926
|
+
quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!.decimals),
|
|
1050
927
|
}
|
|
1051
928
|
|
|
1052
929
|
const hyperxtBTC: HyperLSTStrategySettings = {
|
|
@@ -1059,8 +936,9 @@ const hyperxtBTC: HyperLSTStrategySettings = {
|
|
|
1059
936
|
adapters: [],
|
|
1060
937
|
targetHealthFactor: 1.1,
|
|
1061
938
|
minHealthFactor: 1.05,
|
|
1062
|
-
borrowable_assets: Global.getDefaultTokens().
|
|
939
|
+
borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
|
|
1063
940
|
underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'tBTC')!,
|
|
941
|
+
quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'tBTC')!.decimals),
|
|
1064
942
|
}
|
|
1065
943
|
|
|
1066
944
|
const hyperxsBTC: HyperLSTStrategySettings = {
|
|
@@ -1073,8 +951,9 @@ const hyperxsBTC: HyperLSTStrategySettings = {
|
|
|
1073
951
|
adapters: [],
|
|
1074
952
|
targetHealthFactor: 1.1,
|
|
1075
953
|
minHealthFactor: 1.05,
|
|
1076
|
-
borrowable_assets: Global.getDefaultTokens().
|
|
954
|
+
borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
|
|
1077
955
|
underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'solvBTC')!,
|
|
956
|
+
quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'solvBTC')!.decimals),
|
|
1078
957
|
}
|
|
1079
958
|
|
|
1080
959
|
const hyperxLBTC: HyperLSTStrategySettings = {
|
|
@@ -1087,21 +966,56 @@ const hyperxLBTC: HyperLSTStrategySettings = {
|
|
|
1087
966
|
adapters: [],
|
|
1088
967
|
targetHealthFactor: 1.1,
|
|
1089
968
|
minHealthFactor: 1.05,
|
|
1090
|
-
borrowable_assets: Global.getDefaultTokens().
|
|
969
|
+
borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
|
|
1091
970
|
underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'LBTC')!,
|
|
971
|
+
quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'LBTC')!.decimals),
|
|
1092
972
|
}
|
|
1093
973
|
|
|
1094
|
-
|
|
974
|
+
const hypermRe7BTC: HyperLSTStrategySettings = {
|
|
975
|
+
vaultAddress: ContractAddr.from('0x6c89b75d09de82477edb86b2c2918cfc1a5dc0177cfbdea46278386b6499645'),
|
|
976
|
+
manager: ContractAddr.from('0x7bc2d0535e13352d3ab78ea047616a3162068294297304943d2302122a150a1'),
|
|
977
|
+
vaultAllocator: ContractAddr.from('0x760f9cebca9d2ee624f4224591da6da8b3ea5fd2410590735709551bd4e7570'),
|
|
978
|
+
redeemRequestNFT: ContractAddr.from('0x5e045ae0160f7650f8a4dd7c826f25630a89fe62434db4441e7e0075608180f'),
|
|
979
|
+
aumOracle: ContractAddr.from('0x3958df341b838813c24efb9183c23bddd1c57d44b1b86c0dd57f67887b89fba'),
|
|
980
|
+
leafAdapters: [],
|
|
981
|
+
adapters: [],
|
|
982
|
+
targetHealthFactor: 1.1,
|
|
983
|
+
minHealthFactor: 1.05,
|
|
984
|
+
borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
|
|
985
|
+
underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
|
|
986
|
+
quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'mRe7BTC')!.decimals),
|
|
987
|
+
}
|
|
988
|
+
// Contract deployed: Vault, addr: 0x42797ab4eb1f72787442e91a73d63a39e3a141c1106470a946ecc328db6896c
|
|
989
|
+
// Contract deployed: RedeemRequest, addr: 0x4bbb25c2568af07967342833f7db1aece1be1be2330798dab4ee585aa6c2c72
|
|
990
|
+
// Contract deployed: VaultAllocator, addr: 0x456c4c6afca90512aeb5c735d84405fea6e51ab06d1851ac8cdb0a235e14f15
|
|
991
|
+
// Contract deployed: Manager, addr: 0x435b45d40fbb406cf69ac84bb471e7b7a4ea2295d0893c05dd2db565295e77f
|
|
992
|
+
|
|
993
|
+
const hypermRe7YIELD: HyperLSTStrategySettings = {
|
|
994
|
+
vaultAddress: ContractAddr.from('0x42797ab4eb1f72787442e91a73d63a39e3a141c1106470a946ecc328db6896c'),
|
|
995
|
+
manager: ContractAddr.from('0x435b45d40fbb406cf69ac84bb471e7b7a4ea2295d0893c05dd2db565295e77f'),
|
|
996
|
+
vaultAllocator: ContractAddr.from('0x456c4c6afca90512aeb5c735d84405fea6e51ab06d1851ac8cdb0a235e14f15'),
|
|
997
|
+
redeemRequestNFT: ContractAddr.from('0x4bbb25c2568af07967342833f7db1aece1be1be2330798dab4ee585aa6c2c72'),
|
|
998
|
+
aumOracle: ContractAddr.from('0x3e1f2825158cafccc9b42a8165d17ceb6b8e966474d9c63587d338746888382'),
|
|
999
|
+
leafAdapters: [],
|
|
1000
|
+
adapters: [],
|
|
1001
|
+
targetHealthFactor: 1.1,
|
|
1002
|
+
minHealthFactor: 1.05,
|
|
1003
|
+
borrowable_assets: [Global.getDefaultTokens().find(token => token.symbol === 'USDC')!],
|
|
1004
|
+
underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'USDC')!,
|
|
1005
|
+
quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'mRe7BTC')!.decimals),
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
export function getInvestmentSteps(lstSymbol: string, underlyingSymbol: string) {
|
|
1095
1009
|
return [
|
|
1096
1010
|
`Deposit ${lstSymbol} into the vault`,
|
|
1097
1011
|
`The vault manager loops the ${underlyingSymbol} to buy ${lstSymbol}`,
|
|
1098
1012
|
`The vault manager collateralizes the ${lstSymbol} on Vesu`,
|
|
1099
1013
|
`The vault manager borrows more ${underlyingSymbol} to loop further`,
|
|
1100
|
-
`If required, adjust leverage or re-allocate assets within
|
|
1014
|
+
`If required, adjust leverage or re-allocate assets within pool on Vesu to optimize yield`
|
|
1101
1015
|
]
|
|
1102
1016
|
}
|
|
1103
1017
|
|
|
1104
|
-
function getStrategySettings(lstSymbol: string, underlyingSymbol: string, addresses: HyperLSTStrategySettings, isPreview: boolean = false): IStrategyMetadata<HyperLSTStrategySettings> {
|
|
1018
|
+
function getStrategySettings(lstSymbol: string, underlyingSymbol: string, addresses: HyperLSTStrategySettings, isPreview: boolean = false, isLST: boolean): IStrategyMetadata<HyperLSTStrategySettings> {
|
|
1105
1019
|
return {
|
|
1106
1020
|
name: `Hyper ${lstSymbol}`,
|
|
1107
1021
|
description: getDescription(lstSymbol, underlyingSymbol),
|
|
@@ -1121,18 +1035,37 @@ function getStrategySettings(lstSymbol: string, underlyingSymbol: string, addres
|
|
|
1121
1035
|
protocols: [Protocols.ENDUR, Protocols.VESU],
|
|
1122
1036
|
maxTVL: Web3Number.fromWei(0, 18),
|
|
1123
1037
|
contractDetails: getContractDetails(addresses),
|
|
1124
|
-
faqs: getFAQs(lstSymbol, underlyingSymbol),
|
|
1038
|
+
faqs: getFAQs(lstSymbol, underlyingSymbol, isLST),
|
|
1125
1039
|
investmentSteps: getInvestmentSteps(lstSymbol, underlyingSymbol),
|
|
1126
1040
|
isPreview: isPreview,
|
|
1127
|
-
apyMethodology: 'Current annualized APY in terms of base asset of the LST. There is no additional fee taken by Troves on LST APY. We charge a 10% performance fee on the additional gain which is already accounted in the APY shown.'
|
|
1041
|
+
apyMethodology: isLST ? '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.' : 'Current annualized APY in terms of base asset of the Yield Token. There is no additional fee taken by Troves on yield token APY. We charge a 10% performance fee on the additional gain which is already accounted in the APY shown.'
|
|
1128
1042
|
}
|
|
1129
1043
|
}
|
|
1130
1044
|
|
|
1045
|
+
|
|
1046
|
+
|
|
1047
|
+
// const hyperxWBTCTest: HyperLSTStrategySettings = {
|
|
1048
|
+
// vaultAddress: ContractAddr.from('0x5535e01a0d0438a888267ba6cd6519a40e653cdfb5dce4af475221d9cf11e63'),
|
|
1049
|
+
// manager: ContractAddr.from('0x3caa816a9d9b55a6621a47c1a7e6773141dd05fb3ca4ec4b774656f360b32a1'),
|
|
1050
|
+
// vaultAllocator: ContractAddr.from('0x3d4f82f8bfa8e5b2f0242c4d8ed87287c9ad5427be8b982a31a0393cf3075d1'),
|
|
1051
|
+
// redeemRequestNFT: ContractAddr.from('0x6190460d0f1fd5d142bd5378d7b3270e70253bdd652d6826175f6c0b1ad4f32'),
|
|
1052
|
+
// aumOracle: ContractAddr.from('0x43f5aa7c67b29b5e69ad03ab427f7613e43350d6ddede481e42e8184f49cb2f'),
|
|
1053
|
+
// leafAdapters: [],
|
|
1054
|
+
// adapters: [],
|
|
1055
|
+
// targetHealthFactor: 1.1,
|
|
1056
|
+
// minHealthFactor: 1.05,
|
|
1057
|
+
// borrowable_assets: borrowableAssets.map(asset => Global.getDefaultTokens().find(token => token.symbol === asset)!),
|
|
1058
|
+
// underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
|
|
1059
|
+
// quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!.decimals),
|
|
1060
|
+
// }
|
|
1061
|
+
|
|
1131
1062
|
export const HyperLSTStrategies: IStrategyMetadata<HyperLSTStrategySettings>[] =
|
|
1132
1063
|
[
|
|
1133
|
-
getStrategySettings('xSTRK', 'STRK', hyperxSTRK, false),
|
|
1134
|
-
getStrategySettings('xWBTC', 'WBTC', hyperxWBTC, false),
|
|
1135
|
-
getStrategySettings('xtBTC', 'tBTC', hyperxtBTC, false),
|
|
1136
|
-
getStrategySettings('xsBTC', 'solvBTC', hyperxsBTC, false),
|
|
1137
|
-
getStrategySettings('xLBTC', 'LBTC', hyperxLBTC, false),
|
|
1064
|
+
getStrategySettings('xSTRK', 'STRK', hyperxSTRK, false, true),
|
|
1065
|
+
getStrategySettings('xWBTC', 'WBTC', hyperxWBTC, false, false),
|
|
1066
|
+
getStrategySettings('xtBTC', 'tBTC', hyperxtBTC, false, false),
|
|
1067
|
+
getStrategySettings('xsBTC', 'solvBTC', hyperxsBTC, false, false),
|
|
1068
|
+
getStrategySettings('xLBTC', 'LBTC', hyperxLBTC, false, false),
|
|
1069
|
+
getStrategySettings('mRe7BTC', 'mRe7BTC', hypermRe7BTC, false, false),
|
|
1070
|
+
getStrategySettings('mRe7YIELD', 'mRe7YIELD', hypermRe7YIELD, false, false),
|
|
1138
1071
|
]
|