@defisaver/positions-sdk 2.1.127-dev → 2.1.127-midnight-1-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.
- package/cjs/config/contracts.d.ts +249 -0
- package/cjs/config/contracts.js +11 -1
- package/cjs/contracts.d.ts +1510 -0
- package/cjs/contracts.js +3 -2
- package/cjs/helpers/index.d.ts +1 -0
- package/cjs/helpers/index.js +2 -1
- package/cjs/helpers/morphoBlueHelpers/index.js +0 -1
- package/cjs/helpers/morphoMidnightHelpers/index.d.ts +50 -0
- package/cjs/helpers/morphoMidnightHelpers/index.js +170 -0
- package/cjs/index.d.ts +2 -1
- package/cjs/index.js +3 -1
- package/cjs/markets/index.d.ts +1 -0
- package/cjs/markets/index.js +4 -1
- package/cjs/markets/morphoMidnight/index.d.ts +16 -0
- package/cjs/markets/morphoMidnight/index.js +159 -0
- package/cjs/morphoBlue/index.d.ts +6 -8
- package/cjs/morphoBlue/index.js +40 -69
- package/cjs/morphoMidnight/index.d.ts +14 -0
- package/cjs/morphoMidnight/index.js +244 -0
- package/cjs/portfolio/discovery.js +4 -0
- package/cjs/portfolio/index.js +45 -1
- package/cjs/services/viem.d.ts +11 -11
- package/cjs/types/index.d.ts +1 -0
- package/cjs/types/index.js +1 -0
- package/cjs/types/morphoBlue.d.ts +0 -9
- package/cjs/types/morphoMidnight.d.ts +94 -0
- package/cjs/types/morphoMidnight.js +15 -0
- package/cjs/types/portfolio.d.ts +4 -0
- package/esm/config/contracts.d.ts +249 -0
- package/esm/config/contracts.js +9 -0
- package/esm/contracts.d.ts +1510 -0
- package/esm/contracts.js +1 -0
- package/esm/helpers/index.d.ts +1 -0
- package/esm/helpers/index.js +1 -0
- package/esm/helpers/morphoBlueHelpers/index.js +0 -1
- package/esm/helpers/morphoMidnightHelpers/index.d.ts +50 -0
- package/esm/helpers/morphoMidnightHelpers/index.js +159 -0
- package/esm/index.d.ts +2 -1
- package/esm/index.js +2 -1
- package/esm/markets/index.d.ts +1 -0
- package/esm/markets/index.js +1 -0
- package/esm/markets/morphoMidnight/index.d.ts +16 -0
- package/esm/markets/morphoMidnight/index.js +148 -0
- package/esm/morphoBlue/index.d.ts +6 -8
- package/esm/morphoBlue/index.js +39 -62
- package/esm/morphoMidnight/index.d.ts +14 -0
- package/esm/morphoMidnight/index.js +231 -0
- package/esm/portfolio/discovery.js +5 -1
- package/esm/portfolio/index.js +47 -3
- package/esm/services/viem.d.ts +11 -11
- package/esm/types/index.d.ts +1 -0
- package/esm/types/index.js +1 -0
- package/esm/types/morphoBlue.d.ts +0 -9
- package/esm/types/morphoMidnight.d.ts +94 -0
- package/esm/types/morphoMidnight.js +12 -0
- package/esm/types/portfolio.d.ts +4 -0
- package/package.json +1 -1
- package/src/config/contracts.ts +9 -0
- package/src/contracts.ts +1 -0
- package/src/helpers/index.ts +1 -0
- package/src/helpers/morphoBlueHelpers/index.ts +0 -1
- package/src/helpers/morphoMidnightHelpers/index.ts +219 -0
- package/src/index.ts +2 -0
- package/src/markets/index.ts +1 -0
- package/src/markets/morphoMidnight/index.ts +161 -0
- package/src/morphoBlue/index.ts +44 -101
- package/src/morphoMidnight/index.ts +261 -0
- package/src/portfolio/discovery.ts +6 -0
- package/src/portfolio/index.ts +46 -2
- package/src/types/index.ts +1 -0
- package/src/types/morphoBlue.ts +0 -11
- package/src/types/morphoMidnight.ts +110 -0
- package/src/types/portfolio.ts +4 -0
package/src/morphoBlue/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
DFSFeedRegistryContractViem, FeedRegistryContractViem, MorphoBlueViewContractViem,
|
|
9
9
|
} from '../contracts';
|
|
10
10
|
import {
|
|
11
|
-
MorphoBlueAssetsData,
|
|
11
|
+
MorphoBlueAssetsData, MorphoBlueMarketData, MorphoBlueMarketInfo, MorphoBluePositionData,
|
|
12
12
|
} from '../types';
|
|
13
13
|
import { USD_QUOTE, WAD } from '../constants';
|
|
14
14
|
import { calculateNetApy, getStakingApy, STAKING_ASSETS } from '../staking';
|
|
@@ -21,39 +21,7 @@ import { getViemProvider, setViemBlockNumber } from '../services/viem';
|
|
|
21
21
|
|
|
22
22
|
const HARDCODED_USD_STABLE_PRICE = '100000000'; // $1 with 8 decimals
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
token: 'MORPHO',
|
|
26
|
-
apy,
|
|
27
|
-
incentiveKind: IncentiveKind.Reward,
|
|
28
|
-
description: 'Eligible for protocol-level MORPHO incentives.',
|
|
29
|
-
}];
|
|
30
|
-
|
|
31
|
-
export const addMorphoBlueRewardsToMarketInfo = (
|
|
32
|
-
marketInfo: MorphoBlueMarketInfo,
|
|
33
|
-
rewards: MorphoBlueMarketRewards,
|
|
34
|
-
): MorphoBlueMarketInfo => ({
|
|
35
|
-
...marketInfo,
|
|
36
|
-
assetsData: {
|
|
37
|
-
...marketInfo.assetsData,
|
|
38
|
-
[marketInfo.loanToken]: {
|
|
39
|
-
...marketInfo.assetsData[marketInfo.loanToken],
|
|
40
|
-
supplyIncentives: [
|
|
41
|
-
...marketInfo.assetsData[marketInfo.loanToken].supplyIncentives.filter(({ token }) => token !== 'MORPHO'),
|
|
42
|
-
...getMorphoRewardIncentives(rewards.supplyApy),
|
|
43
|
-
],
|
|
44
|
-
borrowIncentives: [
|
|
45
|
-
...marketInfo.assetsData[marketInfo.loanToken].borrowIncentives.filter(({ token }) => token !== 'MORPHO'),
|
|
46
|
-
...getMorphoRewardIncentives(rewards.borrowApy),
|
|
47
|
-
],
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
async function getMorphoBlueMarketDataInternal(
|
|
53
|
-
provider: Client,
|
|
54
|
-
network: NetworkNumber,
|
|
55
|
-
selectedMarket: MorphoBlueMarketData,
|
|
56
|
-
): Promise<MorphoBlueMarketInfo> {
|
|
24
|
+
export async function _getMorphoBlueMarketData(provider: Client, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo> {
|
|
57
25
|
const {
|
|
58
26
|
loanToken, collateralToken, oracle, irm, lltv, oracleType,
|
|
59
27
|
} = selectedMarket;
|
|
@@ -91,6 +59,16 @@ async function getMorphoBlueMarketDataInternal(
|
|
|
91
59
|
loanTokenPrice = loanTokenPriceRound[1].toString();
|
|
92
60
|
}
|
|
93
61
|
|
|
62
|
+
let morphoSupplyApy = '0';
|
|
63
|
+
let morphoBorrowApy = '0';
|
|
64
|
+
try {
|
|
65
|
+
const { supplyApy: _morphoSupplyApy, borrowApy: _morphoBorrowApy } = await getRewardsForMarket(selectedMarket.marketId, network);
|
|
66
|
+
morphoSupplyApy = _morphoSupplyApy;
|
|
67
|
+
morphoBorrowApy = _morphoBorrowApy;
|
|
68
|
+
} catch (e) {
|
|
69
|
+
console.error(e);
|
|
70
|
+
}
|
|
71
|
+
|
|
94
72
|
const supplyRate = getSupplyRate(marketInfo.totalSupplyAssets.toString(), marketInfo.totalBorrowAssets.toString(), marketInfo.borrowRate.toString(), marketInfo.fee.toString());
|
|
95
73
|
const compoundedBorrowRate = getBorrowRate(marketInfo.borrowRate.toString(), marketInfo.totalBorrowShares.toString());
|
|
96
74
|
const utillization = new Dec(marketInfo.totalBorrowAssets.toString()).div(marketInfo.totalSupplyAssets.toString()).mul(100).toString();
|
|
@@ -112,8 +90,18 @@ async function getMorphoBlueMarketDataInternal(
|
|
|
112
90
|
totalBorrow: new Dec(marketInfo.totalBorrowAssets.toString()).div(scale).toString(),
|
|
113
91
|
canBeSupplied: true,
|
|
114
92
|
canBeBorrowed: true,
|
|
115
|
-
supplyIncentives: [
|
|
116
|
-
|
|
93
|
+
supplyIncentives: [{
|
|
94
|
+
token: 'MORPHO',
|
|
95
|
+
apy: morphoSupplyApy,
|
|
96
|
+
incentiveKind: IncentiveKind.Reward,
|
|
97
|
+
description: 'Eligible for protocol-level MORPHO incentives.',
|
|
98
|
+
}],
|
|
99
|
+
borrowIncentives: [{
|
|
100
|
+
token: 'MORPHO',
|
|
101
|
+
apy: morphoBorrowApy,
|
|
102
|
+
incentiveKind: IncentiveKind.Reward,
|
|
103
|
+
description: 'Eligible for protocol-level MORPHO incentives.',
|
|
104
|
+
}],
|
|
117
105
|
};
|
|
118
106
|
|
|
119
107
|
assetsData[wethToEth(collateralTokenInfo.symbol)] = {
|
|
@@ -150,70 +138,10 @@ async function getMorphoBlueMarketDataInternal(
|
|
|
150
138
|
};
|
|
151
139
|
}
|
|
152
140
|
|
|
153
|
-
export async function _getMorphoBlueMarketData(provider: Client, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo> {
|
|
154
|
-
const marketInfo = await getMorphoBlueMarketDataInternal(provider, network, selectedMarket);
|
|
155
|
-
|
|
156
|
-
try {
|
|
157
|
-
const rewards = await getRewardsForMarket(selectedMarket.marketId, network);
|
|
158
|
-
return addMorphoBlueRewardsToMarketInfo(marketInfo, rewards);
|
|
159
|
-
} catch (error) {
|
|
160
|
-
console.error(error);
|
|
161
|
-
return addMorphoBlueRewardsToMarketInfo(marketInfo, { supplyApy: '0', borrowApy: '0' });
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export function _getMorphoBluePortfolioMarketData(provider: Client, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo> {
|
|
166
|
-
return getMorphoBlueMarketDataInternal(provider, network, selectedMarket);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
141
|
export async function getMorphoBlueMarketData(provider: EthereumProvider, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo> {
|
|
170
142
|
return _getMorphoBlueMarketData(getViemProvider(provider, network), network, selectedMarket);
|
|
171
143
|
}
|
|
172
144
|
|
|
173
|
-
export function getMorphoBluePortfolioMarketData(provider: EthereumProvider, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo> {
|
|
174
|
-
return _getMorphoBluePortfolioMarketData(getViemProvider(provider, network), network, selectedMarket);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export const getMorphoBluePositionDataWithMarketInfo = (
|
|
178
|
-
data: MorphoBluePositionData,
|
|
179
|
-
marketInfo: MorphoBlueMarketInfo,
|
|
180
|
-
): MorphoBluePositionData => ({
|
|
181
|
-
...data,
|
|
182
|
-
...getMorphoBlueAggregatedPositionData({
|
|
183
|
-
usedAssets: data.usedAssets,
|
|
184
|
-
assetsData: marketInfo.assetsData,
|
|
185
|
-
marketInfo,
|
|
186
|
-
}),
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
export const getMorphoEarnDataWithMarketInfo = (data: MorphoBlueEarnData, marketInfo: MorphoBlueMarketInfo): MorphoBlueEarnData => {
|
|
190
|
-
const loanTokenInfo = marketInfo.assetsData[marketInfo.loanToken];
|
|
191
|
-
const usedAssets: MMUsedAssets = {
|
|
192
|
-
[marketInfo.loanToken]: {
|
|
193
|
-
symbol: loanTokenInfo.symbol,
|
|
194
|
-
supplied: data.amount,
|
|
195
|
-
borrowed: '0',
|
|
196
|
-
isSupplied: new Dec(data.amount).gt(0),
|
|
197
|
-
isBorrowed: false,
|
|
198
|
-
collateral: false,
|
|
199
|
-
suppliedUsd: data.amountUsd,
|
|
200
|
-
borrowedUsd: '0',
|
|
201
|
-
},
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
return {
|
|
205
|
-
...data,
|
|
206
|
-
apy: calculateNetApy({ usedAssets, assetsData: marketInfo.assetsData as unknown as MMAssetsData }).netApy,
|
|
207
|
-
};
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
export function getMorphoBlueMarketRewards(
|
|
211
|
-
network: NetworkNumber,
|
|
212
|
-
selectedMarket: MorphoBlueMarketData,
|
|
213
|
-
): Promise<MorphoBlueMarketRewards> {
|
|
214
|
-
return getRewardsForMarket(selectedMarket.marketId, network);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
145
|
export const _getMorphoBlueAccountBalances = async (provider: Client, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress, selectedMarket: MorphoBlueMarketData): Promise<PositionBalances> => {
|
|
218
146
|
let balances: PositionBalances = {
|
|
219
147
|
collateral: {},
|
|
@@ -310,7 +238,7 @@ export async function getMorphoBlueAccountData(provider: EthereumProvider, netwo
|
|
|
310
238
|
return _getMorphoBlueAccountData(getViemProvider(provider, network), network, account, selectedMarket, marketInfo);
|
|
311
239
|
}
|
|
312
240
|
|
|
313
|
-
export async function getMorphoEarn(provider: Client, network: NetworkNumber, account: EthAddress, selectedMarket: MorphoBlueMarketData, marketInfo: MorphoBlueMarketInfo): Promise<
|
|
241
|
+
export async function getMorphoEarn(provider: Client, network: NetworkNumber, account: EthAddress, selectedMarket: MorphoBlueMarketData, marketInfo: MorphoBlueMarketInfo): Promise<{ apy: string, amount: string, amountUsd: string }> {
|
|
314
242
|
const {
|
|
315
243
|
loanToken, collateralToken, oracle, irm, lltv,
|
|
316
244
|
} = selectedMarket;
|
|
@@ -326,9 +254,24 @@ export async function getMorphoEarn(provider: Client, network: NetworkNumber, ac
|
|
|
326
254
|
const loanTokenInfo = marketInfo.assetsData[marketInfo.loanToken];
|
|
327
255
|
const loanTokenSupplied = assetAmountInEth(loanInfo.suppliedInAssets.toString(), marketInfo.loanToken);
|
|
328
256
|
const loanTokenSuppliedUsd = new Dec(loanTokenSupplied).mul(loanTokenInfo.price).toString();
|
|
329
|
-
|
|
330
|
-
|
|
257
|
+
const usedAssets: MMUsedAssets = {
|
|
258
|
+
[marketInfo.loanToken]: {
|
|
259
|
+
symbol: loanTokenInfo.symbol,
|
|
260
|
+
supplied: loanTokenSupplied,
|
|
261
|
+
borrowed: '0',
|
|
262
|
+
isSupplied: new Dec(loanInfo.suppliedInAssets.toString()).gt(0),
|
|
263
|
+
isBorrowed: false,
|
|
264
|
+
collateral: false,
|
|
265
|
+
suppliedUsd: loanTokenSuppliedUsd,
|
|
266
|
+
borrowedUsd: '0',
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const { netApy } = calculateNetApy({ usedAssets, assetsData: marketInfo.assetsData as unknown as MMAssetsData });
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
apy: netApy,
|
|
331
274
|
amount: loanTokenSupplied,
|
|
332
275
|
amountUsd: loanTokenSuppliedUsd,
|
|
333
|
-
}
|
|
334
|
-
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import Dec from 'decimal.js';
|
|
2
|
+
import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
|
|
3
|
+
import { Client } from 'viem';
|
|
4
|
+
import {
|
|
5
|
+
Blockish, EthAddress, EthereumProvider, MMAssetsData, MMUsedAssets, NetworkNumber, PositionBalances,
|
|
6
|
+
} from '../types/common';
|
|
7
|
+
import {
|
|
8
|
+
DFSFeedRegistryContractViem, FeedRegistryContractViem, MorphoMidnightViewContractViem,
|
|
9
|
+
} from '../contracts';
|
|
10
|
+
import {
|
|
11
|
+
MorphoMidnightAssetsData, MorphoMidnightMarketData, MorphoMidnightMarketInfo, MorphoMidnightPositionData,
|
|
12
|
+
} from '../types';
|
|
13
|
+
import { USD_QUOTE } from '../constants';
|
|
14
|
+
import { calculateNetApy } from '../staking';
|
|
15
|
+
import { isMainnetNetwork, wethToEth } from '../services/utils';
|
|
16
|
+
import { getMorphoMidnightAggregatedPositionData, getMorphoMidnightUserBorrowInfo } from '../helpers/morphoMidnightHelpers';
|
|
17
|
+
import { getChainlinkAssetAddress } from '../services/priceService';
|
|
18
|
+
import { getViemProvider, setViemBlockNumber } from '../services/viem';
|
|
19
|
+
|
|
20
|
+
const HARDCODED_USD_STABLE_PRICE = '100000000'; // $1 with 8 decimals
|
|
21
|
+
const nowInSeconds = () => Math.floor(Date.now() / 1000);
|
|
22
|
+
|
|
23
|
+
export async function _getMorphoMidnightMarketData(provider: Client, network: NetworkNumber, selectedMarket: MorphoMidnightMarketData): Promise<MorphoMidnightMarketInfo> {
|
|
24
|
+
const { loanToken, collaterals, marketId } = selectedMarket;
|
|
25
|
+
const loanTokenInfo = getAssetInfoByAddress(loanToken, network);
|
|
26
|
+
const loanSym = wethToEth(loanTokenInfo.symbol);
|
|
27
|
+
const loanTokenFeedAddress = getChainlinkAssetAddress(loanTokenInfo.symbol, network);
|
|
28
|
+
|
|
29
|
+
const viewContract = MorphoMidnightViewContractViem(provider, network);
|
|
30
|
+
const isHardcodedUsdStable = ['USDA', 'RLUSD'].includes(loanTokenInfo.symbol);
|
|
31
|
+
|
|
32
|
+
let marketInfo;
|
|
33
|
+
let loanTokenPrice;
|
|
34
|
+
if (isMainnetNetwork(network)) {
|
|
35
|
+
const feedRegistryContract = FeedRegistryContractViem(provider, NetworkNumber.Eth);
|
|
36
|
+
const [_loanTokenPrice, _marketInfo] = await Promise.all([
|
|
37
|
+
isHardcodedUsdStable ? Promise.resolve(HARDCODED_USD_STABLE_PRICE) : feedRegistryContract.read.latestAnswer([loanTokenFeedAddress, USD_QUOTE]),
|
|
38
|
+
viewContract.read.getMarketInfo([marketId as `0x${string}`]),
|
|
39
|
+
]);
|
|
40
|
+
marketInfo = _marketInfo;
|
|
41
|
+
loanTokenPrice = _loanTokenPrice;
|
|
42
|
+
} else {
|
|
43
|
+
// Currently only Base is supported
|
|
44
|
+
const feedRegistryContract = DFSFeedRegistryContractViem(provider, network);
|
|
45
|
+
const [loanTokenPriceRound, _marketInfo] = await Promise.all([
|
|
46
|
+
isHardcodedUsdStable ? Promise.resolve([0, HARDCODED_USD_STABLE_PRICE]) : feedRegistryContract.read.latestRoundData([loanTokenFeedAddress, USD_QUOTE]),
|
|
47
|
+
viewContract.read.getMarketInfo([marketId as `0x${string}`]),
|
|
48
|
+
]);
|
|
49
|
+
marketInfo = _marketInfo;
|
|
50
|
+
loanTokenPrice = loanTokenPriceRound[1].toString();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const loanTokenUsd = new Dec(loanTokenPrice).div(1e8).toString();
|
|
54
|
+
|
|
55
|
+
const totalUnits = marketInfo.totalUnits.toString();
|
|
56
|
+
const withdrawable = marketInfo.withdrawable.toString();
|
|
57
|
+
const totalDebt = Dec.max(new Dec(totalUnits).sub(withdrawable), 0).toString();
|
|
58
|
+
|
|
59
|
+
const assetsData: MorphoMidnightAssetsData = {};
|
|
60
|
+
assetsData[loanSym] = {
|
|
61
|
+
symbol: loanSym,
|
|
62
|
+
address: loanToken,
|
|
63
|
+
price: loanTokenUsd,
|
|
64
|
+
supplyRate: '0', // fixed rate is orderbook-derived, not exposed on-chain (MVP)
|
|
65
|
+
borrowRate: '0',
|
|
66
|
+
totalSupply: assetAmountInEth(totalUnits, loanSym),
|
|
67
|
+
totalBorrow: assetAmountInEth(totalDebt, loanSym),
|
|
68
|
+
canBeSupplied: true,
|
|
69
|
+
canBeBorrowed: true,
|
|
70
|
+
supplyIncentives: [],
|
|
71
|
+
borrowIncentives: [],
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const collateralSymbols: string[] = [];
|
|
75
|
+
collaterals.forEach((coll, i) => {
|
|
76
|
+
const collInfo = getAssetInfoByAddress(coll.token, network);
|
|
77
|
+
const collSym = wethToEth(collInfo.symbol);
|
|
78
|
+
collateralSymbols.push(collSym);
|
|
79
|
+
// Oracle price is returned in loan-token terms, scaled by 10^(36 + loanDec - collDec)
|
|
80
|
+
const scale = new Dec(10).pow(new Dec(36).add(loanTokenInfo.decimals).sub(collInfo.decimals).toString()).toString();
|
|
81
|
+
const rawPrice = marketInfo.prices[i] ? marketInfo.prices[i].toString() : '0';
|
|
82
|
+
const oracleRate = new Dec(rawPrice).div(scale).toString();
|
|
83
|
+
assetsData[collSym] = {
|
|
84
|
+
symbol: collSym,
|
|
85
|
+
address: coll.token,
|
|
86
|
+
price: new Dec(loanTokenUsd).mul(oracleRate).toString(),
|
|
87
|
+
supplyRate: '0',
|
|
88
|
+
borrowRate: '0',
|
|
89
|
+
lltv: new Dec(coll.lltv).toString(),
|
|
90
|
+
canBeSupplied: true,
|
|
91
|
+
canBeBorrowed: false,
|
|
92
|
+
supplyIncentives: [],
|
|
93
|
+
borrowIncentives: [],
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const utillization = new Dec(totalUnits).eq(0) ? '0' : new Dec(totalDebt).div(totalUnits).mul(100).toString();
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
id: marketInfo.id,
|
|
101
|
+
loanToken: loanSym,
|
|
102
|
+
collaterals: collateralSymbols,
|
|
103
|
+
maturity: selectedMarket.maturity,
|
|
104
|
+
isMatured: nowInSeconds() >= selectedMarket.maturity,
|
|
105
|
+
totalUnits: assetAmountInEth(totalUnits, loanSym),
|
|
106
|
+
withdrawable: assetAmountInEth(withdrawable, loanSym),
|
|
107
|
+
totalDebt: assetAmountInEth(totalDebt, loanSym),
|
|
108
|
+
lossFactor: marketInfo.lossFactor.toString(),
|
|
109
|
+
tickSpacing: marketInfo.tickSpacing,
|
|
110
|
+
utillization,
|
|
111
|
+
assetsData,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function getMorphoMidnightMarketData(provider: EthereumProvider, network: NetworkNumber, selectedMarket: MorphoMidnightMarketData): Promise<MorphoMidnightMarketInfo> {
|
|
116
|
+
return _getMorphoMidnightMarketData(getViemProvider(provider, network), network, selectedMarket);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function _getMorphoMidnightAccountData(provider: Client, network: NetworkNumber, account: EthAddress, selectedMarket: MorphoMidnightMarketData, marketInfo: MorphoMidnightMarketInfo): Promise<MorphoMidnightPositionData> {
|
|
120
|
+
const { marketId, collaterals } = selectedMarket;
|
|
121
|
+
const viewContract = MorphoMidnightViewContractViem(provider, network);
|
|
122
|
+
const positionInfo = await viewContract.read.getPositionInfo([marketId as `0x${string}`, account]);
|
|
123
|
+
|
|
124
|
+
const usedAssets: MMUsedAssets = {};
|
|
125
|
+
|
|
126
|
+
const loanTokenData = marketInfo.assetsData[marketInfo.loanToken];
|
|
127
|
+
const credit = assetAmountInEth(positionInfo.credit.toString(), marketInfo.loanToken);
|
|
128
|
+
const debt = assetAmountInEth(positionInfo.debt.toString(), marketInfo.loanToken);
|
|
129
|
+
// A user holds either credit (lender) or debt (borrower) in a market, never both.
|
|
130
|
+
usedAssets[marketInfo.loanToken] = {
|
|
131
|
+
symbol: marketInfo.loanToken,
|
|
132
|
+
supplied: credit,
|
|
133
|
+
borrowed: debt,
|
|
134
|
+
isSupplied: new Dec(positionInfo.credit.toString()).gt(0),
|
|
135
|
+
isBorrowed: new Dec(positionInfo.debt.toString()).gt(0),
|
|
136
|
+
collateral: false,
|
|
137
|
+
suppliedUsd: new Dec(credit).mul(loanTokenData.price).toString(),
|
|
138
|
+
borrowedUsd: new Dec(debt).mul(loanTokenData.price).toString(),
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// positionInfo.collateral is index-aligned with the market's collateral set (0 where unused).
|
|
142
|
+
collaterals.forEach((coll, i) => {
|
|
143
|
+
const collInfo = getAssetInfoByAddress(coll.token, network);
|
|
144
|
+
const collSym = wethToEth(collInfo.symbol);
|
|
145
|
+
const rawAmount = positionInfo.collateral[i] ? positionInfo.collateral[i].toString() : '0';
|
|
146
|
+
const supplied = assetAmountInEth(rawAmount, collSym);
|
|
147
|
+
const collData = marketInfo.assetsData[collSym];
|
|
148
|
+
usedAssets[collSym] = {
|
|
149
|
+
symbol: collSym,
|
|
150
|
+
supplied,
|
|
151
|
+
borrowed: '0',
|
|
152
|
+
isSupplied: new Dec(rawAmount).gt(0),
|
|
153
|
+
isBorrowed: false,
|
|
154
|
+
collateral: true,
|
|
155
|
+
suppliedUsd: new Dec(supplied).mul(collData?.price || 0).toString(),
|
|
156
|
+
borrowedUsd: '0',
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Enrich borrower positions with the orderbook-derived rate + principal/interest split (off-chain).
|
|
161
|
+
// MidnightView only stores `debt` (= face value at maturity), so we default to full-debt-as-principal and
|
|
162
|
+
// override from the transactions API when available. Never throw here — the position must still render.
|
|
163
|
+
let borrowRate = '0';
|
|
164
|
+
let debtBase = debt; // fallback: treat the full on-chain debt as principal until fill history is known
|
|
165
|
+
let debtInterest = '0';
|
|
166
|
+
let assetsDataForApy = marketInfo.assetsData;
|
|
167
|
+
if (new Dec(positionInfo.debt.toString()).gt(0)) {
|
|
168
|
+
try {
|
|
169
|
+
const borrowInfo = await getMorphoMidnightUserBorrowInfo(account, marketId, marketInfo.maturity, marketInfo.loanToken);
|
|
170
|
+
borrowRate = borrowInfo.borrowRate;
|
|
171
|
+
debtBase = borrowInfo.debtBase;
|
|
172
|
+
debtInterest = borrowInfo.debtInterest;
|
|
173
|
+
usedAssets[marketInfo.loanToken].borrowRate = borrowRate;
|
|
174
|
+
// Reflect the real borrow cost in netApy without mutating the shared marketInfo.assetsData.
|
|
175
|
+
assetsDataForApy = {
|
|
176
|
+
...marketInfo.assetsData,
|
|
177
|
+
[marketInfo.loanToken]: { ...loanTokenData, borrowRate },
|
|
178
|
+
};
|
|
179
|
+
} catch (err) {
|
|
180
|
+
// Orderbook API unavailable — keep the on-chain-only fallback above.
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
usedAssets,
|
|
186
|
+
credit,
|
|
187
|
+
debt,
|
|
188
|
+
borrowRate,
|
|
189
|
+
debtBase,
|
|
190
|
+
debtInterest,
|
|
191
|
+
maturity: marketInfo.maturity,
|
|
192
|
+
isMatured: marketInfo.isMatured,
|
|
193
|
+
...getMorphoMidnightAggregatedPositionData({ usedAssets, assetsData: assetsDataForApy, marketInfo }),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export async function getMorphoMidnightAccountData(provider: EthereumProvider, network: NetworkNumber, account: EthAddress, selectedMarket: MorphoMidnightMarketData, marketInfo: MorphoMidnightMarketInfo): Promise<MorphoMidnightPositionData> {
|
|
198
|
+
return _getMorphoMidnightAccountData(getViemProvider(provider, network), network, account, selectedMarket, marketInfo);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export const _getMorphoMidnightAccountBalances = async (provider: Client, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress, selectedMarket: MorphoMidnightMarketData): Promise<PositionBalances> => {
|
|
202
|
+
const balances: PositionBalances = { collateral: {}, debt: {} };
|
|
203
|
+
if (!address) return balances;
|
|
204
|
+
|
|
205
|
+
const { marketId, loanToken, collaterals } = selectedMarket;
|
|
206
|
+
const viewContract = MorphoMidnightViewContractViem(provider, network, block);
|
|
207
|
+
const positionInfo = await viewContract.read.getPositionInfo([marketId as `0x${string}`, address], setViemBlockNumber(block));
|
|
208
|
+
|
|
209
|
+
const loanTokenInfo = getAssetInfoByAddress(loanToken, network);
|
|
210
|
+
balances.debt = {
|
|
211
|
+
[addressMapping ? loanTokenInfo.address.toLowerCase() : wethToEth(loanTokenInfo.symbol)]: assetAmountInEth(positionInfo.debt.toString(), wethToEth(loanTokenInfo.symbol)),
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const collateral: Record<string, string> = {};
|
|
215
|
+
collaterals.forEach((coll, i) => {
|
|
216
|
+
const collInfo = getAssetInfoByAddress(coll.token, network);
|
|
217
|
+
const rawAmount = positionInfo.collateral[i] ? positionInfo.collateral[i].toString() : '0';
|
|
218
|
+
collateral[addressMapping ? collInfo.address.toLowerCase() : wethToEth(collInfo.symbol)] = assetAmountInEth(rawAmount, wethToEth(collInfo.symbol));
|
|
219
|
+
});
|
|
220
|
+
balances.collateral = collateral;
|
|
221
|
+
|
|
222
|
+
return balances;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export const getMorphoMidnightAccountBalances = async (
|
|
226
|
+
provider: EthereumProvider,
|
|
227
|
+
network: NetworkNumber,
|
|
228
|
+
block: Blockish,
|
|
229
|
+
addressMapping: boolean,
|
|
230
|
+
address: EthAddress,
|
|
231
|
+
selectedMarket: MorphoMidnightMarketData,
|
|
232
|
+
): Promise<PositionBalances> => _getMorphoMidnightAccountBalances(getViemProvider(provider, network), network, block, addressMapping, address, selectedMarket);
|
|
233
|
+
|
|
234
|
+
// Lender-side (credit-only) supply position, mirroring getMorphoEarn. `amount` is the credit face value
|
|
235
|
+
// redeemable at maturity; `apy` is '0' in MVP because the fixed yield is orderbook-derived, not on-chain.
|
|
236
|
+
export async function getMorphoMidnightEarn(provider: Client, network: NetworkNumber, account: EthAddress, selectedMarket: MorphoMidnightMarketData, marketInfo: MorphoMidnightMarketInfo): Promise<{ apy: string, amount: string, amountUsd: string }> {
|
|
237
|
+
const { marketId } = selectedMarket;
|
|
238
|
+
const viewContract = MorphoMidnightViewContractViem(provider, network);
|
|
239
|
+
const positionInfo = await viewContract.read.getPositionInfo([marketId as `0x${string}`, account]);
|
|
240
|
+
|
|
241
|
+
const loanTokenData = marketInfo.assetsData[marketInfo.loanToken];
|
|
242
|
+
const credit = assetAmountInEth(positionInfo.credit.toString(), marketInfo.loanToken);
|
|
243
|
+
const creditUsd = new Dec(credit).mul(loanTokenData.price).toString();
|
|
244
|
+
|
|
245
|
+
const usedAssets: MMUsedAssets = {
|
|
246
|
+
[marketInfo.loanToken]: {
|
|
247
|
+
symbol: marketInfo.loanToken,
|
|
248
|
+
supplied: credit,
|
|
249
|
+
borrowed: '0',
|
|
250
|
+
isSupplied: new Dec(positionInfo.credit.toString()).gt(0),
|
|
251
|
+
isBorrowed: false,
|
|
252
|
+
collateral: false,
|
|
253
|
+
suppliedUsd: creditUsd,
|
|
254
|
+
borrowedUsd: '0',
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const { netApy } = calculateNetApy({ usedAssets, assetsData: marketInfo.assetsData as unknown as MMAssetsData });
|
|
259
|
+
|
|
260
|
+
return { apy: netApy, amount: credit, amountUsd: creditUsd };
|
|
261
|
+
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
CrvUsdMarkets,
|
|
11
11
|
LlamaLendMarkets,
|
|
12
12
|
MorphoBlueMarkets,
|
|
13
|
+
MorphoMidnightMarkets,
|
|
13
14
|
SparkMarkets,
|
|
14
15
|
} from '../markets';
|
|
15
16
|
import { AaveVersions, CompoundVersions } from '../types';
|
|
@@ -19,6 +20,7 @@ import { _getCompoundV3AccountBalances } from '../compoundV3';
|
|
|
19
20
|
import { _getCrvUsdAccountBalances } from '../curveUsd';
|
|
20
21
|
import { _getLlamaLendAccountBalances } from '../llamaLend';
|
|
21
22
|
import { _getMorphoBlueAccountBalances } from '../morphoBlue';
|
|
23
|
+
import { _getMorphoMidnightAccountBalances } from '../morphoMidnight';
|
|
22
24
|
import { createViemContractFromConfigFunc } from '../contracts';
|
|
23
25
|
|
|
24
26
|
const hasAnyBalance = (balances: PositionBalances): boolean => (
|
|
@@ -109,6 +111,10 @@ export async function getUserPositionsExistence(
|
|
|
109
111
|
tasks.push(balanceTask(market.value, () => _getMorphoBlueAccountBalances(client, network, block, false, address, market)));
|
|
110
112
|
});
|
|
111
113
|
|
|
114
|
+
Object.values(MorphoMidnightMarkets(network)).filter((market) => market.chainIds.includes(network)).forEach((market) => {
|
|
115
|
+
tasks.push(balanceTask(market.value, () => _getMorphoMidnightAccountBalances(client, network, block, false, address, market)));
|
|
116
|
+
});
|
|
117
|
+
|
|
112
118
|
await Promise.all(tasks);
|
|
113
119
|
return existence;
|
|
114
120
|
}
|
package/src/portfolio/index.ts
CHANGED
|
@@ -9,9 +9,11 @@ import {
|
|
|
9
9
|
LiquityV2Markets,
|
|
10
10
|
LlamaLendMarkets,
|
|
11
11
|
MorphoBlueMarkets,
|
|
12
|
+
MorphoMidnightMarkets,
|
|
12
13
|
SparkMarkets,
|
|
13
14
|
} from '../markets';
|
|
14
|
-
import { _getMorphoBlueAccountData,
|
|
15
|
+
import { _getMorphoBlueAccountData, _getMorphoBlueMarketData, getMorphoEarn } from '../morphoBlue';
|
|
16
|
+
import { _getMorphoMidnightAccountData, _getMorphoMidnightMarketData, getMorphoMidnightEarn } from '../morphoMidnight';
|
|
15
17
|
import {
|
|
16
18
|
AaveV2MarketData,
|
|
17
19
|
AaveV3MarketData,
|
|
@@ -26,6 +28,7 @@ import {
|
|
|
26
28
|
LiquityV2MarketData,
|
|
27
29
|
LlamaLendGlobalMarketData,
|
|
28
30
|
MorphoBlueMarketInfo,
|
|
31
|
+
MorphoMidnightMarketInfo,
|
|
29
32
|
PortfolioPositionsData,
|
|
30
33
|
SparkMarketsData,
|
|
31
34
|
} from '../types';
|
|
@@ -62,6 +65,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
62
65
|
const isFluidSupported = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Base, NetworkNumber.Plasma].includes(network);
|
|
63
66
|
|
|
64
67
|
const morphoMarkets = Object.values(MorphoBlueMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
68
|
+
const morphoMidnightMarkets = Object.values(MorphoMidnightMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
65
69
|
const compoundV3Markets = Object.values(CompoundMarkets(network)).filter((market) => market.chainIds.includes(network) && market.value !== CompoundVersions.CompoundV2);
|
|
66
70
|
const sparkMarkets = Object.values(SparkMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
67
71
|
const eulerV2Markets = Object.values(EulerV2Markets(network)).filter((market) => market.chainIds.includes(network));
|
|
@@ -79,6 +83,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
79
83
|
const defaultClient = getViemProvider(defaultProvider, ...args);
|
|
80
84
|
|
|
81
85
|
const morphoMarketsData: Record<string, MorphoBlueMarketInfo> = {};
|
|
86
|
+
const morphoMidnightMarketsData: Record<string, MorphoMidnightMarketInfo> = {};
|
|
82
87
|
const compoundV3MarketsData: Record<string, CompoundV3MarketsData> = {};
|
|
83
88
|
const sparkMarketsData: Record<string, SparkMarketsData> = {};
|
|
84
89
|
const eulerV2MarketsData: Record<string, EulerV2FullMarketData> = {};
|
|
@@ -93,6 +98,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
93
98
|
|
|
94
99
|
const markets = {
|
|
95
100
|
morphoMarketsData,
|
|
101
|
+
morphoMidnightMarketsData,
|
|
96
102
|
compoundV3MarketsData,
|
|
97
103
|
sparkMarketsData,
|
|
98
104
|
eulerV2MarketsData,
|
|
@@ -115,6 +121,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
115
121
|
aaveV3: {},
|
|
116
122
|
aaveV4: {},
|
|
117
123
|
morphoBlue: {},
|
|
124
|
+
morphoMidnight: {},
|
|
118
125
|
compoundV3: {},
|
|
119
126
|
spark: {},
|
|
120
127
|
eulerV2: {},
|
|
@@ -136,6 +143,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
136
143
|
stakingPositions[address.toLowerCase() as EthAddress] = {
|
|
137
144
|
aaveV3: {},
|
|
138
145
|
morphoBlue: {},
|
|
146
|
+
morphoMidnight: {},
|
|
139
147
|
compoundV3: {},
|
|
140
148
|
spark: {},
|
|
141
149
|
aaveV2: {},
|
|
@@ -162,9 +170,13 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
162
170
|
await Promise.allSettled([
|
|
163
171
|
// === MARKET DATA (needs to be fetched first) ===
|
|
164
172
|
...morphoMarkets.map(async (market) => {
|
|
165
|
-
const marketData = await
|
|
173
|
+
const marketData = await _getMorphoBlueMarketData(client, network, market);
|
|
166
174
|
morphoMarketsData[market.value] = marketData;
|
|
167
175
|
}),
|
|
176
|
+
...morphoMidnightMarkets.map(async (market) => {
|
|
177
|
+
const marketData = await _getMorphoMidnightMarketData(client, network, market);
|
|
178
|
+
morphoMidnightMarketsData[market.value] = marketData;
|
|
179
|
+
}),
|
|
168
180
|
...compoundV3Markets.map(async (market) => {
|
|
169
181
|
const marketData = await _getCompoundV3MarketsData(client, network, market, defaultClient);
|
|
170
182
|
compoundV3MarketsData[market.value] = marketData;
|
|
@@ -456,6 +468,38 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
456
468
|
positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
|
|
457
469
|
}
|
|
458
470
|
})).flat(),
|
|
471
|
+
...morphoMidnightMarkets.map((market) => addresses.map(async (address) => {
|
|
472
|
+
try {
|
|
473
|
+
const [accDataPromise, earnDataPromise] = await Promise.allSettled([
|
|
474
|
+
_getMorphoMidnightAccountData(client, network, address, market, morphoMidnightMarketsData[market.value]),
|
|
475
|
+
getMorphoMidnightEarn(client, network, address, market, morphoMidnightMarketsData[market.value]),
|
|
476
|
+
]);
|
|
477
|
+
if (accDataPromise.status === 'rejected') {
|
|
478
|
+
console.error(`Error fetching MorphoMidnight account data for address ${address} on market ${market.value}:`, accDataPromise.reason);
|
|
479
|
+
positions[address.toLowerCase() as EthAddress].morphoMidnight[market.value] = { error: `Error fetching MorphoMidnight account data for address ${address} on market ${market.value}`, data: null };
|
|
480
|
+
}
|
|
481
|
+
if (earnDataPromise.status === 'rejected') {
|
|
482
|
+
console.error(`Error fetching MorphoMidnight account data for address ${address} on market ${market.value}:`, earnDataPromise.reason);
|
|
483
|
+
positions[address.toLowerCase() as EthAddress].morphoMidnight[market.value] = { error: `Error fetching MorphoMidnight account data for address ${address} on market ${market.value}`, data: null };
|
|
484
|
+
}
|
|
485
|
+
if (accDataPromise.status !== 'rejected') {
|
|
486
|
+
const accData = accDataPromise.value;
|
|
487
|
+
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].morphoMidnight[market.value] = { error: '', data: accData };
|
|
488
|
+
}
|
|
489
|
+
if (earnDataPromise.status !== 'rejected') {
|
|
490
|
+
const earnData = earnDataPromise.value;
|
|
491
|
+
if (earnData && new Dec(earnData.amount).gt(0)) {
|
|
492
|
+
stakingPositions[address.toLowerCase() as EthAddress].morphoMidnight[market.value] = {
|
|
493
|
+
error: '',
|
|
494
|
+
data: earnData,
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
} catch (error) {
|
|
499
|
+
console.error(`Error fetching MorphoMidnight account data for address ${address} on market ${market.value}:`, error);
|
|
500
|
+
positions[address.toLowerCase() as EthAddress].morphoMidnight[market.value] = { error: `Error fetching MorphoMidnight account data for address ${address} on market ${market.value}`, data: null };
|
|
501
|
+
}
|
|
502
|
+
})).flat(),
|
|
459
503
|
...compoundV3Markets.map((market) => addresses.map(async (address) => {
|
|
460
504
|
try {
|
|
461
505
|
const accData = await _getCompoundV3AccountData(client, network, address, ZERO_ADDRESS, { selectedMarket: market, assetsData: compoundV3MarketsData[market.value].assetsData });
|
package/src/types/index.ts
CHANGED
package/src/types/morphoBlue.ts
CHANGED
|
@@ -235,17 +235,6 @@ export interface MorphoBluePositionData {
|
|
|
235
235
|
exposure: string,
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
-
export interface MorphoBlueEarnData {
|
|
239
|
-
apy: string,
|
|
240
|
-
amount: string,
|
|
241
|
-
amountUsd: string,
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
export interface MorphoBlueMarketRewards {
|
|
245
|
-
supplyApy: string,
|
|
246
|
-
borrowApy: string,
|
|
247
|
-
}
|
|
248
|
-
|
|
249
238
|
export interface MorphoBlueVault {
|
|
250
239
|
address: string,
|
|
251
240
|
}
|