@strkfarm/sdk 1.0.56 → 1.0.57

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.
@@ -55,7 +55,6 @@ export class VesuAdapter extends BaseAdapter {
55
55
  config: VesuAdapterConfig;
56
56
  networkConfig: IConfig | undefined;
57
57
  pricer: PricerBase | undefined;
58
- cache: Record<string, any> = {};
59
58
 
60
59
  constructor(config: VesuAdapterConfig) {
61
60
  super();
@@ -121,17 +120,19 @@ export class VesuAdapter extends BaseAdapter {
121
120
  denomination: this.formatAmountDenominationEnum(params.collateralAmount.denomination),
122
121
  value: {
123
122
  abs: uint256.bnToUint256(params.collateralAmount.value.abs.toWei()),
124
- is_negative: params.collateralAmount.value.is_negative
123
+ is_negative: params.collateralAmount.value.abs.isZero() ? false: params.collateralAmount.value.is_negative
125
124
  }
126
125
  };
126
+ logger.verbose(`VesuAdapter::ConstructingModify::Collateral::${JSON.stringify(_collateral)}`)
127
127
  const _debt = {
128
128
  amount_type: this.formatAmountTypeEnum(params.debtAmount.amount_type),
129
129
  denomination: this.formatAmountDenominationEnum(params.debtAmount.denomination),
130
130
  value: {
131
131
  abs: uint256.bnToUint256(params.debtAmount.value.abs.toWei()),
132
- is_negative: params.debtAmount.value.is_negative
132
+ is_negative: params.debtAmount.value.abs.isZero() ? false: params.debtAmount.value.is_negative
133
133
  }
134
134
  };
135
+ logger.verbose(`VesuAdapter::ConstructingModify::Debt::${JSON.stringify(_debt)}`)
135
136
  const singletonContract = new Contract(VesuSingletonAbi, this.VESU_SINGLETON.toString(), new RpcProvider({nodeUrl: ''}));
136
137
  const call = singletonContract.populate('modify_position', {
137
138
  params: {
@@ -182,13 +183,14 @@ export class VesuAdapter extends BaseAdapter {
182
183
 
183
184
  async getLTVConfig(config: IConfig) {
184
185
  const CACHE_KEY = 'ltv_config';
185
- if (this.cache[CACHE_KEY]) {
186
- return this.cache[CACHE_KEY] as number;
186
+ const cacheData = this.getCache<number>(CACHE_KEY);
187
+ if (cacheData) {
188
+ return cacheData as number;
187
189
  }
188
190
  const output: any = await this.getVesuSingletonContract(config)
189
191
  .call('ltv_config', [this.config.poolId.address, this.config.collateral.address.address, this.config.debt.address.address])
190
- this.cache[CACHE_KEY] = Number(output.max_ltv) / 1e18;
191
- return this.cache[CACHE_KEY] as number;
192
+ this.setCache(CACHE_KEY, Number(output.max_ltv) / 1e18, 300000); // ttl: 5min
193
+ return this.getCache<number>(CACHE_KEY) as number;
192
194
  }
193
195
 
194
196
  async getPositions(config: IConfig): Promise<VaultPosition[]> {
@@ -197,8 +199,9 @@ export class VesuAdapter extends BaseAdapter {
197
199
  }
198
200
  // { '0': { collateral_shares: 0n, nominal_debt: 0n }, '1': 0n, '2': 0n }
199
201
  const CACHE_KEY = 'positions';
200
- if (this.cache[CACHE_KEY]) {
201
- return this.cache[CACHE_KEY];
202
+ const cacheData = this.getCache<VaultPosition[]>(CACHE_KEY);
203
+ if (cacheData) {
204
+ return cacheData;
202
205
  }
203
206
  const output: any = await this.getVesuSingletonContract(config)
204
207
  .call('position_unsafe', [
@@ -224,10 +227,77 @@ export class VesuAdapter extends BaseAdapter {
224
227
  usdValue: debtAmount.multipliedBy(token2Price.price).toNumber(),
225
228
  remarks: "Debt"
226
229
  }];
227
- this.cache[CACHE_KEY] = value;
230
+ this.setCache(CACHE_KEY, value, 60000); // ttl: 1min
228
231
  return value;
229
232
  }
230
233
 
234
+ async getCollateralization(config: IConfig): Promise<Omit<VaultPosition, 'amount'>[]> {
235
+ if (!this.pricer) {
236
+ throw new Error('Pricer is not initialized');
237
+ }
238
+ // { '0': bool, '1': 0n, '2': 0n }
239
+ const CACHE_KEY = 'collateralization';
240
+ const cacheData = this.getCache<Omit<VaultPosition, 'amount'>[]>(CACHE_KEY);
241
+ if (cacheData) {
242
+ return cacheData;
243
+ }
244
+ const output: any = await this.getVesuSingletonContract(config)
245
+ .call('check_collateralization_unsafe', [
246
+ this.config.poolId.address,
247
+ this.config.collateral.address.address,
248
+ this.config.debt.address.address,
249
+ this.config.vaultAllocator.address
250
+ ]);
251
+
252
+ // usd values
253
+ const collateralAmount = Web3Number.fromWei(output['1'].toString(), 18);
254
+ const debtAmount = Web3Number.fromWei(output['2'].toString(), 18);
255
+ const value = [{
256
+ token: this.config.collateral,
257
+ usdValue: collateralAmount.toNumber(),
258
+ remarks: "Collateral"
259
+ }, {
260
+ token: this.config.debt,
261
+ usdValue: debtAmount.toNumber(),
262
+ remarks: "Debt"
263
+ }];
264
+ this.setCache(CACHE_KEY, value, 60000); // ttl: 1min
265
+ return value;
266
+ }
267
+
268
+ async getAssetPrices() {
269
+ const collateralizationProm = this.getCollateralization(this.networkConfig!);
270
+ const positionsProm = this.getPositions(this.networkConfig!);
271
+ const ltvProm = this.getLTVConfig(this.networkConfig!);
272
+
273
+ const output = await Promise.all([collateralizationProm, positionsProm, ltvProm]);
274
+ const [collateralization, positions, ltv] = output;
275
+
276
+ const collateralTokenAmount = positions[0].amount;
277
+ const collateralUSDAmount = collateralization[0].usdValue;
278
+ const collateralPrice = collateralUSDAmount / collateralTokenAmount.toNumber();
279
+
280
+ const debtTokenAmount = positions[1].amount;
281
+ const debtUSDAmount = collateralization[1].usdValue;
282
+ const debtPrice = debtUSDAmount / debtTokenAmount.toNumber();
283
+
284
+ return {
285
+ collateralTokenAmount,
286
+ collateralUSDAmount,
287
+ collateralPrice,
288
+ debtTokenAmount,
289
+ debtUSDAmount,
290
+ debtPrice,
291
+ ltv
292
+ }
293
+ }
294
+
295
+ async getHealthFactor() {
296
+ const ltv = await this.getLTVConfig(this.networkConfig!);
297
+ const collateralisation = await this.getCollateralization(this.networkConfig!);
298
+ return collateralisation[0].usdValue * ltv / collateralisation[1].usdValue;
299
+ }
300
+
231
301
  static async getVesuPools(
232
302
  retry = 0
233
303
  ): Promise<VesuPoolsInfo> {
@@ -15,7 +15,9 @@ export interface UniversalStrategySettings {
15
15
  vaultAllocator: ContractAddr,
16
16
  redeemRequestNFT: ContractAddr,
17
17
  leafAdapters: LeafAdapterFn<any>[],
18
- adapters: {id: string, adapter: BaseAdapter}[]
18
+ adapters: {id: string, adapter: BaseAdapter}[],
19
+ targetHealthFactor: number,
20
+ minHealthFactor: number
19
21
  }
20
22
 
21
23
 
@@ -166,7 +168,7 @@ export class UniversalStrategy<
166
168
  * Calculates the weighted average APY across all pools based on USD value.
167
169
  * @returns {Promise<number>} The weighted average APY across all pools
168
170
  */
169
- async netAPY(): Promise<number> {
171
+ async netAPY(): Promise<{ net: number, splits: { apy: number, id: string }[] }> {
170
172
  const [vesuAdapter1, vesuAdapter2] = this.getVesuAdapters();
171
173
  const pools = await VesuAdapter.getVesuPools();
172
174
  const pool1 = pools.pools.find(p => vesuAdapter1.config.poolId.eqString(num.getHexString(p.id)));
@@ -182,20 +184,32 @@ export class UniversalStrategy<
182
184
  // supplyApy: { value: '8057256029163289', decimals: 18 },
183
185
  // defiSpringSupplyApr: { value: '46856062629264560', decimals: 18 },
184
186
  // borrowApr: { value: '12167825982336000', decimals: 18 },
185
- const collateral1APY = Number(collateralAsset1.supplyApy.value) / 1e18 + Number(collateralAsset1.defiSpringSupplyApr.value) / 1e18;
187
+ const collateral1APY = Number(collateralAsset1.supplyApy.value) / 1e18;
186
188
  const debt1APY = Number(debtAsset1.borrowApr.value) / 1e18;
187
- const collateral2APY = Number(collateralAsset2.supplyApy.value) / 1e18 + Number(collateralAsset2.defiSpringSupplyApr.value) / 1e18;
189
+ const collateral2APY = Number(collateralAsset2.supplyApy.value) / 1e18;
188
190
  const debt2APY = Number(debtAsset2.borrowApr.value) / 1e18;
189
191
 
190
- const apys = [collateral1APY, debt1APY, collateral2APY, debt2APY];
191
192
  const positions = await this.getVaultPositions();
192
- assert(positions.length == apys.length, "Positions and APYs length mismatch");
193
- const weightedAPYs = positions.map((pos, i) => {
194
- return pos.usdValue * apys[i] * (i % 2 == 0 ? 1 : -1);
195
- });
196
- const netPosition = positions.reduce((acc, val, index) => acc + val.usdValue * (index % 2 == 0 ? 1 : -1), 0);
197
- const apy = weightedAPYs.reduce((acc, val) => acc + val, 0) / netPosition;
198
- return apy;
193
+ const weights = positions.map((p, index) => p.usdValue * (index % 2 == 0 ? 1 : -1));
194
+ const baseAPYs = [collateral1APY, debt1APY, collateral2APY, debt2APY];
195
+ assert(positions.length == baseAPYs.length, "Positions and APYs length mismatch");
196
+ const rewardAPYs = [Number(collateralAsset1.defiSpringSupplyApr.value) / 1e18, 0, Number(collateralAsset2.defiSpringSupplyApr.value) / 1e18, 0];
197
+ const baseAPY = this.computeAPY(baseAPYs, weights);
198
+ const rewardAPY = this.computeAPY(rewardAPYs, weights);
199
+ const apys = [...baseAPYs, ...rewardAPYs];
200
+ const netAPY = baseAPY + rewardAPY;
201
+ return { net: netAPY, splits: [{
202
+ apy: baseAPY, id: 'base'
203
+ }, {
204
+ apy: rewardAPY, id: 'defispring'
205
+ }] };
206
+ }
207
+
208
+ private computeAPY(apys: number[], weights: number[]) {
209
+ assert(apys.length === weights.length, "APYs and weights length mismatch");
210
+ const weightedSum = apys.reduce((acc, apy, i) => acc + apy * weights[i], 0);
211
+ const totalWeight = weights.reduce((acc, weight) => acc + weight, 0);
212
+ return weightedSum / totalWeight;
199
213
  }
200
214
 
201
215
  /**
@@ -219,23 +233,53 @@ export class UniversalStrategy<
219
233
  };
220
234
  }
221
235
 
222
- async getAUM(): Promise<SingleTokenInfo> {
236
+ async getAUM(): Promise<{net: SingleTokenInfo, prevAum: Web3Number, splits: {id: string, aum: Web3Number}[]}> {
237
+ const currentAUM: bigint = await this.contract.call('aum', []) as bigint;
238
+ const lastReportTime = await this.contract.call('last_report_timestamp', []);
239
+
240
+ const token1Price = await this.pricer.getPrice(this.metadata.depositTokens[0].symbol);
241
+
242
+ // calculate actual aum
223
243
  const [vesuAdapter1, vesuAdapter2] = this.getVesuAdapters();
224
244
  const leg1AUM = await vesuAdapter1.getPositions(this.config);
225
245
  const leg2AUM = await vesuAdapter2.getPositions(this.config);
226
246
 
227
- const token1Price = await this.pricer.getPrice(vesuAdapter1.config.collateral.symbol);
228
-
229
247
  const aumToken = leg1AUM[0].amount
230
248
  .plus(leg2AUM[0].usdValue / token1Price.price)
231
249
  .minus(leg1AUM[1].usdValue / token1Price.price)
232
250
  .minus(leg2AUM[1].amount);
233
-
234
- return {
251
+ logger.verbose(`${this.getTag()} Actual AUM: ${aumToken}`);
252
+
253
+ // calculate estimated growth from strk rewards
254
+ const netAPY = await this.netAPY();
255
+ const defispringAPY = netAPY.splits.find(s => s.id === 'defispring')?.apy || 0;
256
+ if (!defispringAPY) throw new Error('DefiSpring APY not found');
257
+
258
+ const timeDiff = (Math.round(Date.now() / 1000) - Number(lastReportTime));
259
+ const growthRate = timeDiff * defispringAPY / (365 * 24 * 60 * 60);
260
+ const prevAum = Web3Number.fromWei(currentAUM.toString(), this.asset().decimals);
261
+ const rewardAssets = prevAum.multipliedBy(growthRate);
262
+ logger.verbose(`${this.getTag()} DefiSpring AUM time difference: ${timeDiff}`);
263
+ logger.verbose(`${this.getTag()} Current AUM: ${currentAUM}`);
264
+ logger.verbose(`${this.getTag()} Net APY: ${JSON.stringify(netAPY)}`);
265
+ logger.verbose(`${this.getTag()} rewards AUM: ${rewardAssets}`);
266
+
267
+ const newAUM = aumToken.plus(rewardAssets);
268
+ logger.verbose(`${this.getTag()} New AUM: ${newAUM}`);
269
+
270
+ const net = {
235
271
  tokenInfo: this.asset(),
236
- amount: aumToken,
237
- usdValue: aumToken.multipliedBy(token1Price.price).toNumber()
238
- }
272
+ amount: newAUM,
273
+ usdValue: newAUM.multipliedBy(token1Price.price).toNumber()
274
+ };
275
+ const splits = [{
276
+ id: 'finalised',
277
+ aum: aumToken
278
+ }, {
279
+ id: 'defispring',
280
+ aum: rewardAssets
281
+ }];
282
+ return { net, splits, prevAum };
239
283
  }
240
284
 
241
285
  getVesuAdapters() {
@@ -243,6 +287,8 @@ export class UniversalStrategy<
243
287
  const vesuAdapter2 = this.getAdapter(UNIVERSAL_ADAPTERS.VESU_LEG2) as VesuAdapter;
244
288
  vesuAdapter1.pricer = this.pricer;
245
289
  vesuAdapter2.pricer = this.pricer;
290
+ vesuAdapter1.networkConfig = this.config;
291
+ vesuAdapter2.networkConfig = this.config;
246
292
 
247
293
  return [vesuAdapter1, vesuAdapter2];
248
294
  }
@@ -297,12 +343,14 @@ export class UniversalStrategy<
297
343
 
298
344
  const output = [{
299
345
  proofs: manage5Info.proofs,
300
- manageCall: manageCall5
346
+ manageCall: manageCall5,
347
+ step: STEP2_ID
301
348
  }];
302
349
  if (approveAmount.gt(0)) {
303
350
  output.unshift({
304
351
  proofs: manage4Info.proofs,
305
- manageCall: manageCall4
352
+ manageCall: manageCall4,
353
+ step: STEP1_ID
306
354
  })
307
355
  }
308
356
  return output;
@@ -312,12 +360,114 @@ export class UniversalStrategy<
312
360
  return `${UniversalStrategy.name}:${this.metadata.name}`;
313
361
  }
314
362
 
363
+ async getVesuHealthFactors() {
364
+ return await Promise.all(this.getVesuAdapters().map(v => v.getHealthFactor()));
365
+ }
366
+
367
+ async computeRebalanceConditionAndReturnCalls(): Promise<Call[]> {
368
+ const vesuAdapters = this.getVesuAdapters();
369
+ const healthFactors = await this.getVesuHealthFactors();
370
+ const leg1HealthFactor = healthFactors[0];
371
+ const leg2HealthFactor = healthFactors[1];
372
+ logger.verbose(`${this.getTag()}: HealthFactorLeg1: ${leg1HealthFactor}`);
373
+ logger.verbose(`${this.getTag()}: HealthFactorLeg2: ${leg2HealthFactor}`);
374
+
375
+ const minHf = this.metadata.additionalInfo.minHealthFactor;
376
+ const isRebalanceNeeded1 = leg1HealthFactor < minHf;
377
+ const isRebalanceNeeded2 = leg2HealthFactor < minHf;
378
+ if (!isRebalanceNeeded1 && !isRebalanceNeeded2) {
379
+ return [];
380
+ }
381
+
382
+ if (isRebalanceNeeded1) {
383
+ const amount = await this.getLegRebalanceAmount(vesuAdapters[0], leg1HealthFactor, false);
384
+ const leg2HF = await this.getNewHealthFactor(vesuAdapters[1], amount, true);
385
+ assert(leg2HF > minHf, `Rebalance Leg1 failed: Leg2 HF after rebalance would be too low: ${leg2HF}`);
386
+ return [await this.getRebalanceCall({
387
+ isLeg1toLeg2: false,
388
+ amount: amount
389
+ })];
390
+ } else {
391
+ const amount = await this.getLegRebalanceAmount(vesuAdapters[1], leg2HealthFactor, true);
392
+ const leg1HF = await this.getNewHealthFactor(vesuAdapters[0], amount, false);
393
+ assert(leg1HF > minHf, `Rebalance Leg2 failed: Leg1 HF after rebalance would be too low: ${leg1HF}`);
394
+ return [await this.getRebalanceCall({
395
+ isLeg1toLeg2: true,
396
+ amount: amount
397
+ })];
398
+ }
399
+ }
400
+
401
+ private async getNewHealthFactor(vesuAdapter: VesuAdapter, newAmount: Web3Number, isWithdraw: boolean) {
402
+ const {
403
+ collateralTokenAmount,
404
+ collateralUSDAmount,
405
+ collateralPrice,
406
+ debtTokenAmount,
407
+ debtUSDAmount,
408
+ debtPrice,
409
+ ltv
410
+ } = await vesuAdapter.getAssetPrices();
411
+
412
+ if (isWithdraw) {
413
+ const newHF = ((collateralTokenAmount.toNumber() - newAmount.toNumber()) * collateralPrice * ltv) / debtUSDAmount;
414
+ logger.verbose(`getNewHealthFactor:: HF: ${newHF}, amoutn: ${newAmount.toNumber()}, isDeposit`);
415
+ return newHF;
416
+ } else { // is borrow
417
+ const newHF = (collateralUSDAmount * ltv) / ((debtTokenAmount.toNumber() + newAmount.toNumber()) * debtPrice);
418
+ logger.verbose(`getNewHealthFactor:: HF: ${newHF}, amoutn: ${newAmount.toNumber()}, isRepay`);
419
+ return newHF;
420
+ }
421
+ }
422
+
423
+ /**
424
+ *
425
+ * @param vesuAdapter
426
+ * @param currentHf
427
+ * @param isDeposit if true, attempt by adding collateral, else by repaying
428
+ * @returns
429
+ */
430
+ private async getLegRebalanceAmount(vesuAdapter: VesuAdapter, currentHf: number, isDeposit: boolean) {
431
+ const {
432
+ collateralTokenAmount,
433
+ collateralUSDAmount,
434
+ collateralPrice,
435
+ debtTokenAmount,
436
+ debtUSDAmount,
437
+ debtPrice,
438
+ ltv
439
+ } = await vesuAdapter.getAssetPrices();
440
+
441
+ // debt is zero, nothing to rebalance
442
+ if(debtTokenAmount.isZero()) {
443
+ return Web3Number.fromWei(0, 0);
444
+ }
445
+
446
+ assert(collateralPrice > 0 && debtPrice > 0, "getRebalanceAmount: Invalid price");
447
+
448
+ // avoid calculating for too close
449
+ const targetHF = this.metadata.additionalInfo.targetHealthFactor;
450
+ if (currentHf > targetHF - 0.01)
451
+ throw new Error("getLegRebalanceAmount: Current health factor is healthy");
452
+
453
+ if (isDeposit) {
454
+ // TargetHF = (collAmount + newAmount) * price * ltv / debtUSD
455
+ const newAmount = targetHF * debtUSDAmount / (collateralPrice * ltv) - collateralTokenAmount.toNumber();
456
+ logger.verbose(`${this.getTag()}:: getLegRebalanceAmount: addCollateral, currentHf: ${currentHf}, targetHF: ${targetHF}, collAmount: ${collateralTokenAmount.toString()}, collUSD: ${collateralUSDAmount}, collPrice: ${collateralPrice}, debtAmount: ${debtTokenAmount.toString()}, debtUSD: ${debtUSDAmount}, debtPrice: ${debtPrice}, ltv: ${ltv}, newAmount: ${newAmount}`);
457
+ return new Web3Number(newAmount.toFixed(8), collateralTokenAmount.decimals);
458
+ } else {
459
+ // TargetHF = collUSD * ltv / (debtAmount - newAmount) * debtPrice
460
+ const newAmount = debtTokenAmount.toNumber() - collateralUSDAmount * ltv / (targetHF * debtPrice);
461
+ logger.verbose(`${this.getTag()}:: getLegRebalanceAmount: repayDebt, currentHf: ${currentHf}, targetHF: ${targetHF}, collAmount: ${collateralTokenAmount.toString()}, collUSD: ${collateralUSDAmount}, collPrice: ${collateralPrice}, debtAmount: ${debtTokenAmount.toString()}, debtUSD: ${debtUSDAmount}, debtPrice: ${debtPrice}, ltv: ${ltv}, newAmount: ${newAmount}`);
462
+ return new Web3Number(newAmount.toFixed(8), debtTokenAmount.decimals);
463
+ }
464
+ }
465
+
315
466
  async getVesuMultiplyCall(params: {
316
467
  isDeposit: boolean,
317
468
  leg1DepositAmount: Web3Number
318
469
  }) {
319
- const vesuAdapter1 = this.getAdapter(UNIVERSAL_ADAPTERS.VESU_LEG1) as VesuAdapter;
320
- const vesuAdapter2 = this.getAdapter(UNIVERSAL_ADAPTERS.VESU_LEG2) as VesuAdapter;
470
+ const [vesuAdapter1, vesuAdapter2] = this.getVesuAdapters();
321
471
  const leg1LTV = await vesuAdapter1.getLTVConfig(this.config);
322
472
  const leg2LTV = await vesuAdapter2.getLTVConfig(this.config);
323
473
  logger.verbose(`${this.getTag()}: LTVLeg1: ${leg1LTV}`);
@@ -328,7 +478,7 @@ export class UniversalStrategy<
328
478
  logger.verbose(`${this.getTag()}: Price${vesuAdapter1.config.collateral.symbol}: ${token1Price.price}`);
329
479
  logger.verbose(`${this.getTag()}: Price${vesuAdapter2.config.collateral.symbol}: ${token2Price.price}`);
330
480
 
331
- const TARGET_HF = 1.3;
481
+ const TARGET_HF = this.metadata.additionalInfo.targetHealthFactor;
332
482
 
333
483
  const k1 = token1Price.price * leg1LTV / token2Price.price / TARGET_HF;
334
484
  const k2 = token1Price.price * TARGET_HF / token2Price.price / leg2LTV;
@@ -399,14 +549,12 @@ export class UniversalStrategy<
399
549
 
400
550
  if (params.isLeg1toLeg2) {
401
551
  const manageCall = this.getManageCall([
402
- UNIVERSAL_MANAGE_IDS.VESU_LEG1,
403
- UNIVERSAL_MANAGE_IDS.VESU_LEG2
552
+ ...callSet1.map(i => i.step), ...callSet2.map(i => i.step)
404
553
  ], [...callSet1.map(i => i.manageCall), ...callSet2.map(i => i.manageCall)]);
405
554
  return manageCall;
406
555
  } else {
407
556
  const manageCall = this.getManageCall([
408
- UNIVERSAL_MANAGE_IDS.VESU_LEG2,
409
- UNIVERSAL_MANAGE_IDS.VESU_LEG1
557
+ ...callSet2.map(i => i.step), ...callSet1.map(i => i.step)
410
558
  ], [...callSet2.map(i => i.manageCall), ...callSet1.map(i => i.manageCall)]);
411
559
  return manageCall;
412
560
  }
@@ -485,7 +633,9 @@ const usdcVaultSettings: UniversalStrategySettings = {
485
633
  vaultAllocator: ContractAddr.from('0x228cca1005d3f2b55cbaba27cb291dacf1b9a92d1d6b1638195fbd3d0c1e3ba'),
486
634
  redeemRequestNFT: ContractAddr.from('0x906d03590010868cbf7590ad47043959d7af8e782089a605d9b22567b64fda'),
487
635
  leafAdapters: [],
488
- adapters: []
636
+ adapters: [],
637
+ targetHealthFactor: 1.3,
638
+ minHealthFactor: 1.25
489
639
  }
490
640
 
491
641
  const wbtcVaultSettings: UniversalStrategySettings = {
@@ -493,7 +643,9 @@ const wbtcVaultSettings: UniversalStrategySettings = {
493
643
  vaultAllocator: ContractAddr.from('0x1e01c25f0d9494570226ad28a7fa856c0640505e809c366a9fab4903320e735'),
494
644
  redeemRequestNFT: ContractAddr.from('0x4fec59a12f8424281c1e65a80b5de51b4e754625c60cddfcd00d46941ec37b2'),
495
645
  leafAdapters: [],
496
- adapters: []
646
+ adapters: [],
647
+ targetHealthFactor: 1.3,
648
+ minHealthFactor: 1.25
497
649
  }
498
650
 
499
651
  export const UniversalStrategies: IStrategyMetadata<UniversalStrategySettings>[] =
@@ -0,0 +1,29 @@
1
+ interface CacheData {
2
+ timestamp: number;
3
+ ttl: number;
4
+ data: any;
5
+ }
6
+ export class CacheClass {
7
+ readonly cache: Map<string, CacheData> = new Map();
8
+
9
+ setCache(key: string, data: any, ttl: number = 60000): void {
10
+ const timestamp = Date.now();
11
+ this.cache.set(key, { timestamp, ttl, data });
12
+ }
13
+
14
+ getCache<T>(key: string): T | null {
15
+ const cachedData = this.cache.get(key);
16
+ if (!cachedData || !this.isCacheValid(key)) {
17
+ return null;
18
+ }
19
+ return cachedData.data;
20
+ }
21
+
22
+ isCacheValid(key: string): boolean {
23
+ const cachedData = this.cache.get(key);
24
+ if (!cachedData) return false;
25
+
26
+ const { timestamp, ttl } = cachedData;
27
+ return Date.now() - timestamp <= ttl;
28
+ }
29
+ }
@@ -59,15 +59,6 @@ export class StandardMerkleTree extends MerkleTreeImpl<LeafData> {
59
59
  return new StandardMerkleTree(tree, indexedValues, leafEncoding);
60
60
  }
61
61
 
62
- static load(data: StandardMerkleTreeData<LeafData>): StandardMerkleTree {
63
- validateArgument(data.format === 'standard-v1', `Unknown format '${data.format}'`);
64
- validateArgument(data.leafEncoding !== undefined, 'Expected leaf encoding');
65
-
66
- const tree = new StandardMerkleTree(data.tree, data.values, data.leafEncoding);
67
- tree.validate();
68
- return tree;
69
- }
70
-
71
62
  static verify<T extends any[]>(root: BytesLike, leafEncoding: ValueType[], leaf: T, proof: BytesLike[]): boolean {
72
63
  // use default nodeHash (standardNodeHash) for processProof
73
64
  return toHex(root) === processProof(standardLeafHash(leafEncoding, leaf), proof);