@strkfarm/sdk 2.0.0-dev.17 → 2.0.0-dev.18

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.
@@ -54,7 +54,7 @@ import {
54
54
  } from "./utils/helper";
55
55
  import { SingleTokenInfo } from "../base-strategy";
56
56
  import { Call } from "starknet";
57
- import { PositionTypeAvnuExtended} from "../universal-strategy";
57
+ import { PositionTypeAvnuExtended } from "../universal-strategy";
58
58
  import { TransactionMetadata, TransactionResult } from "./types/transaction-metadata";
59
59
 
60
60
 
@@ -67,6 +67,7 @@ export interface VesuExtendedStrategySettings
67
67
  minHealthFactor: number;
68
68
  aumOracle: ContractAddr;
69
69
  minimumWBTCDifferenceForAvnuSwap: number;
70
+ walletAddress: string;
70
71
  }
71
72
 
72
73
  export class VesuExtendedMultiplierStrategy<
@@ -182,7 +183,7 @@ export class VesuExtendedMultiplierStrategy<
182
183
  }
183
184
 
184
185
  async moveAssetsToVaultAllocator(amount: Web3Number, extendedAdapter: ExtendedAdapter): Promise<{
185
- calls:Call[],
186
+ calls: Call[],
186
187
  status: boolean,
187
188
  }> {
188
189
  try {
@@ -234,7 +235,7 @@ export class VesuExtendedMultiplierStrategy<
234
235
  const vesuAdapter = await this.getVesuAdapter();
235
236
  const extendedAdapter = await this.getExtendedAdapter();
236
237
  logger.info(`${VesuExtendedMultiplierStrategy.name}::shouldInvest adapters fetched: vesuAdapter=${!!vesuAdapter}, extendedAdapter=${!!extendedAdapter}, extendedAdapter.client=${!!extendedAdapter?.client}`);
237
-
238
+
238
239
  if (!vesuAdapter) {
239
240
  logger.error(
240
241
  `Vesu adapter not configured in metadata. This is a configuration issue, not a temporary failure.`
@@ -277,10 +278,10 @@ export class VesuExtendedMultiplierStrategy<
277
278
  vesuLeverage: 0,
278
279
  };
279
280
  }
280
-
281
+
281
282
  logger.info(`${VesuExtendedMultiplierStrategy.name}::shouldInvest calling getUnusedBalance`);
282
283
  const balance = await this.getUnusedBalance();
283
-
284
+
284
285
  if (!Number.isFinite(balance.usdValue) || balance.usdValue < 0) {
285
286
  logger.error(
286
287
  `Invalid balance.usdValue: ${balance.usdValue}. Expected a finite, non-negative number.`
@@ -297,7 +298,7 @@ export class VesuExtendedMultiplierStrategy<
297
298
  }
298
299
  logger.info(`${VesuExtendedMultiplierStrategy.name}::shouldInvest balance: ${balance.usdValue}`);
299
300
  const usdcBalanceOnExtended = await extendedAdapter.getExtendedDepositAmount();
300
-
301
+
301
302
  if (usdcBalanceOnExtended) {
302
303
  const availableForWithdrawal = parseFloat(usdcBalanceOnExtended.availableForWithdrawal);
303
304
  if (!Number.isFinite(availableForWithdrawal) || availableForWithdrawal < 0) {
@@ -315,11 +316,11 @@ export class VesuExtendedMultiplierStrategy<
315
316
  };
316
317
  }
317
318
  }
318
-
319
+
319
320
  /** The LIMIT_BALANCE is the bffer amount to keep in the investing Cycle */
320
321
  const amountToInvest = new Web3Number(balance.usdValue, USDC_TOKEN_DECIMALS).plus(usdcBalanceOnExtended?.availableForWithdrawal ?? 0).multipliedBy(1 - LIMIT_BALANCE);
321
322
 
322
-
323
+
323
324
  const amountToInvestNumber = amountToInvest.toNumber();
324
325
  if (!Number.isFinite(amountToInvestNumber)) {
325
326
  logger.error(
@@ -335,7 +336,7 @@ export class VesuExtendedMultiplierStrategy<
335
336
  vesuLeverage: 0,
336
337
  };
337
338
  }
338
-
339
+
339
340
  logger.info(`${VesuExtendedMultiplierStrategy.name}::shouldInvest amountToInvest: ${amountToInvestNumber}`);
340
341
  if (amountToInvest.lessThan(LIMIT_BALANCE_VALUE)) {
341
342
  return {
@@ -369,7 +370,7 @@ export class VesuExtendedMultiplierStrategy<
369
370
  collateralPrice,
370
371
  debtPrice
371
372
  } = await this.getAssetPrices();
372
-
373
+
373
374
 
374
375
  if (!Number.isFinite(collateralPrice.price) || collateralPrice.price <= 0) {
375
376
  logger.error(
@@ -399,7 +400,7 @@ export class VesuExtendedMultiplierStrategy<
399
400
  vesuLeverage: 0,
400
401
  };
401
402
  }
402
-
403
+
403
404
  const { vesu_amount, extended_amount, extended_leverage, vesu_leverage } =
404
405
  await calculateAmountDistribution(
405
406
  amountToInvest.toNumber(),
@@ -482,35 +483,35 @@ export class VesuExtendedMultiplierStrategy<
482
483
  if (extendedAmount.isNegative() && extendedAmount.abs().greaterThan(extendedAdapter.minimumExtendedMovementAmount)) {
483
484
  totalExtendedWithdrawal = totalExtendedWithdrawal.plus(extendedAmount.abs());
484
485
  }
485
-
486
+
486
487
  // Calculate remaining Extended difference (target vs current)
487
488
  // If extendedAmount was negative, we've already accounted for that withdrawal
488
489
  // So we calculate based on what Extended will be after that withdrawal
489
490
  const extendedTargetAmount = extendedAmount.abs(); // Use absolute value as target
490
491
  let projectedExtendedBalance = usdcAmountOnExtendedAvailableForWithdrawal;
491
-
492
+
492
493
  if (extendedAmount.isNegative()) {
493
494
  projectedExtendedBalance = projectedExtendedBalance - extendedAmount.abs().toNumber();
494
495
  }
495
-
496
+
496
497
  const extendedAmountDifference = extendedTargetAmount.minus(projectedExtendedBalance);
497
498
  const extendedAmountDifferenceAbs = extendedAmountDifference.abs();
498
-
499
+
499
500
  // Track additional Extended movements
500
501
  if (extendedAmountDifference.lessThan(0)) {
501
502
  totalExtendedWithdrawal = totalExtendedWithdrawal.plus(extendedAmountDifferenceAbs);
502
503
  } else if (extendedAmountDifference.greaterThan(0)) {
503
504
  totalExtendedDeposit = totalExtendedDeposit.plus(extendedAmountDifference);
504
505
  }
505
-
506
+
506
507
  const vesuTargetAmount = vesuAmount.abs();
507
508
  const projectedWalletBalance = usdcAmountInWallet
508
509
  .plus(totalExtendedWithdrawal)
509
510
  .minus(totalExtendedDeposit);
510
-
511
+
511
512
  let vesuAmountDifference = vesuTargetAmount.minus(projectedWalletBalance);
512
513
  const vesuAmountDifferenceAbs = vesuAmountDifference.abs();
513
-
514
+
514
515
  logger.info(`${VesuExtendedMultiplierStrategy.name}::shouldMoveAssets calculated movements - Extended withdrawal: ${totalExtendedWithdrawal.toNumber()}, Extended deposit: ${totalExtendedDeposit.toNumber()}, Extended diff: ${extendedAmountDifference.toNumber()}, Projected wallet: ${projectedWalletBalance.toNumber()}, Vesu diff: ${vesuAmountDifference.toNumber()}`);
515
516
  let calls: Call[] = [];
516
517
  let transactionResults: TransactionResult[] = [];
@@ -634,7 +635,7 @@ export class VesuExtendedMultiplierStrategy<
634
635
  }
635
636
  }
636
637
  }
637
-
638
+
638
639
  // Handle Vesu adjustments based on calculated difference (already adjusted for Extended movements)
639
640
  if (vesuAmountDifferenceAbs.greaterThan(vesuAdapter.minimumVesuMovementAmount)) {
640
641
  if (vesuAmountDifference.lessThanOrEqualTo(0)) {
@@ -672,7 +673,7 @@ export class VesuExtendedMultiplierStrategy<
672
673
  }
673
674
  }
674
675
  }
