@strkfarm/sdk 1.1.17 → 1.1.20

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.
@@ -3,7 +3,7 @@ import { AUMTypes, getContractDetails, UNIVERSAL_ADAPTERS, UNIVERSAL_MANAGE_IDS,
3
3
  import { PricerBase } from "@/modules/pricerBase";
4
4
  import { ContractAddr, Web3Number } from "@/dataTypes";
5
5
  import { Global } from "@/global";
6
- import { ApproveCallParams, CommonAdapter, ManageCall, Swap, VesuAdapter, VesuModifyDelegationCallParams, VesuMultiplyCallParams, VesuPools } from "./universal-adapters";
6
+ import { ApproveCallParams, CommonAdapter, getVesuSingletonAddress, ManageCall, Swap, VesuAdapter, VesuModifyDelegationCallParams, VesuMultiplyCallParams, VesuPools } from "./universal-adapters";
7
7
  import { AVNU_MIDDLEWARE } from "./universal-adapters/adapter-utils";
8
8
  import { LiquidationRiskLevel, SmartContractRiskLevel, TechnicalRiskLevel } from "@/interfaces/risks";
9
9
  import { EkuboQuoter, ERC20, PricerLST } from "@/modules";
@@ -17,7 +17,7 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
17
17
 
18
18
  private quoteAmountToFetchPrice = new Web3Number(1, 18);
19
19
 
20
- constructor(config: IConfig, pricer: PricerLST, metadata: IStrategyMetadata<UniversalStrategySettings>) {
20
+ constructor(config: IConfig, pricer: PricerBase, metadata: IStrategyMetadata<UniversalStrategySettings>) {
21
21
  super(config, pricer, metadata);
22
22
 
23
23
  const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
@@ -30,8 +30,17 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
30
30
  }
31
31
  }
32
32
 
33
+ asset() {
34
+ const vesuAdapter1 = this.getAdapter(UNIVERSAL_ADAPTERS.VESU_LEG1) as VesuAdapter;
35
+ return vesuAdapter1.config.collateral;
36
+ }
37
+
38
+ getTag() {
39
+ return `${UniversalLstMultiplierStrategy.name}:${this.metadata.name}`;
40
+ }
41
+
33
42
  // only one leg is used
34
- // todo support lending assets of underlying as well
43
+ // todo support lending assets of underlying as well (like if xSTRK looping is not viable, simply supply STRK)
35
44
  getVesuAdapters() {
36
45
  const vesuAdapter1 = this.getAdapter(UNIVERSAL_ADAPTERS.VESU_LEG1) as VesuAdapter;
37
46
  vesuAdapter1.pricer = this.pricer;
@@ -42,9 +51,13 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
42
51
 
43
52
  // not applicable for this strategy
44
53
  // 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
- // }
54
+ protected async getRewardsAUM(prevAum: Web3Number): Promise<Web3Number> {
55
+ const lstToken = this.asset();
56
+ if (lstToken.symbol === 'xSTRK') {
57
+ return super.getRewardsAUM(prevAum);
58
+ }
59
+ return Web3Number.fromWei("0", lstToken.decimals);
60
+ }
48
61
 
49
62
  async getLSTDexPrice() {
50
63
  const ekuboQuoter = new EkuboQuoter(this.config);
@@ -74,6 +87,7 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
74
87
  }) {
75
88
  const [vesuAdapter1] = this.getVesuAdapters();
76
89
  const legLTV = await vesuAdapter1.getLTVConfig(this.config);
90
+ logger.verbose(`${this.getTag()}::getVesuMultiplyCall legLTV: ${legLTV}`);
77
91
 
78
92
  const existingPositions = await vesuAdapter1.getPositions(this.config);
79
93
  const collateralisation = await vesuAdapter1.getCollateralization(this.config);
@@ -103,8 +117,11 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
103
117
  .multipliedBy(params.isDeposit ? 1 : -1)
104
118
  logger.verbose(`${this.getTag()}::getVesuMultiplyCall addedCollateral: ${addedCollateral}`);
105
119
  const numeratorPart1 = (existingCollateralInfo.amount.plus((addedCollateral))).multipliedBy(collateralPrice).multipliedBy(legLTV);
120
+ logger.verbose(`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}`);
106
121
  const numeratorPart2 = existingDebtInfo.amount.multipliedBy(debtPrice).multipliedBy(this.metadata.additionalInfo.targetHealthFactor);
122
+ logger.verbose(`${this.getTag()}::getVesuMultiplyCall numeratorPart2: ${numeratorPart2}`);
107
123
  const denominatorPart = this.metadata.additionalInfo.targetHealthFactor - (legLTV / dexPrice);
124
+ logger.verbose(`${this.getTag()}::getVesuMultiplyCall denominatorPart: ${denominatorPart}`);
108
125
  const x_debt_usd = numeratorPart1.minus(numeratorPart2).dividedBy(denominatorPart);
109
126
  logger.verbose(`${this.getTag()}::getVesuMultiplyCall x_debt_usd: ${x_debt_usd}`);
110
127
  logger.debug(`${this.getTag()}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}, numeratorPart2: ${numeratorPart2}, denominatorPart: ${denominatorPart}`);
@@ -159,12 +176,20 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
159
176
  lstDexPriceInUnderlying: number,
160
177
  isIncrease: boolean
161
178
  }): Promise<Call[]> {
179
+ logger.verbose(`${this.getTag()}::getModifyLeverCall marginAmount: ${params.marginAmount}, debtAmount: ${params.debtAmount}, lstDexPriceInUnderlying: ${params.lstDexPriceInUnderlying}, isIncrease: ${params.isIncrease}`);
162
180
  assert(!params.marginAmount.isZero() || !params.debtAmount.isZero(), 'Deposit/debt must be non-0');
163
-
181
+
164
182
  const [vesuAdapter1] = this.getVesuAdapters();
165
183
  const lstTokenInfo = this.asset();
166
184
  const lstUnderlyingTokenInfo = vesuAdapter1.config.debt;
167
185
 
186
+ // todo make it more general
187
+ // 500k STRK (~75k$) or 0.5 BTC (~60k$)
188
+ const maxAmounts = lstTokenInfo.symbol == 'xSTRK' ? 500000 : 0.5;
189
+ if (params.marginAmount.greaterThan(maxAmounts)) {
190
+ throw new Error(`Margin amount is greater than max amount: ${params.marginAmount.toNumber()} > ${maxAmounts}`);
191
+ }
192
+
168
193
  const proofsIDs: string[] = [];
169
194
  const manageCalls: ManageCall[] = [];
170
195
 
@@ -181,13 +206,11 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
181
206
  }
