@defisaver/positions-sdk 2.1.127-midnight-14-dev → 2.1.127-midnight-15-dev

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.
Files changed (100) hide show
  1. package/cjs/aaveV3/merkl.js +10 -9
  2. package/cjs/aaveV4/merkl.js +5 -6
  3. package/cjs/claiming/aaveV3.d.ts +2 -1
  4. package/cjs/claiming/aaveV3.js +13 -4
  5. package/cjs/compoundV3/index.d.ts +1 -0
  6. package/cjs/compoundV3/index.js +8 -2
  7. package/cjs/compoundV3/merkl.d.ts +16 -0
  8. package/cjs/compoundV3/merkl.js +77 -0
  9. package/cjs/fluid/index.d.ts +9 -0
  10. package/cjs/fluid/index.js +36 -10
  11. package/cjs/fluid/merkl.d.ts +32 -0
  12. package/cjs/fluid/merkl.js +103 -0
  13. package/cjs/helpers/fluidHelpers/index.d.ts +3 -1
  14. package/cjs/helpers/fluidHelpers/index.js +37 -4
  15. package/cjs/helpers/liquityV2Helpers/index.d.ts +2 -1
  16. package/cjs/helpers/liquityV2Helpers/index.js +3 -2
  17. package/cjs/helpers/morphoMidnightHelpers/headroom.d.ts +48 -0
  18. package/cjs/helpers/morphoMidnightHelpers/headroom.js +52 -0
  19. package/cjs/helpers/morphoMidnightHelpers/index.d.ts +2 -0
  20. package/cjs/helpers/morphoMidnightHelpers/index.js +10 -4
  21. package/cjs/liquityV2/index.js +3 -4
  22. package/cjs/moneymarket/moneymarketCommonService.js +2 -0
  23. package/cjs/morphoBlue/index.d.ts +1 -0
  24. package/cjs/morphoBlue/index.js +16 -10
  25. package/cjs/morphoBlue/merkl.d.ts +35 -0
  26. package/cjs/morphoBlue/merkl.js +102 -0
  27. package/cjs/portfolio/index.js +5 -5
  28. package/cjs/services/merkl.d.ts +6 -0
  29. package/cjs/services/merkl.js +39 -0
  30. package/cjs/staking/eligibility.d.ts +4 -0
  31. package/cjs/staking/eligibility.js +11 -1
  32. package/cjs/types/claiming.d.ts +12 -1
  33. package/cjs/types/claiming.js +2 -0
  34. package/cjs/types/common.d.ts +6 -1
  35. package/cjs/types/common.js +6 -1
  36. package/cjs/types/fluid.d.ts +2 -0
  37. package/cjs/types/liquityV2.d.ts +3 -0
  38. package/cjs/types/merkl.d.ts +8 -0
  39. package/esm/aaveV3/merkl.js +11 -10
  40. package/esm/aaveV4/merkl.js +5 -6
  41. package/esm/claiming/aaveV3.d.ts +2 -1
  42. package/esm/claiming/aaveV3.js +12 -4
  43. package/esm/compoundV3/index.d.ts +1 -0
  44. package/esm/compoundV3/index.js +5 -1
  45. package/esm/compoundV3/merkl.d.ts +16 -0
  46. package/esm/compoundV3/merkl.js +72 -0
  47. package/esm/fluid/index.d.ts +9 -0
  48. package/esm/fluid/index.js +31 -9
  49. package/esm/fluid/merkl.d.ts +32 -0
  50. package/esm/fluid/merkl.js +95 -0
  51. package/esm/helpers/fluidHelpers/index.d.ts +3 -1
  52. package/esm/helpers/fluidHelpers/index.js +38 -6
  53. package/esm/helpers/liquityV2Helpers/index.d.ts +2 -1
  54. package/esm/helpers/liquityV2Helpers/index.js +3 -2
  55. package/esm/helpers/morphoMidnightHelpers/headroom.d.ts +48 -0
  56. package/esm/helpers/morphoMidnightHelpers/headroom.js +45 -0
  57. package/esm/helpers/morphoMidnightHelpers/index.d.ts +2 -0
  58. package/esm/helpers/morphoMidnightHelpers/index.js +7 -3
  59. package/esm/liquityV2/index.js +3 -4
  60. package/esm/moneymarket/moneymarketCommonService.js +2 -0
  61. package/esm/morphoBlue/index.d.ts +1 -0
  62. package/esm/morphoBlue/index.js +11 -9
  63. package/esm/morphoBlue/merkl.d.ts +35 -0
  64. package/esm/morphoBlue/merkl.js +96 -0
  65. package/esm/portfolio/index.js +6 -6
  66. package/esm/services/merkl.d.ts +6 -0
  67. package/esm/services/merkl.js +35 -0
  68. package/esm/staking/eligibility.d.ts +4 -0
  69. package/esm/staking/eligibility.js +9 -0
  70. package/esm/types/claiming.d.ts +12 -1
  71. package/esm/types/claiming.js +2 -0
  72. package/esm/types/common.d.ts +6 -1
  73. package/esm/types/common.js +5 -0
  74. package/esm/types/fluid.d.ts +2 -0
  75. package/esm/types/liquityV2.d.ts +3 -0
  76. package/esm/types/merkl.d.ts +8 -0
  77. package/package.json +1 -1
  78. package/src/aaveV3/merkl.ts +7 -7
  79. package/src/aaveV4/merkl.ts +5 -5
  80. package/src/claiming/aaveV3.ts +19 -7
  81. package/src/compoundV3/index.ts +6 -1
  82. package/src/compoundV3/merkl.ts +76 -0
  83. package/src/fluid/index.ts +37 -9
  84. package/src/fluid/merkl.ts +102 -0
  85. package/src/helpers/fluidHelpers/index.ts +68 -6
  86. package/src/helpers/liquityV2Helpers/index.ts +4 -1
  87. package/src/helpers/morphoMidnightHelpers/headroom.ts +82 -0
  88. package/src/helpers/morphoMidnightHelpers/index.ts +11 -3
  89. package/src/liquityV2/index.ts +4 -3
  90. package/src/moneymarket/moneymarketCommonService.ts +2 -0
  91. package/src/morphoBlue/index.ts +24 -9
  92. package/src/morphoBlue/merkl.ts +121 -0
  93. package/src/portfolio/index.ts +6 -6
  94. package/src/services/merkl.ts +31 -0
  95. package/src/staking/eligibility.ts +10 -0
  96. package/src/types/claiming.ts +13 -0
  97. package/src/types/common.ts +6 -0
  98. package/src/types/fluid.ts +3 -1
  99. package/src/types/liquityV2.ts +3 -0
  100. package/src/types/merkl.ts +6 -0
@@ -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.mergeUsedAssets = exports.EMPTY_USED_ASSET = exports.mergeAssetData = exports.parseDexBorrowData = exports.parseDexSupplyData = exports.getFluidAggregatedData = void 0;
6
+ exports.mergeUsedAssets = exports.EMPTY_USED_ASSET = exports.mergeAssetData = exports.parseDexBorrowData = exports.parseDexSupplyData = exports.getFluidAggregatedData = exports.getUniqueFluidMerklIncentives = void 0;
7
7
  const decimal_js_1 = __importDefault(require("decimal.js"));
