@defisaver/positions-sdk 2.1.144-dev → 2.1.145

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 (96) hide show
  1. package/cjs/config/contracts.d.ts +249 -0
  2. package/cjs/config/contracts.js +11 -1
  3. package/cjs/constants/index.d.ts +1 -0
  4. package/cjs/constants/index.js +2 -1
  5. package/cjs/contracts.d.ts +1510 -0
  6. package/cjs/contracts.js +3 -2
  7. package/cjs/fluid/index.js +6 -2
  8. package/cjs/helpers/fluidHelpers/index.js +5 -4
  9. package/cjs/helpers/index.d.ts +1 -0
  10. package/cjs/helpers/index.js +2 -1
  11. package/cjs/helpers/morphoMidnightHelpers/headroom.d.ts +48 -0
  12. package/cjs/helpers/morphoMidnightHelpers/headroom.js +52 -0
  13. package/cjs/helpers/morphoMidnightHelpers/index.d.ts +113 -0
  14. package/cjs/helpers/morphoMidnightHelpers/index.js +298 -0
  15. package/cjs/helpers/morphoMidnightHelpers/rate.d.ts +15 -0
  16. package/cjs/helpers/morphoMidnightHelpers/rate.js +62 -0
  17. package/cjs/helpers/morphoMidnightHelpers/tenor.d.ts +129 -0
  18. package/cjs/helpers/morphoMidnightHelpers/tenor.js +254 -0
  19. package/cjs/index.d.ts +2 -1
  20. package/cjs/index.js +3 -1
  21. package/cjs/markets/index.d.ts +1 -0
  22. package/cjs/markets/index.js +6 -1
  23. package/cjs/markets/morphoMidnight/index.d.ts +60 -0
  24. package/cjs/markets/morphoMidnight/index.js +659 -0
  25. package/cjs/morphoMidnight/index.d.ts +14 -0
  26. package/cjs/morphoMidnight/index.js +261 -0
  27. package/cjs/portfolio/discovery.js +4 -0
  28. package/cjs/portfolio/index.js +44 -0
  29. package/cjs/services/viem.d.ts +11 -11
  30. package/cjs/staking/eligibility.d.ts +15 -1
  31. package/cjs/staking/eligibility.js +12 -1
  32. package/cjs/staking/staking.js +18 -22
  33. package/cjs/types/index.d.ts +1 -0
  34. package/cjs/types/index.js +1 -0
  35. package/cjs/types/morphoMidnight.d.ts +141 -0
  36. package/cjs/types/morphoMidnight.js +47 -0
  37. package/cjs/types/portfolio.d.ts +4 -0
  38. package/esm/config/contracts.d.ts +249 -0
  39. package/esm/config/contracts.js +9 -0
  40. package/esm/constants/index.d.ts +1 -0
  41. package/esm/constants/index.js +1 -0
  42. package/esm/contracts.d.ts +1510 -0
  43. package/esm/contracts.js +1 -0
  44. package/esm/fluid/index.js +6 -2
  45. package/esm/helpers/fluidHelpers/index.js +6 -5
  46. package/esm/helpers/index.d.ts +1 -0
  47. package/esm/helpers/index.js +1 -0
  48. package/esm/helpers/morphoMidnightHelpers/headroom.d.ts +48 -0
  49. package/esm/helpers/morphoMidnightHelpers/headroom.js +45 -0
  50. package/esm/helpers/morphoMidnightHelpers/index.d.ts +113 -0
  51. package/esm/helpers/morphoMidnightHelpers/index.js +271 -0
  52. package/esm/helpers/morphoMidnightHelpers/rate.d.ts +15 -0
  53. package/esm/helpers/morphoMidnightHelpers/rate.js +50 -0
  54. package/esm/helpers/morphoMidnightHelpers/tenor.d.ts +129 -0
  55. package/esm/helpers/morphoMidnightHelpers/tenor.js +238 -0
  56. package/esm/index.d.ts +2 -1
  57. package/esm/index.js +2 -1
  58. package/esm/markets/index.d.ts +1 -0
  59. package/esm/markets/index.js +1 -0
  60. package/esm/markets/morphoMidnight/index.d.ts +60 -0
  61. package/esm/markets/morphoMidnight/index.js +631 -0
  62. package/esm/morphoMidnight/index.d.ts +14 -0
  63. package/esm/morphoMidnight/index.js +248 -0
  64. package/esm/portfolio/discovery.js +5 -1
  65. package/esm/portfolio/index.js +45 -1
  66. package/esm/services/viem.d.ts +11 -11
  67. package/esm/staking/eligibility.d.ts +15 -1
  68. package/esm/staking/eligibility.js +10 -0
  69. package/esm/staking/staking.js +19 -23
  70. package/esm/types/index.d.ts +1 -0
  71. package/esm/types/index.js +1 -0
  72. package/esm/types/morphoMidnight.d.ts +141 -0
  73. package/esm/types/morphoMidnight.js +44 -0
  74. package/esm/types/portfolio.d.ts +4 -0
  75. package/package.json +1 -1
  76. package/src/config/contracts.ts +9 -0
  77. package/src/constants/index.ts +1 -0
  78. package/src/contracts.ts +1 -0
  79. package/src/fluid/index.ts +6 -2
  80. package/src/helpers/fluidHelpers/index.ts +6 -5
  81. package/src/helpers/index.ts +1 -0
  82. package/src/helpers/morphoMidnightHelpers/headroom.ts +82 -0
  83. package/src/helpers/morphoMidnightHelpers/index.ts +485 -0
  84. package/src/helpers/morphoMidnightHelpers/rate.ts +71 -0
  85. package/src/helpers/morphoMidnightHelpers/tenor.ts +407 -0
  86. package/src/index.ts +2 -0
  87. package/src/markets/index.ts +3 -0
  88. package/src/markets/morphoMidnight/index.ts +675 -0
  89. package/src/morphoMidnight/index.ts +276 -0
  90. package/src/portfolio/discovery.ts +6 -0
  91. package/src/portfolio/index.ts +44 -0
  92. package/src/staking/eligibility.ts +29 -2
  93. package/src/staking/staking.ts +20 -22
  94. package/src/types/index.ts +1 -0
  95. package/src/types/morphoMidnight.ts +163 -0
  96. package/src/types/portfolio.ts +4 -0