182
207
 
183
208
  const lstDexPriceInUnderlying = params.lstDexPriceInUnderlying;
209
+ const lstTrueExchangeRate = await this.getLSTExchangeRate();
184
210
  const ekuboQuoter = new EkuboQuoter(this.config);
185
211
 
186
- // compute quotes for margin swap
187
- const marginSwap: Swap[] = [];
188
-
189
212
  // compute quotes for lever swap
190
- const MAX_SLIPPAGE = 0.01;
213
+ const MAX_SLIPPAGE = 0.002;
191
214
  // when increasing, debt is swapped to collateral (LST)
192
215
  // when decreasing, collateral is swapped to debt (underlying)
193
216
  // but both cases, we denominate amount in underlying. negative for decrease (exact amount out)
@@ -198,11 +221,29 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
198
221
  toToken.address.address,
199
222
  params.debtAmount // negative for exact amount out
200
223
  );
201
- assert(leverSwapQuote.price_impact < MAX_SLIPPAGE, 'getIncreaseLeverCall: Price impact is too high [Debt swap]');
224
+ logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwapQuote: ${JSON.stringify(leverSwapQuote)}`);
225
+ // Ekubo's price impact can randomly show high numbers sometimes.
226
+ assert(leverSwapQuote.price_impact < 0.01, 'getIncreaseLeverCall: Price impact is too high [Debt swap]');
202
227
  const leverSwap = ekuboQuoter.getVesuMultiplyQuote(leverSwapQuote, fromToken, toToken);
203
- const minExpectedDebt = params.debtAmount.dividedBy(lstDexPriceInUnderlying).multipliedBy(1 - MAX_SLIPPAGE); // used for increase
204
- const maxUsedCollateral = params.debtAmount.abs().dividedBy(lstDexPriceInUnderlying).multipliedBy(1 + MAX_SLIPPAGE); // +ve for exact amount out, used for decrease
205
-
228
+ logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwap: ${JSON.stringify(leverSwap)}`);
229
+
230
+ // todo double check this logic
231
+ // is Deposit
232
+ let minLSTReceived = params.debtAmount.dividedBy(lstDexPriceInUnderlying).multipliedBy(1 - MAX_SLIPPAGE); // used for increase
233
+ const minLSTReceivedAsPerTruePrice = params.debtAmount.dividedBy(lstTrueExchangeRate); // execution output to be <= True LST price
234
+ if (minLSTReceived < minLSTReceivedAsPerTruePrice) {
235
+ minLSTReceived = minLSTReceivedAsPerTruePrice; // the execution shouldn't be bad than True price logi
236
+ }
237
+ logger.verbose(`${this.getTag()}::getModifyLeverCall minLSTReceivedAsPerTruePrice: ${minLSTReceivedAsPerTruePrice}, minLSTReceived: ${minLSTReceived}`);
238
+
239
+ // is withdraw
240
+ let maxUsedCollateral = params.debtAmount.abs().dividedBy(lstDexPriceInUnderlying).multipliedBy(1 + MAX_SLIPPAGE); // +ve for exact amount out, used for decrease
241
+ const maxUsedCollateralInLST = params.debtAmount.abs().dividedBy(lstTrueExchangeRate).multipliedBy(1.005); // 0.5% slippage, worst case based on true price
242
+ logger.verbose(`${this.getTag()}::getModifyLeverCall maxUsedCollateralInLST: ${maxUsedCollateralInLST}, maxUsedCollateral: ${maxUsedCollateral}`);
243
+ if (maxUsedCollateralInLST > maxUsedCollateral) {
244
+ maxUsedCollateral = maxUsedCollateralInLST;
245
+ }
246
+
206
247
  const STEP2_ID = LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_ON;
