@defisaver/positions-sdk 2.0.0 → 2.0.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.
@@ -199,9 +199,10 @@ const getAdditionalMarketRateForDex = (token1PerShare, token0PerShare, incentive
199
199
  const getTradingApy = (poolAddress) => __awaiter(void 0, void 0, void 0, function* () {
200
200
  let res;
201
201
  try {
202
- res = yield fetch(`https://api.fluid.instadapp.io/v2/1/dexes/${poolAddress}/apy`, { signal: AbortSignal.timeout(2000) });
202
+ res = yield fetch(`https://api.fluid.instadapp.io/v2/1/dexes/${poolAddress}/apy`, { signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT) });
203
203
  }
204
204
  catch (e) {
205
+ console.error('External API Failure: Fluid Trading Apy');
205
206
  return '0';
206
207
  }
207
208
  if (!res.ok) {
@@ -38,7 +38,7 @@ const formatMarketData = (data, network, baseAssetPrice) => {
38
38
  const assetInfo = (0, tokens_1.getAssetInfoByAddress)(data.tokenAddr, network);
39
39
  const isWETH = assetInfo.symbol === 'WETH';
40
40
  const price = (0, utils_1.getEthAmountForDecimals)(data.price, 8);
41
- return (Object.assign(Object.assign({}, data), { priceInBaseAsset: (0, utils_1.getEthAmountForDecimals)(data.price, 8), price: new decimal_js_1.default(price).mul(baseAssetPrice).toString(), collateralFactor: (0, utils_1.getEthAmountForDecimals)(data.borrowCollateralFactor, 18), liquidationRatio: (0, utils_1.getEthAmountForDecimals)(data.liquidateCollateralFactor, 18), supplyCap: (0, utils_1.getEthAmountForDecimals)(data.supplyCap, assetInfo.decimals), totalSupply: (0, utils_1.getEthAmountForDecimals)(data.totalSupply, assetInfo.decimals), symbol: isWETH ? 'ETH' : assetInfo.symbol, supplyRate: '0', borrowRate: '0', canBeBorrowed: false, canBeSupplied: true }));
41
+ return (Object.assign(Object.assign({}, data), { borrowCollateralFactor: data.borrowCollateralFactor.toString(), liquidateCollateralFactor: data.liquidateCollateralFactor.toString(), liquidationFactor: data.liquidationFactor.toString(), supplyReserved: data.supplyReserved.toString(), priceInBaseAsset: (0, utils_1.getEthAmountForDecimals)(data.price, 8), price: new decimal_js_1.default(price).mul(baseAssetPrice).toString(), collateralFactor: (0, utils_1.getEthAmountForDecimals)(data.borrowCollateralFactor, 18), liquidationRatio: (0, utils_1.getEthAmountForDecimals)(data.liquidateCollateralFactor, 18), supplyCap: (0, utils_1.getEthAmountForDecimals)(data.supplyCap, assetInfo.decimals), totalSupply: (0, utils_1.getEthAmountForDecimals)(data.totalSupply, assetInfo.decimals), symbol: isWETH ? 'ETH' : assetInfo.symbol, supplyRate: '0', borrowRate: '0', canBeBorrowed: false, canBeSupplied: true }));
42
42
  };
43
43
  exports.formatMarketData = formatMarketData;
44
44
  // TODO: maybe not hardcode decimals
@@ -46,7 +46,7 @@ const formatBaseData = (data, network, baseAssetPrice) => {
46
46
  const assetInfo = (0, tokens_1.getAssetInfoByAddress)(data.tokenAddr, network);
47
47
  const totalSupply = (0, utils_1.getEthAmountForDecimals)(new decimal_js_1.default(data.totalSupply).mul(data.supplyIndex).toString(), 15 + assetInfo.decimals);
48
48
  const totalBorrow = (0, utils_1.getEthAmountForDecimals)(new decimal_js_1.default(data.totalBorrow).mul(data.borrowIndex).toString(), 15 + assetInfo.decimals);
49
- return (Object.assign(Object.assign({}, data), { supplyRate: (0, moneymarket_1.aprToApy)(new decimal_js_1.default(data.supplyRate).div(1e18).mul(constants_1.SECONDS_PER_YEAR).mul(100)
49
+ return (Object.assign(Object.assign({}, data), { baseBorrowMin: data.baseBorrowMin.toString(), baseTrackingBorrowRewardsSpeed: data.baseTrackingBorrowRewardsSpeed.toString(), baseTrackingSupplyRewardsSpeed: data.baseTrackingSupplyRewardsSpeed.toString(), borrowIndex: data.borrowIndex.toString(), supplyIndex: data.supplyIndex.toString(), trackingBorrowIndex: data.trackingBorrowIndex.toString(), trackingSupplyIndex: data.trackingSupplyIndex.toString(), supplyRate: (0, moneymarket_1.aprToApy)(new decimal_js_1.default(data.supplyRate).div(1e18).mul(constants_1.SECONDS_PER_YEAR).mul(100)
50
50
  .toString()), borrowRate: (0, moneymarket_1.aprToApy)(new decimal_js_1.default(data.borrowRate).div(1e18).mul(constants_1.SECONDS_PER_YEAR).mul(100)
51
51
  .toString()), utilization: (0, utils_1.getEthAmountForDecimals)(data.utilization, 16), // utilization is totalSupply/totalBorrow in 1e18, but we need % so when we mul with 100 it's 16 decimals
52
52
  totalSupply,
@@ -204,19 +204,29 @@ const REWARDS_QUERY = `
204
204
  */
205
205
  const getReallocatableLiquidity = (marketId_1, ...args_1) => __awaiter(void 0, [marketId_1, ...args_1], void 0, function* (marketId, network = common_1.NetworkNumber.Eth) {
206
206
  var _a;
207
- const response = yield fetch(API_URL, {
208
- method: 'POST',
209
- headers: { 'Content-Type': 'application/json' },
210
- body: JSON.stringify({
211
- query: MARKET_QUERY,
212
- variables: { uniqueKey: marketId, chainId: network },
213
- }),
214
- });
215
- const data = yield response.json();
216
- const marketData = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.marketByUniqueKey;
217
- if (!marketData)
218
- throw new Error('Market data not found');
219
- return { reallocatableLiquidity: marketData.reallocatableLiquidityAssets, targetBorrowUtilization: marketData.targetBorrowUtilization };
207
+ try {
208
+ const response = yield fetch(API_URL, {
209
+ method: 'POST',
210
+ headers: { 'Content-Type': 'application/json' },
211
+ body: JSON.stringify({
212
+ query: MARKET_QUERY,
213
+ variables: { uniqueKey: marketId, chainId: network },
214
+ }),
215
+ signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT),
216
+ });
217
+ const data = yield response.json();
218
+ const marketData = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.marketByUniqueKey;
219
+ if (!marketData)
220
+ throw new Error('Market data not found');
221
+ return {
222
+ reallocatableLiquidity: marketData.reallocatableLiquidityAssets,
223
+ targetBorrowUtilization: marketData.targetBorrowUtilization,
224
+ };
225
+ }
226
+ catch (error) {
227
+ console.error('External API Failure: Morpho blue reallocatable liquidity', error);
228
+ throw new Error('Failed to fetch reallocatable liquidity');
229
+ }
220
230
  });
221
231
  exports.getReallocatableLiquidity = getReallocatableLiquidity;
222
232
  /**
@@ -257,71 +267,78 @@ exports.getLiquidityToAllocate = getLiquidityToAllocate;
257
267
  */
258
268
  const getReallocation = (market_1, assetsData_1, amountToBorrow_1, ...args_1) => __awaiter(void 0, [market_1, assetsData_1, amountToBorrow_1, ...args_1], void 0, function* (market, assetsData, amountToBorrow, network = common_1.NetworkNumber.Eth) {
259
269
  var _a, _b, _c;
260
- const { marketId, loanToken } = market;
261
- const response = yield fetch(API_URL, {
262
- method: 'POST',
263
- headers: { 'Content-Type': 'application/json' },
264
- body: JSON.stringify({
265
- query: MARKET_QUERY,
266
- variables: { uniqueKey: marketId, chainId: network },
267
- }),
268
- });
269
- const data = yield response.json();
270
- const marketData = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.marketByUniqueKey;
271
- if (!marketData)
272
- throw new Error('Market data not found');
273
- const loanAssetInfo = (0, tokens_1.getAssetInfoByAddress)(loanToken, network);
274
- const { totalBorrow, totalSupply } = assetsData[loanAssetInfo.symbol] || { totalBorrow: '0', totalSupply: '0' };
275
- const totalBorrowWei = (0, tokens_1.assetAmountInWei)(totalBorrow, loanAssetInfo.symbol);
276
- const totalSupplyWei = (0, tokens_1.assetAmountInWei)(totalSupply, loanAssetInfo.symbol);
277
- const newTotalBorrowAssets = new decimal_js_1.default(totalBorrowWei).add(amountToBorrow).toString();
278
- const newUtil = new decimal_js_1.default(newTotalBorrowAssets).div(totalSupplyWei).toString();
279
- const newUtilScaled = new decimal_js_1.default(newUtil).mul(1e18).toString();
280
- if (new decimal_js_1.default(newUtilScaled).lt(marketData.targetBorrowUtilization))
281
- return { vaults: [], withdrawals: [] };
282
- const liquidityToAllocate = (0, exports.getLiquidityToAllocate)(amountToBorrow, totalBorrowWei, totalSupplyWei, marketData.targetBorrowUtilization, marketData.reallocatableLiquidityAssets);
283
- const vaultTotalAssets = marketData.publicAllocatorSharedLiquidity.reduce((acc, item) => {
284
- const vaultAddress = item.vault.address;
285
- acc[vaultAddress] = new decimal_js_1.default(acc[vaultAddress] || '0').add(item.assets).toString();
286
- return acc;
287
- }, {});
288
- const sortedVaults = Object.entries(vaultTotalAssets).sort(([, a], [, b]) => new decimal_js_1.default(b || '0').sub(a || '0').toNumber());
289
- const withdrawalsPerVault = {};
290
- let totalReallocated = '0';
291
- for (const [vaultAddress] of sortedVaults) {
292
- if (new decimal_js_1.default(totalReallocated).gte(liquidityToAllocate))
293
- break;
294
- const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter((item) => (0, utils_1.compareAddresses)(item.vault.address, vaultAddress));
295
- for (const item of vaultAllocations) {
270
+ try {
271
+ const { marketId, loanToken } = market;
272
+ const response = yield fetch(API_URL, {
273
+ method: 'POST',
274
+ headers: { 'Content-Type': 'application/json' },
275
+ body: JSON.stringify({
276
+ query: MARKET_QUERY,
277
+ variables: { uniqueKey: marketId, chainId: network },
278
+ }),
279
+ signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT),
280
+ });
281
+ const data = yield response.json();
282
+ const marketData = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.marketByUniqueKey;
283
+ if (!marketData)
284
+ throw new Error('Market data not found');
285
+ const loanAssetInfo = (0, tokens_1.getAssetInfoByAddress)(loanToken, network);
286
+ const { totalBorrow, totalSupply } = assetsData[loanAssetInfo.symbol] || { totalBorrow: '0', totalSupply: '0' };
287
+ const totalBorrowWei = (0, tokens_1.assetAmountInWei)(totalBorrow, loanAssetInfo.symbol);
288
+ const totalSupplyWei = (0, tokens_1.assetAmountInWei)(totalSupply, loanAssetInfo.symbol);
289
+ const newTotalBorrowAssets = new decimal_js_1.default(totalBorrowWei).add(amountToBorrow).toString();
290
+ const newUtil = new decimal_js_1.default(newTotalBorrowAssets).div(totalSupplyWei).toString();
291
+ const newUtilScaled = new decimal_js_1.default(newUtil).mul(1e18).toString();
292
+ if (new decimal_js_1.default(newUtilScaled).lt(marketData.targetBorrowUtilization))
293
+ return { vaults: [], withdrawals: [] };
294
+ const liquidityToAllocate = (0, exports.getLiquidityToAllocate)(amountToBorrow, totalBorrowWei, totalSupplyWei, marketData.targetBorrowUtilization, marketData.reallocatableLiquidityAssets);
295
+ const vaultTotalAssets = marketData.publicAllocatorSharedLiquidity.reduce((acc, item) => {
296
+ const vaultAddress = item.vault.address;
297
+ acc[vaultAddress] = new decimal_js_1.default(acc[vaultAddress] || '0').add(item.assets).toString();
298
+ return acc;
299
+ }, {});
300
+ const sortedVaults = Object.entries(vaultTotalAssets).sort(([, a], [, b]) => new decimal_js_1.default(b || '0').sub(a || '0').toNumber());
301
+ const withdrawalsPerVault = {};
302
+ let totalReallocated = '0';
303
+ for (const [vaultAddress] of sortedVaults) {
296
304
  if (new decimal_js_1.default(totalReallocated).gte(liquidityToAllocate))
297
305
  break;
298
- const itemAmount = item.assets;
299
- const leftToAllocate = new decimal_js_1.default(liquidityToAllocate).sub(totalReallocated).toString();
300
- const amountToTake = new decimal_js_1.default(itemAmount).lt(leftToAllocate) ? itemAmount : leftToAllocate;
301
- totalReallocated = new decimal_js_1.default(totalReallocated).add(amountToTake).toString();
302
- const withdrawal = [
303
- [
304
- item.allocationMarket.loanAsset.address,
305
- (_b = item.allocationMarket.collateralAsset) === null || _b === void 0 ? void 0 : _b.address,
306
- (_c = item.allocationMarket.oracle) === null || _c === void 0 ? void 0 : _c.address,
307
- item.allocationMarket.irmAddress,
308
- item.allocationMarket.lltv,
309
- ],
310
- amountToTake.toString(),
311
- item.allocationMarket.uniqueKey,
312
- ];
313
- if (!withdrawalsPerVault[vaultAddress]) {
314
- withdrawalsPerVault[vaultAddress] = [];
306
+ const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter((item) => (0, utils_1.compareAddresses)(item.vault.address, vaultAddress));
307
+ for (const item of vaultAllocations) {
308
+ if (new decimal_js_1.default(totalReallocated).gte(liquidityToAllocate))
309
+ break;
310
+ const itemAmount = item.assets;
311
+ const leftToAllocate = new decimal_js_1.default(liquidityToAllocate).sub(totalReallocated).toString();
312
+ const amountToTake = new decimal_js_1.default(itemAmount).lt(leftToAllocate) ? itemAmount : leftToAllocate;
313
+ totalReallocated = new decimal_js_1.default(totalReallocated).add(amountToTake).toString();
314
+ const withdrawal = [
315
+ [
316
+ item.allocationMarket.loanAsset.address,
317
+ (_b = item.allocationMarket.collateralAsset) === null || _b === void 0 ? void 0 : _b.address,
318
+ (_c = item.allocationMarket.oracle) === null || _c === void 0 ? void 0 : _c.address,
319
+ item.allocationMarket.irmAddress,
320
+ item.allocationMarket.lltv,
321
+ ],
322
+ amountToTake.toString(),
323
+ item.allocationMarket.uniqueKey,
324
+ ];
325
+ if (!withdrawalsPerVault[vaultAddress]) {
326
+ withdrawalsPerVault[vaultAddress] = [];
327
+ }
328
+ withdrawalsPerVault[vaultAddress].push(withdrawal);
315
329
  }
316
- withdrawalsPerVault[vaultAddress].push(withdrawal);
317
330
  }
331
+ const vaults = Object.keys(withdrawalsPerVault);
332
+ const withdrawals = vaults.map((vaultAddress) => withdrawalsPerVault[vaultAddress].sort((a, b) => a[2].localeCompare(b[2])).map(w => [w[0], w[1]]));
333
+ return {
334
+ vaults,
335
+ withdrawals,
336
+ };
337
+ }
338
+ catch (error) {
339
+ console.error('External API Failure: Morpho blue reallocation', error);
340
+ throw new Error('Failed to fetch reallocation data');
318
341
  }
319
- const vaults = Object.keys(withdrawalsPerVault);
320
- const withdrawals = vaults.map((vaultAddress) => withdrawalsPerVault[vaultAddress].sort((a, b) => a[2].localeCompare(b[2])).map(w => [w[0], w[1]]));
321
- return {
322
- vaults,
323
- withdrawals,
324
- };
325
342
  });
326
343
  exports.getReallocation = getReallocation;
327
344
  const getRewardsForMarket = (marketId_1, ...args_1) => __awaiter(void 0, [marketId_1, ...args_1], void 0, function* (marketId, network = common_1.NetworkNumber.Eth) {
@@ -24,3 +24,4 @@ export declare const isEnabledOnBitmap: (bitmap: number, assetId: number) => big
24
24
  export declare const MAXUINT: string;
25
25
  export declare const isMaxuint: (amount: string) => boolean;
26
26
  export declare const isMainnetNetwork: (network: NetworkNumber) => network is NetworkNumber.Eth;
27
+ export declare const DEFAULT_TIMEOUT = 2000;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.isMainnetNetwork = exports.isMaxuint = exports.MAXUINT = exports.isEnabledOnBitmap = exports.mapRange = exports.bytesToString = exports.ethToWethByAddress = exports.wethToEthByAddress = exports.handleWbtcLegacy = exports.getEthAmountForDecimals = exports.getWeiAmountForDecimals = exports.compareAddresses = exports.isAddress = exports.ADDRESS_REGEX = exports.getAbiItem = exports.wstEthToStEth = exports.stEthToWstEth = exports.wethToEth = exports.ethToWeth = exports.addToArrayIf = exports.addToObjectIf = exports.isLayer2Network = void 0;
6
+ exports.DEFAULT_TIMEOUT = exports.isMainnetNetwork = exports.isMaxuint = exports.MAXUINT = exports.isEnabledOnBitmap = exports.mapRange = exports.bytesToString = exports.ethToWethByAddress = exports.wethToEthByAddress = exports.handleWbtcLegacy = exports.getEthAmountForDecimals = exports.getWeiAmountForDecimals = exports.compareAddresses = exports.isAddress = exports.ADDRESS_REGEX = exports.getAbiItem = exports.wstEthToStEth = exports.stEthToWstEth = exports.wethToEth = exports.ethToWeth = exports.addToArrayIf = exports.addToObjectIf = exports.isLayer2Network = void 0;
7
7
  const decimal_js_1 = __importDefault(require("decimal.js"));
8
8
  const tokens_1 = require("@defisaver/tokens");
9
9
  const common_1 = require("../types/common");
@@ -63,3 +63,4 @@ const isMaxuint = (amount) => (0, exports.compareAddresses)(exports.MAXUINT, amo
63
63
  exports.isMaxuint = isMaxuint;
64
64
  const isMainnetNetwork = (network) => network === common_1.NetworkNumber.Eth;
65
65
  exports.isMainnetNetwork = isMainnetNetwork;
66
+ exports.DEFAULT_TIMEOUT = 2000; // 2 seconds
@@ -16,34 +16,54 @@ exports.calculateNetApy = exports.isEligibleForEthenaUSDeRewards = exports.calcu
16
16
  const decimal_js_1 = __importDefault(require("decimal.js"));
17
17
  const memoizee_1 = __importDefault(require("memoizee"));
18
18
  const constants_1 = require("../constants");
19
+ const utils_1 = require("../services/utils");
19
20
  const getSsrApy = () => __awaiter(void 0, void 0, void 0, function* () {
20
- const res = yield fetch('https://fe.defisaver.com/api/sky/data');
21
- const data = yield res.json();
22
- return new decimal_js_1.default(data.data.skyData[0].sky_savings_rate_apy).mul(100).toString();
21
+ try {
22
+ const res = yield fetch('https://fe.defisaver.com/api/sky/data', { signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT) });
23
+ const data = yield res.json();
24
+ return new decimal_js_1.default(data.data.skyData[0].sky_savings_rate_apy).mul(100).toString();
25
+ }
26
+ catch (e) {
27
+ console.error('External API Failure: Failed to fetch SSR APY from external API', e);
28
+ return '0';
29
+ }
23
30
  });
24
31
  const getSuperOETHApy = () => __awaiter(void 0, void 0, void 0, function* () {
25
- const res = yield fetch('https://origin.squids.live/origin-squid/graphql', {
26
- method: 'POST',
27
- headers: {
28
- 'Content-Type': 'application/json',
29
- },
30
- body: JSON.stringify({
31
- query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
32
- variables: {
33
- token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
34
- chainId: 8453,
32
+ try {
33
+ const res = yield fetch('https://origin.squids.live/origin-squid/graphql', {
34
+ method: 'POST',
35
+ headers: {
36
+ 'Content-Type': 'application/json',
35
37
  },
36
- }),
37
- });
38
- const data = yield res.json();
39
- return new decimal_js_1.default(data.data.oTokenApies[0].apy).mul(100).toString();
38
+ body: JSON.stringify({
39
+ query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
40
+ variables: {
41
+ token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
42
+ chainId: 8453,
43
+ },
44
+ }),
45
+ signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT),
46
+ });
47
+ const data = yield res.json();
48
+ return new decimal_js_1.default(data.data.oTokenApies[0].apy).mul(100).toString();
49
+ }
50
+ catch (e) {
51
+ console.error('External API Failure: Failed to fetch Super OETH APY from external API', e);
52
+ return '0';
53
+ }
40
54
  });
41
55
  const getApyFromDfsApi = (asset) => __awaiter(void 0, void 0, void 0, function* () {
42
- const res = yield fetch(`https://fe.defisaver.com/api/staking/apy?asset=${asset}`);
43
- if (!res.ok)
44
- throw new Error(`Failed to fetch APY for ${asset}`);
45
- const data = yield res.json();
46
- return String(data.apy);
56
+ try {
57
+ const res = yield fetch(`https://fe.defisaver.com/api/staking/apy?asset=${asset}`, { signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT) });
58
+ if (!res.ok)
59
+ throw new Error(`Failed to fetch APY for ${asset}`);
60
+ const data = yield res.json();
61
+ return String(data.apy);
62
+ }
63
+ catch (e) {
64
+ console.error(`External API Failure: Failed to fetch APY for ${asset} from DFS API`, e);
65
+ return '0';
66
+ }
47
67
  });
48
68
  exports.STAKING_ASSETS = ['cbETH', 'wstETH', 'cbETH', 'rETH', 'sDAI', 'weETH', 'sUSDe', 'osETH', 'ezETH', 'ETHx', 'rsETH', 'pufETH', 'wrsETH', 'wsuperOETHb', 'sUSDS', 'PT eUSDe May', 'PT sUSDe July', 'PT USDe July', 'PT eUSDe Aug', 'tETH', 'PT sUSDe Sep', 'PT USDe Sep'];
49
69
  exports.getStakingApy = (0, memoizee_1.default)((asset) => __awaiter(void 0, void 0, void 0, function* () {
@@ -50,7 +50,9 @@ export interface CompoundV2AssetData extends CompoundAssetData {
50
50
  export interface CompoundV3AssetData extends CompoundAssetData {
51
51
  borrowCollateralFactor: string;
52
52
  liquidateCollateralFactor: string;
53
+ liquidationFactor: string;
53
54
  minDebt: string;
55
+ supplyReserved: string;
54
56
  liquidationRatio: string;
55
57
  supplyCap: string;
56
58
  priceInBaseAsset: string;
@@ -12,7 +12,7 @@ import { assetAmountInEth, getAssetInfo, getAssetInfoByAddress, } from '@defisav
12
12
  import { NetworkNumber } from '../types/common';
13
13
  import { FluidVaultType, } from '../types';
14
14
  import { BTCPriceFeedContractViem, DFSFeedRegistryContractViem, ETHPriceFeedContractViem, FeedRegistryContractViem, FluidViewContractViem, } from '../contracts';
15
- import { compareAddresses, getEthAmountForDecimals, isMainnetNetwork } from '../services/utils';
15
+ import { compareAddresses, DEFAULT_TIMEOUT, getEthAmountForDecimals, isMainnetNetwork, } from '../services/utils';
16
16
  import { getFluidAggregatedData, mergeAssetData, mergeUsedAssets, parseDexBorrowData, parseDexSupplyData, } from '../helpers/fluidHelpers';
17
17
  import { getFluidMarketInfoById, getFluidVersionsDataForNetwork, getFTokenAddress } from '../markets';
18
18
  import { USD_QUOTE, ZERO_ADDRESS } from '../constants';
@@ -193,9 +193,10 @@ const getAdditionalMarketRateForDex = (token1PerShare, token0PerShare, incentive
193
193
  const getTradingApy = (poolAddress) => __awaiter(void 0, void 0, void 0, function* () {
194
194
  let res;
195
195
  try {
196
- res = yield fetch(`https://api.fluid.instadapp.io/v2/1/dexes/${poolAddress}/apy`, { signal: AbortSignal.timeout(2000) });
196
+ res = yield fetch(`https://api.fluid.instadapp.io/v2/1/dexes/${poolAddress}/apy`, { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
197
197
  }
198
198
  catch (e) {
199
+ console.error('External API Failure: Fluid Trading Apy');
199
200
  return '0';
200
201
  }
201
202
  if (!res.ok) {
@@ -32,14 +32,14 @@ export const formatMarketData = (data, network, baseAssetPrice) => {
32
32
  const assetInfo = getAssetInfoByAddress(data.tokenAddr, network);
33
33
  const isWETH = assetInfo.symbol === 'WETH';
34
34
  const price = getEthAmountForDecimals(data.price, 8);
35
- return (Object.assign(Object.assign({}, data), { priceInBaseAsset: getEthAmountForDecimals(data.price, 8), price: new Dec(price).mul(baseAssetPrice).toString(), collateralFactor: getEthAmountForDecimals(data.borrowCollateralFactor, 18), liquidationRatio: getEthAmountForDecimals(data.liquidateCollateralFactor, 18), supplyCap: getEthAmountForDecimals(data.supplyCap, assetInfo.decimals), totalSupply: getEthAmountForDecimals(data.totalSupply, assetInfo.decimals), symbol: isWETH ? 'ETH' : assetInfo.symbol, supplyRate: '0', borrowRate: '0', canBeBorrowed: false, canBeSupplied: true }));
35
+ return (Object.assign(Object.assign({}, data), { borrowCollateralFactor: data.borrowCollateralFactor.toString(), liquidateCollateralFactor: data.liquidateCollateralFactor.toString(), liquidationFactor: data.liquidationFactor.toString(), supplyReserved: data.supplyReserved.toString(), priceInBaseAsset: getEthAmountForDecimals(data.price, 8), price: new Dec(price).mul(baseAssetPrice).toString(), collateralFactor: getEthAmountForDecimals(data.borrowCollateralFactor, 18), liquidationRatio: getEthAmountForDecimals(data.liquidateCollateralFactor, 18), supplyCap: getEthAmountForDecimals(data.supplyCap, assetInfo.decimals), totalSupply: getEthAmountForDecimals(data.totalSupply, assetInfo.decimals), symbol: isWETH ? 'ETH' : assetInfo.symbol, supplyRate: '0', borrowRate: '0', canBeBorrowed: false, canBeSupplied: true }));
36
36
  };
37
37
  // TODO: maybe not hardcode decimals
38
38
  export const formatBaseData = (data, network, baseAssetPrice) => {
39
39
  const assetInfo = getAssetInfoByAddress(data.tokenAddr, network);
40
40
  const totalSupply = getEthAmountForDecimals(new Dec(data.totalSupply).mul(data.supplyIndex).toString(), 15 + assetInfo.decimals);
41
41
  const totalBorrow = getEthAmountForDecimals(new Dec(data.totalBorrow).mul(data.borrowIndex).toString(), 15 + assetInfo.decimals);
42
- return (Object.assign(Object.assign({}, data), { supplyRate: aprToApy(new Dec(data.supplyRate).div(1e18).mul(SECONDS_PER_YEAR).mul(100)
42
+ return (Object.assign(Object.assign({}, data), { baseBorrowMin: data.baseBorrowMin.toString(), baseTrackingBorrowRewardsSpeed: data.baseTrackingBorrowRewardsSpeed.toString(), baseTrackingSupplyRewardsSpeed: data.baseTrackingSupplyRewardsSpeed.toString(), borrowIndex: data.borrowIndex.toString(), supplyIndex: data.supplyIndex.toString(), trackingBorrowIndex: data.trackingBorrowIndex.toString(), trackingSupplyIndex: data.trackingSupplyIndex.toString(), supplyRate: aprToApy(new Dec(data.supplyRate).div(1e18).mul(SECONDS_PER_YEAR).mul(100)
43
43
  .toString()), borrowRate: aprToApy(new Dec(data.borrowRate).div(1e18).mul(SECONDS_PER_YEAR).mul(100)
44
44
  .toString()), utilization: getEthAmountForDecimals(data.utilization, 16), // utilization is totalSupply/totalBorrow in 1e18, but we need % so when we mul with 100 it's 16 decimals
45
45
  totalSupply,
@@ -14,7 +14,7 @@ import { calculateNetApy } from '../../staking';
14
14
  import { NetworkNumber, } from '../../types/common';
15
15
  import { borrowOperations, SECONDS_PER_YEAR, WAD } from '../../constants';
16
16
  import { MorphoBlueViewContractViem } from '../../contracts';
17
- import { compareAddresses } from '../../services/utils';
17
+ import { compareAddresses, DEFAULT_TIMEOUT } from '../../services/utils';
18
18
  import { getViemProvider } from '../../services/viem';
19
19
  export const getMorphoBlueAggregatedPositionData = ({ usedAssets, assetsData, marketInfo }) => {
20
20
  var _a, _b, _c, _d, _e, _f;
@@ -194,19 +194,29 @@ const REWARDS_QUERY = `
194
194
  */
195
195
  export const getReallocatableLiquidity = (marketId_1, ...args_1) => __awaiter(void 0, [marketId_1, ...args_1], void 0, function* (marketId, network = NetworkNumber.Eth) {
196
196
  var _a;
197
- const response = yield fetch(API_URL, {
198
- method: 'POST',
199
- headers: { 'Content-Type': 'application/json' },
200
- body: JSON.stringify({
201
- query: MARKET_QUERY,
202
- variables: { uniqueKey: marketId, chainId: network },
203
- }),
204
- });
205
- const data = yield response.json();
206
- const marketData = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.marketByUniqueKey;
207
- if (!marketData)
208
- throw new Error('Market data not found');
209
- return { reallocatableLiquidity: marketData.reallocatableLiquidityAssets, targetBorrowUtilization: marketData.targetBorrowUtilization };
197
+ try {
198
+ const response = yield fetch(API_URL, {
199
+ method: 'POST',
200
+ headers: { 'Content-Type': 'application/json' },
201
+ body: JSON.stringify({
202
+ query: MARKET_QUERY,
203
+ variables: { uniqueKey: marketId, chainId: network },
204
+ }),
205
+ signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
206
+ });
207
+ const data = yield response.json();
208
+ const marketData = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.marketByUniqueKey;
209
+ if (!marketData)
210
+ throw new Error('Market data not found');
211
+ return {
212
+ reallocatableLiquidity: marketData.reallocatableLiquidityAssets,
213
+ targetBorrowUtilization: marketData.targetBorrowUtilization,
214
+ };
215
+ }
216
+ catch (error) {
217
+ console.error('External API Failure: Morpho blue reallocatable liquidity', error);
218
+ throw new Error('Failed to fetch reallocatable liquidity');
219
+ }
210
220
  });
211
221
  /**
212
222
  * Get liquidity to allocate for a given amount to borrow.
@@ -245,71 +255,78 @@ export const getLiquidityToAllocate = (amountToBorrow, totalBorrow, totalSupply,
245
255
  */
246
256
  export const getReallocation = (market_1, assetsData_1, amountToBorrow_1, ...args_1) => __awaiter(void 0, [market_1, assetsData_1, amountToBorrow_1, ...args_1], void 0, function* (market, assetsData, amountToBorrow, network = NetworkNumber.Eth) {
247
257
  var _a, _b, _c;
248
- const { marketId, loanToken } = market;
249
- const response = yield fetch(API_URL, {
250
- method: 'POST',
251
- headers: { 'Content-Type': 'application/json' },
252
- body: JSON.stringify({
253
- query: MARKET_QUERY,
254
- variables: { uniqueKey: marketId, chainId: network },
255
- }),
256
- });
257
- const data = yield response.json();
258
- const marketData = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.marketByUniqueKey;
259
- if (!marketData)
260
- throw new Error('Market data not found');
261
- const loanAssetInfo = getAssetInfoByAddress(loanToken, network);
262
- const { totalBorrow, totalSupply } = assetsData[loanAssetInfo.symbol] || { totalBorrow: '0', totalSupply: '0' };
263
- const totalBorrowWei = assetAmountInWei(totalBorrow, loanAssetInfo.symbol);
264
- const totalSupplyWei = assetAmountInWei(totalSupply, loanAssetInfo.symbol);
265
- const newTotalBorrowAssets = new Dec(totalBorrowWei).add(amountToBorrow).toString();
266
- const newUtil = new Dec(newTotalBorrowAssets).div(totalSupplyWei).toString();
267
- const newUtilScaled = new Dec(newUtil).mul(1e18).toString();
268
- if (new Dec(newUtilScaled).lt(marketData.targetBorrowUtilization))
269
- return { vaults: [], withdrawals: [] };
270
- const liquidityToAllocate = getLiquidityToAllocate(amountToBorrow, totalBorrowWei, totalSupplyWei, marketData.targetBorrowUtilization, marketData.reallocatableLiquidityAssets);
271
- const vaultTotalAssets = marketData.publicAllocatorSharedLiquidity.reduce((acc, item) => {
272
- const vaultAddress = item.vault.address;
273
- acc[vaultAddress] = new Dec(acc[vaultAddress] || '0').add(item.assets).toString();
274
- return acc;
275
- }, {});
276
- const sortedVaults = Object.entries(vaultTotalAssets).sort(([, a], [, b]) => new Dec(b || '0').sub(a || '0').toNumber());
277
- const withdrawalsPerVault = {};
278
- let totalReallocated = '0';
279
- for (const [vaultAddress] of sortedVaults) {
280
- if (new Dec(totalReallocated).gte(liquidityToAllocate))
281
- break;
282
- const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter((item) => compareAddresses(item.vault.address, vaultAddress));
283
- for (const item of vaultAllocations) {
258
+ try {
259
+ const { marketId, loanToken } = market;
260
+ const response = yield fetch(API_URL, {
261
+ method: 'POST',
262
+ headers: { 'Content-Type': 'application/json' },
263
+ body: JSON.stringify({
264
+ query: MARKET_QUERY,
265
+ variables: { uniqueKey: marketId, chainId: network },
266
+ }),
267
+ signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
268
+ });
269
+ const data = yield response.json();
270
+ const marketData = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.marketByUniqueKey;
271
+ if (!marketData)
272
+ throw new Error('Market data not found');
273
+ const loanAssetInfo = getAssetInfoByAddress(loanToken, network);
274
+ const { totalBorrow, totalSupply } = assetsData[loanAssetInfo.symbol] || { totalBorrow: '0', totalSupply: '0' };
275
+ const totalBorrowWei = assetAmountInWei(totalBorrow, loanAssetInfo.symbol);
276
+ const totalSupplyWei = assetAmountInWei(totalSupply, loanAssetInfo.symbol);
277
+ const newTotalBorrowAssets = new Dec(totalBorrowWei).add(amountToBorrow).toString();
278
+ const newUtil = new Dec(newTotalBorrowAssets).div(totalSupplyWei).toString();
279
+ const newUtilScaled = new Dec(newUtil).mul(1e18).toString();
280
+ if (new Dec(newUtilScaled).lt(marketData.targetBorrowUtilization))
281
+ return { vaults: [], withdrawals: [] };
282
+ const liquidityToAllocate = getLiquidityToAllocate(amountToBorrow, totalBorrowWei, totalSupplyWei, marketData.targetBorrowUtilization, marketData.reallocatableLiquidityAssets);
283
+ const vaultTotalAssets = marketData.publicAllocatorSharedLiquidity.reduce((acc, item) => {
284
+ const vaultAddress = item.vault.address;
285
+ acc[vaultAddress] = new Dec(acc[vaultAddress] || '0').add(item.assets).toString();
286
+ return acc;
287
+ }, {});
288
+ const sortedVaults = Object.entries(vaultTotalAssets).sort(([, a], [, b]) => new Dec(b || '0').sub(a || '0').toNumber());
289
+ const withdrawalsPerVault = {};
290
+ let totalReallocated = '0';
291
+ for (const [vaultAddress] of sortedVaults) {
284
292
  if (new Dec(totalReallocated).gte(liquidityToAllocate))
285
293
  break;
286
- const itemAmount = item.assets;
287
- const leftToAllocate = new Dec(liquidityToAllocate).sub(totalReallocated).toString();
288
- const amountToTake = new Dec(itemAmount).lt(leftToAllocate) ? itemAmount : leftToAllocate;
289
- totalReallocated = new Dec(totalReallocated).add(amountToTake).toString();
290
- const withdrawal = [
291
- [
292
- item.allocationMarket.loanAsset.address,
293
- (_b = item.allocationMarket.collateralAsset) === null || _b === void 0 ? void 0 : _b.address,
294
- (_c = item.allocationMarket.oracle) === null || _c === void 0 ? void 0 : _c.address,
295
- item.allocationMarket.irmAddress,
296
- item.allocationMarket.lltv,
297
- ],
298
- amountToTake.toString(),
299
- item.allocationMarket.uniqueKey,
300
- ];
301
- if (!withdrawalsPerVault[vaultAddress]) {
302
- withdrawalsPerVault[vaultAddress] = [];
294
+ const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter((item) => compareAddresses(item.vault.address, vaultAddress));
295
+ for (const item of vaultAllocations) {
296
+ if (new Dec(totalReallocated).gte(liquidityToAllocate))
297
+ break;
298
+ const itemAmount = item.assets;
299
+ const leftToAllocate = new Dec(liquidityToAllocate).sub(totalReallocated).toString();
300
+ const amountToTake = new Dec(itemAmount).lt(leftToAllocate) ? itemAmount : leftToAllocate;
301
+ totalReallocated = new Dec(totalReallocated).add(amountToTake).toString();
302
+ const withdrawal = [
303
+ [
304
+ item.allocationMarket.loanAsset.address,
305
+ (_b = item.allocationMarket.collateralAsset) === null || _b === void 0 ? void 0 : _b.address,
306
+ (_c = item.allocationMarket.oracle) === null || _c === void 0 ? void 0 : _c.address,
307
+ item.allocationMarket.irmAddress,
308
+ item.allocationMarket.lltv,
309
+ ],
310
+ amountToTake.toString(),
311
+ item.allocationMarket.uniqueKey,
312
+ ];
313
+ if (!withdrawalsPerVault[vaultAddress]) {
314
+ withdrawalsPerVault[vaultAddress] = [];
315
+ }
316
+ withdrawalsPerVault[vaultAddress].push(withdrawal);
303
317
  }
304
- withdrawalsPerVault[vaultAddress].push(withdrawal);
305
318
  }
319
+ const vaults = Object.keys(withdrawalsPerVault);
320
+ const withdrawals = vaults.map((vaultAddress) => withdrawalsPerVault[vaultAddress].sort((a, b) => a[2].localeCompare(b[2])).map(w => [w[0], w[1]]));
321
+ return {
322
+ vaults,
323
+ withdrawals,
324
+ };
325
+ }
326
+ catch (error) {
327
+ console.error('External API Failure: Morpho blue reallocation', error);
328
+ throw new Error('Failed to fetch reallocation data');
306
329
  }
307
- const vaults = Object.keys(withdrawalsPerVault);
308
- const withdrawals = vaults.map((vaultAddress) => withdrawalsPerVault[vaultAddress].sort((a, b) => a[2].localeCompare(b[2])).map(w => [w[0], w[1]]));
309
- return {
310
- vaults,
311
- withdrawals,
312
- };
313
330
  });
314
331
  export const getRewardsForMarket = (marketId_1, ...args_1) => __awaiter(void 0, [marketId_1, ...args_1], void 0, function* (marketId, network = NetworkNumber.Eth) {
315
332
  var _a;
@@ -24,3 +24,4 @@ export declare const isEnabledOnBitmap: (bitmap: number, assetId: number) => big
24
24
  export declare const MAXUINT: string;
25
25
  export declare const isMaxuint: (amount: string) => boolean;
26
26
  export declare const isMainnetNetwork: (network: NetworkNumber) => network is NetworkNumber.Eth;
27
+ export declare const DEFAULT_TIMEOUT = 2000;
@@ -37,3 +37,4 @@ export const isEnabledOnBitmap = (bitmap, assetId) => (BigInt(bitmap) >> BigInt(
37
37
  export const MAXUINT = '115792089237316195423570985008687907853269984665640564039457584007913129639935';
38
38
  export const isMaxuint = (amount) => compareAddresses(MAXUINT, amount);
39
39
  export const isMainnetNetwork = (network) => network === NetworkNumber.Eth;
40
+ export const DEFAULT_TIMEOUT = 2000; // 2 seconds
@@ -10,34 +10,54 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import Dec from 'decimal.js';
11
11
  import memoize from 'memoizee';
12
12
  import { BLOCKS_IN_A_YEAR } from '../constants';
13
+ import { DEFAULT_TIMEOUT } from '../services/utils';
13
14
  const getSsrApy = () => __awaiter(void 0, void 0, void 0, function* () {
14
- const res = yield fetch('https://fe.defisaver.com/api/sky/data');
15
- const data = yield res.json();
16
- return new Dec(data.data.skyData[0].sky_savings_rate_apy).mul(100).toString();
15
+ try {
16
+ const res = yield fetch('https://fe.defisaver.com/api/sky/data', { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
17
+ const data = yield res.json();
18
+ return new Dec(data.data.skyData[0].sky_savings_rate_apy).mul(100).toString();
19
+ }
20
+ catch (e) {
21
+ console.error('External API Failure: Failed to fetch SSR APY from external API', e);
22
+ return '0';
23
+ }
17
24
  });
18
25
  const getSuperOETHApy = () => __awaiter(void 0, void 0, void 0, function* () {
19
- const res = yield fetch('https://origin.squids.live/origin-squid/graphql', {
20
- method: 'POST',
21
- headers: {
22
- 'Content-Type': 'application/json',
23
- },
24
- body: JSON.stringify({
25
- query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
26
- variables: {
27
- token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
28
- chainId: 8453,
26
+ try {
27
+ const res = yield fetch('https://origin.squids.live/origin-squid/graphql', {
28
+ method: 'POST',
29
+ headers: {
30
+ 'Content-Type': 'application/json',
29
31
  },
30
- }),
31
- });
32
- const data = yield res.json();
33
- return new Dec(data.data.oTokenApies[0].apy).mul(100).toString();
32
+ body: JSON.stringify({
33
+ query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
34
+ variables: {
35
+ token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
36
+ chainId: 8453,
37
+ },
38
+ }),
39
+ signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
40
+ });
41
+ const data = yield res.json();
42
+ return new Dec(data.data.oTokenApies[0].apy).mul(100).toString();
43
+ }
44
+ catch (e) {
45
+ console.error('External API Failure: Failed to fetch Super OETH APY from external API', e);
46
+ return '0';
47
+ }
34
48
  });
35
49
  const getApyFromDfsApi = (asset) => __awaiter(void 0, void 0, void 0, function* () {
36
- const res = yield fetch(`https://fe.defisaver.com/api/staking/apy?asset=${asset}`);
37
- if (!res.ok)
38
- throw new Error(`Failed to fetch APY for ${asset}`);
39
- const data = yield res.json();
40
- return String(data.apy);
50
+ try {
51
+ const res = yield fetch(`https://fe.defisaver.com/api/staking/apy?asset=${asset}`, { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
52
+ if (!res.ok)
53
+ throw new Error(`Failed to fetch APY for ${asset}`);
54
+ const data = yield res.json();
55
+ return String(data.apy);
56
+ }
57
+ catch (e) {
58
+ console.error(`External API Failure: Failed to fetch APY for ${asset} from DFS API`, e);
59
+ return '0';
60
+ }
41
61
  });
42
62
  export const STAKING_ASSETS = ['cbETH', 'wstETH', 'cbETH', 'rETH', 'sDAI', 'weETH', 'sUSDe', 'osETH', 'ezETH', 'ETHx', 'rsETH', 'pufETH', 'wrsETH', 'wsuperOETHb', 'sUSDS', 'PT eUSDe May', 'PT sUSDe July', 'PT USDe July', 'PT eUSDe Aug', 'tETH', 'PT sUSDe Sep', 'PT USDe Sep'];
43
63
  export const getStakingApy = memoize((asset) => __awaiter(void 0, void 0, void 0, function* () {
@@ -50,7 +50,9 @@ export interface CompoundV2AssetData extends CompoundAssetData {
50
50
  export interface CompoundV3AssetData extends CompoundAssetData {
51
51
  borrowCollateralFactor: string;
52
52
  liquidateCollateralFactor: string;
53
+ liquidationFactor: string;
53
54
  minDebt: string;
55
+ supplyReserved: string;
54
56
  liquidationRatio: string;
55
57
  supplyCap: string;
56
58
  priceInBaseAsset: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defisaver/positions-sdk",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "",
5
5
  "main": "./cjs/index.js",
6
6
  "module": "./esm/index.js",
@@ -22,7 +22,9 @@ import {
22
22
  import {
23
23
  BTCPriceFeedContractViem, DFSFeedRegistryContractViem, ETHPriceFeedContractViem, FeedRegistryContractViem, FluidViewContractViem,
24
24
  } from '../contracts';
25
- import { compareAddresses, getEthAmountForDecimals, isMainnetNetwork } from '../services/utils';
25
+ import {
26
+ compareAddresses, DEFAULT_TIMEOUT, getEthAmountForDecimals, isMainnetNetwork,
27
+ } from '../services/utils';
26
28
  import {
27
29
  getFluidAggregatedData,
28
30
  mergeAssetData,
@@ -256,8 +258,10 @@ const getAdditionalMarketRateForDex = (token1PerShare: string, token0PerShare: s
256
258
  const getTradingApy = async (poolAddress: EthAddress) => {
257
259
  let res;
258
260
  try {
259
- res = await fetch(`https://api.fluid.instadapp.io/v2/1/dexes/${poolAddress}/apy`, { signal: AbortSignal.timeout(2000) });
261
+ res = await fetch(`https://api.fluid.instadapp.io/v2/1/dexes/${poolAddress}/apy`,
262
+ { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
260
263
  } catch (e) {
264
+ console.error('External API Failure: Fluid Trading Apy');
261
265
  return '0';
262
266
  }
263
267
  if (!res.ok) {
@@ -19,6 +19,10 @@ export const formatMarketData = (data: any, network: NetworkNumber, baseAssetPri
19
19
  const price = getEthAmountForDecimals(data.price, 8);
20
20
  return ({
21
21
  ...data,
22
+ borrowCollateralFactor: data.borrowCollateralFactor.toString(),
23
+ liquidateCollateralFactor: data.liquidateCollateralFactor.toString(),
24
+ liquidationFactor: data.liquidationFactor.toString(),
25
+ supplyReserved: data.supplyReserved.toString(),
22
26
  priceInBaseAsset: getEthAmountForDecimals(data.price, 8),
23
27
  price: new Dec(price).mul(baseAssetPrice).toString(),
24
28
  collateralFactor: getEthAmountForDecimals(data.borrowCollateralFactor, 18),
@@ -40,6 +44,13 @@ export const formatBaseData = (data: any, network: NetworkNumber, baseAssetPrice
40
44
  const totalBorrow = getEthAmountForDecimals(new Dec(data.totalBorrow).mul(data.borrowIndex).toString(), 15 + assetInfo.decimals);
41
45
  return ({
42
46
  ...data,
47
+ baseBorrowMin: data.baseBorrowMin.toString(),
48
+ baseTrackingBorrowRewardsSpeed: data.baseTrackingBorrowRewardsSpeed.toString(),
49
+ baseTrackingSupplyRewardsSpeed: data.baseTrackingSupplyRewardsSpeed.toString(),
50
+ borrowIndex: data.borrowIndex.toString(),
51
+ supplyIndex: data.supplyIndex.toString(),
52
+ trackingBorrowIndex: data.trackingBorrowIndex.toString(),
53
+ trackingSupplyIndex: data.trackingSupplyIndex.toString(),
43
54
  supplyRate: aprToApy(new Dec(data.supplyRate).div(1e18).mul(SECONDS_PER_YEAR).mul(100)
44
55
  .toString()),
45
56
  borrowRate: aprToApy(new Dec(data.borrowRate).div(1e18).mul(SECONDS_PER_YEAR).mul(100)
@@ -14,7 +14,7 @@ import {
14
14
  } from '../../types';
15
15
  import { borrowOperations, SECONDS_PER_YEAR, WAD } from '../../constants';
16
16
  import { MorphoBlueViewContractViem } from '../../contracts';
17
- import { compareAddresses } from '../../services/utils';
17
+ import { compareAddresses, DEFAULT_TIMEOUT } from '../../services/utils';
18
18
  import { getViemProvider } from '../../services/viem';
19
19
 
20
20
  export const getMorphoBlueAggregatedPositionData = ({ usedAssets, assetsData, marketInfo }: { usedAssets: MMUsedAssets, assetsData: MorphoBlueAssetsData, marketInfo: MorphoBlueMarketInfo }): MorphoBlueAggregatedPositionData => {
@@ -214,21 +214,30 @@ const REWARDS_QUERY = `
214
214
  * @returns The reallocatable liquidity and target borrow utilization
215
215
  */
216
216
  export const getReallocatableLiquidity = async (marketId: string, network: NetworkNumber = NetworkNumber.Eth): Promise<{ reallocatableLiquidity: string, targetBorrowUtilization: string }> => {
217
- const response = await fetch(API_URL, {
218
- method: 'POST',
219
- headers: { 'Content-Type': 'application/json' },
220
- body: JSON.stringify({
221
- query: MARKET_QUERY,
222
- variables: { uniqueKey: marketId, chainId: network },
223
- }),
224
- });
225
-
226
- const data: { data: { marketByUniqueKey: MorphoBlueRealloactionMarketData } } = await response.json();
227
- const marketData: MorphoBlueRealloactionMarketData = data?.data?.marketByUniqueKey;
228
-
229
- if (!marketData) throw new Error('Market data not found');
217
+ try {
218
+ const response = await fetch(API_URL, {
219
+ method: 'POST',
220
+ headers: { 'Content-Type': 'application/json' },
221
+ body: JSON.stringify({
222
+ query: MARKET_QUERY,
223
+ variables: { uniqueKey: marketId, chainId: network },
224
+ }),
225
+ signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
226
+ });
227
+
228
+ const data: { data: { marketByUniqueKey: MorphoBlueRealloactionMarketData } } = await response.json();
229
+ const marketData: MorphoBlueRealloactionMarketData = data?.data?.marketByUniqueKey;
230
+
231
+ if (!marketData) throw new Error('Market data not found');
230
232
 
231
- return { reallocatableLiquidity: marketData.reallocatableLiquidityAssets, targetBorrowUtilization: marketData.targetBorrowUtilization };
233
+ return {
234
+ reallocatableLiquidity: marketData.reallocatableLiquidityAssets,
235
+ targetBorrowUtilization: marketData.targetBorrowUtilization,
236
+ };
237
+ } catch (error) {
238
+ console.error('External API Failure: Morpho blue reallocatable liquidity', error);
239
+ throw new Error('Failed to fetch reallocatable liquidity');
240
+ }
232
241
  };
233
242
 
234
243
  /**
@@ -269,90 +278,96 @@ export const getLiquidityToAllocate = (amountToBorrow: string, totalBorrow: stri
269
278
  * @returns The vaults and withdrawals needed to reallocate liquidity
270
279
  */
271
280
  export const getReallocation = async (market: MorphoBlueMarketData, assetsData: MorphoBlueAssetsData, amountToBorrow: string, network: NetworkNumber = NetworkNumber.Eth): Promise<{ vaults: string[], withdrawals: (string | string[])[][][] }> => {
272
- const { marketId, loanToken } = market;
273
- const response = await fetch(API_URL, {
274
- method: 'POST',
275
- headers: { 'Content-Type': 'application/json' },
276
- body: JSON.stringify({
277
- query: MARKET_QUERY,
278
- variables: { uniqueKey: marketId, chainId: network },
279
- }),
280
- });
281
-
282
- const data: { data: { marketByUniqueKey: MorphoBlueRealloactionMarketData } } = await response.json();
283
- const marketData: MorphoBlueRealloactionMarketData = data?.data?.marketByUniqueKey;
284
-
285
- if (!marketData) throw new Error('Market data not found');
286
-
287
- const loanAssetInfo = getAssetInfoByAddress(loanToken, network);
288
- const { totalBorrow, totalSupply } = assetsData[loanAssetInfo.symbol] || { totalBorrow: '0', totalSupply: '0' };
289
- const totalBorrowWei = assetAmountInWei(totalBorrow!, loanAssetInfo.symbol);
290
- const totalSupplyWei = assetAmountInWei(totalSupply!, loanAssetInfo.symbol);
291
-
292
- const newTotalBorrowAssets = new Dec(totalBorrowWei).add(amountToBorrow).toString();
293
-
294
- const newUtil = new Dec(newTotalBorrowAssets).div(totalSupplyWei).toString();
295
- const newUtilScaled = new Dec(newUtil).mul(1e18).toString();
296
-
297
- if (new Dec(newUtilScaled).lt(marketData.targetBorrowUtilization)) return { vaults: [], withdrawals: [] };
298
-
299
- const liquidityToAllocate = getLiquidityToAllocate(amountToBorrow, totalBorrowWei, totalSupplyWei, marketData.targetBorrowUtilization, marketData.reallocatableLiquidityAssets);
300
-
301
- const vaultTotalAssets = marketData.publicAllocatorSharedLiquidity.reduce(
302
- (acc: Record<string, string>, item: MorphoBluePublicAllocatorItem) => {
303
- const vaultAddress = item.vault.address;
304
- acc[vaultAddress] = new Dec(acc[vaultAddress] || '0').add(item.assets).toString();
305
- return acc;
306
- },
307
- {},
308
- );
309
-
310
- const sortedVaults = Object.entries(vaultTotalAssets).sort(
311
- ([, a]: [string, string], [, b]: [string, string]) => new Dec(b || '0').sub(a || '0').toNumber(),
312
- );
313
-
314
- const withdrawalsPerVault: Record<string, [string[], string, string][]> = {};
315
- let totalReallocated = '0';
316
- for (const [vaultAddress] of sortedVaults) {
317
- if (new Dec(totalReallocated).gte(liquidityToAllocate)) break;
281
+ try {
282
+ const { marketId, loanToken } = market;
283
+ const response = await fetch(API_URL, {
284
+ method: 'POST',
285
+ headers: { 'Content-Type': 'application/json' },
286
+ body: JSON.stringify({
287
+ query: MARKET_QUERY,
288
+ variables: { uniqueKey: marketId, chainId: network },
289
+ }),
290
+ signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
291
+ });
292
+
293
+ const data: { data: { marketByUniqueKey: MorphoBlueRealloactionMarketData } } = await response.json();
294
+ const marketData: MorphoBlueRealloactionMarketData = data?.data?.marketByUniqueKey;
295
+
296
+ if (!marketData) throw new Error('Market data not found');
297
+
298
+ const loanAssetInfo = getAssetInfoByAddress(loanToken, network);
299
+ const { totalBorrow, totalSupply } = assetsData[loanAssetInfo.symbol] || { totalBorrow: '0', totalSupply: '0' };
300
+ const totalBorrowWei = assetAmountInWei(totalBorrow!, loanAssetInfo.symbol);
301
+ const totalSupplyWei = assetAmountInWei(totalSupply!, loanAssetInfo.symbol);
302
+
303
+ const newTotalBorrowAssets = new Dec(totalBorrowWei).add(amountToBorrow).toString();
304
+
305
+ const newUtil = new Dec(newTotalBorrowAssets).div(totalSupplyWei).toString();
306
+ const newUtilScaled = new Dec(newUtil).mul(1e18).toString();
307
+
308
+ if (new Dec(newUtilScaled).lt(marketData.targetBorrowUtilization)) return { vaults: [], withdrawals: [] };
309
+
310
+ const liquidityToAllocate = getLiquidityToAllocate(amountToBorrow, totalBorrowWei, totalSupplyWei, marketData.targetBorrowUtilization, marketData.reallocatableLiquidityAssets);
311
+
312
+ const vaultTotalAssets = marketData.publicAllocatorSharedLiquidity.reduce(
313
+ (acc: Record<string, string>, item: MorphoBluePublicAllocatorItem) => {
314
+ const vaultAddress = item.vault.address;
315
+ acc[vaultAddress] = new Dec(acc[vaultAddress] || '0').add(item.assets).toString();
316
+ return acc;
317
+ },
318
+ {},
319
+ );
318
320
 
319
- const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter(
320
- (item: MorphoBluePublicAllocatorItem) => compareAddresses(item.vault.address, vaultAddress),
321
+ const sortedVaults = Object.entries(vaultTotalAssets).sort(
322
+ ([, a]: [string, string], [, b]: [string, string]) => new Dec(b || '0').sub(a || '0').toNumber(),
321
323
  );
322
- for (const item of vaultAllocations) {
324
+
325
+ const withdrawalsPerVault: Record<string, [string[], string, string][]> = {};
326
+ let totalReallocated = '0';
327
+ for (const [vaultAddress] of sortedVaults) {
323
328
  if (new Dec(totalReallocated).gte(liquidityToAllocate)) break;
324
- const itemAmount = item.assets;
325
- const leftToAllocate = new Dec(liquidityToAllocate).sub(totalReallocated).toString();
326
- const amountToTake = new Dec(itemAmount).lt(leftToAllocate) ? itemAmount : leftToAllocate;
327
- totalReallocated = new Dec(totalReallocated).add(amountToTake).toString();
328
- const withdrawal: [string[], string, string] = [
329
- [
330
- item.allocationMarket.loanAsset.address,
331
- item.allocationMarket.collateralAsset?.address,
332
- item.allocationMarket.oracle?.address,
333
- item.allocationMarket.irmAddress,
334
- item.allocationMarket.lltv,
335
- ],
336
- amountToTake.toString(),
337
- item.allocationMarket.uniqueKey,
338
- ];
339
- if (!withdrawalsPerVault[vaultAddress]) {
340
- withdrawalsPerVault[vaultAddress] = [];
329
+
330
+ const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter(
331
+ (item: MorphoBluePublicAllocatorItem) => compareAddresses(item.vault.address, vaultAddress),
332
+ );
333
+ for (const item of vaultAllocations) {
334
+ if (new Dec(totalReallocated).gte(liquidityToAllocate)) break;
335
+ const itemAmount = item.assets;
336
+ const leftToAllocate = new Dec(liquidityToAllocate).sub(totalReallocated).toString();
337
+ const amountToTake = new Dec(itemAmount).lt(leftToAllocate) ? itemAmount : leftToAllocate;
338
+ totalReallocated = new Dec(totalReallocated).add(amountToTake).toString();
339
+ const withdrawal: [string[], string, string] = [
340
+ [
341
+ item.allocationMarket.loanAsset.address,
342
+ item.allocationMarket.collateralAsset?.address,
343
+ item.allocationMarket.oracle?.address,
344
+ item.allocationMarket.irmAddress,
345
+ item.allocationMarket.lltv,
346
+ ],
347
+ amountToTake.toString(),
348
+ item.allocationMarket.uniqueKey,
349
+ ];
350
+ if (!withdrawalsPerVault[vaultAddress]) {
351
+ withdrawalsPerVault[vaultAddress] = [];
352
+ }
353
+ withdrawalsPerVault[vaultAddress].push(withdrawal);
341
354
  }
342
- withdrawalsPerVault[vaultAddress].push(withdrawal);
343
355
  }
344
- }
345
356
 
346
- const vaults = Object.keys(withdrawalsPerVault);
347
- const withdrawals = vaults.map(
348
- (vaultAddress) => withdrawalsPerVault[vaultAddress].sort(
349
- (a, b) => a[2].localeCompare(b[2]),
350
- ).map(w => [w[0], w[1]]),
351
- );
352
- return {
353
- vaults,
354
- withdrawals,
355
- };
357
+ const vaults = Object.keys(withdrawalsPerVault);
358
+ const withdrawals = vaults.map(
359
+ (vaultAddress) => withdrawalsPerVault[vaultAddress].sort(
360
+ (a, b) => a[2].localeCompare(b[2]),
361
+ ).map(w => [w[0], w[1]]),
362
+ );
363
+ return {
364
+ vaults,
365
+ withdrawals,
366
+ };
367
+ } catch (error) {
368
+ console.error('External API Failure: Morpho blue reallocation', error);
369
+ throw new Error('Failed to fetch reallocation data');
370
+ }
356
371
  };
357
372
 
358
373
  export const getRewardsForMarket = async (marketId: string, network: NetworkNumber = NetworkNumber.Eth) => {
@@ -60,3 +60,5 @@ export const MAXUINT:string = '1157920892373161954235709850086879078532699846656
60
60
  export const isMaxuint = (amount: string) => compareAddresses(MAXUINT, amount);
61
61
 
62
62
  export const isMainnetNetwork = (network: NetworkNumber) => network === NetworkNumber.Eth;
63
+
64
+ export const DEFAULT_TIMEOUT = 2000; // 2 seconds
@@ -2,37 +2,56 @@ import Dec from 'decimal.js';
2
2
  import memoize from 'memoizee';
3
3
  import { MMAssetsData, MMUsedAssets } from '../types/common';
4
4
  import { BLOCKS_IN_A_YEAR } from '../constants';
5
+ import { DEFAULT_TIMEOUT } from '../services/utils';
5
6
 
6
7
  const getSsrApy = async () => {
7
- const res = await fetch('https://fe.defisaver.com/api/sky/data');
8
- const data = await res.json();
9
- return new Dec(data.data.skyData[0].sky_savings_rate_apy).mul(100).toString();
8
+ try {
9
+ const res = await fetch('https://fe.defisaver.com/api/sky/data',
10
+ { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
11
+ const data = await res.json();
12
+ return new Dec(data.data.skyData[0].sky_savings_rate_apy).mul(100).toString();
13
+ } catch (e) {
14
+ console.error('External API Failure: Failed to fetch SSR APY from external API', e);
15
+ return '0';
16
+ }
10
17
  };
11
18
 
12
19
  const getSuperOETHApy = async () => {
13
- const res = await fetch('https://origin.squids.live/origin-squid/graphql', {
14
- method: 'POST',
15
- headers: {
16
- 'Content-Type': 'application/json',
17
- },
18
- body: JSON.stringify({
19
- query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
20
- variables: {
21
- token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
22
- chainId: 8453,
20
+ try {
21
+ const res = await fetch('https://origin.squids.live/origin-squid/graphql', {
22
+ method: 'POST',
23
+ headers: {
24
+ 'Content-Type': 'application/json',
23
25
  },
24
- }),
25
- });
26
-
27
- const data = await res.json();
28
- return new Dec(data.data.oTokenApies[0].apy).mul(100).toString();
26
+ body: JSON.stringify({
27
+ query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
28
+ variables: {
29
+ token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
30
+ chainId: 8453,
31
+ },
32
+ }),
33
+ signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
34
+ });
35
+
36
+ const data = await res.json();
37
+ return new Dec(data.data.oTokenApies[0].apy).mul(100).toString();
38
+ } catch (e) {
39
+ console.error('External API Failure: Failed to fetch Super OETH APY from external API', e);
40
+ return '0';
41
+ }
29
42
  };
30
43
 
31
44
  const getApyFromDfsApi = async (asset: string) => {
32
- const res = await fetch(`https://fe.defisaver.com/api/staking/apy?asset=${asset}`);
33
- if (!res.ok) throw new Error(`Failed to fetch APY for ${asset}`);
34
- const data = await res.json();
35
- return String(data.apy);
45
+ try {
46
+ const res = await fetch(`https://fe.defisaver.com/api/staking/apy?asset=${asset}`,
47
+ { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
48
+ if (!res.ok) throw new Error(`Failed to fetch APY for ${asset}`);
49
+ const data = await res.json();
50
+ return String(data.apy);
51
+ } catch (e) {
52
+ console.error(`External API Failure: Failed to fetch APY for ${asset} from DFS API`, e);
53
+ return '0';
54
+ }
36
55
  };
37
56
 
38
57
  export const STAKING_ASSETS = ['cbETH', 'wstETH', 'cbETH', 'rETH', 'sDAI', 'weETH', 'sUSDe', 'osETH', 'ezETH', 'ETHx', 'rsETH', 'pufETH', 'wrsETH', 'wsuperOETHb', 'sUSDS', 'PT eUSDe May', 'PT sUSDe July', 'PT USDe July', 'PT eUSDe Aug', 'tETH', 'PT sUSDe Sep', 'PT USDe Sep'];
@@ -63,7 +63,9 @@ export interface CompoundV2AssetData extends CompoundAssetData {
63
63
  export interface CompoundV3AssetData extends CompoundAssetData {
64
64
  borrowCollateralFactor: string,
65
65
  liquidateCollateralFactor: string,
66
+ liquidationFactor: string,
66
67
  minDebt: string,
68
+ supplyReserved: string,
67
69
  liquidationRatio: string,
68
70
  supplyCap: string,
69
71
  priceInBaseAsset: string,