675
-
676
+
676
677
  return transactionResults;
677
678
  } catch (err) {
678
679
  logger.error(`Failed moving assets to vesu: ${err}`);
@@ -808,10 +809,10 @@ export class VesuExtendedMultiplierStrategy<
808
809
  receivedTxnHash: withdrawalFromExtendedTxnHash,
809
810
  } =
810
811
  await extendedAdapter.withdrawFromExtended(params.amount);
811
- /**
812
- * This logic needs fixing
813
- */
814
- logger.info(`withdrawalFromExtendedStatus: ${withdrawalFromExtendedStatus}, withdrawalFromExtendedTxnHash: ${withdrawalFromExtendedTxnHash}`);
812
+ /**
813
+ * This logic needs fixing
814
+ */
815
+ logger.info(`withdrawalFromExtendedStatus: ${withdrawalFromExtendedStatus}, withdrawalFromExtendedTxnHash: ${withdrawalFromExtendedTxnHash}`);
815
816
  if (withdrawalFromExtendedStatus && withdrawalFromExtendedTxnHash) {
816
817
  /**
817
818
  * We need to move assets from my wallet back to vault contract
@@ -819,10 +820,10 @@ export class VesuExtendedMultiplierStrategy<
819
820
  const extendedHoldings = await extendedAdapter.getExtendedDepositAmount();
820
821
  logger.info(`extendedHoldings after withdrawal ${extendedHoldings?.availableForWithdrawal}`);
821
822
  await new Promise(resolve => setTimeout(resolve, 5000));
822
- const {calls, status} = await this.moveAssetsToVaultAllocator(params.amount, extendedAdapter);
823
+ const { calls, status } = await this.moveAssetsToVaultAllocator(params.amount, extendedAdapter);
823
824
  if (calls.length > 0 && status) {
824
825
  return this.createTransactionResult(calls, true, params, "WITHDRAWAL", params.cycleType);
825
- }else {
826
+ } else {
826
827
  /**
827
828
  * This is a fallback scenario, where the funds were withdrawn from extended, but didn't get transferred to the wallet
828
829
  * We need to return a successful transaction result, but with no calls
@@ -830,7 +831,7 @@ export class VesuExtendedMultiplierStrategy<
830
831
  */
831
832
  return this.createTransactionResult([], true, params, "WITHDRAWAL", params.cycleType);
832
833
  }