207
248
  const manage2Info = this.getProofs<VesuModifyDelegationCallParams>(STEP2_ID);
208
249
  const manageCall2 = manage2Info.callConstructor({
@@ -216,10 +257,10 @@ export class UniversalLstMultiplierStrategy extends UniversalStrategy<UniversalS
216
257
  isIncrease: true,
217
258
  increaseParams: {
218
259
  add_margin: params.marginAmount,
219
- margin_swap: marginSwap,
260
+ margin_swap: [],
220
261
  margin_swap_limit_amount: Web3Number.fromWei(0, this.asset().decimals),
221
262
  lever_swap: leverSwap,
222
- lever_swap_limit_amount: minExpectedDebt
263
+ lever_swap_limit_amount: minLSTReceived
223
264
  }
224
265
  } : {
225
266
  isIncrease: false,
@@ -321,7 +362,9 @@ function getLooperSettings(
321
362
  })
322
363
 
323
364
  // vesu looping
324
- vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, vesuAdapterLST.VESU_MULTIPLY, UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1).bind(commonAdapter));
365
+ const { isV2 } = getVesuSingletonAddress(pool1);
366
+ const VESU_MULTIPLY = isV2 ? vesuAdapterLST.VESU_MULTIPLY : vesuAdapterLST.VESU_MULTIPLY_V1;
367
+ vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, VESU_MULTIPLY, UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1).bind(commonAdapter));
325
368
  vaultSettings.leafAdapters.push(vesuAdapterLST.getMultiplyAdapter.bind(vesuAdapterLST));
326
369
  vaultSettings.leafAdapters.push(vesuAdapterLST.getVesuModifyDelegationAdapter(LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_ON).bind(vesuAdapterLST));
327
370
  vaultSettings.leafAdapters.push(vesuAdapterLST.getVesuModifyDelegationAdapter(LST_MULTIPLIER_MANAGE_IDS.SWITCH_DELEGATION_OFF).bind(vesuAdapterLST));
