@defisaver/positions-sdk 2.1.169-portfolio-api-dev → 2.1.169
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/aaveV3/index.d.ts +12 -2
- package/cjs/aaveV3/sgho.d.ts +16 -2
- package/cjs/aaveV4/lend.d.ts +43 -2
- package/cjs/claiming/aaveV3.d.ts +2 -2
- package/cjs/claiming/ethena.d.ts +1 -2
- package/cjs/claiming/king.d.ts +1 -2
- package/cjs/claiming/spark.d.ts +2 -2
- package/cjs/fluid/index.d.ts +49 -5
- package/cjs/liquity/index.d.ts +12 -2
- package/cjs/liquityV2/index.d.ts +23 -8
- package/cjs/liquityV2/index.js +19 -37
- package/cjs/markets/morphoBlue/index.d.ts +2 -0
- package/cjs/markets/morphoBlue/index.js +18 -1
- package/cjs/portfolio/index.d.ts +8 -16
- package/cjs/portfolio/index.js +520 -458
- package/cjs/savings/index.d.ts +2 -8
- package/cjs/savings/index.js +4 -33
- package/cjs/types/aave.d.ts +2 -98
- package/cjs/types/aaveV4.d.ts +0 -44
- package/cjs/types/compound.d.ts +2 -26
- package/cjs/types/curveUsd.d.ts +1 -9
- package/cjs/types/fluid.d.ts +0 -13
- package/cjs/types/index.d.ts +0 -1
- package/cjs/types/index.js +0 -1
- package/cjs/types/liquity.d.ts +0 -12
- package/cjs/types/liquityV2.d.ts +0 -24
- package/cjs/types/llamaLend.d.ts +1 -12
- package/cjs/types/morphoBlue.d.ts +1 -2
- package/cjs/types/morphoBlue.js +1 -0
- package/cjs/types/portfolio.d.ts +5 -125
- package/cjs/types/savings/index.d.ts +1 -9
- package/cjs/types/spark.d.ts +1 -21
- package/cjs/umbrella/index.d.ts +3 -2
- package/esm/aaveV3/index.d.ts +12 -2
- package/esm/aaveV3/sgho.d.ts +16 -2
- package/esm/aaveV4/lend.d.ts +43 -2
- package/esm/claiming/aaveV3.d.ts +2 -2
- package/esm/claiming/aaveV3.js +1 -1
- package/esm/claiming/ethena.d.ts +1 -2
- package/esm/claiming/king.d.ts +1 -2
- package/esm/claiming/spark.d.ts +2 -2
- package/esm/claiming/spark.js +1 -1
- package/esm/fluid/index.d.ts +49 -5
- package/esm/liquity/index.d.ts +12 -2
- package/esm/liquityV2/index.d.ts +23 -8
- package/esm/liquityV2/index.js +18 -35
- package/esm/markets/morphoBlue/index.d.ts +2 -0
- package/esm/markets/morphoBlue/index.js +16 -0
- package/esm/portfolio/index.d.ts +8 -16
- package/esm/portfolio/index.js +522 -458
- package/esm/savings/index.d.ts +2 -8
- package/esm/savings/index.js +3 -31
- package/esm/types/aave.d.ts +2 -98
- package/esm/types/aaveV4.d.ts +0 -44
- package/esm/types/compound.d.ts +2 -26
- package/esm/types/curveUsd.d.ts +1 -9
- package/esm/types/fluid.d.ts +0 -13
- package/esm/types/index.d.ts +0 -1
- package/esm/types/index.js +0 -1
- package/esm/types/liquity.d.ts +0 -12
- package/esm/types/liquityV2.d.ts +0 -24
- package/esm/types/llamaLend.d.ts +1 -12
- package/esm/types/morphoBlue.d.ts +1 -2
- package/esm/types/morphoBlue.js +1 -0
- package/esm/types/portfolio.d.ts +5 -125
- package/esm/types/savings/index.d.ts +1 -9
- package/esm/types/spark.d.ts +1 -21
- package/esm/umbrella/index.d.ts +3 -2
- package/package.json +2 -2
- package/src/aaveV3/index.ts +1 -2
- package/src/aaveV3/sgho.ts +17 -2
- package/src/aaveV4/lend.ts +48 -2
- package/src/claiming/aaveV3.ts +2 -4
- package/src/claiming/ethena.ts +5 -5
- package/src/claiming/king.ts +3 -3
- package/src/claiming/spark.ts +4 -6
- package/src/fluid/index.ts +3 -4
- package/src/liquity/index.ts +2 -2
- package/src/liquityV2/index.ts +22 -48
- package/src/markets/morphoBlue/index.ts +16 -0
- package/src/portfolio/index.ts +331 -306
- package/src/savings/index.ts +4 -41
- package/src/types/aave.ts +1 -104
- package/src/types/aaveV4.ts +0 -49
- package/src/types/compound.ts +2 -38
- package/src/types/curveUsd.ts +1 -17
- package/src/types/fluid.ts +0 -14
- package/src/types/index.ts +0 -1
- package/src/types/liquity.ts +1 -13
- package/src/types/liquityV2.ts +1 -26
- package/src/types/llamaLend.ts +1 -19
- package/src/types/morphoBlue.ts +1 -3
- package/src/types/portfolio.ts +5 -151
- package/src/types/savings/index.ts +1 -12
- package/src/types/spark.ts +1 -25
- package/src/umbrella/index.ts +1 -2
- package/cjs/portfolio/lendingProtocols.d.ts +0 -184
- package/cjs/portfolio/lendingProtocols.js +0 -196
- package/esm/portfolio/lendingProtocols.d.ts +0 -184
- package/esm/portfolio/lendingProtocols.js +0 -189
- package/src/portfolio/lendingProtocols.ts +0 -281
package/src/portfolio/index.ts
CHANGED
|
@@ -9,158 +9,137 @@ import {
|
|
|
9
9
|
LlamaLendMarkets,
|
|
10
10
|
MakerActiveIlks,
|
|
11
11
|
MorphoBlueMarkets,
|
|
12
|
+
MorphoMidnightMarkets,
|
|
12
13
|
SparkMarkets,
|
|
13
14
|
} from '../markets';
|
|
14
15
|
import { _getMorphoBlueAccountData, _getMorphoBluePortfolioMarketData, getMorphoEarn } from '../morphoBlue';
|
|
16
|
+
import { _getMorphoMidnightAccountData, _getMorphoMidnightMarketData } from '../morphoMidnight';
|
|
15
17
|
import {
|
|
18
|
+
AaveV2MarketData,
|
|
19
|
+
AaveV3MarketData,
|
|
20
|
+
AaveV4SpokeData,
|
|
16
21
|
AaveVersions,
|
|
17
22
|
CdpInfo,
|
|
23
|
+
CompoundV2MarketsData,
|
|
24
|
+
CompoundV3MarketsData,
|
|
18
25
|
CompoundVersions,
|
|
26
|
+
CrvUSDGlobalMarketData,
|
|
19
27
|
LiquityV2MarketData,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
PortfolioLiquityV2Troves,
|
|
28
|
+
LlamaLendGlobalMarketData,
|
|
29
|
+
MorphoBlueMarketInfo,
|
|
30
|
+
MorphoMidnightMarketInfo,
|
|
24
31
|
PortfolioMarketsData,
|
|
25
|
-
PortfolioMarketsErrors,
|
|
26
|
-
PortfolioMarketsResult,
|
|
27
32
|
PortfolioPositionsData,
|
|
28
|
-
|
|
29
|
-
PortfolioRewardsData,
|
|
30
|
-
PortfolioStakingPositionsData,
|
|
31
|
-
PortfolioUserData,
|
|
32
|
-
ShifterPortfolioMarketsData,
|
|
33
|
+
SparkMarketsData,
|
|
33
34
|
} from '../types';
|
|
34
35
|
import { _getCompoundV3AccountData, _getCompoundV3MarketsData } from '../compoundV3';
|
|
35
36
|
import { _getSparkAccountData, _getSparkMarketsData } from '../spark';
|
|
36
37
|
import { _getCurveUsdGlobalData, _getCurveUsdUserData } from '../curveUsd';
|
|
37
38
|
import { _getLlamaLendGlobalData, _getLlamaLendUserData } from '../llamaLend';
|
|
38
39
|
import { _getAaveV3AccountData, _getAaveV3MarketData, getStakeAaveData } from '../aaveV3';
|
|
39
|
-
import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
|
|
40
40
|
import { ZERO_ADDRESS } from '../constants';
|
|
41
41
|
import { _getMakerCdpData, _getMakerIlksData, _getUserCdps } from '../maker';
|
|
42
42
|
import { _getAaveV2AccountData, _getAaveV2MarketsData } from '../aaveV2';
|
|
43
43
|
import { _getCompoundV2AccountData, _getCompoundV2MarketsData } from '../compoundV2';
|
|
44
44
|
import { getViemProvider } from '../services/viem';
|
|
45
45
|
import { _getLiquityTroveInfo, getLiquityStakingData } from '../liquity';
|
|
46
|
-
import {
|
|
47
|
-
_getLiquityV2MarketData, _getLiquityV2TroveData, _getLiquityV2UsersTroveIds, getLiquitySAndYBold, getLiquityV2Staking,
|
|
48
|
-
} from '../liquityV2';
|
|
46
|
+
import { _getLiquityV2MarketData, getLiquitySAndYBold, getLiquityV2Staking } from '../liquityV2';
|
|
49
47
|
import { _getAllFluidMarketDataPortfolio, _getAllUserEarnPositionsWithFTokens, _getUserPositionsPortfolio } from '../fluid';
|
|
50
48
|
import { getUmbrellaData } from '../umbrella';
|
|
51
49
|
import { getMerklUnclaimedRewards, getUnclaimedRewardsForAllMarkets } from '../claiming/aaveV3';
|
|
52
50
|
import { fetchSparkAirdropRewards, fetchSparkRewards } from '../claiming/spark';
|
|
53
51
|
import { getKingRewards } from '../claiming/king';
|
|
54
52
|
import { fetchEthenaAirdropRewards } from '../claiming/ethena';
|
|
53
|
+
import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
|
|
55
54
|
import { getUniswapRewards } from '../claiming/uniswap';
|
|
56
|
-
import {
|
|
57
|
-
aaveV3Lending, LENDING_PROTOCOLS, LendingKey, morphoBlueLending, requireMarket, sparkLending,
|
|
58
|
-
} from './lendingProtocols';
|
|
59
55
|
|
|
60
|
-
|
|
56
|
+
export async function getPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], isSim = false): Promise<{
|
|
57
|
+
positions: PortfolioPositionsData;
|
|
58
|
+
stakingPositions: any;
|
|
59
|
+
rewardsData: any;
|
|
60
|
+
markets: any;
|
|
61
|
+
}> {
|
|
62
|
+
const isMainnet = network === NetworkNumber.Eth;
|
|
63
|
+
const isFluidSupported = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Base, NetworkNumber.Plasma].includes(network);
|
|
64
|
+
|
|
65
|
+
const morphoMarkets = Object.values(MorphoBlueMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
66
|
+
const morphoMidnightMarkets = Object.values(MorphoMidnightMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
67
|
+
const compoundV3Markets = Object.values(CompoundMarkets(network)).filter((market) => market.chainIds.includes(network) && market.value !== CompoundVersions.CompoundV2);
|
|
68
|
+
const sparkMarkets = Object.values(SparkMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
69
|
+
const aaveV3Markets = [AaveVersions.AaveV3, AaveVersions.AaveV3Lido, AaveVersions.AaveV3Etherfi].map((version) => AaveMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
|
|
70
|
+
const aaveV2Markets = [AaveVersions.AaveV2].map((version) => AaveMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
|
|
71
|
+
const compoundV2Markets = [CompoundVersions.CompoundV2].map((version) => CompoundMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
|
|
72
|
+
const crvUsdMarkets = Object.values(CrvUsdMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
73
|
+
const llamaLendMarkets = [NetworkNumber.Eth, NetworkNumber.Arb].includes(network) ? Object.values(LlamaLendMarkets(network)).filter((market) => market.chainIds.includes(network)) : [];
|
|
74
|
+
const liquityV2Markets = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)) : [];
|
|
75
|
+
const liquityV2MarketsStaking = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)).filter(market => !market.isLegacy) : [];
|
|
76
|
+
const aaveV4Spokes = Object.values(AaveV4Spokes(network)).filter((market) => market.chainIds.includes(network));
|
|
61
77
|
|
|
62
|
-
/** The markets of the protocols read by their own code below; LENDING_PROTOCOLS has the others'. */
|
|
63
|
-
const getPortfolioMarkets = (network: NetworkNumber) => ({
|
|
64
|
-
liquityV2Markets: [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)) : [],
|
|
65
|
-
liquityV2MarketsStaking: [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)).filter(market => !market.isLegacy) : [],
|
|
66
|
-
});
|
|
67
78
|
|
|
68
|
-
const getPortfolioClients = (provider: EthereumProvider, defaultProvider: EthereumProvider, network: NetworkNumber, isSim: boolean) => {
|
|
69
79
|
// batchSize is viem's cap on a batch's raw subcall calldata (bytes); the JSON-RPC body ends up
|
|
70
80
|
// ~4x larger (hex + aggregate3 ABI + JSON overhead) and RPC providers reject bodies over
|
|
71
81
|
// ~2.5MB with HTTP 413, so keep this small enough that no single body gets near that.
|
|
72
82
|
// 250k gives the largest body of around 1.26MB - if we bump, we can save maybe 2-3 rpc calls but scaling takes a hit
|
|
73
83
|
// (e.g. adding new markets may result in body size going over alchemy body size limit)
|
|
74
84
|
const args: [NetworkNumber, any?] = [network, { batch: { multicall: { batchSize: isSim ? 2_000 : 250_000 } } }];
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
defaultClient: getViemProvider(defaultProvider, ...args),
|
|
78
|
-
};
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const _getPortfolioMarketsData = async (client: PortfolioClient, defaultClient: PortfolioClient, network: NetworkNumber): Promise<PortfolioMarketsResult> => {
|
|
82
|
-
const { liquityV2Markets } = getPortfolioMarkets(network);
|
|
85
|
+
const client = getViemProvider(provider, ...args);
|
|
86
|
+
const defaultClient = getViemProvider(defaultProvider, ...args);
|
|
83
87
|
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
88
|
+
const morphoMarketsData: Record<string, MorphoBlueMarketInfo> = {};
|
|
89
|
+
const morphoMidnightMarketsData: Record<string, MorphoMidnightMarketInfo> = {};
|
|
90
|
+
const compoundV3MarketsData: Record<string, CompoundV3MarketsData> = {};
|
|
91
|
+
const sparkMarketsData: Record<string, SparkMarketsData> = {};
|
|
92
|
+
const aaveV3MarketsData: Record<string, AaveV3MarketData> = {};
|
|
93
|
+
const makerCdps: Record<string, CdpInfo[]> = {};
|
|
94
|
+
const aaveV2MarketsData: Record<string, AaveV2MarketData> = {};
|
|
95
|
+
const compoundV2MarketsData: Record<string, CompoundV2MarketsData> = {};
|
|
96
|
+
const crvUsdMarketsData: Record<string, CrvUSDGlobalMarketData> = {};
|
|
97
|
+
const llamaLendMarketsData: Record<string, LlamaLendGlobalMarketData> = {};
|
|
98
|
+
const liquityV2MarketsData: Record<string, LiquityV2MarketData> = {};
|
|
99
|
+
const aaveV4SpokesData: Record<string, AaveV4SpokeData> = {};
|
|
100
|
+
|
|
101
|
+
const markets = {
|
|
102
|
+
morphoMarketsData,
|
|
103
|
+
morphoMidnightMarketsData,
|
|
104
|
+
compoundV3MarketsData,
|
|
105
|
+
sparkMarketsData,
|
|
106
|
+
aaveV3MarketsData,
|
|
107
|
+
aaveV2MarketsData,
|
|
108
|
+
compoundV2MarketsData,
|
|
109
|
+
crvUsdMarketsData,
|
|
110
|
+
llamaLendMarketsData,
|
|
111
|
+
liquityV2MarketsData,
|
|
112
|
+
aaveV4SpokesData,
|
|
96
113
|
};
|
|
97
|
-
const liquityV2Failed: Record<string, string> = {};
|
|
98
|
-
|
|
99
|
-
const [lendingFailed] = await Promise.all([
|
|
100
|
-
Promise.all(LENDING_PROTOCOLS.map(async (protocol) => [protocol.marketsKey, await protocol.readMarkets({ client, defaultClient, network }, markets)] as const)),
|
|
101
|
-
// Liquity V2's markets: its troves are read by their own code below.
|
|
102
|
-
...liquityV2Markets.map(async (market) => {
|
|
103
|
-
try {
|
|
104
|
-
markets.liquityV2MarketsData[market.value] = await _getLiquityV2MarketData(client, network, market);
|
|
105
|
-
} catch (error) {
|
|
106
|
-
console.error(`Error fetching liquityV2MarketsData for market ${market.value}:`, error);
|
|
107
|
-
liquityV2Failed[market.value] = `Error fetching liquityV2MarketsData for market ${market.value}`;
|
|
108
|
-
}
|
|
109
|
-
}),
|
|
110
|
-
]);
|
|
111
|
-
|
|
112
|
-
// A market that failed is left out of markets and named here, under its protocol's key.
|
|
113
|
-
const errors: PortfolioMarketsErrors = {};
|
|
114
|
-
for (const [key, failed] of [...lendingFailed, ['liquityV2MarketsData', liquityV2Failed] as const]) {
|
|
115
|
-
if (Object.keys(failed).length) errors[key] = failed;
|
|
116
|
-
}
|
|
117
|
-
return { markets, errors };
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const _getPortfolioUserData = async (
|
|
121
|
-
client: PortfolioClient,
|
|
122
|
-
network: NetworkNumber,
|
|
123
|
-
addresses: EthAddress[],
|
|
124
|
-
marketsData: Promise<PortfolioMarketsData>,
|
|
125
|
-
isSim: boolean,
|
|
126
|
-
options: PortfolioDataOptions,
|
|
127
|
-
): Promise<PortfolioUserData> => {
|
|
128
|
-
const isMainnet = network === NetworkNumber.Eth;
|
|
129
|
-
const includePositions = options.positions !== false;
|
|
130
|
-
const includeStaking = options.staking !== false;
|
|
131
|
-
const includeRewards = options.rewards !== false;
|
|
132
|
-
const includeLiquityV2 = includePositions && options.liquityV2 === true;
|
|
133
|
-
// The addresses positions, staking, rewards and Liquity V2 troves are read for: none when left out, so their fetches
|
|
134
|
-
// below make no calls.
|
|
135
|
-
const positionsAddresses = includePositions ? addresses : [];
|
|
136
|
-
const stakingAddresses = includeStaking ? addresses : [];
|
|
137
|
-
const rewardsAddresses = includeRewards ? addresses : [];
|
|
138
|
-
const liquityV2Addresses = includeLiquityV2 ? addresses : [];
|
|
139
|
-
const isFluidSupported = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Base, NetworkNumber.Plasma].includes(network);
|
|
140
|
-
|
|
141
|
-
const { liquityV2Markets, liquityV2MarketsStaking } = getPortfolioMarkets(network);
|
|
142
114
|
|
|
143
115
|
const positions: PortfolioPositionsData = {};
|
|
144
|
-
const stakingPositions:
|
|
145
|
-
const rewardsData:
|
|
146
|
-
const
|
|
116
|
+
const stakingPositions: any = {};
|
|
117
|
+
const rewardsData: any = {};
|
|
118
|
+
const allAddresses = [...addresses];
|
|
147
119
|
|
|
148
|
-
for (const address of
|
|
120
|
+
for (const address of allAddresses) {
|
|
149
121
|
positions[address.toLowerCase() as EthAddress] = {
|
|
150
|
-
|
|
151
|
-
|
|
122
|
+
aaveV3: {},
|
|
123
|
+
aaveV4: {},
|
|
124
|
+
morphoBlue: {},
|
|
125
|
+
morphoMidnight: {},
|
|
126
|
+
compoundV3: {},
|
|
127
|
+
spark: {},
|
|
152
128
|
maker: {},
|
|
129
|
+
aaveV2: {},
|
|
130
|
+
compoundV2: {},
|
|
153
131
|
liquity: {},
|
|
132
|
+
crvUsd: {},
|
|
133
|
+
llamaLend: {},
|
|
154
134
|
fluid: {
|
|
155
135
|
error: '',
|
|
156
136
|
data: {},
|
|
157
137
|
},
|
|
158
|
-
...(includeLiquityV2 ? { liquityV2: {} } : {}),
|
|
159
138
|
};
|
|
160
139
|
}
|
|
161
140
|
|
|
162
141
|
// TODO: check default values, probably needed when fetching portfolio on unsupported networks
|
|
163
|
-
for (const address of
|
|
142
|
+
for (const address of addresses) {
|
|
164
143
|
stakingPositions[address.toLowerCase() as EthAddress] = {
|
|
165
144
|
aaveV3: {},
|
|
166
145
|
morphoBlue: {},
|
|
@@ -174,52 +153,72 @@ const _getPortfolioUserData = async (
|
|
|
174
153
|
error: '',
|
|
175
154
|
data: {},
|
|
176
155
|
},
|
|
177
|
-
// Set by the sBOLD/yBOLD fetch below, whether it succeeds or fails.
|
|
178
|
-
liquityV2SBoldYBold: { error: '', data: null },
|
|
179
156
|
};
|
|
180
|
-
}
|
|
181
157
|
|
|
182
|
-
for (const address of rewardsAddresses) {
|
|
183
|
-
// merkl, spk, king and uniswap are set by their fetches below, whether they succeed or fail.
|
|
184
158
|
rewardsData[address.toLowerCase() as EthAddress] = {
|
|
185
|
-
merkl: {
|
|
159
|
+
merkl: {},
|
|
186
160
|
aaveV3: {},
|
|
187
161
|
spark: {},
|
|
188
|
-
spk: {
|
|
189
|
-
king: {
|
|
162
|
+
spk: {},
|
|
163
|
+
king: {},
|
|
190
164
|
ethena: {},
|
|
191
|
-
uniswap: { error: '', data: null },
|
|
192
165
|
};
|
|
193
166
|
}
|
|
194
167
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
168
|
+
await Promise.allSettled([
|
|
169
|
+
// === MARKET DATA (needs to be fetched first) ===
|
|
170
|
+
...morphoMarkets.map(async (market) => {
|
|
171
|
+
const marketData = await _getMorphoBluePortfolioMarketData(client, network, market);
|
|
172
|
+
morphoMarketsData[market.value] = marketData;
|
|
173
|
+
}),
|
|
174
|
+
...morphoMidnightMarkets.map(async (market) => {
|
|
175
|
+
const marketData = await _getMorphoMidnightMarketData(client, network, market);
|
|
176
|
+
morphoMidnightMarketsData[market.value] = marketData;
|
|
177
|
+
}),
|
|
178
|
+
...compoundV3Markets.map(async (market) => {
|
|
179
|
+
const marketData = await _getCompoundV3MarketsData(client, network, market, defaultClient);
|
|
180
|
+
compoundV3MarketsData[market.value] = marketData;
|
|
181
|
+
}),
|
|
182
|
+
...sparkMarkets.map(async (market) => {
|
|
183
|
+
const marketData = await _getSparkMarketsData(client, network, market);
|
|
184
|
+
sparkMarketsData[market.value] = marketData;
|
|
185
|
+
}),
|
|
186
|
+
...aaveV3Markets.map(async (market) => {
|
|
187
|
+
const marketData = await _getAaveV3MarketData(client, network, market);
|
|
188
|
+
aaveV3MarketsData[market.value] = marketData;
|
|
189
|
+
}),
|
|
190
|
+
...aaveV4Spokes.map(async (spoke) => {
|
|
191
|
+
const spokeData = await _getAaveV4SpokeData(client, network, spoke);
|
|
192
|
+
aaveV4SpokesData[spoke.value] = spokeData;
|
|
193
|
+
}),
|
|
194
|
+
...aaveV2Markets.map(async (market) => {
|
|
195
|
+
const marketData = await _getAaveV2MarketsData(client, network, market);
|
|
196
|
+
aaveV2MarketsData[market.value] = marketData;
|
|
197
|
+
}),
|
|
198
|
+
...compoundV2Markets.map(async (market) => {
|
|
199
|
+
const marketData = await _getCompoundV2MarketsData(client, network);
|
|
200
|
+
compoundV2MarketsData[market.value] = marketData;
|
|
201
|
+
}),
|
|
202
|
+
...crvUsdMarkets.map(async (market) => {
|
|
203
|
+
const marketData = await _getCurveUsdGlobalData(client, network, market);
|
|
204
|
+
crvUsdMarketsData[market.value] = marketData;
|
|
205
|
+
}),
|
|
206
|
+
...llamaLendMarkets.map(async (market) => {
|
|
207
|
+
const marketData = await _getLlamaLendGlobalData(client, network, market);
|
|
208
|
+
llamaLendMarketsData[market.value] = marketData;
|
|
209
|
+
}),
|
|
210
|
+
...liquityV2Markets.map(async (market) => {
|
|
211
|
+
const marketData = await _getLiquityV2MarketData(client, network, market);
|
|
212
|
+
liquityV2MarketsData[market.value] = marketData;
|
|
213
|
+
}),
|
|
214
|
+
|
|
215
|
+
// === INDEPENDENT USER DATA (doesn't depend on market data) ===
|
|
216
|
+
...addresses.map(async (address) => {
|
|
201
217
|
if (!isMainnet) return; // Maker CDPs are only available on mainnet
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
cdps = await _getUserCdps(client, network, address);
|
|
205
|
-
} catch (error) {
|
|
206
|
-
console.error(`Error fetching Maker CDPs for address ${address}:`, error);
|
|
207
|
-
makerCdpsErrors[address.toLowerCase() as EthAddress] = `Error fetching Maker CDPs for address ${address}`;
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
await Promise.all(cdps.map(async (cdpInfo) => {
|
|
211
|
-
try {
|
|
212
|
-
const cdpData = await _getMakerCdpData(client, network, cdpInfo);
|
|
213
|
-
if (cdpData) {
|
|
214
|
-
positions[address.toLowerCase() as EthAddress].maker[cdpInfo.id] = { error: '', data: cdpData };
|
|
215
|
-
}
|
|
216
|
-
} catch (error) {
|
|
217
|
-
console.error(`Error fetching Maker CDP data for address ${address} with ID ${cdpInfo.id}:`, error);
|
|
218
|
-
positions[address.toLowerCase() as EthAddress].maker[cdpInfo.id] = { error: `Error fetching Maker CDP data for address ${address} with ID ${cdpInfo.id}`, data: null };
|
|
219
|
-
}
|
|
220
|
-
}));
|
|
218
|
+
const makerCdp = await _getUserCdps(client, network, address);
|
|
219
|
+
makerCdps[address.toLowerCase() as EthAddress] = makerCdp;
|
|
221
220
|
}),
|
|
222
|
-
...
|
|
221
|
+
...addresses.map(async (address) => {
|
|
223
222
|
try {
|
|
224
223
|
if (!isFluidSupported) return; // Fluid is not available on Optimism
|
|
225
224
|
const userPositions = (await _getUserPositionsPortfolio(client, network, address));
|
|
@@ -236,94 +235,71 @@ const _getPortfolioUserData = async (
|
|
|
236
235
|
};
|
|
237
236
|
}
|
|
238
237
|
}),
|
|
239
|
-
...positionsAddresses.map(async (address) => {
|
|
240
|
-
try {
|
|
241
|
-
if (!isMainnet) return; // Liquity trove info is only available on mainnet
|
|
242
|
-
const troveInfo = await _getLiquityTroveInfo(client, network, address);
|
|
243
|
-
if (new Dec(troveInfo.collateral).gt(0)) positions[address.toLowerCase() as EthAddress].liquity = { error: '', data: troveInfo };
|
|
244
|
-
} catch (error) {
|
|
245
|
-
console.error(`Error fetching Liquity trove info for address ${address}:`, error);
|
|
246
|
-
positions[address.toLowerCase() as EthAddress].liquity = { error: `Error fetching Liquity trove info for address ${address}`, data: null };
|
|
247
|
-
}
|
|
248
|
-
}),
|
|
249
238
|
|
|
250
|
-
// === STAKING DATA (independent of market data
|
|
251
|
-
...
|
|
239
|
+
// === STAKING DATA (independent of market data) ===
|
|
240
|
+
...addresses.map(async (address) => {
|
|
252
241
|
try {
|
|
253
242
|
if (!isFluidSupported) return;
|
|
254
|
-
stakingPositions[address.toLowerCase()
|
|
243
|
+
stakingPositions[address.toLowerCase()].fluid = await _getAllUserEarnPositionsWithFTokens(client, network, address);
|
|
255
244
|
} catch (error) {
|
|
256
245
|
console.error(`Error fetching Fluid lend data for address ${address}:`, error);
|
|
257
|
-
stakingPositions[address.toLowerCase()
|
|
246
|
+
stakingPositions[address.toLowerCase()].fluid = { error: `Error fetching Fluid lend data for address ${address}`, data: null };
|
|
258
247
|
}
|
|
259
248
|
}),
|
|
260
|
-
...
|
|
249
|
+
...addresses.map(async (address) => {
|
|
261
250
|
try {
|
|
262
251
|
if (!isMainnet) return;
|
|
263
|
-
stakingPositions[address.toLowerCase()
|
|
252
|
+
stakingPositions[address.toLowerCase()].liquity = await getLiquityStakingData(client, network, address);
|
|
264
253
|
} catch (error) {
|
|
265
254
|
console.error(`Error fetching Liquity staking data for address ${address}:`, error);
|
|
266
|
-
stakingPositions[address.toLowerCase()
|
|
255
|
+
stakingPositions[address.toLowerCase()].liquity = { error: `Error fetching Liquity staking data for address ${address}`, data: null };
|
|
267
256
|
}
|
|
268
257
|
}),
|
|
269
|
-
...
|
|
258
|
+
...addresses.map(async (address) => {
|
|
270
259
|
try {
|
|
271
260
|
if (!isMainnet) return;
|
|
272
|
-
stakingPositions[address.toLowerCase()
|
|
261
|
+
stakingPositions[address.toLowerCase()].aaveV3 = await getStakeAaveData(client, network, address);
|
|
273
262
|
} catch (error) {
|
|
274
263
|
console.error(`Error fetching Aave V3 staking data for address ${address}:`, error);
|
|
275
|
-
stakingPositions[address.toLowerCase()
|
|
264
|
+
stakingPositions[address.toLowerCase()].aaveV3 = { error: `Error fetching Aave V3 staking data for address ${address}`, data: null };
|
|
276
265
|
}
|
|
277
266
|
}),
|
|
278
|
-
...
|
|
267
|
+
...addresses.map(async (address) => {
|
|
279
268
|
try {
|
|
280
269
|
if (!isMainnet) return;
|
|
281
|
-
stakingPositions[address.toLowerCase()
|
|
270
|
+
stakingPositions[address.toLowerCase()].umbrella = await getUmbrellaData(client, network, address);
|
|
282
271
|
} catch (error) {
|
|
283
272
|
console.error(`Error fetching Umbrella staking data for address ${address}:`, error);
|
|
284
|
-
stakingPositions[address.toLowerCase()
|
|
273
|
+
stakingPositions[address.toLowerCase()].umbrella = { error: `Error fetching Umbrella staking data for address ${address}`, data: null };
|
|
285
274
|
}
|
|
286
275
|
}),
|
|
287
|
-
// Liquity V2
|
|
288
|
-
...
|
|
289
|
-
await Promise.all(liquityV2MarketsStaking.map(async (market) => {
|
|
290
|
-
try {
|
|
291
|
-
if (!isMainnet) {
|
|
292
|
-
stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: '', data: null };
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
const liquityV2StakingData = await getLiquityV2Staking(client, network, market.value, address);
|
|
296
|
-
stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: '', data: liquityV2StakingData };
|
|
297
|
-
} catch (error) {
|
|
298
|
-
console.error(`Error fetching Liquity V2 staking data for address ${address}, market ${market.value}:`, error);
|
|
299
|
-
stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: `Error fetching Liquity V2 staking data for address ${address}`, data: null };
|
|
300
|
-
}
|
|
301
|
-
}));
|
|
276
|
+
// Liquity V2 staking
|
|
277
|
+
...liquityV2MarketsStaking.map(market => addresses.map(async (address) => {
|
|
302
278
|
try {
|
|
303
279
|
if (!isMainnet) {
|
|
304
|
-
stakingPositions[address.toLowerCase()
|
|
280
|
+
stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: '', data: null };
|
|
305
281
|
return;
|
|
306
282
|
}
|
|
307
|
-
const
|
|
308
|
-
stakingPositions[address.toLowerCase()
|
|
283
|
+
const liquityV2StakingData = await getLiquityV2Staking(client, network, market.value, address);
|
|
284
|
+
stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: '', data: liquityV2StakingData };
|
|
309
285
|
} catch (error) {
|
|
310
|
-
console.error(`Error fetching
|
|
311
|
-
stakingPositions[address.toLowerCase()
|
|
286
|
+
console.error(`Error fetching Liquity V2 staking data for address ${address}, market ${market.value}:`, error);
|
|
287
|
+
stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: `Error fetching Liquity V2 staking data for address ${address}`, data: null };
|
|
312
288
|
}
|
|
313
|
-
}),
|
|
289
|
+
})).flat(),
|
|
314
290
|
|
|
315
291
|
// === REWARDS DATA (independent of market data) ===
|
|
316
292
|
// Batch King rewards
|
|
317
293
|
(async () => {
|
|
318
294
|
try {
|
|
319
295
|
if (!isMainnet) {
|
|
320
|
-
for (const address of
|
|
321
|
-
rewardsData[address.toLowerCase()
|
|
296
|
+
for (const address of addresses) {
|
|
297
|
+
rewardsData[address.toLowerCase()].king = { error: '', data: [] };
|
|
322
298
|
}
|
|
323
299
|
return;
|
|
324
300
|
}
|
|
325
|
-
const kingRewards = await getKingRewards(client, network,
|
|
326
|
-
for (const address of
|
|
301
|
+
const kingRewards = await getKingRewards(client, network, addresses);
|
|
302
|
+
for (const address of addresses) {
|
|
327
303
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
328
304
|
rewardsData[lowerAddress].king = {
|
|
329
305
|
error: '',
|
|
@@ -332,7 +308,7 @@ const _getPortfolioUserData = async (
|
|
|
332
308
|
}
|
|
333
309
|
} catch (error) {
|
|
334
310
|
console.error('Error fetching King rewards data in batch:', error);
|
|
335
|
-
for (const address of
|
|
311
|
+
for (const address of addresses) {
|
|
336
312
|
rewardsData[address.toLowerCase() as EthAddress].king = {
|
|
337
313
|
error: 'Error fetching King rewards data in batch',
|
|
338
314
|
data: null,
|
|
@@ -344,13 +320,13 @@ const _getPortfolioUserData = async (
|
|
|
344
320
|
(async () => {
|
|
345
321
|
try {
|
|
346
322
|
if (!isMainnet) {
|
|
347
|
-
for (const address of
|
|
348
|
-
rewardsData[address.toLowerCase()
|
|
323
|
+
for (const address of addresses) {
|
|
324
|
+
rewardsData[address.toLowerCase()].uniswap = { error: '', data: [] };
|
|
349
325
|
}
|
|
350
326
|
return;
|
|
351
327
|
}
|
|
352
|
-
const uniswapRewards = await getUniswapRewards(client, network,
|
|
353
|
-
for (const address of
|
|
328
|
+
const uniswapRewards = await getUniswapRewards(client, network, addresses);
|
|
329
|
+
for (const address of addresses) {
|
|
354
330
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
355
331
|
rewardsData[lowerAddress].uniswap = {
|
|
356
332
|
error: '',
|
|
@@ -359,7 +335,7 @@ const _getPortfolioUserData = async (
|
|
|
359
335
|
}
|
|
360
336
|
} catch (error) {
|
|
361
337
|
console.error('Error fetching Uniswap rewards data in batch:', error);
|
|
362
|
-
for (const address of
|
|
338
|
+
for (const address of addresses) {
|
|
363
339
|
rewardsData[address.toLowerCase() as EthAddress].uniswap = {
|
|
364
340
|
error: 'Error fetching Uniswap rewards data in batch',
|
|
365
341
|
data: null,
|
|
@@ -367,10 +343,10 @@ const _getPortfolioUserData = async (
|
|
|
367
343
|
}
|
|
368
344
|
}
|
|
369
345
|
})(),
|
|
370
|
-
...
|
|
346
|
+
...sparkMarkets.map((market) => addresses.map(async address => {
|
|
371
347
|
try {
|
|
372
348
|
if (!isMainnet) {
|
|
373
|
-
rewardsData[address.toLowerCase()
|
|
349
|
+
rewardsData[address.toLowerCase()].spark[market.value] = { error: '', data: [] };
|
|
374
350
|
return;
|
|
375
351
|
}
|
|
376
352
|
const sparkData = await fetchSparkRewards(client, network, address, market.providerAddress);
|
|
@@ -380,7 +356,7 @@ const _getPortfolioUserData = async (
|
|
|
380
356
|
rewardsData[address.toLowerCase() as EthAddress].spark[market.value] = { error: `Error fetching Spark rewards data for address ${address}`, data: null };
|
|
381
357
|
}
|
|
382
358
|
})).flat(),
|
|
383
|
-
...
|
|
359
|
+
...addresses.map(async (address) => {
|
|
384
360
|
try {
|
|
385
361
|
const merklData = await getMerklUnclaimedRewards(address, network);
|
|
386
362
|
rewardsData[address.toLowerCase() as EthAddress].merkl = { error: '', data: merklData };
|
|
@@ -389,28 +365,27 @@ const _getPortfolioUserData = async (
|
|
|
389
365
|
rewardsData[address.toLowerCase() as EthAddress].merkl = { error: `Error fetching Merkl rewards data for address ${address}`, data: null };
|
|
390
366
|
}
|
|
391
367
|
}),
|
|
392
|
-
...
|
|
368
|
+
...aaveV3Markets.map(market => addresses.map(async (address) => {
|
|
393
369
|
try {
|
|
394
370
|
const aaveData = await getUnclaimedRewardsForAllMarkets(client, network, address, market.providerAddress);
|
|
395
371
|
rewardsData[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: '', data: aaveData };
|
|
396
372
|
} catch (error) {
|
|
397
373
|
console.error(`Error fetching Aave V3 Merit rewards data for address ${address}:`, error);
|
|
398
|
-
|
|
399
|
-
rewardsData[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: `Error fetching Aave V3 rewards data for address ${address} on market ${market.value}`, data: null };
|
|
374
|
+
rewardsData[address.toLowerCase() as EthAddress].aaveV3 = { error: `Error fetching Aave V3 rewards data for address ${address}`, data: null };
|
|
400
375
|
}
|
|
401
376
|
})).flat(),
|
|
402
377
|
// Batch Spark Airdrop rewards
|
|
403
378
|
(async () => {
|
|
404
379
|
try {
|
|
405
380
|
if (!isMainnet) {
|
|
406
|
-
for (const address of
|
|
407
|
-
rewardsData[address.toLowerCase()
|
|
381
|
+
for (const address of addresses) {
|
|
382
|
+
rewardsData[address.toLowerCase()].spk = { error: '', data: [] };
|
|
408
383
|
}
|
|
409
384
|
return;
|
|
410
385
|
}
|
|
411
386
|
|
|
412
|
-
const sparkAirdropRewards = await fetchSparkAirdropRewards(client, network,
|
|
413
|
-
for (const address of
|
|
387
|
+
const sparkAirdropRewards = await fetchSparkAirdropRewards(client, network, addresses);
|
|
388
|
+
for (const address of addresses) {
|
|
414
389
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
415
390
|
rewardsData[lowerAddress].spk = {
|
|
416
391
|
error: '',
|
|
@@ -419,7 +394,7 @@ const _getPortfolioUserData = async (
|
|
|
419
394
|
}
|
|
420
395
|
} catch (error) {
|
|
421
396
|
console.error('Error fetching Spark Airdrop rewards data in batch:', error);
|
|
422
|
-
for (const address of
|
|
397
|
+
for (const address of addresses) {
|
|
423
398
|
rewardsData[address.toLowerCase() as EthAddress].spk = {
|
|
424
399
|
error: 'Error fetching Spark Airdrop rewards data in batch',
|
|
425
400
|
data: null,
|
|
@@ -433,8 +408,8 @@ const _getPortfolioUserData = async (
|
|
|
433
408
|
return;
|
|
434
409
|
}
|
|
435
410
|
|
|
436
|
-
const ethenaAirdropRewards = await fetchEthenaAirdropRewards(
|
|
437
|
-
for (const address of
|
|
411
|
+
const ethenaAirdropRewards = await fetchEthenaAirdropRewards(addresses);
|
|
412
|
+
for (const address of addresses) {
|
|
438
413
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
439
414
|
rewardsData[lowerAddress].ethena = {
|
|
440
415
|
error: '',
|
|
@@ -443,7 +418,7 @@ const _getPortfolioUserData = async (
|
|
|
443
418
|
}
|
|
444
419
|
} catch (error) {
|
|
445
420
|
console.error('Error fetching Ethena Airdrop rewards data:', error);
|
|
446
|
-
for (const address of
|
|
421
|
+
for (const address of addresses) {
|
|
447
422
|
rewardsData[address.toLowerCase() as EthAddress].ethena = {
|
|
448
423
|
error: 'Error fetching Ethena Airdrop rewards data in batch',
|
|
449
424
|
data: null,
|
|
@@ -451,63 +426,162 @@ const _getPortfolioUserData = async (
|
|
|
451
426
|
}
|
|
452
427
|
}
|
|
453
428
|
})(),
|
|
429
|
+
]);
|
|
454
430
|
|
|
455
|
-
|
|
456
|
-
...
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
const marketData = requireMarket((await marketsData).morphoMarketsData, market.value);
|
|
461
|
-
const earnData = await getMorphoEarn(client, network, address, market, marketData);
|
|
462
|
-
if (earnData && new Dec(earnData.amount).gt(0)) {
|
|
463
|
-
stakingPositions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: '', data: earnData };
|
|
464
|
-
}
|
|
431
|
+
await Promise.all([
|
|
432
|
+
...aaveV3Markets.map((market) => allAddresses.map(async (address) => {
|
|
433
|
+
try {
|
|
434
|
+
const accData = await _getAaveV3AccountData(client, network, address, { selectedMarket: market, ...aaveV3MarketsData[market.value] });
|
|
435
|
+
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: '', data: accData };
|
|
465
436
|
} catch (error) {
|
|
466
|
-
console.error(`Error fetching
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
})),
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
console.error(`Error fetching Liquity V2 trove ${troveId} for address ${address} on market ${market.value}:`, error);
|
|
505
|
-
failed.push(troveId);
|
|
437
|
+
console.error(`Error fetching AaveV3 account data for address ${address} on market ${market.value}:`, error);
|
|
438
|
+
positions[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: `Error fetching AaveV3 account data for address ${address} on market ${market.value}`, data: null };
|
|
439
|
+
}
|
|
440
|
+
})).flat(),
|
|
441
|
+
...aaveV4Spokes.map((spoke) => allAddresses.map(async (address) => {
|
|
442
|
+
try {
|
|
443
|
+
const accData = await _getAaveV4AccountData(client, network, aaveV4SpokesData[spoke.value], address);
|
|
444
|
+
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].aaveV4[spoke.value] = { error: '', data: accData };
|
|
445
|
+
} catch (error) {
|
|
446
|
+
console.error(`Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}:`, error);
|
|
447
|
+
positions[address.toLowerCase() as EthAddress].aaveV4[spoke.value] = { error: `Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}`, data: null };
|
|
448
|
+
}
|
|
449
|
+
})).flat(),
|
|
450
|
+
...morphoMarkets.map((market) => addresses.map(async (address) => {
|
|
451
|
+
try {
|
|
452
|
+
const [accDataPromise, earnDataPromise] = await Promise.allSettled([
|
|
453
|
+
_getMorphoBlueAccountData(client, network, address, market, morphoMarketsData[market.value]),
|
|
454
|
+
getMorphoEarn(client, network, address, market, morphoMarketsData[market.value]),
|
|
455
|
+
]);
|
|
456
|
+
if (accDataPromise.status === 'rejected') {
|
|
457
|
+
console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, accDataPromise.reason);
|
|
458
|
+
positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
|
|
459
|
+
}
|
|
460
|
+
if (earnDataPromise.status === 'rejected') {
|
|
461
|
+
console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, earnDataPromise.reason);
|
|
462
|
+
positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
|
|
463
|
+
}
|
|
464
|
+
if (accDataPromise.status !== 'rejected') {
|
|
465
|
+
const accData = accDataPromise.value;
|
|
466
|
+
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: '', data: accData };
|
|
467
|
+
}
|
|
468
|
+
if (earnDataPromise.status !== 'rejected') {
|
|
469
|
+
const earnData = earnDataPromise.value;
|
|
470
|
+
if (earnData && new Dec(earnData.amount).gt(0)) {
|
|
471
|
+
stakingPositions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = {
|
|
472
|
+
error: '',
|
|
473
|
+
data: earnData,
|
|
474
|
+
};
|
|
506
475
|
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
476
|
+
}
|
|
477
|
+
} catch (error) {
|
|
478
|
+
console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, error);
|
|
479
|
+
positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
|
|
480
|
+
}
|
|
481
|
+
})).flat(),
|
|
482
|
+
...morphoMidnightMarkets.map((market) => addresses.map(async (address) => {
|
|
483
|
+
try {
|
|
484
|
+
// Markets are created lazily on the first position, so an uncreated one can hold no position.
|
|
485
|
+
if (!morphoMidnightMarketsData[market.value]?.isCreated) return;
|
|
486
|
+
const accData = await _getMorphoMidnightAccountData(client, network, address, market, morphoMidnightMarketsData[market.value]);
|
|
487
|
+
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].morphoMidnight[market.value] = { error: '', data: accData };
|
|
488
|
+
} catch (error) {
|
|
489
|
+
console.error(`Error fetching MorphoMidnight account data for address ${address} on market ${market.value}:`, error);
|
|
490
|
+
positions[address.toLowerCase() as EthAddress].morphoMidnight[market.value] = { error: `Error fetching MorphoMidnight account data for address ${address} on market ${market.value}`, data: null };
|
|
491
|
+
}
|
|
492
|
+
})).flat(),
|
|
493
|
+
...compoundV3Markets.map((market) => addresses.map(async (address) => {
|
|
494
|
+
try {
|
|
495
|
+
const accData = await _getCompoundV3AccountData(client, network, address, ZERO_ADDRESS, { selectedMarket: market, assetsData: compoundV3MarketsData[market.value].assetsData });
|
|
496
|
+
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].compoundV3[market.value] = { error: '', data: accData };
|
|
497
|
+
} catch (error) {
|
|
498
|
+
console.error(`Error fetching CompoundV3 account data for address ${address} on market ${market.value}:`, error);
|
|
499
|
+
positions[address.toLowerCase() as EthAddress].compoundV3[market.value] = { error: `Error fetching CompoundV3 account data for address ${address} on market ${market.value}`, data: null };
|
|
500
|
+
}
|
|
501
|
+
})).flat(),
|
|
502
|
+
...sparkMarkets.map((market) => allAddresses.map(async (address) => {
|
|
503
|
+
try {
|
|
504
|
+
const accData = await _getSparkAccountData(client, network, address, { selectedMarket: market, assetsData: sparkMarketsData[market.value].assetsData, eModeCategoriesData: sparkMarketsData[market.value].eModeCategoriesData });
|
|
505
|
+
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].spark[market.value] = { error: '', data: accData };
|
|
506
|
+
} catch (error) {
|
|
507
|
+
console.error(`Error fetching Spark account data for address ${address} on market ${market.value}:`, error);
|
|
508
|
+
positions[address.toLowerCase() as EthAddress].spark[market.value] = { error: `Error fetching Spark account data for address ${address} on market ${market.value}`, data: null };
|
|
509
|
+
}
|
|
510
|
+
})).flat(),
|
|
511
|
+
...addresses.map((address) => (makerCdps[address.toLowerCase() as EthAddress] || []).map(async (cdpInfo) => {
|
|
512
|
+
try {
|
|
513
|
+
const cdpData = await _getMakerCdpData(client, network, cdpInfo);
|
|
514
|
+
if (cdpData) {
|
|
515
|
+
positions[address.toLowerCase() as EthAddress].maker[cdpInfo.id] = { error: '', data: cdpData };
|
|
516
|
+
}
|
|
517
|
+
} catch (error) {
|
|
518
|
+
console.error(`Error fetching Maker CDP data for address ${address} with ID ${cdpInfo.id}:`, error);
|
|
519
|
+
positions[address.toLowerCase() as EthAddress].maker[cdpInfo.id] = { error: `Error fetching Maker CDP data for address ${address} with ID ${cdpInfo.id}`, data: null };
|
|
520
|
+
}
|
|
521
|
+
})).flat(),
|
|
522
|
+
...aaveV2Markets.map((market) => addresses.map(async (address) => {
|
|
523
|
+
try {
|
|
524
|
+
const accData = await _getAaveV2AccountData(client, network, address, aaveV2MarketsData[market.value].assetsData, market);
|
|
525
|
+
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].aaveV2[market.value] = { error: '', data: accData };
|
|
526
|
+
} catch (error) {
|
|
527
|
+
console.error(`Error fetching AaveV2 account data for address ${address}:`, error);
|
|
528
|
+
positions[address.toLowerCase() as EthAddress].aaveV2[market.value] = { error: `Error fetching AaveV2 account data for address ${address}`, data: null };
|
|
529
|
+
}
|
|
530
|
+
})).flat(),
|
|
531
|
+
...compoundV2Markets.map((market) => addresses.map(async (address) => {
|
|
532
|
+
try {
|
|
533
|
+
const accData = await _getCompoundV2AccountData(client, network, address, compoundV2MarketsData[market.value].assetsData);
|
|
534
|
+
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].compoundV2[market.value] = { error: '', data: accData };
|
|
535
|
+
} catch (error) {
|
|
536
|
+
console.error(`Error fetching CompoundV2 account data for address ${address}:`, error);
|
|
537
|
+
positions[address.toLowerCase() as EthAddress].compoundV2[market.value] = { error: `Error fetching CompoundV2 account data for address ${address}`, data: null };
|
|
538
|
+
}
|
|
539
|
+
})).flat(),
|
|
540
|
+
...addresses.map(async (address) => {
|
|
541
|
+
try {
|
|
542
|
+
if (!isMainnet) return; // Liquity trove info is only available on mainnet
|
|
543
|
+
const troveInfo = await _getLiquityTroveInfo(client, network, address);
|
|
544
|
+
if (new Dec(troveInfo.collateral).gt(0)) positions[address.toLowerCase() as EthAddress].liquity = { error: '', data: troveInfo };
|
|
545
|
+
} catch (error) {
|
|
546
|
+
console.error(`Error fetching Liquity trove info for address ${address}:`, error);
|
|
547
|
+
positions[address.toLowerCase() as EthAddress].liquity = { error: `Error fetching Liquity trove info for address ${address}`, data: null };
|
|
548
|
+
}
|
|
549
|
+
}),
|
|
550
|
+
...crvUsdMarkets.map((market) => addresses.map(async (address) => {
|
|
551
|
+
try {
|
|
552
|
+
const accData = await _getCurveUsdUserData(client, network, address, market, crvUsdMarketsData[market.value].activeBand, crvUsdMarketsData[market.value].borrowRate);
|
|
553
|
+
if (new Dec(accData.suppliedUsd).gt(0) || new Dec(accData.borrowedUsd).gt(0)) {
|
|
554
|
+
positions[address.toLowerCase() as EthAddress].crvUsd[market.value] = { error: '', data: accData };
|
|
555
|
+
}
|
|
556
|
+
} catch (error) {
|
|
557
|
+
console.error(`Error fetching Curve USD account data for address ${address} on market ${market.value}:`, error);
|
|
558
|
+
positions[address.toLowerCase() as EthAddress].crvUsd[market.value] = { error: `Error fetching Curve USD account data for address ${address} on market ${market.value}`, data: null };
|
|
559
|
+
}
|
|
560
|
+
})).flat(),
|
|
561
|
+
...llamaLendMarkets.map((market) => addresses.map(async (address) => {
|
|
562
|
+
try {
|
|
563
|
+
const accData = await _getLlamaLendUserData(client, network, address, market, llamaLendMarketsData[market.value]);
|
|
564
|
+
if (new Dec(accData.suppliedUsd).gt(0) || new Dec(accData.borrowedUsd).gt(0)) {
|
|
565
|
+
positions[address.toLowerCase() as EthAddress].llamaLend[market.value] = { error: '', data: { ...accData, borrowRate: llamaLendMarketsData[market.value].borrowRate } };
|
|
566
|
+
}
|
|
567
|
+
} catch (error) {
|
|
568
|
+
console.error(`Error fetching LlamaLend account data for address ${address} on market ${market.value}:`, error);
|
|
569
|
+
positions[address.toLowerCase() as EthAddress].llamaLend[market.value] = { error: `Error fetching LlamaLend account data for address ${address} on market ${market.value}`, data: null };
|
|
570
|
+
}
|
|
571
|
+
})).flat(),
|
|
572
|
+
// liquity sBold/yBold and staking options
|
|
573
|
+
...addresses.map(async (address) => {
|
|
574
|
+
try {
|
|
575
|
+
if (!isMainnet) {
|
|
576
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data: null };
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
const data = await getLiquitySAndYBold(client, network, stakingPositions[address.toLowerCase()].liquityV2, address);
|
|
580
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data };
|
|
581
|
+
} catch (error) {
|
|
582
|
+
console.error(`Error fetching SBold/YBold data for address ${address}:`, error);
|
|
583
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: `Error fetching sBold/yBold data for address ${address}`, data: null };
|
|
584
|
+
}
|
|
511
585
|
}),
|
|
512
586
|
]);
|
|
513
587
|
|
|
@@ -515,63 +589,14 @@ const _getPortfolioUserData = async (
|
|
|
515
589
|
positions,
|
|
516
590
|
stakingPositions,
|
|
517
591
|
rewardsData,
|
|
518
|
-
errors: Object.keys(makerCdpsErrors).length ? { makerCdps: makerCdpsErrors } : {},
|
|
519
|
-
};
|
|
520
|
-
};
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* Every market the portfolio reads on a network — the markets half of getPortfolioData, for a caller that keeps
|
|
524
|
-
* markets apart from what it reads per address (a cache shared by many addresses). A market that fails is left out
|
|
525
|
-
* of `markets` and named in `errors`.
|
|
526
|
-
*/
|
|
527
|
-
export async function getPortfolioMarketsData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, isSim = false): Promise<PortfolioMarketsResult> {
|
|
528
|
-
const { client, defaultClient } = getPortfolioClients(provider, defaultProvider, network, isSim);
|
|
529
|
-
return _getPortfolioMarketsData(client, defaultClient, network);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* What the portfolio reads per address — the other half of getPortfolioData — against `markets` from
|
|
534
|
-
* getPortfolioMarketsData. `markets` can be a promise: reads that do not need a market (Maker, Fluid, Liquity, staking,
|
|
535
|
-
* rewards) start at once, and each read on a market waits for it. A position on a market missing from `markets` is an
|
|
536
|
-
* entry with an error.
|
|
537
|
-
*/
|
|
538
|
-
export async function getPortfolioUserData(
|
|
539
|
-
provider: EthereumProvider,
|
|
540
|
-
network: NetworkNumber,
|
|
541
|
-
addresses: EthAddress[],
|
|
542
|
-
markets: PortfolioMarketsData | Promise<PortfolioMarketsData>,
|
|
543
|
-
isSim = false,
|
|
544
|
-
options: PortfolioDataOptions = {},
|
|
545
|
-
): Promise<PortfolioUserData> {
|
|
546
|
-
const { client } = getPortfolioClients(provider, provider, network, isSim);
|
|
547
|
-
return _getPortfolioUserData(client, network, addresses, Promise.resolve(markets), isSim, options);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
export async function getPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], isSim = false, options: PortfolioDataOptions = {}): Promise<PortfolioData> {
|
|
551
|
-
// One pair of clients for both halves, so their calls batch together.
|
|
552
|
-
const { client, defaultClient } = getPortfolioClients(provider, defaultProvider, network, isSim);
|
|
553
|
-
const marketsResult = _getPortfolioMarketsData(client, defaultClient, network);
|
|
554
|
-
const [{ markets, errors: marketsErrors }, userData] = await Promise.all([
|
|
555
|
-
marketsResult,
|
|
556
|
-
_getPortfolioUserData(client, network, addresses, marketsResult.then((result) => result.markets), isSim, options),
|
|
557
|
-
]);
|
|
558
|
-
|
|
559
|
-
return {
|
|
560
|
-
positions: userData.positions,
|
|
561
|
-
stakingPositions: userData.stakingPositions,
|
|
562
|
-
rewardsData: userData.rewardsData,
|
|
563
592
|
markets,
|
|
564
|
-
errors: {
|
|
565
|
-
...(Object.keys(marketsErrors).length ? { markets: marketsErrors } : {}),
|
|
566
|
-
...userData.errors,
|
|
567
|
-
},
|
|
568
593
|
};
|
|
569
594
|
}
|
|
570
595
|
|
|
571
596
|
|
|
572
597
|
export async function getShifterPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], isSim = false): Promise<{
|
|
573
598
|
positions: PortfolioPositionsData;
|
|
574
|
-
markets:
|
|
599
|
+
markets: PortfolioMarketsData;
|
|
575
600
|
}> {
|
|
576
601
|
const isMainnet = network === NetworkNumber.Eth;
|
|
577
602
|
const isFluidSupported = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Base, NetworkNumber.Plasma].includes(network);
|
|
@@ -591,7 +616,7 @@ export async function getShifterPortfolioData(provider: EthereumProvider, networ
|
|
|
591
616
|
const client = getViemProvider(provider, ...args);
|
|
592
617
|
const defaultClient = getViemProvider(defaultProvider, ...args);
|
|
593
618
|
|
|
594
|
-
const markets:
|
|
619
|
+
const markets: PortfolioMarketsData = {
|
|
595
620
|
morphoMarketsData: {},
|
|
596
621
|
morphoMidnightMarketsData: {}, // Morpho Midnight is not fetched for the shifter; key kept so the payload shape is stable
|
|
597
622
|
compoundV3MarketsData: {},
|