@defisaver/positions-sdk 2.1.127-midnight-1-dev → 2.1.127-midnight-3-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 (76) hide show
  1. package/cjs/aaveV3/index.js +7 -1
  2. package/cjs/aaveV4/lend.js +3 -3
  3. package/cjs/claiming/index.d.ts +2 -1
  4. package/cjs/claiming/index.js +3 -1
  5. package/cjs/claiming/uniswap.d.ts +5 -0
  6. package/cjs/claiming/uniswap.js +75 -0
  7. package/cjs/config/contracts.d.ts +22 -0
  8. package/cjs/config/contracts.js +9 -1
  9. package/cjs/contracts.d.ts +35 -0
  10. package/cjs/contracts.js +2 -1
  11. package/cjs/curveUsd/index.js +1 -1
  12. package/cjs/helpers/morphoBlueHelpers/index.js +1 -0
  13. package/cjs/helpers/morphoMidnightHelpers/index.d.ts +45 -7
  14. package/cjs/helpers/morphoMidnightHelpers/index.js +101 -13
  15. package/cjs/llamaLend/index.js +1 -1
  16. package/cjs/markets/aaveV4/index.d.ts +2 -0
  17. package/cjs/markets/aaveV4/index.js +14 -1
  18. package/cjs/morphoBlue/index.d.ts +8 -6
  19. package/cjs/morphoBlue/index.js +69 -40
  20. package/cjs/portfolio/index.js +31 -2
  21. package/cjs/savings/summerVaults/options.js +10 -10
  22. package/cjs/staking/staking.js +3 -0
  23. package/cjs/types/aaveV4.d.ts +2 -1
  24. package/cjs/types/aaveV4.js +1 -0
  25. package/cjs/types/claiming.d.ts +10 -0
  26. package/cjs/types/claiming.js +2 -0
  27. package/cjs/types/morphoBlue.d.ts +9 -0
  28. package/cjs/types/morphoMidnight.d.ts +9 -0
  29. package/esm/aaveV3/index.js +7 -1
  30. package/esm/aaveV4/lend.js +3 -3
  31. package/esm/claiming/index.d.ts +2 -1
  32. package/esm/claiming/index.js +2 -1
  33. package/esm/claiming/uniswap.d.ts +5 -0
  34. package/esm/claiming/uniswap.js +67 -0
  35. package/esm/config/contracts.d.ts +22 -0
  36. package/esm/config/contracts.js +8 -0
  37. package/esm/contracts.d.ts +35 -0
  38. package/esm/contracts.js +1 -0
  39. package/esm/curveUsd/index.js +1 -1
  40. package/esm/helpers/morphoBlueHelpers/index.js +1 -0
  41. package/esm/helpers/morphoMidnightHelpers/index.d.ts +45 -7
  42. package/esm/helpers/morphoMidnightHelpers/index.js +98 -13
  43. package/esm/llamaLend/index.js +1 -1
  44. package/esm/markets/aaveV4/index.d.ts +2 -0
  45. package/esm/markets/aaveV4/index.js +12 -0
  46. package/esm/morphoBlue/index.d.ts +8 -6
  47. package/esm/morphoBlue/index.js +62 -39
  48. package/esm/portfolio/index.js +32 -3
  49. package/esm/savings/summerVaults/options.js +10 -10
  50. package/esm/staking/staking.js +3 -0
  51. package/esm/types/aaveV4.d.ts +2 -1
  52. package/esm/types/aaveV4.js +1 -0
  53. package/esm/types/claiming.d.ts +10 -0
  54. package/esm/types/claiming.js +2 -0
  55. package/esm/types/morphoBlue.d.ts +9 -0
  56. package/esm/types/morphoMidnight.d.ts +9 -0
  57. package/package.json +2 -2
  58. package/src/aaveV3/index.ts +13 -1
  59. package/src/aaveV4/lend.ts +3 -3
  60. package/src/claiming/index.ts +2 -0
  61. package/src/claiming/uniswap.ts +70 -0
  62. package/src/config/contracts.ts +9 -0
  63. package/src/contracts.ts +2 -0
  64. package/src/curveUsd/index.ts +1 -1
  65. package/src/helpers/morphoBlueHelpers/index.ts +1 -0
  66. package/src/helpers/morphoMidnightHelpers/index.ts +142 -17
  67. package/src/llamaLend/index.ts +1 -1
  68. package/src/markets/aaveV4/index.ts +13 -0
  69. package/src/morphoBlue/index.ts +101 -44
  70. package/src/portfolio/index.ts +32 -3
  71. package/src/savings/summerVaults/options.ts +10 -10
  72. package/src/staking/staking.ts +2 -0
  73. package/src/types/aaveV4.ts +1 -0
  74. package/src/types/claiming.ts +11 -0
  75. package/src/types/morphoBlue.ts +11 -0
  76. package/src/types/morphoMidnight.ts +12 -0
@@ -619752,3 +619752,38 @@ export declare const AaveV4ViewContractViem: (client: Client, network: NetworkNu
619752
619752
  readonly type: "function";
619753
619753
  }];
619754
619754
  };