833
- }else if(withdrawalFromExtendedStatus && !withdrawalFromExtendedTxnHash){
834
+ } else if (withdrawalFromExtendedStatus && !withdrawalFromExtendedTxnHash) {
834
835
  logger.error("withdrawal from extended successful, but funds didn't get transferred to the wallet");
835
836
  return this.createTransactionResult([], true, params, "WITHDRAWAL", params.cycleType);
836
837
  } else {
@@ -906,7 +907,7 @@ export class VesuExtendedMultiplierStrategy<
906
907
  return this.createTransactionResult([], false, params, "NONE", params.cycleType);
907
908
  } catch (err) {
908
909
  logger.error(`error moving assets: ${err}`);
909
- return this.createTransactionResult([], false, params,"NONE", params.cycleType);
910
+ return this.createTransactionResult([], false, params, "NONE", params.cycleType);
910
911
  }
911
912
  }
912
913
 
@@ -938,7 +939,7 @@ export class VesuExtendedMultiplierStrategy<
938
939
  const price = ask.plus(bid).dividedBy(2);
939
940
  const btcToken = vesuAdapter.config.supportedPositions[0].asset;
940
941
  const btcPriceAvnu = await avnuAdapter.getPriceOfToken(btcToken.address.toString());
941
-
942
+
942
943
  if (!btcPriceAvnu) {
943
944
  logger.error(`error getting btc price avnu: ${btcPriceAvnu}`);
944
945
  return false;
@@ -947,15 +948,15 @@ export class VesuExtendedMultiplierStrategy<
947
948
  logger.info(`btcPriceAvnu: ${btcPriceAvnu}`);
948
949
  const priceDifference = new Web3Number(price.minus(btcPriceAvnu).toFixed(2), 0);
949
950
  logger.info(`priceDifference: ${priceDifference}`);
950
- if(priceDifference.isNegative()){
951
+ if (priceDifference.isNegative()) {
951
952
  return false;
952
953
  }
953
- if(positionType === PositionTypeAvnuExtended.OPEN){
954
+ if (positionType === PositionTypeAvnuExtended.OPEN) {
954
955
  logger.info(`price difference between avnu and extended for open position: ${priceDifference.toNumber()}, minimumExtendedPriceDifferenceForSwapOpen: ${avnuAdapter.config.minimumExtendedPriceDifferenceForSwapOpen}`);
955
956
  const result = priceDifference.greaterThanOrEqualTo(avnuAdapter.config.minimumExtendedPriceDifferenceForSwapOpen); // 500 for now
956
957
  logger.info(`result: ${result}`);
957
958
  return result;
958
- }else{
959
+ } else {
959
960
  logger.info(`price difference between avnu and extended for close position: ${priceDifference.toNumber()}, maximumExtendedPriceDifferenceForSwapClosing: ${avnuAdapter.config.maximumExtendedPriceDifferenceForSwapClosing}`);
960
961
  const result = priceDifference.lessThanOrEqualTo(avnuAdapter.config.maximumExtendedPriceDifferenceForSwapClosing); // 1000 for now
961
962
  logger.info(`result: ${result}`);
@@ -1127,7 +1128,7 @@ export class VesuExtendedMultiplierStrategy<
1127
1128
  }, prevAum: prevAum, splits: [realAUM, estimatedAUMDelta]
1128
1129
  };
1129
1130
  }
1130
-
1131
+
1131
1132
  async processTransactionDataFromSDK(txnData: TransactionResult<any>[]): Promise<{ callsToBeExecutedFinal: Call[], txnMetadata: TransactionMetadata[] } | null> {
1132
1133
  try {
1133
1134
  const txnsToBeExecuted = txnData.filter(txn => {
@@ -1160,6 +1161,155 @@ export class VesuExtendedMultiplierStrategy<
1160
1161
  return null;
1161
1162
  }
1162
1163
  }
1164
+
1165
+ async getMaxBorrowableAmount(): Promise<Web3Number> {
1166
+ const vesuAdapter = await this.getVesuAdapter();
1167
+ const extendedAdapter = await this.getExtendedAdapter();
1168
+ if (!vesuAdapter || !extendedAdapter) {
1169
+ return new Web3Number(0, 0);
1170
+ }
1171
+ const extendedFundingRate = new Web3Number((await extendedAdapter.getNetAPY()).toFixed(4), 0);
1172
+ const extendedPositions = await extendedAdapter.getAllOpenPositions();
1173
+ if (!extendedPositions || extendedPositions.length === 0) {
1174
+ logger.info(`no extended positions found`);
1175
+ return new Web3Number(0, 0);
1176
+ }
1177
+ const extendePositionSizeUSD = new Web3Number(extendedPositions[0].value || 0, 0);
1178
+ const vesuPositions = await vesuAdapter.getPositions();
1179
+ const vesuSupplyApy = vesuPositions[0].apy.apy;
1180
+ const vesuCollateralSizeUSD = new Web3Number(vesuPositions[0].usdValue.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
1181
+ const vesuDebtSizeUSD = new Web3Number(vesuPositions[1].usdValue.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS)
1182
+ const num1 = extendePositionSizeUSD.multipliedBy(extendedFundingRate);
1183
+ const num2 = vesuCollateralSizeUSD.multipliedBy(vesuSupplyApy);
1184
+ const num3 = vesuDebtSizeUSD.abs()
1185
+ const maxBorrowApy = num1.plus(num2).minus(0.1).dividedBy(num3);
1186
+ const vesuMaxBorrowableAmount = await vesuAdapter.vesuAdapter.getMaxBorrowableByInterestRate(this.config, vesuAdapter.config.debt, maxBorrowApy.toNumber());
1187
+ return new Web3Number(vesuMaxBorrowableAmount.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
1188
+ }
1189
+
1190
+ async getVesuHealthFactors(): Promise<number[]> {
1191
+ const vesuAdapter = await this.getVesuAdapter();
1192
+ const extendedAdapter = await this.getExtendedAdapter();
1193
+ if (!vesuAdapter || !extendedAdapter) {
1194
+ return [0, 0];
1195
+ }
1196
+ const vesuPositions = await vesuAdapter.getPositions();
1197
+ const vesuCollateralSizeUSD = new Web3Number(vesuPositions[0].usdValue.toFixed(USDC_TOKEN_DECIMALS), 0);
1198
+ const vesuDebtSizeUSD = new Web3Number(vesuPositions[1].usdValue.toFixed(USDC_TOKEN_DECIMALS), 0);
1199
+ const actualLtv = vesuDebtSizeUSD.dividedBy(vesuCollateralSizeUSD).abs();
1200
+ logger.info(`actualLtv: ${actualLtv.toNumber()}`);
1201
+ const maxLtv = new Web3Number(await vesuAdapter.vesuAdapter.getLTVConfig(this.config), 4);
1202
+ const healthFactor = new Web3Number(maxLtv.dividedBy(actualLtv).toFixed(4), 4);
1203
+ logger.info(`healthFactor: ${healthFactor.toNumber()}`);
1204
+ const extendedBalance = await extendedAdapter.getExtendedDepositAmount();
1205
+ if (!extendedBalance) {
1206
+ return [0, 0];
1207
+ }
1208
+ const extendedLeverage = new Web3Number((Number(extendedBalance.marginRatio) * 100).toFixed(4), 4);
1209
+ logger.info(`extendedLeverage: ${extendedLeverage.toNumber()}`);
1210
+ return [healthFactor.toNumber(), extendedLeverage.toNumber()];
1211
+ }
1212
+
1213
+ async netAPY(): Promise<{ net: number; splits: { apy: number; id: string; }[]; }> {
1214
+ const allPositions: PositionInfo[] = [];
1215
+ for (let adapter of this.metadata.additionalInfo.adapters) {
1216
+ if(adapter.adapter.name !== ExtendedAdapter.name){
1217
+ let positions = await adapter.adapter.getPositions();
1218
+ if(positions.length > 0){
1219
+ allPositions.push(...positions);
1220
+ }
1221
+ }
1222
+ }
1223
+ const extendedAdapter =await this.getExtendedAdapter()
1224
+ if(!extendedAdapter){
1225
+ return {
1226
+ net: 0,
1227
+ splits: []
1228
+ }
1229
+ }
1230
+ let vesuPositions = allPositions.filter((item) => item.protocol === Protocols.VESU);
1231
+ vesuPositions.map((item) =>{
1232
+ item.apy.apy = item.apy.apy * 0.1;
1233
+ })
1234
+ const extendedPositions = await extendedAdapter.getAllOpenPositions();
1235
+ const usdcToken = Global.getDefaultTokens().find(token => token.symbol === "USDC");
1236
+ if(!extendedPositions || !usdcToken){
1237
+ return {
1238
+ net: 0,
1239
+ splits: []
1240
+ }
1241
+ }
1242
+ const extendedPosition = extendedPositions[0] || 0;
1243
+ const extendedEquity = (await extendedAdapter.getExtendedDepositAmount())?.equity || 0;
1244
+ const extendedApy = await extendedAdapter.getNetAPY();
1245
+ const totalHoldingsUSDValue = allPositions.reduce((acc, curr) => acc + curr.usdValue, 0) + Number(extendedEquity);
1246
+ console.log(totalHoldingsUSDValue)
1247
+ const extendedPositionSizeMultipliedByApy = Number(extendedPosition.value) * extendedApy;
1248
+ let weightedAPYs = allPositions.reduce((acc, curr) => acc + curr.apy.apy * curr.usdValue, 0) + extendedPositionSizeMultipliedByApy;
1249
+ console.log(weightedAPYs)
1250
+ const netAPY = weightedAPYs / totalHoldingsUSDValue;
1251
+ console.log(netAPY)
1252
+ allPositions.push({
1253
+ tokenInfo: usdcToken,
1254
+ amount: new Web3Number(extendedPosition.size, 0),
1255
+ usdValue: Number(extendedEquity),
1256
+ apy: { apy: extendedApy, type: APYType.BASE },
1257
+ remarks: AUMTypes.FINALISED,
1258
+ protocol: Protocols.EXTENDED
1259
+ })
1260
+ return {
1261
+ net: netAPY,
1262
+ splits: allPositions.map(p => ({apy: p.apy.apy, id: p.remarks ?? ''}))
1263
+ };
1264
+ }
1265
+
1266
+ async getWalletHoldings(): Promise<{
1267
+ tokenInfo: TokenInfo,
1268
+ amount: Web3Number,
1269
+ usdValue: number
1270
+ }[]> {
1271
+ const usdceToken = Global.getDefaultTokens().find(token => token.symbol === "USDCe");
1272
+ const wbtcToken = Global.getDefaultTokens().find(token => token.symbol === "WBTC");
1273
+ const usdcToken = Global.getDefaultTokens().find(token => token.symbol === "USDC");
1274
+ if (!usdceToken || !wbtcToken || !usdcToken) {
1275
+ return [];
1276
+ }
1277
+ const walletAddress = this.metadata.additionalInfo.walletAddress;
1278
+ const usdceWalletBalance = await new ERC20(this.config).balanceOf(
1279
+ usdceToken.address,
1280
+ walletAddress,
1281
+ usdceToken.decimals
1282
+ );
1283
+ const usdcWalletBalance = await new ERC20(this.config).balanceOf(
1284
+ usdcToken.address,
1285
+ walletAddress,
1286
+ usdcToken.decimals
1287
+ );
1288
+ const wbtcWalletBalance = await new ERC20(this.config).balanceOf(
1289
+ wbtcToken.address,
1290
+ walletAddress,
1291
+ wbtcToken.decimals
1292
+ );
1293
+ const price = await this.pricer.getPrice(usdceToken.symbol);
1294
+ const wbtcPrice = await this.pricer.getPrice(wbtcToken.symbol);
1295
+ const usdceUsdValue = Number(usdceWalletBalance.toFixed(usdceToken.decimals)) * price.price;
1296
+ const usdcUsdValue = Number(usdcWalletBalance.toFixed(usdcToken.decimals)) * price.price;
1297
+ const wbtcUsdValue = Number(wbtcWalletBalance.toFixed(wbtcToken.decimals)) * wbtcPrice.price;
1298
+ return [{
1299
+ tokenInfo: usdceToken,
1300
+ amount: usdceWalletBalance,
1301
+ usdValue: usdceUsdValue
1302
+ },
1303
+ {
1304
+ tokenInfo: usdcToken,
1305
+ amount: usdcWalletBalance,
1306
+ usdValue: usdcUsdValue
1307
+ }, {
1308
+ tokenInfo: wbtcToken,
1309
+ amount: wbtcWalletBalance,
1310
+ usdValue: wbtcUsdValue
1311
+ }];
1312
+ }
1163
1313
  }
1164
1314
 
1165
1315
  function getLooperSettings(
@@ -1369,6 +1519,7 @@ const re7UsdcPrimeDevansh: VesuExtendedStrategySettings = {
1369
1519
  ),
1370
1520
  borrowable_assets: [Global.getDefaultTokens().find(token => token.symbol === "WBTC")!],
1371
1521
  minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
1522
+ walletAddress: WALLET_ADDRESS,
1372
1523
  }
1373
1524
 
1374
1525
  export const VesuExtendedTestStrategies = (extendedBackendUrl: string, extendedApiKey: string, vaultIdExtended: number, minimumExtendedMovementAmount: number, minimumVesuMovementAmount: number, minimumExtendedRetriesDelayForOrderStatus: number, minimumExtendedPriceDifferenceForSwapOpen: number, maximumExtendedPriceDifferenceForSwapClosing: number): IStrategyMetadata<VesuExtendedStrategySettings>[] => {