@@ -12,7 +12,7 @@ import memoize from 'memoizee';
12
12
  import { NetworkNumber, } from '../types/common';
13
13
  import { BLOCKS_IN_A_YEAR } from '../constants';
14
14
  import { DEFAULT_TIMEOUT } from '../services/utils';
15
- import { EligibilityMapping } from './eligibility';
15
+ import { getIncentiveEligibility } from './eligibility';
16
16
  const getSsrApy = () => __awaiter(void 0, void 0, void 0, function* () {
17
17
  try {
18
18
  const res = yield fetch('https://fe.defisaver.com/api/sky/data', { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
@@ -194,17 +194,15 @@ export const calculateNetApy = ({ usedAssets, assetsData, optionalData, }) => {
194
194
  acc.supplyInterest = new Dec(acc.supplyInterest).add(supplyInterest.toString()).toString();
195
195
  if (assetData.supplyIncentives) {
196
196
  for (const supplyIncentive of assetData.supplyIncentives) {
197
- const { apy, eligibilityId } = supplyIncentive;
198
- const eligibilityCheck = eligibilityId ? EligibilityMapping[eligibilityId] : null;
199
- if (eligibilityCheck) {
200
- const { isEligible, eligibleUSDAmount } = eligibilityCheck(usedAssets, optionalData);
201
- const incentiveInterest = isEligible ? calculateInterestEarned(eligibleUSDAmount, apy, 'year', true) : '0';
202
- acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
203
- }
204
- else {
205
- const incentiveInterest = calculateInterestEarned(amount, apy, 'year', true);
206
- acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
207
- }
197
+ const { apy } = supplyIncentive;
198
+ const { isEligible, eligibleUSDAmount } = getIncentiveEligibility({
199
+ incentive: supplyIncentive,
200
+ usedAssets,
201
+ defaultEligibleUSDAmount: amount,
202
+ optionalData,
203
+ });
204
+ const incentiveInterest = isEligible ? calculateInterestEarned(eligibleUSDAmount, apy, 'year', true) : '0';
205
+ acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
208
206
  }
209
207
  }
210
208
  }
@@ -216,17 +214,15 @@ export const calculateNetApy = ({ usedAssets, assetsData, optionalData, }) => {
216
214
  acc.borrowInterest = new Dec(acc.borrowInterest).sub(borrowInterest.toString()).toString();
217
215
  if (assetData.borrowIncentives) {
218
216
  for (const borrowIncentive of assetData.borrowIncentives) {
219
- const { apy, eligibilityId } = borrowIncentive;
220
- const eligibilityCheck = eligibilityId ? EligibilityMapping[eligibilityId] : null;
221
- if (eligibilityCheck) {
222
- const { isEligible, eligibleUSDAmount } = eligibilityCheck(usedAssets, optionalData);
223
- const incentiveInterest = isEligible ? calculateInterestEarned(eligibleUSDAmount, apy, 'year', true) : '0';
224
- acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
225
- }
226
- else {
227
- const incentiveInterest = calculateInterestEarned(amount, apy, 'year', true);
228
- acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
229
- }
217
+ const { apy } = borrowIncentive;
218
+ const { isEligible, eligibleUSDAmount } = getIncentiveEligibility({
219
+ incentive: borrowIncentive,
220
+ usedAssets,
221
+ defaultEligibleUSDAmount: amount,
222
+ optionalData,
223
+ });
224
+ const incentiveInterest = isEligible ? calculateInterestEarned(eligibleUSDAmount, apy, 'year', true) : '0';
225
+ acc.incentiveUsd = new Dec(acc.incentiveUsd).add(incentiveInterest).toString();
230
226
  }
231
227
  }
232
228
  }
@@ -6,6 +6,7 @@ export * from './liquity';
6
6
  export * from './liquityV2';
7
7
  export * from './maker';
8
8
  export * from './morphoBlue';
9
+ export * from './morphoMidnight';
9
10
  export * from './llamaLend';
10
11
  export * from './fluid';
11
12
  export * from './portfolio';
@@ -6,6 +6,7 @@ export * from './liquity';
6
6
  export * from './liquityV2';
7
7
  export * from './maker';
8
8
  export * from './morphoBlue';
9
+ export * from './morphoMidnight';
9
10
  export * from './llamaLend';
10
11
  export * from './fluid';
11
12
  export * from './portfolio';
@@ -0,0 +1,141 @@
1
+ import { EthAddress, IncentiveData, LeverageType, MMUsedAssets, NetworkNumber } from './common';
2
+ export declare enum MorphoMidnightVersions {
3
+ MorphoMidnightCbBTCUSDC_860_20260731_Base = "morphomidnightcbbtcusdc_860_20260731_base",
4
+ MorphoMidnightCbBTCUSDC_860_20260828_Base = "morphomidnightcbbtcusdc_860_20260828_base",
5
+ MorphoMidnightCbBTCUSDC_860_20260925_Base = "morphomidnightcbbtcusdc_860_20260925_base",
6
+ MorphoMidnightCbBTCUSDC_860_20261030_Base = "morphomidnightcbbtcusdc_860_20261030_base",
7
+ MorphoMidnightCbBTCUSDC_860_20261127_Base = "morphomidnightcbbtcusdc_860_20261127_base",
8
+ MorphoMidnightCbBTCUSDC_860_20261225_Base = "morphomidnightcbbtcusdc_860_20261225_base",
9
+ MorphoMidnightTenorCbBTCUSDC_20260827_Base = "morphomidnighttenorcbbtcusdc_20260827_base",
10
+ MorphoMidnightTenorCbBTCUSDC_20260924_Base = "morphomidnighttenorcbbtcusdc_20260924_base",
11
+ MorphoMidnightTenorCbBTCUSDC_20261022_Base = "morphomidnighttenorcbbtcusdc_20261022_base",
12
+ MorphoMidnightTenorCbBTCUSDC_20261119_Base = "morphomidnighttenorcbbtcusdc_20261119_base",
13
+ MorphoMidnightTenorCbBTCUSDC_20261217_Base = "morphomidnighttenorcbbtcusdc_20261217_base",
14
+ MorphoMidnightTenorWETHUSDC_20260827_Base = "morphomidnighttenorwethusdc_20260827_base",
15
+ MorphoMidnightTenorWETHUSDC_20260924_Base = "morphomidnighttenorwethusdc_20260924_base",
16
+ MorphoMidnightTenorWETHUSDC_20261022_Base = "morphomidnighttenorwethusdc_20261022_base",
17
+ MorphoMidnightTenorWETHUSDC_20261119_Base = "morphomidnighttenorwethusdc_20261119_base",
18
+ MorphoMidnightTenorWETHUSDC_20261217_Base = "morphomidnighttenorwethusdc_20261217_base",
19
+ MorphoMidnightTenorCbETHWETH_20260827_Base = "morphomidnighttenorcbethweth_20260827_base",
20
+ MorphoMidnightTenorCbETHWETH_20260924_Base = "morphomidnighttenorcbethweth_20260924_base",
21
+ MorphoMidnightTenorCbETHWETH_20261022_Base = "morphomidnighttenorcbethweth_20261022_base",
22
+ MorphoMidnightTenorCbETHWETH_20261119_Base = "morphomidnighttenorcbethweth_20261119_base",
23
+ MorphoMidnightTenorCbETHWETH_20261217_Base = "morphomidnighttenorcbethweth_20261217_base"
24
+ }
25
+ export type MorphoMidnightCurator = 'Morpho' | 'Tenor';
26
+ export interface MorphoMidnightCollateralParams {
27
+ token: EthAddress;
28
+ lltv: number | string;
29
+ liquidationCursor: number | string;
30
+ oracle: EthAddress;
31
+ }
32
+ export interface MorphoMidnightMarketData {
33
+ chainIds: NetworkNumber[];
34
+ label: string;
35
+ shortLabel: string;
36
+ url: string;
37
+ value: MorphoMidnightVersions;
38
+ midnight: EthAddress;
39
+ loanToken: EthAddress;
40
+ collaterals: MorphoMidnightCollateralParams[];
41
+ /**
42
+ * Tenor's curated markets list the curator's own vault share token next to the real collateral.
43
+ */
44
+ hiddenCollaterals?: MorphoMidnightCollateralParams[];
45
+ maturity: number;
46
+ rcfThreshold: number | string;
47
+ enterGate: EthAddress;
48
+ liquidatorGate: EthAddress;
49
+ marketId: string;
50
+ protocolName: string;
51
+ curator: MorphoMidnightCurator;
52
+ }
53
+ export interface MorphoMidnightAssetData {
54
+ symbol: string;
55
+ address: string;
56
+ price: string;
57
+ supplyRate: string;
58
+ borrowRate: string;
59
+ supplyIncentives: IncentiveData[];
60
+ borrowIncentives: IncentiveData[];
61
+ totalSupply?: string;
62
+ totalBorrow?: string;
63
+ canBeSupplied?: boolean;
64
+ canBeBorrowed?: boolean;
65
+ lltv?: string;
66
+ }
67
+ export type MorphoMidnightAssetsData = {
68
+ [key: string]: MorphoMidnightAssetData;
69
+ };
70
+ export interface MorphoMidnightMarketInfo {
71
+ id: string;
72
+ loanToken: string;
73
+ collaterals: string[];
74
+ maturity: number;
75
+ isMatured: boolean;
76
+ totalUnits: string;
77
+ withdrawable: string;
78
+ totalDebt: string;
79
+ lossFactor: string;
80
+ tickSpacing: number;
81
+ utillization: string;
82
+ assetsData: MorphoMidnightAssetsData;
83
+ }
84
+ export type MorphoMidnightBookSide = 'bids' | 'asks';
85
+ export interface MorphoMidnightBookOffer {
86
+ rate: string;
87
+ liquidity: string;
88
+ }
89
+ export interface MorphoMidnightParsedBook {
90
+ bestRate: string;
91
+ totalLiquidity: string;
92
+ offers: MorphoMidnightBookOffer[];
93
+ }
94
+ export interface MorphoMidnightAggregatedPositionData {
95
+ suppliedUsd: string;
96
+ suppliedCollateralUsd: string;
97
+ borrowedUsd: string;
98
+ borrowLimitUsd: string;
99
+ liquidationLimitUsd: string;
100
+ leftToBorrowUsd: string;
101
+ leftToBorrow: string;
102
+ netApy: string;
103
+ incentiveUsd: string;
104
+ totalInterestUsd: string;
105
+ ltv: string;
106
+ ratio: string;
107
+ healthRatio: string;
108
+ leveragedType: LeverageType;
109
+ leveragedAsset?: string;
110
+ currentVolatilePairRatio?: string;
111
+ liquidationPrice?: string;
112
+ minCollRatio?: string;
113
+ collLiquidationRatio?: string;
114
+ exposure: string;
115
+ }
116
+ /**
117
+ * How much weight `borrowRate` / `debtBase` / `debtInterest` carry on a given position. They fall back to
118
+ * `'0'` / `debt` / `'0'`, which is indistinguishable from a real 0%-interest position, so anything
119
+ * displaying them has to read this to know whether it is looking at a number or at a placeholder.
120
+ *
121
+ * - `Available` — reported and reconciled against the on-chain debt (also lenders and debt-free positions,
122
+ * which have nothing to report).
123
+ * - `Pending` — the indexer has not caught up with the chain yet: it does not know the position, or its
124
+ * split describes a different debt. Refetching resolves it; `getMorphoMidnightUserBorrowInfo` failing
125
+ * outright lands here too, since the next call may well succeed.
126
+ */
127
+ export declare enum MorphoMidnightBorrowInfoStatus {
128
+ Available = "available",
129
+ Pending = "pending"
130
+ }
131
+ export interface MorphoMidnightPositionData extends MorphoMidnightAggregatedPositionData {
132
+ usedAssets: MMUsedAssets;
133
+ credit: string;
134
+ debt: string;
135
+ borrowRate: string;
136
+ debtBase: string;
137
+ debtInterest: string;
138
+ borrowInfoStatus: MorphoMidnightBorrowInfoStatus;
139
+ maturity: number;
140
+ isMatured: boolean;
141
+ }
@@ -0,0 +1,44 @@
1
+ export var MorphoMidnightVersions;
2
+ (function (MorphoMidnightVersions) {
3
+ // BASE
4
+ // Fixed-term markets are disambiguated by maturity (YYYYMMDD), so the same pair recurs across dates.
5
+ // Sourced from the official listing at https://markets.morpho.org/fixed/base (see sitemap.xml).
6
+ MorphoMidnightVersions["MorphoMidnightCbBTCUSDC_860_20260731_Base"] = "morphomidnightcbbtcusdc_860_20260731_base";
7
+ MorphoMidnightVersions["MorphoMidnightCbBTCUSDC_860_20260828_Base"] = "morphomidnightcbbtcusdc_860_20260828_base";
8
+ MorphoMidnightVersions["MorphoMidnightCbBTCUSDC_860_20260925_Base"] = "morphomidnightcbbtcusdc_860_20260925_base";
9
+ MorphoMidnightVersions["MorphoMidnightCbBTCUSDC_860_20261030_Base"] = "morphomidnightcbbtcusdc_860_20261030_base";
10
+ MorphoMidnightVersions["MorphoMidnightCbBTCUSDC_860_20261127_Base"] = "morphomidnightcbbtcusdc_860_20261127_base";
11
+ MorphoMidnightVersions["MorphoMidnightCbBTCUSDC_860_20261225_Base"] = "morphomidnightcbbtcusdc_860_20261225_base";
12
+ // Tenor-hosted Midnight markets (same core, different order book)
13
+ MorphoMidnightVersions["MorphoMidnightTenorCbBTCUSDC_20260827_Base"] = "morphomidnighttenorcbbtcusdc_20260827_base";
14
+ MorphoMidnightVersions["MorphoMidnightTenorCbBTCUSDC_20260924_Base"] = "morphomidnighttenorcbbtcusdc_20260924_base";
15
+ MorphoMidnightVersions["MorphoMidnightTenorCbBTCUSDC_20261022_Base"] = "morphomidnighttenorcbbtcusdc_20261022_base";
16
+ MorphoMidnightVersions["MorphoMidnightTenorCbBTCUSDC_20261119_Base"] = "morphomidnighttenorcbbtcusdc_20261119_base";
17
+ MorphoMidnightVersions["MorphoMidnightTenorCbBTCUSDC_20261217_Base"] = "morphomidnighttenorcbbtcusdc_20261217_base";
18
+ MorphoMidnightVersions["MorphoMidnightTenorWETHUSDC_20260827_Base"] = "morphomidnighttenorwethusdc_20260827_base";
19
+ MorphoMidnightVersions["MorphoMidnightTenorWETHUSDC_20260924_Base"] = "morphomidnighttenorwethusdc_20260924_base";
20
+ MorphoMidnightVersions["MorphoMidnightTenorWETHUSDC_20261022_Base"] = "morphomidnighttenorwethusdc_20261022_base";
21
+ MorphoMidnightVersions["MorphoMidnightTenorWETHUSDC_20261119_Base"] = "morphomidnighttenorwethusdc_20261119_base";
22
+ MorphoMidnightVersions["MorphoMidnightTenorWETHUSDC_20261217_Base"] = "morphomidnighttenorwethusdc_20261217_base";
23
+ MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20260827_Base"] = "morphomidnighttenorcbethweth_20260827_base";
24
+ MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20260924_Base"] = "morphomidnighttenorcbethweth_20260924_base";
25
+ MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20261022_Base"] = "morphomidnighttenorcbethweth_20261022_base";
26
+ MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20261119_Base"] = "morphomidnighttenorcbethweth_20261119_base";
27
+ MorphoMidnightVersions["MorphoMidnightTenorCbETHWETH_20261217_Base"] = "morphomidnighttenorcbethweth_20261217_base";
28
+ })(MorphoMidnightVersions || (MorphoMidnightVersions = {}));
29
+ /**
30
+ * How much weight `borrowRate` / `debtBase` / `debtInterest` carry on a given position. They fall back to
31
+ * `'0'` / `debt` / `'0'`, which is indistinguishable from a real 0%-interest position, so anything
32
+ * displaying them has to read this to know whether it is looking at a number or at a placeholder.
33
+ *
34
+ * - `Available` — reported and reconciled against the on-chain debt (also lenders and debt-free positions,
35
+ * which have nothing to report).
36
+ * - `Pending` — the indexer has not caught up with the chain yet: it does not know the position, or its
37
+ * split describes a different debt. Refetching resolves it; `getMorphoMidnightUserBorrowInfo` failing
38
+ * outright lands here too, since the next call may well succeed.
39
+ */
40
+ export var MorphoMidnightBorrowInfoStatus;
41
+ (function (MorphoMidnightBorrowInfoStatus) {
42
+ MorphoMidnightBorrowInfoStatus["Available"] = "available";
43
+ MorphoMidnightBorrowInfoStatus["Pending"] = "pending";
44
+ })(MorphoMidnightBorrowInfoStatus || (MorphoMidnightBorrowInfoStatus = {}));
@@ -8,6 +8,7 @@ import { LiquityTroveInfo } from './liquity';
8
8
  import { LlamaLendUserData, LlamaLendVersionsType } from './llamaLend';
9
9
  import { CdpData } from './maker';
10
10
  import { MorphoBluePositionData, MorphoBlueVersions } from './morphoBlue';
11
+ import { MorphoMidnightPositionData, MorphoMidnightVersions } from './morphoMidnight';
11
12
  import { SparkPositionData, SparkVersions } from './spark';
12
13
  export interface PortfolioProtocolData<T> {
13
14
  error: string;
@@ -20,6 +21,9 @@ export interface PortfolioPositionsDataForAddress {
20
21
  morphoBlue: {
21
22
  [key in MorphoBlueVersions]?: PortfolioProtocolData<MorphoBluePositionData>;
22
23
  };
24
+ morphoMidnight: {
25
+ [key in MorphoMidnightVersions]?: PortfolioProtocolData<MorphoMidnightPositionData>;
26
+ };
23
27
  compoundV3: {
24
28
  [key in CompoundVersions]?: PortfolioProtocolData<CompoundV3PositionData>;
25
29
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defisaver/positions-sdk",
3
- "version": "2.1.144-dev",
3
+ "version": "2.1.145",
4
4
  "description": "",
5
5
  "main": "./cjs/index.js",
6
6
  "module": "./esm/index.js",
@@ -1113,6 +1113,15 @@ export const MorphoBlueView = {
1113
1113
  }
1114
1114
  },
1115
1115
  } as const;
1116
+ export const MidnightView = {
1117
+ "abi": [{"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"}],"name":"getMarketInfo","outputs":[{"components":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"uint128","name":"totalUnits","type":"uint128"},{"internalType":"uint128","name":"lossFactor","type":"uint128"},{"internalType":"uint128","name":"withdrawable","type":"uint128"},{"internalType":"uint128","name":"continuousFeeCredit","type":"uint128"},{"internalType":"uint16[7]","name":"settlementFees","type":"uint16[7]"},{"internalType":"uint32","name":"continuousFee","type":"uint32"},{"internalType":"uint8","name":"tickSpacing","type":"uint8"},{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"internalType":"struct MidnightView.MarketInfo","name":"info","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"},{"internalType":"address","name":"_user","type":"address"}],"name":"getPositionInfo","outputs":[{"components":[{"internalType":"uint128","name":"credit","type":"uint128"},{"internalType":"uint128","name":"pendingFee","type":"uint128"},{"internalType":"uint128","name":"debt","type":"uint128"},{"internalType":"uint128","name":"collateralBitmap","type":"uint128"},{"internalType":"uint128[]","name":"collateral","type":"uint128[]"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct MidnightView.PositionInfo","name":"pos","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"},{"internalType":"address","name":"_user","type":"address"}],"name":"getRatio","outputs":[{"internalType":"uint256","name":"ratio","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"midnight","type":"address"},{"internalType":"address","name":"loanToken","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"},{"internalType":"uint256","name":"liquidationCursor","type":"uint256"},{"internalType":"address","name":"oracle","type":"address"}],"internalType":"struct CollateralParams[]","name":"collateralParams","type":"tuple[]"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint256","name":"rcfThreshold","type":"uint256"},{"internalType":"address","name":"enterGate","type":"address"},{"internalType":"address","name":"liquidatorGate","type":"address"}],"internalType":"struct Market","name":"_market","type":"tuple"}],"name":"toId","outputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"}],"name":"toMarket","outputs":[{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"midnight","type":"address"},{"internalType":"address","name":"loanToken","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"},{"internalType":"uint256","name":"liquidationCursor","type":"uint256"},{"internalType":"address","name":"oracle","type":"address"}],"internalType":"struct CollateralParams[]","name":"collateralParams","type":"tuple[]"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint256","name":"rcfThreshold","type":"uint256"},{"internalType":"address","name":"enterGate","type":"address"},{"internalType":"address","name":"liquidatorGate","type":"address"}],"internalType":"struct Market","name":"market","type":"tuple"}],"stateMutability":"view","type":"function"}],
1118
+ "networks": {
1119
+ "8453": {
1120
+ "address": "0x3aa272f329E8B562A3bA56Bb6979a44D23A28839",
1121
+ "createdBlock": 48932293,
1122
+ }
1123
+ },
1124
+ } as const;
1116
1125
  export const FeedRegistry = {
1117
1126
  "abi": [{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"address","name":"aggregator","type":"address"}],"name":"confirmFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"description","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAccessController","outputs":[{"internalType":"contract AccessControllerInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"getAnswer","outputs":[{"internalType":"int256","name":"answer","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"getCurrentPhaseId","outputs":[{"internalType":"uint16","name":"currentPhaseId","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"getFeed","outputs":[{"internalType":"contract AggregatorV2V3Interface","name":"aggregator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"getNextRoundId","outputs":[{"internalType":"uint80","name":"nextRoundId","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint16","name":"phaseId","type":"uint16"}],"name":"getPhase","outputs":[{"components":[{"internalType":"uint16","name":"phaseId","type":"uint16"},{"internalType":"uint80","name":"startingAggregatorRoundId","type":"uint80"},{"internalType":"uint80","name":"endingAggregatorRoundId","type":"uint80"}],"internalType":"struct FeedRegistryInterface.Phase","name":"phase","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint16","name":"phaseId","type":"uint16"}],"name":"getPhaseFeed","outputs":[{"internalType":"contract AggregatorV2V3Interface","name":"aggregator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint16","name":"phaseId","type":"uint16"}],"name":"getPhaseRange","outputs":[{"internalType":"uint80","name":"startingRoundId","type":"uint80"},{"internalType":"uint80","name":"endingRoundId","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"getPreviousRoundId","outputs":[{"internalType":"uint80","name":"previousRoundId","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"getProposedFeed","outputs":[{"internalType":"contract AggregatorV2V3Interface","name":"proposedAggregator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint80","name":"_roundId","type":"uint80"}],"name":"getRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"getRoundFeed","outputs":[{"internalType":"contract AggregatorV2V3Interface","name":"aggregator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"getTimestamp","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"aggregator","type":"address"}],"name":"isFeedEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"latestAnswer","outputs":[{"internalType":"int256","name":"answer","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"latestRound","outputs":[{"internalType":"uint256","name":"roundId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"latestRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"latestTimestamp","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"address","name":"aggregator","type":"address"}],"name":"proposeFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"},{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"proposedGetRoundData","outputs":[{"internalType":"uint80","name":"id","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"proposedLatestRoundData","outputs":[{"internalType":"uint80","name":"id","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract AccessControllerInterface","name":"_accessController","type":"address"}],"name":"setAccessController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"base","type":"address"},{"internalType":"address","name":"quote","type":"address"}],"name":"version","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}],
1118
1127
  "networks": {
@@ -6,6 +6,7 @@ export const SECONDS_PER_WEEK = 7 * SECONDS_PER_DAY;
6
6
  export const AVG_BLOCK_TIME = 12;
7
7
  export const BLOCKS_IN_A_YEAR = SECONDS_PER_YEAR / AVG_BLOCK_TIME;
8
8
  export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
9
+ export const ZERO_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000';
9
10
  export const WAD = 1e18;
10
11
  export const USD_QUOTE = '0x0000000000000000000000000000000000000348';
11
12
  export const borrowOperations = ['borrow', 'payback'];
package/src/contracts.ts CHANGED
@@ -122,6 +122,7 @@ export const getYearnV3VaultContractViem = (client: Client, address: HexString)
122
122
  };
123
123
 
124
124
  export const MorphoBlueViewContractViem = createViemContractFromConfigFunc('MorphoBlueView');
125
+ export const MorphoMidnightViewContractViem = createViemContractFromConfigFunc('MidnightView');
125
126
  export const AaveLoanInfoV2ContractViem = createViemContractFromConfigFunc('AaveLoanInfoV2');
126
127
  export const AaveV3ViewContractViem = createViemContractFromConfigFunc('AaveV3View');
127
128
  export const AaveIncentiveDataProviderV3ContractViem = createViemContractFromConfigFunc('AaveUiIncentiveDataProviderV3');
@@ -1689,9 +1689,13 @@ export const getAllUserEarnPositionsWithFTokens = async (
1689
1689
  export const _getUserPositions = async (provider: PublicClient, network: NetworkNumber, user: EthAddress) => {
1690
1690
  const view = FluidViewContractViem(provider, network);
1691
1691
 
1692
- const data = await view.read.getUserPositions([user]);
1692
+ const [data, merklCampaigns] = await Promise.all([
1693
+ view.read.getUserPositions([user]),
1694
+ getFluidMerklCampaigns(network),
1695
+ ]);
1693
1696
 
1694
- const parsedMarketData = (await Promise.all(data[1].map(async (vaultData) => parseMarketData(provider, vaultData, network))));
1697
+ const parsedMarketData = (await Promise.all(data[1].map(async (vaultData) => parseMarketData(provider, vaultData, network))))
1698
+ .map(marketData => (marketData ? attachFluidMerklIncentives(marketData, merklCampaigns) : marketData));
1695
1699
 
1696
1700
  const userData = data[0].map((position, i) => (parsedMarketData[i] && { ...parseUserData(position, parsedMarketData[i]) }));
1697
1701
 
@@ -12,7 +12,7 @@ import {
12
12
  import {
13
13
  calcLeverageLiqPrice, getAssetsTotal, getExposure, isLeveragedPos,
14
14
  } from '../../moneymarket';
15
- import { calculateInterestEarned, calculateNetApy, EligibilityMapping } from '../../staking';
15
+ import { calculateInterestEarned, calculateNetApy, getIncentiveEligibility } from '../../staking';
16
16
  import { IncentiveSide, LeverageType, MMAssetsData } from '../../types/common';
17
17
  import { getEthAmountForDecimals } from '../../services/utils';
18
18
 
@@ -33,10 +33,11 @@ const getMerklIncentiveInterest = (
33
33
  principal: string,
34
34
  usedAssets: FluidUsedAssets,
35
35
  ) => incentives.reduce((total, { apy, eligibilityId }) => {
36
- const eligibilityCheck = eligibilityId ? EligibilityMapping[eligibilityId] : null;
37
- const { isEligible, eligibleUSDAmount } = eligibilityCheck
38
- ? eligibilityCheck(usedAssets, undefined)
39
- : { isEligible: true, eligibleUSDAmount: principal };
36
+ const { isEligible, eligibleUSDAmount } = getIncentiveEligibility({
37
+ incentive: { eligibilityId },
38
+ usedAssets,
39
+ defaultEligibleUSDAmount: principal,
40
+ });
40
41
  const incentiveInterest = isEligible ? calculateInterestEarned(eligibleUSDAmount, apy, 'year', true) : 0;
41
42
 
42
43
  return new Dec(total).add(incentiveInterest).toString();
@@ -4,6 +4,7 @@ export * as sparkHelpers from './sparkHelpers';
4
4
  export * as curveUsdHelpers from './curveUsdHelpers';
5
5
  export * as makerHelpers from './makerHelpers';
6
6
  export * as morphoBlueHelpers from './morphoBlueHelpers';
7
+ export * as morphoMidnightHelpers from './morphoMidnightHelpers';
7
8
  export * as llamaLendHelpers from './llamaLendHelpers';
8
9
  export * as liquityV2Helpers from './liquityV2Helpers';
9
10
  export * as fluidHelpers from './fluidHelpers';
@@ -0,0 +1,82 @@
1
+ import Dec from 'decimal.js';
2
+
3
+ /**
4
+ * Safety margin every Midnight position is held at. On Midnight the lltv is *both* the borrow cap and the
5
+ * liquidation threshold (`liquidationLimitUsd === borrowLimitUsd` in the aggregate), so a position borrowed
6
+ * to its full limit opens at a health ratio of exactly 1 — immediately liquidatable. This is what holds it
7
+ * off that line, and what absorbs swap price impact on a boost.
8
+ */
9
+ export const MIDNIGHT_MIN_HEALTH_RATIO = 1.025;
10
+
11
+ export interface MidnightBorrowHeadroomParams {
12
+ borrowLimitUsd: Dec.Value;
13
+ borrowedUsd: Dec.Value;
14
+ /** Principal a unit of borrow power is worth (loan-per-unit price, ≤ 1), off a book rate. */
15
+ principalPerUnit?: Dec.Value;
16
+ /** Depth the book can fill, in loan tokens, from the same quote. Omitted leaves the answer uncapped. */
17
+ availableAssets?: Dec.Value;
18
+ loanTokenPrice?: Dec.Value;
19
+ /** Loan token decimals. Omitted skips the quantisation, which only a caller ignoring `leftToBorrow` wants. */
20
+ loanTokenDecimals?: number;
21
+ isMatured?: boolean;
22
+ minHealthRatio?: Dec.Value;
23
+ }
24
+
25
+ export interface MidnightBorrowHeadroom {
26
+ /** Face-value headroom, before the order-book discount. What a *boost* solve scales itself. */
27
+ headroomUsd: string;
28
+ /** The borrowable amount in loan tokens — the Borrow action's Max, quantised exactly as it is typed in. */
29
+ leftToBorrow: string;
30
+ /** `leftToBorrow · price`. Derived FROM the token amount, never computed alongside it — see below. */
31
+ leftToBorrowUsd: string;
32
+ borrowPowerUsed: string;
33
+ }
34
+
35
+ /**
36
+ * The one answer to "how much more can this position borrow". Every surface that sizes a Midnight borrow
37
+ * derives from here — the max-borrow getter, the position overview's headroom rows, and the max-boost
38
+ * solve, which scales `headroomUsd` by its own leverage factors — so a max and the figure shown next to it
39
+ * cannot drift into two different formulas. They did: the overview used to price
40
+ * the whole headroom off the top-of-book offer (often dust) with no rate pad and no depth cap, while the
41
+ * max walked the book for its real size, which is what put the two 1–2% apart.
42
+ *
43
+ * Three corrections separate this from a plain `limit − debt`, and dropping any one overstates the answer:
44
+ * `minHealthRatio` reserves the margin, `principalPerUnit` turns face-value headroom into the principal a
45
+ * borrow actually receives (a correction that grows with the term, past 7% on a one-year market), and
46
+ * `availableAssets` caps it at what the book can currently fill.
47
+ *
48
+ * `leftToBorrowUsd` is deliberately `leftToBorrow · price` rather than the USD figure the token amount was
49
+ * derived from: the two differ by the round-down to token decimals, and a UI showing one next to an input
50
+ * holding the other would be reporting a difference the user cannot spend. Everything quantises once, here.
51
+ */
52
+ export const getMorphoMidnightBorrowHeadroom = ({
53
+ borrowLimitUsd,
54
+ borrowedUsd,
55
+ principalPerUnit = 1,
56
+ availableAssets,
57
+ loanTokenPrice = 0,
58
+ loanTokenDecimals,
59
+ isMatured = false,
60
+ minHealthRatio = MIDNIGHT_MIN_HEALTH_RATIO,
61
+ }: MidnightBorrowHeadroomParams): MidnightBorrowHeadroom => {
62
+ const usableLimitUsd = new Dec(borrowLimitUsd).div(minHealthRatio);
63
+ const headroomUsd = isMatured ? new Dec(0) : Dec.max(0, usableLimitUsd.sub(borrowedUsd));
64
+
65
+ const price = new Dec(loanTokenPrice);
66
+ // Capped in loan tokens rather than in USD: `availableAssets` is quoted in tokens, and converting it to
67
+ // USD only to divide the result back out again would re-round the one number that has to land exactly.
68
+ const payout = price.lte(0) ? new Dec(0) : headroomUsd.mul(principalPerUnit).div(price);
69
+ const capped = availableAssets === undefined ? payout : Dec.min(payout, availableAssets);
70
+ // Round down: rounding the last decimal up would eat into the margin we just reserved.
71
+ const leftToBorrow = loanTokenDecimals === undefined ? capped : capped.toDP(loanTokenDecimals, Dec.ROUND_DOWN);
72
+
73
+ return {
74
+ headroomUsd: headroomUsd.toString(),
75
+ leftToBorrow: leftToBorrow.toString(),
76
+ leftToBorrowUsd: leftToBorrow.mul(price).toString(),
77
+ // Measured against the margined limit rather than the raw one, so it reads 100% at max borrow instead
78
+ // of 97.56%, and can exceed 100% inside the reserved band. Deliberately left uncapped by book depth:
79
+ // how much a position has borrowed is a property of the position, not of what the book can fill today.
80
+ borrowPowerUsed: usableLimitUsd.lte(0) ? '0' : new Dec(borrowedUsd).div(usableLimitUsd).mul(100).toString(),
81
+ };
82
+ };