619755
+ export declare const UniswapTokenDistributorViem: (client: Client, network: NetworkNumber, block?: Blockish) => {
619756
+ read: {
619757
+ isClaimed: (args: readonly [bigint], options?: import("viem").Prettify<import("viem").UnionOmit<import("viem").ReadContractParameters<readonly [{
619758
+ readonly inputs: readonly [{
619759
+ readonly internalType: "uint256";
619760
+ readonly name: "index";
619761
+ readonly type: "uint256";
619762
+ }];
619763
+ readonly name: "isClaimed";
619764
+ readonly outputs: readonly [{
619765
+ readonly internalType: "bool";
619766
+ readonly name: "";
619767
+ readonly type: "bool";
619768
+ }];
619769
+ readonly stateMutability: "view";
619770
+ readonly type: "function";
619771
+ }], "isClaimed", readonly [bigint]>, "address" | "args" | "abi" | "functionName">> | undefined) => Promise<boolean>;
619772
+ };
619773
+ address: `0x${string}`;
619774
+ abi: readonly [{
619775
+ readonly inputs: readonly [{
619776
+ readonly internalType: "uint256";
619777
+ readonly name: "index";
619778
+ readonly type: "uint256";
619779
+ }];
619780
+ readonly name: "isClaimed";
619781
+ readonly outputs: readonly [{
619782
+ readonly internalType: "bool";
619783
+ readonly name: "";
619784
+ readonly type: "bool";
619785
+ }];
619786
+ readonly stateMutability: "view";
619787
+ readonly type: "function";
619788
+ }];
619789
+ };
package/esm/contracts.js CHANGED
@@ -142,3 +142,4 @@ export const YearnViewContractViem = createViemContractFromConfigFunc('YearnView
142
142
  export const MakerDsrContractViem = createViemContractFromConfigFunc('MakerDsr');
143
143
  export const SkySavingsContractView = createViemContractFromConfigFunc('SkySavings');
144
144
  export const AaveV4ViewContractViem = createViemContractFromConfigFunc('AaveV4View');
145
+ export const UniswapTokenDistributorViem = createViemContractFromConfigFunc('UniswapTokenDistributor');
@@ -24,7 +24,7 @@ const getAndFormatBands = (provider, network, selectedMarket, _minBand, _maxBand
24
24
  // getBandsData uses a lot of gas to get all of the bands at once, so we use pagination and fetch 200 bands at a time
25
25
  let i = minBand;
26
26
  while (i < maxBand) {
27
- i += 200;
27
+ i += 20;
28
28
  if (i > maxBand) {
29
29
  pivots.push(maxBand);
30
30
  }
@@ -348,6 +348,7 @@ export const getRewardsForMarket = (marketId_1, ...args_1) => __awaiter(void 0,
348
348
  query: REWARDS_QUERY,
349
349
  variables: { marketId, chainId: network },
350
350
  }),
351
+ signal: AbortSignal.timeout(LONGER_TIMEOUT),
351
352
  });
352
353
  const data = yield response.json();
353
354
  const marketData = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.marketById;
@@ -1,6 +1,6 @@
1
1
  import Dec from 'decimal.js';
2
- import { MMUsedAssets } from '../../types/common';
3
- import { MorphoMidnightAggregatedPositionData, MorphoMidnightAssetsData, MorphoMidnightMarketInfo } from '../../types';
2
+ import { MMUsedAssets, NetworkNumber } from '../../types/common';
3
+ import { MorphoMidnightAggregatedPositionData, MorphoMidnightAssetsData, MorphoMidnightMarketData, MorphoMidnightMarketInfo, MorphoMidnightParsedBook } from '../../types';
4
4
  /**
5
5
  * Aggregate a Morpho Midnight position. Midnight markets are multi-collateral, so the borrow limit is
6
6
  * the sum of each collateral's USD value times its own lltv (Aave-v4 style), rather than a single pair.
@@ -34,6 +34,23 @@ export interface MorphoMidnightBorrowQuote {
34
34
  }
35
35
  export declare const midnightTimeToMaturityDays: (maturity: number, atSeconds?: number) => number;
36
36
  export declare const midnightApyFromPrice: (price: Dec.Value, ttmDays: Dec.Value) => string;
37
+ /**
38
+ * Inverse of `midnightApyFromPrice`: the loan-per-unit price a borrow APY implies,
39
+ * price = (1 + rate)^(−ttmDays / 365).
40
+ *
41
+ * This is what turns an absolute rate ceiling into an on-chain `maxUnits` cap (units = assets / price),
42
+ * and equally the principal a unit of borrow power is worth — Midnight debt is recorded at its maturity
43
+ * face value, so borrowing the full limit as principal would overshoot it by the interest.
44
+ */
45
+ export declare const midnightPriceFromApy: (ratePercent: Dec.Value, ttmDays: Dec.Value) => string;
46
+ /**
47
+ * Coerce a slippage into what the quote endpoint accepts: 0.1–100 with at most one decimal place. The
48
+ * validation is lexical, so a computed value (`4.15066671050631467`) is rejected outright — without this
49
+ * the request 400s and the quote looks unavailable.
50
+ *
51
+ * Rounded **down**, since a wider slippage is a looser cap than the caller asked for.
52
+ */
53
+ export declare const midnightSlippageParam: (slippagePercent: Dec.Value) => string;
37
54
  /**
38
55
  * Current borrower rate + debt breakdown from the Midnight transactions API. On-chain we can only read the
39
56
  * total debt at maturity (`units`); the base-vs-interest split and the effective borrow rate require the
@@ -42,9 +59,30 @@ export declare const midnightApyFromPrice: (price: Dec.Value, ttmDays: Dec.Value
42
59
  */
43
60
  export declare const getMorphoMidnightUserBorrowInfo: (account: string, marketId: string, maturity: number, loanTokenSymbol: string) => Promise<MorphoMidnightBorrowInfo>;