8
8
  const tokens_1 = require("@defisaver/tokens");
9
9
  const types_1 = require("../../types");
@@ -11,13 +11,38 @@ const moneymarket_1 = require("../../moneymarket");
11
11
  const staking_1 = require("../../staking");
12
12
  const common_1 = require("../../types/common");
13
13
  const utils_1 = require("../../services/utils");
14
- const calculateNetApyDex = ({ marketData, suppliedUsd, borrowedUsd }) => {
14
+ const getUniqueFluidMerklIncentives = (assetsData, side) => {
15
+ const field = side === common_1.IncentiveSide.Supply ? 'supplyIncentives' : 'borrowIncentives';
16
+ const incentives = Object.values(assetsData)
17
+ .flatMap(asset => asset[field])
18
+ .filter(incentive => incentive.source === types_1.IncentiveSource.Merkl);
19
+ return [...new Map(incentives.map(incentive => [
20
+ [incentive.eligibilityId, incentive.token, incentive.apy, incentive.description].join('|'),
21
+ incentive,
22
+ ])).values()];
23
+ };
24
+ exports.getUniqueFluidMerklIncentives = getUniqueFluidMerklIncentives;
25
+ const getMerklIncentiveInterest = (incentives, principal, usedAssets) => incentives.reduce((total, { apy, eligibilityId }) => {
26
+ const eligibilityCheck = eligibilityId ? staking_1.EligibilityMapping[eligibilityId] : null;
27
+ const { isEligible, eligibleUSDAmount } = eligibilityCheck
28
+ ? eligibilityCheck(usedAssets, undefined)
29
+ : { isEligible: true, eligibleUSDAmount: principal };
30
+ const incentiveInterest = isEligible ? (0, staking_1.calculateInterestEarned)(eligibleUSDAmount, apy, 'year', true) : 0;
31
+ return new decimal_js_1.default(total).add(incentiveInterest).toString();
32
+ }, '0');
33
+ const calculateNetApyDex = ({ marketData, assetsData, usedAssets, suppliedUsd, borrowedUsd, }) => {
15
34
  const { borrowRate, supplyRate, incentiveBorrowRate, incentiveSupplyRate, tradingBorrowRate, tradingSupplyRate, } = marketData;
16
35
  const totalBorrowRate = new decimal_js_1.default(borrowRate).minus(tradingBorrowRate || '0').toString();
17
36
  const totalSupplyRate = new decimal_js_1.default(supplyRate).add(tradingSupplyRate || '0').toString();
18
37
  const borrowIncentive = new decimal_js_1.default(incentiveBorrowRate || '0').mul(borrowedUsd).div(100).toString();
19
38
  const supplyIncentive = new decimal_js_1.default(incentiveSupplyRate || '0').mul(suppliedUsd).div(100).toString();
20
- const incentiveUsd = new decimal_js_1.default(supplyIncentive).minus(borrowIncentive).toString();
39
+ const merklSupplyIncentive = getMerklIncentiveInterest((0, exports.getUniqueFluidMerklIncentives)(assetsData, common_1.IncentiveSide.Supply), suppliedUsd, usedAssets);
40
+ const merklBorrowIncentive = getMerklIncentiveInterest((0, exports.getUniqueFluidMerklIncentives)(assetsData, common_1.IncentiveSide.Borrow), borrowedUsd, usedAssets);
41
+ const incentiveUsd = new decimal_js_1.default(supplyIncentive)
42
+ .minus(borrowIncentive)
43
+ .add(merklSupplyIncentive)
44
+ .add(merklBorrowIncentive)
45
+ .toString();
21
46
  const borrowInterest = new decimal_js_1.default(totalBorrowRate).mul(borrowedUsd).div(100).toString();
22
47
  const supplyInterest = new decimal_js_1.default(totalSupplyRate).mul(suppliedUsd).div(100).toString();
23
48
  const totalInterestUsd = new decimal_js_1.default(supplyInterest).add(incentiveUsd).minus(borrowInterest).toString();
@@ -38,7 +63,15 @@ const getFluidAggregatedData = ({ usedAssets, assetsData, marketData, }, supplyS
38
63
  ? (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isBorrowed }) => isBorrowed, ({ borrowedUsd }) => borrowedUsd)
39
64
  : new decimal_js_1.default(marketData.debtSharePrice).mul(borrowShares).toString();
40
65
  const isDex = [types_1.FluidVaultType.T2, types_1.FluidVaultType.T3, types_1.FluidVaultType.T4].includes(marketData.vaultType);
41
- const { netApy, incentiveUsd, totalInterestUsd } = isDex ? calculateNetApyDex({ marketData, suppliedUsd: payload.suppliedUsd, borrowedUsd: payload.borrowedUsd }) : (0, staking_1.calculateNetApy)({ usedAssets, assetsData: assetsData });
66
+ payload.merklSupplyIncentives = (0, exports.getUniqueFluidMerklIncentives)(assetsData, common_1.IncentiveSide.Supply);
67
+ payload.merklBorrowIncentives = (0, exports.getUniqueFluidMerklIncentives)(assetsData, common_1.IncentiveSide.Borrow);
68
+ const { netApy, incentiveUsd, totalInterestUsd } = isDex ? calculateNetApyDex({
69
+ marketData,
70
+ assetsData,
71
+ usedAssets,
72
+ suppliedUsd: payload.suppliedUsd,
73
+ borrowedUsd: payload.borrowedUsd,
74
+ }) : (0, staking_1.calculateNetApy)({ usedAssets, assetsData: assetsData });
42
75
  payload.netApy = netApy;
43
76
  payload.incentiveUsd = incentiveUsd;
44
77
  payload.totalInterestUsd = totalInterestUsd;
@@ -4,9 +4,10 @@ export declare const calculateNetApyLiquityV2: (usedAssets: LiquityV2UsedAssets,
4
4
  totalInterestUsd: string;
5
5
  incentiveUsd: string;
6
6
  };
7
- export declare const getLiquityV2AggregatedPositionData: ({ usedAssets, assetsData, minCollRatio, interestRate, }: {
7
+ export declare const getLiquityV2AggregatedPositionData: ({ usedAssets, assetsData, minCollRatio, interestRate, liqRatio, }: {
8
8
  usedAssets: LiquityV2UsedAssets;
9
9
  assetsData: LiquityV2AssetsData;
10
10
  minCollRatio: string;
11
11
  interestRate: string;
12
+ liqRatio?: string;
12
13
  }) => LiquityV2AggregatedTroveData;
@@ -38,11 +38,12 @@ const calculateNetApyLiquityV2 = (usedAssets, assetsData, interestRate) => {
38
38
  return { netApy, totalInterestUsd, incentiveUsd };
39
39
  };
40
40
  exports.calculateNetApyLiquityV2 = calculateNetApyLiquityV2;
41
- const getLiquityV2AggregatedPositionData = ({ usedAssets, assetsData, minCollRatio, interestRate, }) => {
41
+ const getLiquityV2AggregatedPositionData = ({ usedAssets, assetsData, minCollRatio, interestRate, liqRatio, }) => {
42
42
  const payload = {};
43
43
  payload.suppliedUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, (usedAsset) => usedAsset, ({ suppliedUsd }) => suppliedUsd);
44
44
  payload.borrowedUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, (usedAsset) => usedAsset, ({ borrowedUsd }) => borrowedUsd);
45
45
  payload.borrowLimitUsd = new decimal_js_1.default(payload.suppliedUsd).div(minCollRatio).mul(100).toString();
46
+ payload.liquidationLimitUsd = new decimal_js_1.default(payload.suppliedUsd).div(liqRatio !== null && liqRatio !== void 0 ? liqRatio : minCollRatio).mul(100).toString();
46
47
  const leftToBorrowUsd = new decimal_js_1.default(payload.borrowLimitUsd).sub(payload.borrowedUsd);
47
48
  payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
48
49
  payload.ratio = (+payload.suppliedUsd && +payload.borrowedUsd) ? new decimal_js_1.default(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
@@ -57,7 +58,7 @@ const getLiquityV2AggregatedPositionData = ({ usedAssets, assetsData, minCollRat
57
58
  payload.liquidationPrice = '';
58
59
  if (leveragedType !== '') {
59
60
  const assetPrice = assetsData[leveragedAsset].price;
60
- payload.liquidationPrice = (0, moneymarket_1.calcLeverageLiqPrice)(leveragedType, assetPrice, payload.borrowedUsd, payload.borrowLimitUsd);
61
+ payload.liquidationPrice = (0, moneymarket_1.calcLeverageLiqPrice)(leveragedType, assetPrice, payload.borrowedUsd, payload.liquidationLimitUsd);
61
62
  }
62
63
  payload.exposure = (0, moneymarket_1.getExposure)(payload.borrowedUsd, payload.suppliedUsd);
63
64
  return payload;
@@ -0,0 +1,48 @@
1
+ import Dec from 'decimal.js';
2
+ /**
3
+ * Safety margin every Midnight position is held at. On Midnight the lltv is *both* the borrow cap and the
4
+ * liquidation threshold (`liquidationLimitUsd === borrowLimitUsd` in the aggregate), so a position borrowed
5
+ * to its full limit opens at a health ratio of exactly 1 — immediately liquidatable. This is what holds it
6
+ * off that line, and what absorbs swap price impact on a boost.
7
+ */
8
+ export declare const MIDNIGHT_MIN_HEALTH_RATIO = 1.025;
9
+ export interface MidnightBorrowHeadroomParams {
10
+ borrowLimitUsd: Dec.Value;
11
+ borrowedUsd: Dec.Value;
12
+ /** Principal a unit of borrow power is worth (loan-per-unit price, ≤ 1), off a book rate. */
13
+ principalPerUnit?: Dec.Value;
14
+ /** Depth the book can fill, in loan tokens, from the same quote. Omitted leaves the answer uncapped. */
15
+ availableAssets?: Dec.Value;
16
+ loanTokenPrice?: Dec.Value;
17
+ /** Loan token decimals. Omitted skips the quantisation, which only a caller ignoring `leftToBorrow` wants. */
18
+ loanTokenDecimals?: number;
19
+ isMatured?: boolean;
20
+ minHealthRatio?: Dec.Value;
21
+ }
22
+ export interface MidnightBorrowHeadroom {
23
+ /** Face-value headroom, before the order-book discount. What a *boost* solve scales itself. */
24
+ headroomUsd: string;
25
+ /** The borrowable amount in loan tokens — the Borrow action's Max, quantised exactly as it is typed in. */
26
+ leftToBorrow: string;
27
+ /** `leftToBorrow · price`. Derived FROM the token amount, never computed alongside it — see below. */
28
+ leftToBorrowUsd: string;
29
+ borrowPowerUsed: string;
30
+ }
31
+ /**
32
+ * The one answer to "how much more can this position borrow". Every surface that sizes a Midnight borrow
33
+ * derives from here — the max-borrow getter, the position overview's headroom rows, and the max-boost
34
+ * solve, which scales `headroomUsd` by its own leverage factors — so a max and the figure shown next to it
35
+ * cannot drift into two different formulas. They did: the overview used to price
36
+ * the whole headroom off the top-of-book offer (often dust) with no rate pad and no depth cap, while the
37
+ * max walked the book for its real size, which is what put the two 1–2% apart.
38
+ *
39
+ * Three corrections separate this from a plain `limit − debt`, and dropping any one overstates the answer:
40
+ * `minHealthRatio` reserves the margin, `principalPerUnit` turns face-value headroom into the principal a
41
+ * borrow actually receives (a correction that grows with the term, past 7% on a one-year market), and
42
+ * `availableAssets` caps it at what the book can currently fill.
43
+ *
44
+ * `leftToBorrowUsd` is deliberately `leftToBorrow · price` rather than the USD figure the token amount was
45
+ * derived from: the two differ by the round-down to token decimals, and a UI showing one next to an input
46
+ * holding the other would be reporting a difference the user cannot spend. Everything quantises once, here.
47
+ */
48
+ export declare const getMorphoMidnightBorrowHeadroom: ({ borrowLimitUsd, borrowedUsd, principalPerUnit, availableAssets, loanTokenPrice, loanTokenDecimals, isMatured, minHealthRatio, }: MidnightBorrowHeadroomParams) => MidnightBorrowHeadroom;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getMorphoMidnightBorrowHeadroom = exports.MIDNIGHT_MIN_HEALTH_RATIO = void 0;
7
+ const decimal_js_1 = __importDefault(require("decimal.js"));
8
+ /**
9
+ * Safety margin every Midnight position is held at. On Midnight the lltv is *both* the borrow cap and the
10
+ * liquidation threshold (`liquidationLimitUsd === borrowLimitUsd` in the aggregate), so a position borrowed
11
+ * to its full limit opens at a health ratio of exactly 1 — immediately liquidatable. This is what holds it
12
+ * off that line, and what absorbs swap price impact on a boost.
13
+ */
14
+ exports.MIDNIGHT_MIN_HEALTH_RATIO = 1.025;
15
+ /**
16
+ * The one answer to "how much more can this position borrow". Every surface that sizes a Midnight borrow
17
+ * derives from here — the max-borrow getter, the position overview's headroom rows, and the max-boost
18
+ * solve, which scales `headroomUsd` by its own leverage factors — so a max and the figure shown next to it
19
+ * cannot drift into two different formulas. They did: the overview used to price
20
+ * the whole headroom off the top-of-book offer (often dust) with no rate pad and no depth cap, while the
21
+ * max walked the book for its real size, which is what put the two 1–2% apart.
22
+ *
23
+ * Three corrections separate this from a plain `limit − debt`, and dropping any one overstates the answer:
24
+ * `minHealthRatio` reserves the margin, `principalPerUnit` turns face-value headroom into the principal a
25
+ * borrow actually receives (a correction that grows with the term, past 7% on a one-year market), and
26
+ * `availableAssets` caps it at what the book can currently fill.
27
+ *
28
+ * `leftToBorrowUsd` is deliberately `leftToBorrow · price` rather than the USD figure the token amount was
29
+ * derived from: the two differ by the round-down to token decimals, and a UI showing one next to an input
30
+ * holding the other would be reporting a difference the user cannot spend. Everything quantises once, here.
31
+ */
32
+ const getMorphoMidnightBorrowHeadroom = ({ borrowLimitUsd, borrowedUsd, principalPerUnit = 1, availableAssets, loanTokenPrice = 0, loanTokenDecimals, isMatured = false, minHealthRatio = exports.MIDNIGHT_MIN_HEALTH_RATIO, }) => {
33
+ const usableLimitUsd = new decimal_js_1.default(borrowLimitUsd).div(minHealthRatio);
34
+ const headroomUsd = isMatured ? new decimal_js_1.default(0) : decimal_js_1.default.max(0, usableLimitUsd.sub(borrowedUsd));
35
+ const price = new decimal_js_1.default(loanTokenPrice);
36
+ // Capped in loan tokens rather than in USD: `availableAssets` is quoted in tokens, and converting it to
37
+ // USD only to divide the result back out again would re-round the one number that has to land exactly.
38
+ const payout = price.lte(0) ? new decimal_js_1.default(0) : headroomUsd.mul(principalPerUnit).div(price);
39
+ const capped = availableAssets === undefined ? payout : decimal_js_1.default.min(payout, availableAssets);
40
+ // Round down: rounding the last decimal up would eat into the margin we just reserved.
41
+ const leftToBorrow = loanTokenDecimals === undefined ? capped : capped.toDP(loanTokenDecimals, decimal_js_1.default.ROUND_DOWN);
42
+ return {
43
+ headroomUsd: headroomUsd.toString(),
44
+ leftToBorrow: leftToBorrow.toString(),
45
+ leftToBorrowUsd: leftToBorrow.mul(price).toString(),
46
+ // Measured against the margined limit rather than the raw one, so it reads 100% at max borrow instead
47
+ // of 97.56%, and can exceed 100% inside the reserved band. Deliberately left uncapped by book depth:
48
+ // how much a position has borrowed is a property of the position, not of what the book can fill today.
49
+ borrowPowerUsed: usableLimitUsd.lte(0) ? '0' : new decimal_js_1.default(borrowedUsd).div(usableLimitUsd).mul(100).toString(),
50
+ };
51
+ };
52
+ exports.getMorphoMidnightBorrowHeadroom = getMorphoMidnightBorrowHeadroom;
@@ -2,6 +2,8 @@ import Dec from 'decimal.js';
2
2
  import { MMUsedAssets, NetworkNumber } from '../../types/common';
3
3
  import { MorphoMidnightAggregatedPositionData, MorphoMidnightAssetsData, MorphoMidnightBookSide, MorphoMidnightMarketData, MorphoMidnightMarketInfo, MorphoMidnightParsedBook } from '../../types';
4
4
  export { buildMidnightParsedBook, midnightApyFromPrice, midnightBookBestFirst, midnightBoundPrice, midnightPriceFromApy, midnightTimeToMaturityDays, MIDNIGHT_DEFAULT_RATE_SLIPPAGE, } from './rate';
5
+ export { getMorphoMidnightBorrowHeadroom, MIDNIGHT_MIN_HEALTH_RATIO, } from './headroom';
6
+ export type { MidnightBorrowHeadroom, MidnightBorrowHeadroomParams } from './headroom';
5
7
  export { tenorBookKeyFor, tenorBookRateToApyPercent, tenorOfferFillToApiFill, tenorOfferToApiOffer, } from './tenor';
6
8
  /**
7
9
  * Aggregate a Morpho Midnight position. Midnight markets are multi-collateral, so the borrow limit is
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.getMorphoMidnightPaybackUnitsQuote = exports.getMorphoMidnightPaybackQuote = exports.getMorphoMidnightBorrowQuote = exports.getMorphoMidnightMarketBook = exports.scaleMorphoMidnightDebtSplit = exports.getMorphoMidnightUserBorrowInfo = exports.midnightSlippageParam = exports.getMorphoMidnightAggregatedPositionData = exports.tenorOfferToApiOffer = exports.tenorOfferFillToApiFill = exports.tenorBookRateToApyPercent = exports.tenorBookKeyFor = exports.MIDNIGHT_DEFAULT_RATE_SLIPPAGE = exports.midnightTimeToMaturityDays = exports.midnightPriceFromApy = exports.midnightBoundPrice = exports.midnightBookBestFirst = exports.midnightApyFromPrice = exports.buildMidnightParsedBook = void 0;
15
+ exports.getMorphoMidnightPaybackUnitsQuote = exports.getMorphoMidnightPaybackQuote = exports.getMorphoMidnightBorrowQuote = exports.getMorphoMidnightMarketBook = exports.scaleMorphoMidnightDebtSplit = exports.getMorphoMidnightUserBorrowInfo = exports.midnightSlippageParam = exports.getMorphoMidnightAggregatedPositionData = exports.tenorOfferToApiOffer = exports.tenorOfferFillToApiFill = exports.tenorBookRateToApyPercent = exports.tenorBookKeyFor = exports.MIDNIGHT_MIN_HEALTH_RATIO = exports.getMorphoMidnightBorrowHeadroom = exports.MIDNIGHT_DEFAULT_RATE_SLIPPAGE = exports.midnightTimeToMaturityDays = exports.midnightPriceFromApy = exports.midnightBoundPrice = exports.midnightBookBestFirst = exports.midnightApyFromPrice = exports.buildMidnightParsedBook = void 0;
16
16
  const decimal_js_1 = __importDefault(require("decimal.js"));
17
17
  const tokens_1 = require("@defisaver/tokens");
18
18
  const moneymarket_1 = require("../../moneymarket");
@@ -31,6 +31,9 @@ Object.defineProperty(exports, "midnightBoundPrice", { enumerable: true, get: fu
31
31
  Object.defineProperty(exports, "midnightPriceFromApy", { enumerable: true, get: function () { return rate_2.midnightPriceFromApy; } });
32
32
  Object.defineProperty(exports, "midnightTimeToMaturityDays", { enumerable: true, get: function () { return rate_2.midnightTimeToMaturityDays; } });
33
33
  Object.defineProperty(exports, "MIDNIGHT_DEFAULT_RATE_SLIPPAGE", { enumerable: true, get: function () { return rate_2.MIDNIGHT_DEFAULT_RATE_SLIPPAGE; } });
34
+ var headroom_1 = require("./headroom");
35
+ Object.defineProperty(exports, "getMorphoMidnightBorrowHeadroom", { enumerable: true, get: function () { return headroom_1.getMorphoMidnightBorrowHeadroom; } });
36
+ Object.defineProperty(exports, "MIDNIGHT_MIN_HEALTH_RATIO", { enumerable: true, get: function () { return headroom_1.MIDNIGHT_MIN_HEALTH_RATIO; } });
34
37
  var tenor_2 = require("./tenor");
35
38
  Object.defineProperty(exports, "tenorBookKeyFor", { enumerable: true, get: function () { return tenor_2.tenorBookKeyFor; } });
36
39
  Object.defineProperty(exports, "tenorBookRateToApyPercent", { enumerable: true, get: function () { return tenor_2.tenorBookRateToApyPercent; } });
@@ -57,9 +60,12 @@ const getMorphoMidnightAggregatedPositionData = ({ usedAssets, assetsData, marke
57
60
  // Same subtraction every other money market uses, but it does NOT mean the same thing here. Elsewhere
58
61
  // `borrowedUsd` is debt at present value, so the remainder is what a borrow would pay out. Midnight
59
62
  // records debt at its face value at maturity, so this is face-value headroom: borrowing it would add
60
- // more debt than the number says, by the market's discount. Anything surfacing this as "what you can
61
- // borrow" has to scale it by the loan-per-unit price first (`midnightPriceFromApy` off a book rate) —
62
- // a correction that grows with the term, past 7% on a one-year market.
63
+ // more debt than the number says, by the market's discount. It also reserves no safety margin, and this
64
+ // aggregate is synchronous so it has no book rate to discount by.
65
+ //
66
+ // DO NOT SURFACE THIS AS "what you can borrow" — use `getMorphoMidnightBorrowHeadroom` instead, which
67
+ // applies both corrections (the margin, and the loan-per-unit price off a book rate) and caps on the
68
+ // depth the book can fill. The discount alone grows past 7% on a one-year market.
63
69
  const leftToBorrowUsd = new decimal_js_1.default(payload.borrowLimitUsd).sub(payload.borrowedUsd);
64
70
  payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
65
71
  const loanTokenPrice = ((_a = assetsData[marketInfo.loanToken]) === null || _a === void 0 ? void 0 : _a.price) || '0';
@@ -322,15 +322,14 @@ const _getLiquityV2TroveData = (provider_1, network_1, _a, ...args_1) => __await
322
322
  const interestBatchManager = data.interestBatchManager;
323
323
  const lastInterestRateAdjTime = data.lastInterestRateAdjTime.toString();
324
324
  const hasInterestBatchManager = !(0, utils_1.compareAddresses)(interestBatchManager, constants_1.ZERO_ADDRESS);
325
- const liqRatio = hasInterestBatchManager ? new decimal_js_1.default(minCollRatio).add(batchCollRatio).toString() : minCollRatio;
325
+ const borrowLimitRatio = hasInterestBatchManager ? new decimal_js_1.default(minCollRatio).add(batchCollRatio).toString() : minCollRatio;
326
326
  const payload = Object.assign(Object.assign({ usedAssets,
327
327
  troveId,
328
328
  interestRate,
329
329
  interestBatchManager,
330
330
  debtInFront,
331
- lastInterestRateAdjTime,
332
- liqRatio, troveStatus: types_1.LIQUITY_V2_TROVE_STATUS_ENUM[parseInt(data.status.toString(), 10)] }, (0, liquityV2Helpers_1.getLiquityV2AggregatedPositionData)({
333
- usedAssets, assetsData, minCollRatio: liqRatio, interestRate,
331
+ lastInterestRateAdjTime, liqRatio: minCollRatio, borrowLimitRatio, troveStatus: types_1.LIQUITY_V2_TROVE_STATUS_ENUM[parseInt(data.status.toString(), 10)] }, (0, liquityV2Helpers_1.getLiquityV2AggregatedPositionData)({
332
+ usedAssets, assetsData, minCollRatio: borrowLimitRatio, interestRate, liqRatio: minCollRatio,
334
333
  })), { collRatio });
335
334
  return payload;
336
335
  });
@@ -34,6 +34,8 @@ exports.STABLE_ASSETS = [
34
34
  'USDtb', 'eUSDe', 'PT USDe July', 'PT eUSDe Aug', 'PT sUSDe Sep', 'PT USDe Sep', 'PT sUSDe Nov', 'PT USDe Nov', 'PT sUSDe Jan', 'PT USDe Jan',
35
35
  'PT sUSDe Feb', 'PT USDe Feb', 'PT sUSDe Apr', 'PT USDe Apr', 'PT sUSDe May', 'PT USDe May', 'PT srUSDe Apr', 'frxUSD', 'PT sUSDe June',
36
36
  'PT USDe June', 'PT srUSDe Jun', 'PT srUSDe Oct', 'PT USDG May', 'PT sUSDS Nov', 'PT sUSDe Oct', 'PT USDG Sep',
37
+ 'USDG', 'mUSD', 'PYUSD', 'USDbC', 'MAI', 'sGHO', 'USD3', 'stUSDS', 'PRIME',
38
+ 'crvUSD', 'syrupUSDC', 'syrupUSDT', 'syrupUSDG', 'PT syrupUSDC Aug',
37
39
  ];
38
40
  const isLeveragedPos = (usedAssets, dustLimit = 5) => {
39
41
  let borrowUnstable = 0;
@@ -1,6 +1,7 @@
1
1
  import { Client } from 'viem';
2
2
  import { Blockish, EthAddress, EthereumProvider, NetworkNumber, PositionBalances } from '../types/common';
3
3
  import { MorphoBlueEarnData, MorphoBlueMarketData, MorphoBlueMarketInfo, MorphoBlueMarketRewards, MorphoBluePositionData } from '../types';
4
+ export { MorphoBlueMerklOpportunityType, addMorphoBlueMerklOpportunitiesToMarketInfo, getMorphoBlueMerklOpportunities, addMorphoBlueMerklRewardsToMarketInfo, } from './merkl';
4
5
  export declare const addMorphoBlueRewardsToMarketInfo: (marketInfo: MorphoBlueMarketInfo, rewards: MorphoBlueMarketRewards) => MorphoBlueMarketInfo;
5
6
  export declare function _getMorphoBlueMarketData(provider: Client, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo>;
6
7
  export declare function _getMorphoBluePortfolioMarketData(provider: Client, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo>;
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.getMorphoBlueAccountBalances = exports._getMorphoBlueAccountBalances = exports.getMorphoEarnDataWithMarketInfo = exports.getMorphoBluePositionDataWithMarketInfo = exports.addMorphoBlueRewardsToMarketInfo = void 0;
15
+ exports.getMorphoBlueAccountBalances = exports._getMorphoBlueAccountBalances = exports.getMorphoEarnDataWithMarketInfo = exports.getMorphoBluePositionDataWithMarketInfo = exports.addMorphoBlueRewardsToMarketInfo = exports.addMorphoBlueMerklRewardsToMarketInfo = exports.getMorphoBlueMerklOpportunities = exports.addMorphoBlueMerklOpportunitiesToMarketInfo = exports.MorphoBlueMerklOpportunityType = void 0;
16
16
  exports._getMorphoBlueMarketData = _getMorphoBlueMarketData;
17
17
  exports._getMorphoBluePortfolioMarketData = _getMorphoBluePortfolioMarketData;
18
18
  exports.getMorphoBlueMarketData = getMorphoBlueMarketData;
@@ -31,6 +31,12 @@ const utils_1 = require("../services/utils");
31
31
  const morphoBlueHelpers_1 = require("../helpers/morphoBlueHelpers");
32
32
  const priceService_1 = require("../services/priceService");
33
33
  const viem_1 = require("../services/viem");
34
+ const merkl_1 = require("./merkl");
35
+ var merkl_2 = require("./merkl");
36
+ Object.defineProperty(exports, "MorphoBlueMerklOpportunityType", { enumerable: true, get: function () { return merkl_2.MorphoBlueMerklOpportunityType; } });
37
+ Object.defineProperty(exports, "addMorphoBlueMerklOpportunitiesToMarketInfo", { enumerable: true, get: function () { return merkl_2.addMorphoBlueMerklOpportunitiesToMarketInfo; } });
38
+ Object.defineProperty(exports, "getMorphoBlueMerklOpportunities", { enumerable: true, get: function () { return merkl_2.getMorphoBlueMerklOpportunities; } });
39
+ Object.defineProperty(exports, "addMorphoBlueMerklRewardsToMarketInfo", { enumerable: true, get: function () { return merkl_2.addMorphoBlueMerklRewardsToMarketInfo; } });
34
40
  const HARDCODED_USD_STABLE_PRICE = '100000000'; // $1 with 8 decimals
35
41
  const getMorphoRewardIncentives = (apy) => [{
36
42
  token: 'MORPHO',
@@ -134,15 +140,15 @@ function getMorphoBlueMarketDataInternal(provider, network, selectedMarket) {
134
140
  }
135
141
  function _getMorphoBlueMarketData(provider, network, selectedMarket) {
136
142
  return __awaiter(this, void 0, void 0, function* () {
137
- const marketInfo = yield getMorphoBlueMarketDataInternal(provider, network, selectedMarket);
138
- try {
139
- const rewards = yield (0, morphoBlueHelpers_1.getRewardsForMarket)(selectedMarket.marketId, network);
140
- return (0, exports.addMorphoBlueRewardsToMarketInfo)(marketInfo, rewards);
141
- }
142
- catch (error) {
143
- console.error(error);
144
- return (0, exports.addMorphoBlueRewardsToMarketInfo)(marketInfo, { supplyApy: '0', borrowApy: '0' });
145
- }
143
+ const [marketInfo, rewards, merklOpportunities] = yield Promise.all([
144
+ getMorphoBlueMarketDataInternal(provider, network, selectedMarket),
145
+ (0, morphoBlueHelpers_1.getRewardsForMarket)(selectedMarket.marketId, network).catch((error) => {
146
+ console.error(error);
147
+ return { supplyApy: '0', borrowApy: '0' };
148
+ }),
149
+ (0, merkl_1.getMorphoBlueMerklOpportunities)(),
150
+ ]);
151
+ return (0, merkl_1.addMorphoBlueMerklOpportunitiesToMarketInfo)((0, exports.addMorphoBlueRewardsToMarketInfo)(marketInfo, rewards), selectedMarket, network, merklOpportunities);
146
152
  });
147
153
  }
148
154
  function _getMorphoBluePortfolioMarketData(provider, network, selectedMarket) {
@@ -0,0 +1,35 @@
1
+ import { MerklOpportunity, MorphoBlueMarketData, MorphoBlueMarketInfo, NetworkNumber } from '../types';
2
+ /**
3
+ * Merkl tags Morpho Blue market-level reward campaigns via the opportunity `type` field:
4
+ * - MORPHOSUPPLY → reward for supplying the market's loan asset
5
+ * - MORPHOCOLLATERAL → reward for depositing the market's collateral asset
6
+ * - MORPHOBORROW → reward for borrowing the market's loan asset
7
+ * (MORPHOVAULT/ERC20LOGPROCESSOR are MetaMorpho vault campaigns and MORPHOSUPPLY_SINGLETOKEN is
8
+ * token-wide rather than market-scoped, so none of them belong on per-market data.)
9
+ *
10
+ * A campaign identifies its market via `identifier` — the first 20 bytes of the 32-byte Morpho
11
+ * market id, formatted as a checksummed address. Whitelist-gated campaigns append a marker to that
12
+ * identifier (e.g. `0x54cf…c46WHITELIST_CAMPAIGN`); their rewards only accrue to whitelisted
13
+ * addresses, so they are skipped rather than advertised to every user.
14
+ */
15
+ export declare enum MorphoBlueMerklOpportunityType {
16
+ Supply = "MORPHOSUPPLY",
17
+ Collateral = "MORPHOCOLLATERAL",
18
+ Borrow = "MORPHOBORROW"
19
+ }
20
+ /**
21
+ * Appends the given live Merkl campaigns for the market to its `assetsData` incentive arrays
22
+ * (collateral supply / loan supply / loan borrow). Existing Merkl incentives are replaced rather
23
+ * than duplicated, so re-applying on refreshed data is safe.
24
+ */
25
+ export declare const addMorphoBlueMerklOpportunitiesToMarketInfo: (marketInfo: MorphoBlueMarketInfo, selectedMarket: MorphoBlueMarketData, chainId: NetworkNumber, opportunities: MerklOpportunity[]) => MorphoBlueMarketInfo;
26
+ /**
27
+ * Fetches live market-scoped Morpho Blue campaigns for all chains. Never throws — on any failure
28
+ * it returns an empty list, so Merkl being down can't break market data.
29
+ */
30
+ export declare const getMorphoBlueMerklOpportunities: () => Promise<MerklOpportunity[]>;
31
+ /**
32
+ * Fetches live Merkl campaigns and appends the ones for the market to its `assetsData` incentive
33
+ * arrays. Never throws — on any failure the market info is returned unchanged.
34
+ */
35
+ export declare const addMorphoBlueMerklRewardsToMarketInfo: (marketInfo: MorphoBlueMarketInfo, selectedMarket: MorphoBlueMarketData, network: NetworkNumber) => Promise<MorphoBlueMarketInfo>;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.addMorphoBlueMerklRewardsToMarketInfo = exports.getMorphoBlueMerklOpportunities = exports.addMorphoBlueMerklOpportunitiesToMarketInfo = exports.MorphoBlueMerklOpportunityType = void 0;
13
+ const moneymarket_1 = require("../moneymarket");
14
+ const merkl_1 = require("../services/merkl");
15
+ const types_1 = require("../types");
16
+ /**
17
+ * Merkl tags Morpho Blue market-level reward campaigns via the opportunity `type` field:
18
+ * - MORPHOSUPPLY → reward for supplying the market's loan asset
19
+ * - MORPHOCOLLATERAL → reward for depositing the market's collateral asset
20
+ * - MORPHOBORROW → reward for borrowing the market's loan asset
21
+ * (MORPHOVAULT/ERC20LOGPROCESSOR are MetaMorpho vault campaigns and MORPHOSUPPLY_SINGLETOKEN is
22
+ * token-wide rather than market-scoped, so none of them belong on per-market data.)
23
+ *
24
+ * A campaign identifies its market via `identifier` — the first 20 bytes of the 32-byte Morpho
25
+ * market id, formatted as a checksummed address. Whitelist-gated campaigns append a marker to that
26
+ * identifier (e.g. `0x54cf…c46WHITELIST_CAMPAIGN`); their rewards only accrue to whitelisted
27
+ * addresses, so they are skipped rather than advertised to every user.
28
+ */
29
+ var MorphoBlueMerklOpportunityType;
30
+ (function (MorphoBlueMerklOpportunityType) {
31
+ MorphoBlueMerklOpportunityType["Supply"] = "MORPHOSUPPLY";
32
+ MorphoBlueMerklOpportunityType["Collateral"] = "MORPHOCOLLATERAL";
33
+ MorphoBlueMerklOpportunityType["Borrow"] = "MORPHOBORROW";
34
+ })(MorphoBlueMerklOpportunityType || (exports.MorphoBlueMerklOpportunityType = MorphoBlueMerklOpportunityType = {}));
35
+ const MERKL_DESCRIPTION_MARKER = 'through Merkl';
36
+ // '0x' + first 20 bytes of the market id
37
+ const MARKET_ID_PREFIX_LENGTH = 42;
38
+ const buildMerklIncentive = (opportunity) => {
39
+ var _a, _b, _c, _d, _e, _f;
40
+ const token = ((_d = (_c = (_b = (_a = opportunity.rewardsRecord) === null || _a === void 0 ? void 0 : _a.breakdowns) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.token) === null || _d === void 0 ? void 0 : _d.symbol) || ((_f = (_e = opportunity.tokens) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.symbol) || '';
41
+ return {
42
+ apy: (0, moneymarket_1.aprToApy)(opportunity.apr),
43
+ token,
44
+ incentiveKind: types_1.IncentiveKind.Reward,
45
+ description: `Eligible for ${token} rewards through Merkl.${opportunity.description ? `\n${opportunity.description}` : ''}`,
46
+ };
47
+ };
48
+ const withoutMerklIncentives = (incentives = []) => incentives
49
+ .filter(({ description }) => !(description === null || description === void 0 ? void 0 : description.includes(MERKL_DESCRIPTION_MARKER)));
50
+ /**
51
+ * Appends the given live Merkl campaigns for the market to its `assetsData` incentive arrays
52
+ * (collateral supply / loan supply / loan borrow). Existing Merkl incentives are replaced rather
53
+ * than duplicated, so re-applying on refreshed data is safe.
54
+ */
55
+ const addMorphoBlueMerklOpportunitiesToMarketInfo = (marketInfo, selectedMarket, chainId, opportunities) => {
56
+ const marketIdPrefix = (selectedMarket.marketId || '').slice(0, MARKET_ID_PREFIX_LENGTH).toLowerCase();
57
+ const collateralAsset = marketInfo.assetsData[marketInfo.collateralToken];
58
+ const loanAsset = marketInfo.assetsData[marketInfo.loanToken];
59
+ if (marketIdPrefix.length !== MARKET_ID_PREFIX_LENGTH || !collateralAsset || !loanAsset)
60
+ return marketInfo;
61
+ const relevant = opportunities.filter((o) => {
62
+ var _a;
63
+ return o.chainId === chainId
64
+ && o.status === types_1.OpportunityStatus.LIVE
65
+ && Object.values(MorphoBlueMerklOpportunityType).includes(o.type)
66
+ // a suffixed identifier marks a whitelist-gated campaign — skip those
67
+ && ((_a = o.identifier) === null || _a === void 0 ? void 0 : _a.length) === MARKET_ID_PREFIX_LENGTH
68
+ && o.identifier.toLowerCase() === marketIdPrefix;
69
+ });
70
+ if (!relevant.length)
71
+ return marketInfo;
72
+ const incentivesByType = (type) => relevant.filter((o) => o.type === type).map(buildMerklIncentive);
73
+ const collateralSupply = incentivesByType(MorphoBlueMerklOpportunityType.Collateral);
74
+ const loanSupply = incentivesByType(MorphoBlueMerklOpportunityType.Supply);
75
+ const loanBorrow = incentivesByType(MorphoBlueMerklOpportunityType.Borrow);
76
+ return Object.assign(Object.assign({}, marketInfo), { assetsData: Object.assign(Object.assign({}, marketInfo.assetsData), { [marketInfo.collateralToken]: Object.assign(Object.assign({}, collateralAsset), { supplyIncentives: [...withoutMerklIncentives(collateralAsset.supplyIncentives), ...collateralSupply] }), [marketInfo.loanToken]: Object.assign(Object.assign({}, loanAsset), { supplyIncentives: [...withoutMerklIncentives(loanAsset.supplyIncentives), ...loanSupply], borrowIncentives: [...withoutMerklIncentives(loanAsset.borrowIncentives), ...loanBorrow] }) }) });
77
+ };
78
+ exports.addMorphoBlueMerklOpportunitiesToMarketInfo = addMorphoBlueMerklOpportunitiesToMarketInfo;
79
+ /**
80
+ * Fetches live market-scoped Morpho Blue campaigns for all chains. Never throws — on any failure
81
+ * it returns an empty list, so Merkl being down can't break market data.
82
+ */
83
+ const getMorphoBlueMerklOpportunities = () => __awaiter(void 0, void 0, void 0, function* () {
84
+ try {
85
+ return yield (0, merkl_1.fetchAllMerklOpportunities)({
86
+ mainProtocolId: 'morpho',
87
+ type: Object.values(MorphoBlueMerklOpportunityType).join(','),
88
+ status: types_1.OpportunityStatus.LIVE,
89
+ });
90
+ }
91
+ catch (e) {
92
+ console.error('Failed to fetch Morpho Blue Merkl campaigns', e);
93
+ return [];
94
+ }
95
+ });
96
+ exports.getMorphoBlueMerklOpportunities = getMorphoBlueMerklOpportunities;
97
+ /**
98
+ * Fetches live Merkl campaigns and appends the ones for the market to its `assetsData` incentive
99
+ * arrays. Never throws — on any failure the market info is returned unchanged.
100
+ */
101
+ const addMorphoBlueMerklRewardsToMarketInfo = (marketInfo, selectedMarket, network) => __awaiter(void 0, void 0, void 0, function* () { return (0, exports.addMorphoBlueMerklOpportunitiesToMarketInfo)(marketInfo, selectedMarket, network, yield (0, exports.getMorphoBlueMerklOpportunities)()); });
102
+ exports.addMorphoBlueMerklRewardsToMarketInfo = addMorphoBlueMerklRewardsToMarketInfo;
@@ -140,7 +140,7 @@ function getPortfolioData(provider_1, network_1, defaultProvider_1, addresses_1)
140
140
  },
141
141
  };
142
142
  rewardsData[address.toLowerCase()] = {
143
- aaveV3merit: {},
143
+ merkl: {},
144
144
  aaveV3: {},
145
145
  compV3: {},
146
146
  spark: {},
@@ -365,12 +365,12 @@ function getPortfolioData(provider_1, network_1, defaultProvider_1, addresses_1)
365
365
  }))).flat(),
366
366
  ...addresses.map((address) => __awaiter(this, void 0, void 0, function* () {
367
367
  try {
368
- const aaveMeritData = yield (0, aaveV3_2.getMeritUnclaimedRewards)(address, network);
369
- rewardsData[address.toLowerCase()].aaveV3merit = { error: '', data: aaveMeritData };
368
+ const merklData = yield (0, aaveV3_2.getMerklUnclaimedRewards)(address, network);
369
+ rewardsData[address.toLowerCase()].merkl = { error: '', data: merklData };
370
370
  }
371
371
  catch (error) {
372
- console.error(`Error fetching Aave V3 Merit rewards data for address ${address}:`, error);
373
- rewardsData[address.toLowerCase()].aaveV3merit = { error: `Error fetching Aave V3 Merit rewards data for address ${address}`, data: null };
372
+ console.error(`Error fetching Merkl rewards data for address ${address}:`, error);
373
+ rewardsData[address.toLowerCase()].merkl = { error: `Error fetching Merkl rewards data for address ${address}`, data: null };
374
374
  }
375
375
  })),
376
376
  ...aaveV3Markets.map(market => addresses.map((address) => __awaiter(this, void 0, void 0, function* () {
@@ -0,0 +1,6 @@
1
+ import { MerklOpportunity } from '../types';
2
+ /**
3
+ * Fetches every page matching the query. Merkl returns 20 opportunities by default, so omitting
4
+ * `items` and advancing `page` avoids imposing a client-side maximum on the result set.
5
+ */
6
+ export declare const fetchAllMerklOpportunities: (query: Record<string, string>) => Promise<MerklOpportunity[]>;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.fetchAllMerklOpportunities = void 0;
13
+ const utils_1 = require("./utils");
14
+ const MERKL_OPPORTUNITIES_URL = 'https://fe.defisaver.com/api/merkl/opportunities';
15
+ /**
16
+ * Fetches every page matching the query. Merkl returns 20 opportunities by default, so omitting
17
+ * `items` and advancing `page` avoids imposing a client-side maximum on the result set.
18
+ */
19
+ const fetchAllMerklOpportunities = (query) => __awaiter(void 0, void 0, void 0, function* () {
20
+ const opportunities = [];
21
+ let page = 0;
22
+ let pageOpportunities;
23
+ do {
24
+ const searchParams = new URLSearchParams(Object.assign(Object.assign({}, query), { page: page.toString() }));
25
+ const res = yield fetch(`${MERKL_OPPORTUNITIES_URL}?${searchParams}`, {
26
+ signal: AbortSignal.timeout(utils_1.LONGER_TIMEOUT),
27
+ });
28
+ if (!res.ok)
29
+ throw new Error(`Failed to fetch Merkl opportunities page ${page}`);
30
+ const data = yield res.json(); // eslint-disable-line no-await-in-loop
31
+ if (!Array.isArray(data))
32
+ throw new Error(`Invalid Merkl opportunities response on page ${page}`);
33
+ pageOpportunities = data;
34
+ opportunities.push(...pageOpportunities);
35
+ page += 1;
36
+ } while (pageOpportunities.length > 0);
37
+ return opportunities;
38
+ });
39
+ exports.fetchAllMerklOpportunities = fetchAllMerklOpportunities;
@@ -19,6 +19,10 @@ export declare const isEligibleForAaveV3ArbitrumETHLSBorrow: (usedAssets: MMUsed
19
19
  isEligible: boolean;
20
20
  eligibleUSDAmount: string;
21
21
  };
22
+ export declare const isEligibleForCompoundV3PufEthWethBorrow: (usedAssets: MMUsedAssets) => {
23
+ isEligible: boolean;
24
+ eligibleUSDAmount: string;
25
+ };
22
26
  export declare const EligibilityMapping: {
23
27
  [key in IncentiveEligibilityId]: (usedAssets: MMUsedAssets, optionalData: any) => {
24
28
  isEligible: boolean;
@@ -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.EligibilityMapping = exports.isEligibleForAaveV3ArbitrumETHLSBorrow = exports.isEligibleForAaveV3ArbitrumEthSupply = exports.isEligibleForEthenaGHORewards = exports.isEligibleForEthenaUSDeRewards = void 0;
6
+ exports.EligibilityMapping = exports.isEligibleForCompoundV3PufEthWethBorrow = exports.isEligibleForAaveV3ArbitrumETHLSBorrow = exports.isEligibleForAaveV3ArbitrumEthSupply = exports.isEligibleForEthenaGHORewards = exports.isEligibleForEthenaUSDeRewards = void 0;
7
7
  const decimal_js_1 = __importDefault(require("decimal.js"));
8
8
  const common_1 = require("../types/common");
9
9
  const isEligibleForEthenaPairRewards = (usedAssets, { healthRatio }, { baseSymbol, pairSymbol, allowedBorrowAssets, maxHealthRatio, rewardSymbol, }) => {
@@ -70,10 +70,20 @@ const isEligibleForAaveV3ArbitrumETHLSBorrow = (usedAssets) => {
70
70
  return { isEligible: true, eligibleUSDAmount: ETHAmountBorrowed };
71
71
  };
72
72
  exports.isEligibleForAaveV3ArbitrumETHLSBorrow = isEligibleForAaveV3ArbitrumETHLSBorrow;
73
+ // "Borrow WETH with pufETH collateral" — rewards are calculated as min(collateral * 80% LTV, borrow amount)
74
+ const isEligibleForCompoundV3PufEthWethBorrow = (usedAssets) => {
75
+ var _a, _b;
76
+ const collateralUsd = ((_a = usedAssets.pufETH) === null || _a === void 0 ? void 0 : _a.suppliedUsd) || '0';
77
+ const borrowedUsd = ((_b = usedAssets.ETH) === null || _b === void 0 ? void 0 : _b.borrowedUsd) || '0';
78
+ const eligibleUSDAmount = decimal_js_1.default.min(new decimal_js_1.default(collateralUsd).mul('0.8'), borrowedUsd).toString();
79
+ return { isEligible: new decimal_js_1.default(eligibleUSDAmount).gt(0), eligibleUSDAmount };
80
+ };
81
+ exports.isEligibleForCompoundV3PufEthWethBorrow = isEligibleForCompoundV3PufEthWethBorrow;
73
82
  exports.EligibilityMapping = {
74
83
  [common_1.IncentiveEligibilityId.AaveV3EthenaLiquidLeverage]: exports.isEligibleForEthenaUSDeRewards,
75
84
  [common_1.IncentiveEligibilityId.AaveV3ArbitrumEthSupply]: exports.isEligibleForAaveV3ArbitrumEthSupply,
76
85
  [common_1.IncentiveEligibilityId.AaveV3ArbitrumETHLSBorrow]: exports.isEligibleForAaveV3ArbitrumETHLSBorrow,
77
86
  [common_1.IncentiveEligibilityId.AaveV3EthenaLiquidLeveragePlasma]: exports.isEligibleForEthenaUSDeRewards,
78
87
  [common_1.IncentiveEligibilityId.AaveV3EthenaLiquidLeveragePlasmaGHO]: exports.isEligibleForEthenaGHORewards,
88
+ [common_1.IncentiveEligibilityId.CompoundV3PufEthWethBorrow]: exports.isEligibleForCompoundV3PufEthWethBorrow,
79
89
  };