@@ -425,7 +468,7 @@ const hyperxWBTC: UniversalStrategySettings = {
425
468
  manager: ContractAddr.from('0x75866db44c81e6986f06035206ee9c7d15833ddb22d6a22c016cfb5c866a491'),
426
469
  vaultAllocator: ContractAddr.from('0x57b5c1bb457b5e840a2714ae53ada87d77be2f3fd33a59b4fe709ef20c020c1'),
427
470
  redeemRequestNFT: ContractAddr.from('0x7a5dc288325456f05e70e9616e16bc02ffbe448f4b89f80b47c0970b989c7c'),
428
- aumOracle: ContractAddr.from(''),
471
+ aumOracle: ContractAddr.from('0x258f8a0ca0d21f542e48ad89d00e92dc4d9db4999084f50ef9c22dfb1e83023'),
429
472
  leafAdapters: [],
430
473
  adapters: [],
431
474
  targetHealthFactor: 1.1,
@@ -437,7 +480,7 @@ const hyperxtBTC: UniversalStrategySettings = {
437
480
  manager: ContractAddr.from('0xc4cc3e08029a0ae076f5fdfca70575abb78d23c5cd1c49a957f7e697885401'),
438
481
  vaultAllocator: ContractAddr.from('0x50bbd4fe69f841ecb13b2619fe50ebfa4e8944671b5d0ebf7868fd80c61b31e'),
439
482
  redeemRequestNFT: ContractAddr.from('0xeac9032f02057779816e38a6cb9185d12d86b3aacc9949b96b36de359c1e3'),
440
- aumOracle: ContractAddr.from(''),
483
+ aumOracle: ContractAddr.from('0x7e0d05cb7ba3f7db77a36c21c21583b5a524c2e685c08c24b3554911fb4a039'),
441
484
  leafAdapters: [],
442
485
  adapters: [],
443
486
  targetHealthFactor: 1.1,
@@ -449,7 +492,7 @@ const hyperxsBTC: UniversalStrategySettings = {
449
492
  manager: ContractAddr.from('0xc9ac023090625b0be3f6532ca353f086746f9c09f939dbc1b2613f09e5f821'),
450
493
  vaultAllocator: ContractAddr.from('0x60c2d856936b975459a5b4eb28b8672d91f757bd76cebb6241f8d670185dc01'),
451
494
  redeemRequestNFT: ContractAddr.from('0x429e8ee8bc7ecd1ade72630d350a2e0f10f9a2507c45f188ba17fe8f2ab4cf3'),
452
- aumOracle: ContractAddr.from(''),
495
+ aumOracle: ContractAddr.from('0x149298ade3e79ec6cbdac6cfad289c57504eaf54e590939136ed1ceca60c345'),
453
496
  leafAdapters: [],
454
497
  adapters: [],
455
498
  targetHealthFactor: 1.1,
@@ -487,7 +530,7 @@ function getStrategySettings(lstSymbol: string, underlyingSymbol: string, addres
487
530
  launchBlock: 0,
488
531
  type: 'Other',
489
532
  depositTokens: [Global.getDefaultTokens().find(token => token.symbol === lstSymbol)!],
490
- additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, addresses, VesuPools.Re7xSTRK),
533
+ additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, addresses, lstSymbol === 'xSTRK' ? VesuPools.Re7xSTRK : VesuPools.Re7xBTC),
491
534
  risk: {
492
535
  riskFactor: _riskFactor,
493
536
  netRisk:
@@ -507,8 +550,8 @@ function getStrategySettings(lstSymbol: string, underlyingSymbol: string, addres
507
550
  export const HyperLSTStrategies: IStrategyMetadata<UniversalStrategySettings>[] =
508
551
  [
509
552
  getStrategySettings('xSTRK', 'STRK', hyperxSTRK, false),
510
- getStrategySettings('xWBTC', 'WBTC', hyperxWBTC, true),
511
- getStrategySettings('xtBTC', 'tBTC', hyperxtBTC, true),
512
- getStrategySettings('xsBTC', 'solvBTC', hyperxsBTC, true),
513
- getStrategySettings('xLBTC', 'LBTC', hyperxLBTC, true),
553
+ getStrategySettings('xWBTC', 'WBTC', hyperxWBTC, false),
554
+ getStrategySettings('xtBTC', 'tBTC', hyperxtBTC, false),
555
+ getStrategySettings('xsBTC', 'solvBTC', hyperxsBTC, false),
556
+ getStrategySettings('xLBTC', 'LBTC', hyperxLBTC, false),
514
557
  ]
@@ -10,7 +10,7 @@ import ManagerAbi from '../data/vault-manager.abi.json';
10
10
  import { ApproveCallParams, AvnuSwapCallParams, BaseAdapter, CommonAdapter, FlashloanCallParams, GenerateCallFn, LeafAdapterFn, ManageCall, VesuAdapter, VesuDefiSpringRewardsCallParams, VesuModifyPositionCallParams, VesuPools } from "./universal-adapters";
11
11
  import { Global } from "@/global";
12
12
  import { AvnuWrapper, ERC20 } from "@/modules";
13
- import { AVNU_MIDDLEWARE } from "./universal-adapters/adapter-utils";
13
+ import { AVNU_MIDDLEWARE, VESU_SINGLETON } from "./universal-adapters/adapter-utils";
14
14
  import { VesuHarvests } from "@/modules/harvests";
15
15
 
16
16
  export interface UniversalManageCall {
@@ -229,12 +229,12 @@ export class UniversalStrategy<
229
229
  const rewardAPYs: number[] = [];
230
230
  for (const [index, pool] of pools.entries()) {
231
231
  const vesuAdapter = vesuAdapters[index];
232
- const collateralAsset = pool.assets.find((a: any) => a.symbol === vesuAdapter.config.collateral.symbol)?.stats!;
233
- const debtAsset = pool.assets.find((a: any) => a.symbol === vesuAdapter.config.debt.symbol)?.stats!;
232
+ const collateralAsset = pool.assets.find((a: any) => a.symbol.toLowerCase() === vesuAdapter.config.collateral.symbol.toLowerCase())?.stats!;
233
+ const debtAsset = pool.assets.find((a: any) => a.symbol.toLowerCase() === vesuAdapter.config.debt.symbol.toLowerCase())?.stats!;
234
234
  const supplyApy = Number(collateralAsset.supplyApy.value || 0) / 1e18;
235
235
  const lstAPY = Number(collateralAsset.lstApr?.value || 0) / 1e18;
236
236
  baseAPYs.push(...[supplyApy + lstAPY, Number(debtAsset.borrowApr.value) / 1e18]);
237
- rewardAPYs.push(...[Number(collateralAsset.defiSpringSupplyApr.value || "0") / 1e18, 0]);
237
+ rewardAPYs.push(...[Number(collateralAsset.defiSpringSupplyApr?.value || "0") / 1e18, 0]);
238
238
  }
239
239
  logger.verbose(`${this.metadata.name}::netAPY: baseAPYs: ${JSON.stringify(baseAPYs)}`);
240
240
  logger.verbose(`${this.metadata.name}::netAPY: rewardAPYs: ${JSON.stringify(rewardAPYs)}`);
@@ -311,6 +311,7 @@ export class UniversalStrategy<
311
311
  let vesuAum = Web3Number.fromWei("0", underlying.decimals);
312
312
 
313
313
  let tokenUnderlyingPrice = await this.pricer.getPrice(this.asset().symbol);
314
+ logger.verbose(`${this.getTag()} tokenUnderlyingPrice: ${tokenUnderlyingPrice.price}`);
314
315
 
315
316
  // handle collateral
316
317
  if (legAUM[0].token.address.eq(underlying.address)) {
@@ -339,7 +340,6 @@ export class UniversalStrategy<
339
340
 
340
341
  async getAUM(): Promise<{net: SingleTokenInfo, prevAum: Web3Number, splits: {id: string, aum: Web3Number}[]}> {
341
342
  const prevAum = await this.getPrevAUM();
342
-
343
343
  const token1Price = await this.pricer.getPrice(this.metadata.depositTokens[0].symbol);
344
344
 
345
345
  // calculate vesu aum
@@ -437,7 +437,7 @@ export class UniversalStrategy<
437
437
  amount: unusedBalance.amount,
438
438
  usdValue: unusedBalance.usdValue,
439
439
  token: this.asset(),
440
- remarks: "Unused Balance"
440
+ remarks: "Unused Balance (may not include recent deposits)"
441
441
  }];
442
442
  }
443
443
 
@@ -848,8 +848,8 @@ function getLooperSettings(
848
848
  vaultSettings.leafAdapters.push(commonAdapter.getFlashloanAdapter.bind(commonAdapter));
849
849
  vaultSettings.leafAdapters.push(vesuAdapterUSDCETH.getModifyPosition.bind(vesuAdapterUSDCETH));
850
850
  vaultSettings.leafAdapters.push(vesuAdapterETHUSDC.getModifyPosition.bind(vesuAdapterETHUSDC));
851
- vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(USDCToken.address, vesuAdapterUSDCETH.VESU_SINGLETON, UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1).bind(commonAdapter));
852
- vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(ETHToken.address, vesuAdapterETHUSDC.VESU_SINGLETON, UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN2).bind(commonAdapter));
851
+ vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(USDCToken.address, VESU_SINGLETON, UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1).bind(commonAdapter));
852
+ vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(ETHToken.address, VESU_SINGLETON, UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN2).bind(commonAdapter));
853
853
 
854
854
  // to bridge liquidity back to vault (used by bring_liquidity)
855
855
  vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(USDCToken.address, vaultSettings.vaultAddress, UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY).bind(commonAdapter));