44
61
  /**
45
- * Estimate the borrow rate + slippage cap for a prospective borrow by quoting the Midnight order book.
46
- * `assetsRaw` (and the returned `newUnits`/`maxUnits`) are raw loan-token base units — callers convert to/from
47
- * human amounts. `maxUnits` (from the slippage-adjusted worst price) is the cap sent on-chain to protect the
48
- * user if better offers get filled first. Throws if the book can't fill the amount (caller handles).
62
+ * A market's resting bids, as rates rather than the API's WAD-scaled loan-per-unit prices. Annualizing
63
+ * each price against time-to-maturity gives the rate a borrower filling that offer pays — verified
64
+ * against Morpho's fixed-market UI, where per-offer rates match to the cent.
65
+ *
66
+ * Returns `null` for an empty book: there is nothing to borrow against, so a market listing should skip
67
+ * the market rather than advertise it at a 0% rate. Throws when the request fails — an error response is
68
+ * rarely JSON, so without the `res.ok` check it parses as an empty book and the market silently vanishes.
69
+ */
70
+ export declare const getMorphoMidnightMarketBook: (market: MorphoMidnightMarketData, network: NetworkNumber) => Promise<MorphoMidnightParsedBook | null>;
71
+ /**
72
+ * Quote a prospective borrow against the Midnight order book: the estimated rate, the debt units it adds,
73
+ * and the `maxUnits` cap sent on-chain to protect the user if better offers get filled first. `assetsRaw`
74
+ * (and the returned `newUnits`/`maxUnits`) are raw loan-token base units — callers convert to/from human
75
+ * amounts. Throws if the book can't fill the amount (caller handles).
76
+ *
77
+ * Two ways to set the cap:
78
+ * - `maxBorrowRate` — an absolute APY ceiling, honoured **exactly**: the cap price is derived locally via
79
+ * `midnightPriceFromApy`. Prefer this when a user pins a max rate.
80
+ * - otherwise `slippagePercent`, the API's own knob. Note it is a **price**-level slippage, not APY points:
81
+ * near maturity the annualisation factor (365 / ttmDays) multiplies it heavily, so on a 22-day market a
82
+ * slippage of 0.5 permitted an APY ~9pp above the estimate, not 0.5pp. It also saturates at the book's
83
+ * cheapest bid. `maxRate` therefore reports what the cap actually permits, derived from the cap price.
84
+ *
85
+ * A `maxBorrowRate` below `estBorrowRate` yields `maxUnits < newUnits` — the borrow would revert on-chain.
86
+ * Compare the two before submitting and tell the user their ceiling is under the market rate.
49
87
  */
50
- export declare const getMorphoMidnightBorrowQuote: (marketId: string, assetsRaw: string, slippagePercent: Dec.Value, maturity: number) => Promise<MorphoMidnightBorrowQuote>;
88
+ export declare const getMorphoMidnightBorrowQuote: (marketId: string, assetsRaw: string, slippagePercent: Dec.Value, maturity: number, maxBorrowRate?: Dec.Value) => Promise<MorphoMidnightBorrowQuote>;
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import Dec from 'decimal.js';
11
- import { assetAmountInEth } from '@defisaver/tokens';
11
+ import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
12
12
  import { calcLeverageLiqPrice, getAssetsTotal, getExposure, isLeveragedPos, } from '../../moneymarket';
13
13
  import { calculateNetApy } from '../../staking';
14
14
  import { LeverageType, } from '../../types/common';
@@ -79,6 +79,13 @@ export const getMorphoMidnightAggregatedPositionData = ({ usedAssets, assetsData
79
79
  // loan-per-unit ratios (< 1 for a discounted fixed-term borrow); annualizing them yields the borrow APY.
80
80
  const MIDNIGHT_API_BASE = 'https://api.morpho.org/v0/midnight';
81
81
  const nowInSeconds = () => Math.floor(Date.now() / 1000);
82
+ // The book endpoint is markedly slower than the rest of the API — `LONGER_TIMEOUT` (5s) aborts it often
83
+ // enough that markets drop out of the list for no reason.
84
+ const MIDNIGHT_BOOK_TIMEOUT = 30000;
85
+ // The quote endpoint's `slippage` query param is validated as a string: 0.1–100, at most one decimal
86
+ // place (`0.50` is rejected even though `0.5` passes). See `midnightSlippageParam`.
87
+ const MIDNIGHT_SLIPPAGE_MIN = 0.1;
88
+ const MIDNIGHT_SLIPPAGE_MAX = 100;
82
89
  // Days remaining until maturity, optionally measured at a past timestamp (for historical fills).
83
90
  export const midnightTimeToMaturityDays = (maturity, atSeconds = nowInSeconds()) => new Dec(maturity).sub(atSeconds).div(SECONDS_PER_DAY).toNumber();
84
91
  // Annualize a fixed-term discount price into an APY percent: (1 / price)^(365 / ttmDays) − 1.
@@ -92,6 +99,29 @@ export const midnightApyFromPrice = (price, ttmDays) => {
92
99
  .mul(100)
93
100
  .toString();
94
101
  };
102
+ /**
103
+ * Inverse of `midnightApyFromPrice`: the loan-per-unit price a borrow APY implies,
104
+ * price = (1 + rate)^(−ttmDays / 365).
105
+ *
106
+ * This is what turns an absolute rate ceiling into an on-chain `maxUnits` cap (units = assets / price),
107
+ * and equally the principal a unit of borrow power is worth — Midnight debt is recorded at its maturity
108
+ * face value, so borrowing the full limit as principal would overshoot it by the interest.
109
+ */
110
+ export const midnightPriceFromApy = (ratePercent, ttmDays) => {
111
+ const rate = new Dec(ratePercent);
112
+ const ttm = new Dec(ttmDays);
113
+ if (rate.lte(0) || ttm.lte(0))
114
+ return '1';
115
+ return new Dec(1).div(new Dec(1).add(rate.div(100)).pow(ttm.div(365))).toString();
116
+ };
117
+ /**
118
+ * Coerce a slippage into what the quote endpoint accepts: 0.1–100 with at most one decimal place. The
119
+ * validation is lexical, so a computed value (`4.15066671050631467`) is rejected outright — without this
120
+ * the request 400s and the quote looks unavailable.
121
+ *
122
+ * Rounded **down**, since a wider slippage is a looser cap than the caller asked for.
123
+ */
124
+ export const midnightSlippageParam = (slippagePercent) => Dec.min(Dec.max(new Dec(slippagePercent), MIDNIGHT_SLIPPAGE_MIN), MIDNIGHT_SLIPPAGE_MAX).toDP(1, Dec.ROUND_DOWN).toString();
95
125
  /**
96
126
  * Current borrower rate + debt breakdown from the Midnight transactions API. On-chain we can only read the
97
127
  * total debt at maturity (`units`); the base-vs-interest split and the effective borrow rate require the
@@ -126,25 +156,80 @@ export const getMorphoMidnightUserBorrowInfo = (account, marketId, maturity, loa
126
156
  };
127
157
  });
128
158
  /**
129
- * Estimate the borrow rate + slippage cap for a prospective borrow by quoting the Midnight order book.
130
- * `assetsRaw` (and the returned `newUnits`/`maxUnits`) are raw loan-token base units — callers convert to/from
131
- * human amounts. `maxUnits` (from the slippage-adjusted worst price) is the cap sent on-chain to protect the
132
- * user if better offers get filled first. Throws if the book can't fill the amount (caller handles).
159
+ * A market's resting bids, as rates rather than the API's WAD-scaled loan-per-unit prices. Annualizing
160
+ * each price against time-to-maturity gives the rate a borrower filling that offer pays — verified
161
+ * against Morpho's fixed-market UI, where per-offer rates match to the cent.
162
+ *
163
+ * Returns `null` for an empty book: there is nothing to borrow against, so a market listing should skip
164
+ * the market rather than advertise it at a 0% rate. Throws when the request fails — an error response is
165
+ * rarely JSON, so without the `res.ok` check it parses as an empty book and the market silently vanishes.
166
+ */
167
+ export const getMorphoMidnightMarketBook = (market, network) => __awaiter(void 0, void 0, void 0, function* () {
168
+ var _a;
169
+ const loanSymbol = getAssetInfoByAddress(market.loanToken, network).symbol;
170
+ const res = yield fetch(`${MIDNIGHT_API_BASE}/books/${market.marketId}`, { signal: AbortSignal.timeout(MIDNIGHT_BOOK_TIMEOUT) });
171
+ if (!res.ok)
172
+ throw new Error(`Midnight book request failed for ${market.value} (${res.status})`);
173
+ const json = yield res.json();
174
+ const ttmDays = midnightTimeToMaturityDays(market.maturity);
175
+ const offers = (((_a = json === null || json === void 0 ? void 0 : json.data) === null || _a === void 0 ? void 0 : _a.bids) || [])
176
+ .map((bid) => ({
177
+ rate: midnightApyFromPrice(new Dec(bid.price).div(WAD), ttmDays),
178
+ liquidity: assetAmountInEth(bid.assets, loanSymbol),
179
+ }))
180
+ .sort((a, b) => new Dec(a.rate).minus(b.rate).toNumber());
181
+ if (offers.length === 0)
182
+ return null;
183
+ return {
184
+ bestRate: offers[0].rate,
185
+ totalLiquidity: offers.reduce((sum, offer) => sum.add(offer.liquidity), new Dec(0)).toString(),
186
+ offers,
187
+ };
188
+ });
189
+ // The API says why a quote failed — NOT_FOUND (market matured or not open yet), INSUFFICIENT_LIQUIDITY
190
+ // (book can't fill the size), VALIDATION_ERROR (bad param, with the offending field in `details`).
191
+ // Callers surface this to the user, so keep the reason rather than collapsing everything into one string.
192
+ const midnightQuoteError = (error) => {
193
+ const detail = ((error === null || error === void 0 ? void 0 : error.details) || []).map(({ issue }) => issue).filter(Boolean).join('; ');
194
+ const reason = detail || (error === null || error === void 0 ? void 0 : error.message) || (error === null || error === void 0 ? void 0 : error.code);
195
+ return reason ? `Morpho Midnight quote unavailable: ${reason}` : 'Morpho Midnight quote unavailable';
196
+ };
197
+ /**
198
+ * Quote a prospective borrow against the Midnight order book: the estimated rate, the debt units it adds,
199
+ * and the `maxUnits` cap sent on-chain to protect the user if better offers get filled first. `assetsRaw`
200
+ * (and the returned `newUnits`/`maxUnits`) are raw loan-token base units — callers convert to/from human
201
+ * amounts. Throws if the book can't fill the amount (caller handles).
202
+ *
203
+ * Two ways to set the cap:
204
+ * - `maxBorrowRate` — an absolute APY ceiling, honoured **exactly**: the cap price is derived locally via
205
+ * `midnightPriceFromApy`. Prefer this when a user pins a max rate.
206
+ * - otherwise `slippagePercent`, the API's own knob. Note it is a **price**-level slippage, not APY points:
207
+ * near maturity the annualisation factor (365 / ttmDays) multiplies it heavily, so on a 22-day market a
208
+ * slippage of 0.5 permitted an APY ~9pp above the estimate, not 0.5pp. It also saturates at the book's
209
+ * cheapest bid. `maxRate` therefore reports what the cap actually permits, derived from the cap price.
210
+ *
211
+ * A `maxBorrowRate` below `estBorrowRate` yields `maxUnits < newUnits` — the borrow would revert on-chain.
212
+ * Compare the two before submitting and tell the user their ceiling is under the market rate.
133
213
  */
134
- export const getMorphoMidnightBorrowQuote = (marketId, assetsRaw, slippagePercent, maturity) => __awaiter(void 0, void 0, void 0, function* () {
135
- const url = `${MIDNIGHT_API_BASE}/books/${marketId}/bids/quote?assets=${assetsRaw}&slippage=${slippagePercent}`;
214
+ export const getMorphoMidnightBorrowQuote = (marketId, assetsRaw, slippagePercent, maturity, maxBorrowRate) => __awaiter(void 0, void 0, void 0, function* () {
215
+ const url = `${MIDNIGHT_API_BASE}/books/${marketId}/bids/quote?assets=${assetsRaw}&slippage=${midnightSlippageParam(slippagePercent)}`;
136
216
  const res = yield fetch(url, { signal: AbortSignal.timeout(LONGER_TIMEOUT) });
137
217
  const json = yield res.json();
138
218
  const d = json === null || json === void 0 ? void 0 : json.data;
139
219
  if (!(d === null || d === void 0 ? void 0 : d.average_best_price))
140
- throw new Error('Morpho Midnight quote unavailable');
220
+ throw new Error(midnightQuoteError(json === null || json === void 0 ? void 0 : json.error));
141
221
  const bestPrice = new Dec(d.average_best_price).div(WAD).toString();
142
- const worstPrice = new Dec(d.average_worst_price).div(WAD).toString();
222
+ const worstPrice = new Dec(d.average_worst_price || 0).div(WAD).toString();
143
223
  const ttmDays = midnightTimeToMaturityDays(maturity);
144
224
  const estBorrowRate = midnightApyFromPrice(bestPrice, ttmDays);
145
- const maxRate = new Dec(estBorrowRate).add(slippagePercent).toString();
225
+ // Price the cap sits at, and the rate that price represents — one derivation, so `maxRate` and
226
+ // `maxUnits` can never disagree about what the user is protected at.
227
+ const capPrice = maxBorrowRate !== undefined && new Dec(maxBorrowRate).gt(0)
228
+ ? midnightPriceFromApy(maxBorrowRate, ttmDays)
229
+ : worstPrice;
230
+ const maxRate = midnightApyFromPrice(capPrice, ttmDays);
146
231
  const newUnits = new Dec(bestPrice).lte(0) ? '0' : new Dec(assetsRaw).div(bestPrice).toFixed(0);
147
- const maxUnits = new Dec(worstPrice).lte(0) ? '0' : new Dec(assetsRaw).div(worstPrice).toFixed(0);
232
+ const maxUnits = new Dec(capPrice).lte(0) ? '0' : new Dec(assetsRaw).div(capPrice).toFixed(0);
148
233
  return {
149
234
  bestPrice,
150
235
  worstPrice,
@@ -152,8 +237,8 @@ export const getMorphoMidnightBorrowQuote = (marketId, assetsRaw, slippagePercen
152
237
  maxRate,
153
238
  newUnits,
154
239
  maxUnits,
155
- availableAssets: d.available_assets,
156
- availableUnits: d.available_units,
240
+ availableAssets: d.available_assets || '0',
241
+ availableUnits: d.available_units || '0',
157
242
  takeableOffers: d.takeable_offers || [],
158
243
  };
159
244
  });
@@ -25,7 +25,7 @@ const getAndFormatBands = (provider, network, selectedMarket, _minBand, _maxBand
25
25
  // getBandsData uses a lot of gas to get all of the bands at once, so we use pagination and fetch 200 bands at a time
26
26
  let i = minBand;
27
27
  while (i < maxBand) {
28
- i += 200;
28
+ i += 20;
29
29
  if (i > maxBand) {
30
30
  pivots.push(maxBand);
31
31
  }
@@ -22,6 +22,7 @@ export declare const AAVE_V4_LIDO_SPOKE: (networkId: NetworkNumber) => AaveV4Spo
22
22
  export declare const AAVE_V4_LOMBARD_BTC_SPOKE: (networkId: NetworkNumber) => AaveV4SpokeInfo;
23
23
  export declare const AAVE_V4_MAIN_SPOKE: (networkId: NetworkNumber) => AaveV4SpokeInfo;
24
24
  export declare const AAVE_V4_USDG_PENDLE_SPOKE: (networkId: NetworkNumber) => AaveV4SpokeInfo;
25
+ export declare const AAVE_V4_USDG_MAPLE_SPOKE: (networkId: NetworkNumber) => AaveV4SpokeInfo;
25
26
  export declare const AaveV4Spokes: (networkId: NetworkNumber) => {
26
27
  readonly aave_v4_bluechip_spoke: AaveV4SpokeInfo;
27
28
  readonly aave_v4_ethena_correlated_spoke: AaveV4SpokeInfo;
@@ -34,6 +35,7 @@ export declare const AaveV4Spokes: (networkId: NetworkNumber) => {
34
35
  readonly aave_v4_lombard_btc_spoke: AaveV4SpokeInfo;
35
36
  readonly aave_v4_main_spoke: AaveV4SpokeInfo;
36
37
  readonly aave_v4_usdg_pendle_spoke: AaveV4SpokeInfo;
38
+ readonly aave_v4_usdg_maple_spoke: AaveV4SpokeInfo;
37
39
  };
38
40
  export declare const getAaveV4SpokeTypeInfo: (type: AaveV4SpokesType, network?: NetworkNumber) => AaveV4SpokeInfo;
39
41
  export declare const findAaveV4SpokeByAddress: (networkId: NetworkNumber, address: string) => AaveV4SpokeInfo | undefined;
@@ -164,6 +164,17 @@ export const AAVE_V4_USDG_PENDLE_SPOKE = (networkId) => ({
164
164
  AAVE_V4_CORE_HUB(NetworkNumber.Eth).address,
165
165
  ],
166
166
  });
167
+ export const AAVE_V4_USDG_MAPLE_SPOKE = (networkId) => ({
168
+ chainIds: [NetworkNumber.Eth],
169
+ label: 'USDG Maple',
170
+ value: AaveV4SpokesType.AaveV4USDGMapleSpoke,
171
+ url: 'usdg-maple',
172
+ address: '0x774b9655413c34809c1f1b16b654465A89EBE989',
173
+ hubs: [
174
+ AAVE_V4_PAXOS_HUB(NetworkNumber.Eth).address,
175
+ AAVE_V4_CORE_HUB(NetworkNumber.Eth).address,
176
+ ],
177
+ });
167
178
  export const AaveV4Spokes = (networkId) => ({
168
179
  [AaveV4SpokesType.AaveV4BluechipSpoke]: AAVE_V4_BLUECHIP_SPOKE(networkId),
169
180
  [AaveV4SpokesType.AaveV4EthenaCorrelatedSpoke]: AAVE_V4_ETHENA_CORRELATED_SPOKE(networkId),
@@ -176,6 +187,7 @@ export const AaveV4Spokes = (networkId) => ({
176
187
  [AaveV4SpokesType.AaveV4LombardBtcSpoke]: AAVE_V4_LOMBARD_BTC_SPOKE(networkId),
177
188
  [AaveV4SpokesType.AaveV4MainSpoke]: AAVE_V4_MAIN_SPOKE(networkId),
178
189
  [AaveV4SpokesType.AaveV4USDGPendleSpoke]: AAVE_V4_USDG_PENDLE_SPOKE(networkId),
190
+ [AaveV4SpokesType.AaveV4USDGMapleSpoke]: AAVE_V4_USDG_MAPLE_SPOKE(networkId),
179
191
  });
180
192
  export const getAaveV4SpokeTypeInfo = (type, network) => (Object.assign({}, AaveV4Spokes(network !== null && network !== void 0 ? network : NetworkNumber.Eth))[type]);
181
193
  export const findAaveV4SpokeByAddress = (networkId, address) => Object.values(AaveV4Spokes(networkId)).find(spoke => spoke.address.toLowerCase() === address.toLowerCase());
@@ -1,14 +1,16 @@
1
1
  import { Client } from 'viem';
2
2
  import { Blockish, EthAddress, EthereumProvider, NetworkNumber, PositionBalances } from '../types/common';
3
- import { MorphoBlueMarketData, MorphoBlueMarketInfo, MorphoBluePositionData } from '../types';
3
+ import { MorphoBlueEarnData, MorphoBlueMarketData, MorphoBlueMarketInfo, MorphoBlueMarketRewards, MorphoBluePositionData } from '../types';
4
+ export declare const addMorphoBlueRewardsToMarketInfo: (marketInfo: MorphoBlueMarketInfo, rewards: MorphoBlueMarketRewards) => MorphoBlueMarketInfo;
4
5
  export declare function _getMorphoBlueMarketData(provider: Client, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo>;
6
+ export declare function _getMorphoBluePortfolioMarketData(provider: Client, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo>;
5
7
  export declare function getMorphoBlueMarketData(provider: EthereumProvider, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo>;
8
+ export declare function getMorphoBluePortfolioMarketData(provider: EthereumProvider, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo>;
9
+ export declare const getMorphoBluePositionDataWithMarketInfo: (data: MorphoBluePositionData, marketInfo: MorphoBlueMarketInfo) => MorphoBluePositionData;
10
+ export declare const getMorphoEarnDataWithMarketInfo: (data: MorphoBlueEarnData, marketInfo: MorphoBlueMarketInfo) => MorphoBlueEarnData;
11
+ export declare function getMorphoBlueMarketRewards(network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketRewards>;
6
12
  export declare const _getMorphoBlueAccountBalances: (provider: Client, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress, selectedMarket: MorphoBlueMarketData) => Promise<PositionBalances>;
7
13
  export declare const getMorphoBlueAccountBalances: (provider: EthereumProvider, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress, selectedMarket: MorphoBlueMarketData) => Promise<PositionBalances>;
8
14
  export declare function _getMorphoBlueAccountData(provider: Client, network: NetworkNumber, account: EthAddress, selectedMarket: MorphoBlueMarketData, marketInfo: MorphoBlueMarketInfo): Promise<MorphoBluePositionData>;
9
15
  export declare function getMorphoBlueAccountData(provider: EthereumProvider, network: NetworkNumber, account: EthAddress, selectedMarket: MorphoBlueMarketData, marketInfo: MorphoBlueMarketInfo): Promise<MorphoBluePositionData>;
10
- export declare function getMorphoEarn(provider: Client, network: NetworkNumber, account: EthAddress, selectedMarket: MorphoBlueMarketData, marketInfo: MorphoBlueMarketInfo): Promise<{
11
- apy: string;
12
- amount: string;
13
- amountUsd: string;
14
- }>;
16
+ export declare function getMorphoEarn(provider: Client, network: NetworkNumber, account: EthAddress, selectedMarket: MorphoBlueMarketData, marketInfo: MorphoBlueMarketInfo): Promise<MorphoBlueEarnData>;
@@ -18,7 +18,20 @@ import { getBorrowRate, getMorphoBlueAggregatedPositionData, getRewardsForMarket
18
18
  import { getChainlinkAssetAddress } from '../services/priceService';
19
19
  import { getViemProvider, setViemBlockNumber } from '../services/viem';
20
20
  const HARDCODED_USD_STABLE_PRICE = '100000000'; // $1 with 8 decimals
21
- export function _getMorphoBlueMarketData(provider, network, selectedMarket) {
21
+ const getMorphoRewardIncentives = (apy) => [{
22
+ token: 'MORPHO',
23
+ apy,
24
+ incentiveKind: IncentiveKind.Reward,
25
+ description: 'Eligible for protocol-level MORPHO incentives.',
26
+ }];
27
+ export const addMorphoBlueRewardsToMarketInfo = (marketInfo, rewards) => (Object.assign(Object.assign({}, marketInfo), { assetsData: Object.assign(Object.assign({}, marketInfo.assetsData), { [marketInfo.loanToken]: Object.assign(Object.assign({}, marketInfo.assetsData[marketInfo.loanToken]), { supplyIncentives: [
28
+ ...marketInfo.assetsData[marketInfo.loanToken].supplyIncentives.filter(({ token }) => token !== 'MORPHO'),
29
+ ...getMorphoRewardIncentives(rewards.supplyApy),
30
+ ], borrowIncentives: [
31
+ ...marketInfo.assetsData[marketInfo.loanToken].borrowIncentives.filter(({ token }) => token !== 'MORPHO'),
32
+ ...getMorphoRewardIncentives(rewards.borrowApy),
33
+ ] }) }) }));
34
+ function getMorphoBlueMarketDataInternal(provider, network, selectedMarket) {
22
35
  return __awaiter(this, void 0, void 0, function* () {
23
36
  const { loanToken, collateralToken, oracle, irm, lltv, oracleType, } = selectedMarket;
24
37
  const lltvInWei = new Dec(lltv).mul(WAD).toString();
@@ -50,16 +63,6 @@ export function _getMorphoBlueMarketData(provider, network, selectedMarket) {
50
63
  marketInfo = _marketInfo;
51
64
  loanTokenPrice = loanTokenPriceRound[1].toString();
52
65
  }
53
- let morphoSupplyApy = '0';
54
- let morphoBorrowApy = '0';
55
- try {
56
- const { supplyApy: _morphoSupplyApy, borrowApy: _morphoBorrowApy } = yield getRewardsForMarket(selectedMarket.marketId, network);
57
- morphoSupplyApy = _morphoSupplyApy;
58
- morphoBorrowApy = _morphoBorrowApy;
59
- }
60
- catch (e) {
61
- console.error(e);
62
- }
63
66
  const supplyRate = getSupplyRate(marketInfo.totalSupplyAssets.toString(), marketInfo.totalBorrowAssets.toString(), marketInfo.borrowRate.toString(), marketInfo.fee.toString());
64
67
  const compoundedBorrowRate = getBorrowRate(marketInfo.borrowRate.toString(), marketInfo.totalBorrowShares.toString());
65
68
  const utillization = new Dec(marketInfo.totalBorrowAssets.toString()).div(marketInfo.totalSupplyAssets.toString()).mul(100).toString();
@@ -78,18 +81,8 @@ export function _getMorphoBlueMarketData(provider, network, selectedMarket) {
78
81
  totalBorrow: new Dec(marketInfo.totalBorrowAssets.toString()).div(scale).toString(),
79
82
  canBeSupplied: true,
80
83
  canBeBorrowed: true,
81
- supplyIncentives: [{
82
- token: 'MORPHO',
83
- apy: morphoSupplyApy,
84
- incentiveKind: IncentiveKind.Reward,
85
- description: 'Eligible for protocol-level MORPHO incentives.',
86
- }],
87
- borrowIncentives: [{
88
- token: 'MORPHO',
89
- apy: morphoBorrowApy,
90
- incentiveKind: IncentiveKind.Reward,
91
- description: 'Eligible for protocol-level MORPHO incentives.',
92
- }],
84
+ supplyIncentives: [],
85
+ borrowIncentives: [],
93
86
  };
94
87
  assetsData[wethToEth(collateralTokenInfo.symbol)] = {
95
88
  symbol: wethToEth(collateralTokenInfo.symbol),
@@ -124,11 +117,54 @@ export function _getMorphoBlueMarketData(provider, network, selectedMarket) {
124
117
  };
125
118
  });
126
119
  }
120
+ export function _getMorphoBlueMarketData(provider, network, selectedMarket) {
121
+ return __awaiter(this, void 0, void 0, function* () {
122
+ const marketInfo = yield getMorphoBlueMarketDataInternal(provider, network, selectedMarket);
123
+ try {
124
+ const rewards = yield getRewardsForMarket(selectedMarket.marketId, network);
125
+ return addMorphoBlueRewardsToMarketInfo(marketInfo, rewards);
126
+ }
127
+ catch (error) {
128
+ console.error(error);
129
+ return addMorphoBlueRewardsToMarketInfo(marketInfo, { supplyApy: '0', borrowApy: '0' });
130
+ }
131
+ });
132
+ }
133
+ export function _getMorphoBluePortfolioMarketData(provider, network, selectedMarket) {
134
+ return getMorphoBlueMarketDataInternal(provider, network, selectedMarket);
135
+ }
127
136
  export function getMorphoBlueMarketData(provider, network, selectedMarket) {
128
137
  return __awaiter(this, void 0, void 0, function* () {
129
138
  return _getMorphoBlueMarketData(getViemProvider(provider, network), network, selectedMarket);
130
139
  });
131
140
  }
141
+ export function getMorphoBluePortfolioMarketData(provider, network, selectedMarket) {
142
+ return _getMorphoBluePortfolioMarketData(getViemProvider(provider, network), network, selectedMarket);
143
+ }
144
+ export const getMorphoBluePositionDataWithMarketInfo = (data, marketInfo) => (Object.assign(Object.assign({}, data), getMorphoBlueAggregatedPositionData({
145
+ usedAssets: data.usedAssets,
146
+ assetsData: marketInfo.assetsData,
147
+ marketInfo,
148
+ })));
149
+ export const getMorphoEarnDataWithMarketInfo = (data, marketInfo) => {
150
+ const loanTokenInfo = marketInfo.assetsData[marketInfo.loanToken];
151
+ const usedAssets = {
152
+ [marketInfo.loanToken]: {
153
+ symbol: loanTokenInfo.symbol,
154
+ supplied: data.amount,
155
+ borrowed: '0',
156
+ isSupplied: new Dec(data.amount).gt(0),
157
+ isBorrowed: false,
158
+ collateral: false,
159
+ suppliedUsd: data.amountUsd,
160
+ borrowedUsd: '0',
161
+ },
162
+ };
163
+ return Object.assign(Object.assign({}, data), { apy: calculateNetApy({ usedAssets, assetsData: marketInfo.assetsData }).netApy });
164
+ };
165
+ export function getMorphoBlueMarketRewards(network, selectedMarket) {
166
+ return getRewardsForMarket(selectedMarket.marketId, network);
167
+ }
132
168
  export const _getMorphoBlueAccountBalances = (provider, network, block, addressMapping, address, selectedMarket) => __awaiter(void 0, void 0, void 0, function* () {
133
169
  let balances = {
134
170
  collateral: {},
@@ -216,23 +252,10 @@ export function getMorphoEarn(provider, network, account, selectedMarket, market
216
252
  const loanTokenInfo = marketInfo.assetsData[marketInfo.loanToken];
217
253
  const loanTokenSupplied = assetAmountInEth(loanInfo.suppliedInAssets.toString(), marketInfo.loanToken);
218
254
  const loanTokenSuppliedUsd = new Dec(loanTokenSupplied).mul(loanTokenInfo.price).toString();
219
- const usedAssets = {
220
- [marketInfo.loanToken]: {
221
- symbol: loanTokenInfo.symbol,
222
- supplied: loanTokenSupplied,
223
- borrowed: '0',
224
- isSupplied: new Dec(loanInfo.suppliedInAssets.toString()).gt(0),
225
- isBorrowed: false,
226
- collateral: false,
227
- suppliedUsd: loanTokenSuppliedUsd,
228
- borrowedUsd: '0',
229
- },
230
- };
231
- const { netApy } = calculateNetApy({ usedAssets, assetsData: marketInfo.assetsData });
232
- return {
233
- apy: netApy,
255
+ return getMorphoEarnDataWithMarketInfo({
256
+ apy: '0',
234
257
  amount: loanTokenSupplied,
235
258
  amountUsd: loanTokenSuppliedUsd,
236
- };
259
+ }, marketInfo);
237
260
  });
238
261
  }
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import Dec from 'decimal.js';
11
11
  import { NetworkNumber } from '../types/common';
12
12
  import { AaveMarkets, AaveV4Spokes, CompoundMarkets, CrvUsdMarkets, EulerV2Markets, LiquityV2Markets, LlamaLendMarkets, MorphoBlueMarkets, MorphoMidnightMarkets, SparkMarkets, } from '../markets';
13
- import { _getMorphoBlueAccountData, _getMorphoBlueMarketData, getMorphoEarn } from '../morphoBlue';
13
+ import { _getMorphoBlueAccountData, _getMorphoBluePortfolioMarketData, getMorphoEarn } from '../morphoBlue';
14
14
  import { _getMorphoMidnightAccountData, _getMorphoMidnightMarketData, getMorphoMidnightEarn } from '../morphoMidnight';
15
15
  import { AaveVersions, CompoundVersions, } from '../types';
16
16
  import { _getCompoundV3AccountData, _getCompoundV3MarketsData } from '../compoundV3';
@@ -35,6 +35,7 @@ import { fetchSparkAirdropRewards, fetchSparkRewards } from '../claiming/spark';
35
35
  import { getKingRewards } from '../claiming/king';
36
36
  import { fetchEthenaAirdropRewards } from '../claiming/ethena';
37
37
  import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
38
+ import { getUniswapRewards } from '../claiming/uniswap';
38
39
  export function getPortfolioData(provider_1, network_1, defaultProvider_1, addresses_1) {
39
40
  return __awaiter(this, arguments, void 0, function* (provider, network, defaultProvider, addresses, isSim = false) {
40
41
  const isMainnet = network === NetworkNumber.Eth;
@@ -52,7 +53,7 @@ export function getPortfolioData(provider_1, network_1, defaultProvider_1, addre
52
53
  const liquityV2Markets = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)) : [];
53
54
  const liquityV2MarketsStaking = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)).filter(market => !market.isLegacy) : [];
54
55
  const aaveV4Spokes = Object.values(AaveV4Spokes(network)).filter((market) => market.chainIds.includes(network));
55
- const args = [network, { batch: { multicall: { batchSize: isSim ? 500000 : 2500000 } } }];
56
+ const args = [network, { batch: { multicall: { batchSize: isSim ? 2000 : 2500000 } } }];
56
57
  const client = getViemProvider(provider, ...args);
57
58
  const defaultClient = getViemProvider(defaultProvider, ...args);
58
59
  const morphoMarketsData = {};
@@ -137,7 +138,7 @@ export function getPortfolioData(provider_1, network_1, defaultProvider_1, addre
137
138
  yield Promise.allSettled([
138
139
  // === MARKET DATA (needs to be fetched first) ===
139
140
  ...morphoMarkets.map((market) => __awaiter(this, void 0, void 0, function* () {
140
- const marketData = yield _getMorphoBlueMarketData(client, network, market);
141
+ const marketData = yield _getMorphoBluePortfolioMarketData(client, network, market);
141
142
  morphoMarketsData[market.value] = marketData;
142
143
  })),
143
144
  ...morphoMidnightMarkets.map((market) => __awaiter(this, void 0, void 0, function* () {
@@ -299,6 +300,34 @@ export function getPortfolioData(provider_1, network_1, defaultProvider_1, addre
299
300
  }
300
301
  }
301
302
  }))(),
303
+ // Batch UNI rewards
304
+ (() => __awaiter(this, void 0, void 0, function* () {
305
+ try {
306
+ if (!isMainnet) {
307
+ for (const address of addresses) {
308
+ rewardsData[address.toLowerCase()].uniswap = { error: '', data: [] };
309
+ }
310
+ return;
311
+ }
312
+ const uniswapRewards = yield getUniswapRewards(client, network, addresses);
313
+ for (const address of addresses) {
314
+ const lowerAddress = address.toLowerCase();
315
+ rewardsData[lowerAddress].uniswap = {
316
+ error: '',
317
+ data: uniswapRewards[lowerAddress] || [],
318
+ };
319
+ }
320
+ }
321
+ catch (error) {
322
+ console.error('Error fetching Uniswap rewards data in batch:', error);
323
+ for (const address of addresses) {
324
+ rewardsData[address.toLowerCase()].uniswap = {
325
+ error: 'Error fetching Uniswap rewards data in batch',
326
+ data: null,
327
+ };
328
+ }
329
+ }
330
+ }))(),
302
331
  ...sparkMarkets.map((market) => addresses.map((address) => __awaiter(this, void 0, void 0, function* () {
303
332
  try {
304
333
  if (!isMainnet) {