@defisaver/positions-sdk 2.1.161-portfolio-api-1-dev → 2.1.161-portfolio-api-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.
- package/cjs/aaveV2/index.js +2 -0
- package/cjs/aaveV3/index.d.ts +41 -12
- package/cjs/aaveV3/index.js +237 -2
- package/cjs/aaveV3/sgho.d.ts +2 -16
- package/cjs/aaveV4/lend.d.ts +2 -43
- package/cjs/claiming/aaveV3.d.ts +2 -2
- package/cjs/claiming/ethena.d.ts +2 -1
- package/cjs/claiming/king.d.ts +2 -1
- package/cjs/claiming/spark.d.ts +2 -2
- package/cjs/curveUsd/index.d.ts +5 -2
- package/cjs/curveUsd/index.js +30 -15
- package/cjs/fluid/index.d.ts +10 -49
- package/cjs/fluid/index.js +36 -1
- package/cjs/helpers/aaveHelpers/index.d.ts +9 -1
- package/cjs/helpers/aaveHelpers/index.js +26 -6
- package/cjs/helpers/aaveV4Helpers/index.js +1 -0
- package/cjs/helpers/compoundHelpers/index.js +2 -0
- package/cjs/helpers/curveUsdHelpers/index.d.ts +3 -1
- package/cjs/helpers/curveUsdHelpers/index.js +15 -2
- package/cjs/helpers/fluidHelpers/index.js +1 -0
- package/cjs/helpers/liquityV2Helpers/index.js +1 -0
- package/cjs/helpers/llamaLendHelpers/index.js +1 -0
- package/cjs/helpers/makerHelpers/index.d.ts +1 -1
- package/cjs/helpers/makerHelpers/index.js +2 -2
- package/cjs/helpers/morphoBlueHelpers/index.js +2 -0
- package/cjs/helpers/morphoMidnightHelpers/index.js +2 -0
- package/cjs/helpers/sparkHelpers/index.js +1 -0
- package/cjs/liquity/index.d.ts +2 -12
- package/cjs/liquity/index.js +15 -2
- package/cjs/liquityV2/index.d.ts +8 -23
- package/cjs/liquityV2/index.js +37 -19
- package/cjs/maker/index.d.ts +7 -2
- package/cjs/maker/index.js +44 -12
- package/cjs/markets/index.d.ts +1 -0
- package/cjs/markets/index.js +3 -1
- package/cjs/markets/maker/index.d.ts +1 -0
- package/cjs/markets/maker/index.js +13 -0
- package/cjs/moneymarket/moneymarketCommonService.d.ts +9 -0
- package/cjs/moneymarket/moneymarketCommonService.js +13 -1
- package/cjs/portfolio/index.d.ts +17 -5
- package/cjs/portfolio/index.js +583 -381
- package/cjs/portfolio/lendingProtocols.d.ts +184 -0
- package/cjs/portfolio/lendingProtocols.js +196 -0
- package/cjs/savings/index.d.ts +8 -2
- package/cjs/savings/index.js +37 -5
- package/cjs/savings/morphoVaults/index.d.ts +1 -1
- package/cjs/savings/morphoVaults/index.js +56 -18
- package/cjs/savings/morphoVaults/options.d.ts +2 -0
- package/cjs/savings/morphoVaults/options.js +61 -1
- package/cjs/spark/index.js +1 -0
- package/cjs/staking/staking.d.ts +20 -0
- package/cjs/staking/staking.js +19 -1
- package/cjs/types/aave.d.ts +86 -0
- package/cjs/types/aaveV4.d.ts +45 -0
- package/cjs/types/compound.d.ts +2 -0
- package/cjs/types/curveUsd.d.ts +6 -0
- package/cjs/types/fluid.d.ts +14 -0
- package/cjs/types/index.d.ts +1 -0
- package/cjs/types/index.js +1 -0
- package/cjs/types/liquity.d.ts +16 -0
- package/cjs/types/liquityV2.d.ts +26 -0
- package/cjs/types/maker.d.ts +4 -0
- package/cjs/types/morphoBlue.d.ts +2 -0
- package/cjs/types/morphoMidnight.d.ts +1 -0
- package/cjs/types/portfolio.d.ts +127 -6
- package/cjs/types/savings/index.d.ts +9 -1
- package/cjs/types/savings/morphoVaults.d.ts +10 -2
- package/cjs/types/savings/morphoVaults.js +9 -1
- package/cjs/types/spark.d.ts +2 -0
- package/cjs/umbrella/index.d.ts +2 -3
- package/esm/aaveV2/index.js +2 -0
- package/esm/aaveV3/index.d.ts +41 -12
- package/esm/aaveV3/index.js +231 -2
- package/esm/aaveV3/sgho.d.ts +2 -16
- package/esm/aaveV4/lend.d.ts +2 -43
- package/esm/claiming/aaveV3.d.ts +2 -2
- package/esm/claiming/aaveV3.js +1 -1
- package/esm/claiming/ethena.d.ts +2 -1
- package/esm/claiming/king.d.ts +2 -1
- package/esm/claiming/spark.d.ts +2 -2
- package/esm/claiming/spark.js +1 -1
- package/esm/curveUsd/index.d.ts +5 -2
- package/esm/curveUsd/index.js +30 -15
- package/esm/fluid/index.d.ts +10 -49
- package/esm/fluid/index.js +33 -0
- package/esm/helpers/aaveHelpers/index.d.ts +9 -1
- package/esm/helpers/aaveHelpers/index.js +23 -5
- package/esm/helpers/aaveV4Helpers/index.js +1 -0
- package/esm/helpers/compoundHelpers/index.js +2 -0
- package/esm/helpers/curveUsdHelpers/index.d.ts +3 -1
- package/esm/helpers/curveUsdHelpers/index.js +16 -3
- package/esm/helpers/fluidHelpers/index.js +1 -0
- package/esm/helpers/liquityV2Helpers/index.js +1 -0
- package/esm/helpers/llamaLendHelpers/index.js +2 -1
- package/esm/helpers/makerHelpers/index.d.ts +1 -1
- package/esm/helpers/makerHelpers/index.js +2 -2
- package/esm/helpers/morphoBlueHelpers/index.js +2 -0
- package/esm/helpers/morphoMidnightHelpers/index.js +2 -0
- package/esm/helpers/sparkHelpers/index.js +1 -0
- package/esm/liquity/index.d.ts +2 -12
- package/esm/liquity/index.js +16 -3
- package/esm/liquityV2/index.d.ts +8 -23
- package/esm/liquityV2/index.js +35 -18
- package/esm/maker/index.d.ts +7 -2
- package/esm/maker/index.js +44 -14
- package/esm/markets/index.d.ts +1 -0
- package/esm/markets/index.js +1 -0
- package/esm/markets/maker/index.d.ts +1 -0
- package/esm/markets/maker/index.js +10 -0
- package/esm/moneymarket/moneymarketCommonService.d.ts +9 -0
- package/esm/moneymarket/moneymarketCommonService.js +11 -0
- package/esm/portfolio/index.d.ts +17 -5
- package/esm/portfolio/index.js +584 -385
- package/esm/portfolio/lendingProtocols.d.ts +184 -0
- package/esm/portfolio/lendingProtocols.js +189 -0
- package/esm/savings/index.d.ts +8 -2
- package/esm/savings/index.js +35 -4
- package/esm/savings/morphoVaults/index.d.ts +1 -1
- package/esm/savings/morphoVaults/index.js +57 -19
- package/esm/savings/morphoVaults/options.d.ts +2 -0
- package/esm/savings/morphoVaults/options.js +61 -1
- package/esm/spark/index.js +1 -0
- package/esm/staking/staking.d.ts +20 -0
- package/esm/staking/staking.js +17 -0
- package/esm/types/aave.d.ts +86 -0
- package/esm/types/aaveV4.d.ts +45 -0
- package/esm/types/compound.d.ts +2 -0
- package/esm/types/curveUsd.d.ts +6 -0
- package/esm/types/fluid.d.ts +14 -0
- package/esm/types/index.d.ts +1 -0
- package/esm/types/index.js +1 -0
- package/esm/types/liquity.d.ts +16 -0
- package/esm/types/liquityV2.d.ts +26 -0
- package/esm/types/maker.d.ts +4 -0
- package/esm/types/morphoBlue.d.ts +2 -0
- package/esm/types/morphoMidnight.d.ts +1 -0
- package/esm/types/portfolio.d.ts +127 -6
- package/esm/types/savings/index.d.ts +9 -1
- package/esm/types/savings/morphoVaults.d.ts +10 -2
- package/esm/types/savings/morphoVaults.js +8 -0
- package/esm/types/spark.d.ts +2 -0
- package/esm/umbrella/index.d.ts +2 -3
- package/package.json +3 -2
- package/src/aaveV2/index.ts +2 -0
- package/src/aaveV3/index.ts +293 -2
- package/src/aaveV3/sgho.ts +2 -17
- package/src/aaveV4/lend.ts +2 -48
- package/src/claiming/aaveV3.ts +4 -2
- package/src/claiming/ethena.ts +5 -5
- package/src/claiming/king.ts +3 -3
- package/src/claiming/spark.ts +6 -4
- package/src/curveUsd/index.ts +33 -16
- package/src/fluid/index.ts +44 -3
- package/src/helpers/aaveHelpers/index.ts +27 -7
- package/src/helpers/aaveV4Helpers/index.ts +1 -0
- package/src/helpers/compoundHelpers/index.ts +2 -0
- package/src/helpers/curveUsdHelpers/index.ts +23 -3
- package/src/helpers/fluidHelpers/index.ts +1 -0
- package/src/helpers/liquityV2Helpers/index.ts +1 -0
- package/src/helpers/llamaLendHelpers/index.ts +2 -1
- package/src/helpers/makerHelpers/index.ts +2 -1
- package/src/helpers/morphoBlueHelpers/index.ts +2 -0
- package/src/helpers/morphoMidnightHelpers/index.ts +2 -0
- package/src/helpers/sparkHelpers/index.ts +1 -0
- package/src/liquity/index.ts +19 -5
- package/src/liquityV2/index.ts +48 -22
- package/src/maker/index.ts +77 -30
- package/src/markets/index.ts +2 -1
- package/src/markets/maker/index.ts +10 -0
- package/src/moneymarket/moneymarketCommonService.ts +15 -0
- package/src/portfolio/index.ts +477 -248
- package/src/portfolio/lendingProtocols.ts +281 -0
- package/src/savings/index.ts +45 -5
- package/src/savings/morphoVaults/index.ts +73 -21
- package/src/savings/morphoVaults/options.ts +67 -1
- package/src/spark/index.ts +1 -0
- package/src/staking/staking.ts +28 -0
- package/src/types/aave.ts +90 -0
- package/src/types/aaveV4.ts +50 -0
- package/src/types/compound.ts +2 -0
- package/src/types/curveUsd.ts +7 -0
- package/src/types/fluid.ts +15 -0
- package/src/types/index.ts +2 -1
- package/src/types/liquity.ts +19 -1
- package/src/types/liquityV2.ts +28 -1
- package/src/types/maker.ts +6 -0
- package/src/types/morphoBlue.ts +2 -0
- package/src/types/morphoMidnight.ts +1 -0
- package/src/types/portfolio.ts +153 -7
- package/src/types/savings/index.ts +12 -1
- package/src/types/savings/morphoVaults.ts +11 -1
- package/src/types/spark.ts +2 -0
- package/src/umbrella/index.ts +2 -1
package/src/portfolio/index.ts
CHANGED
|
@@ -7,138 +7,160 @@ import {
|
|
|
7
7
|
CrvUsdMarkets,
|
|
8
8
|
LiquityV2Markets,
|
|
9
9
|
LlamaLendMarkets,
|
|
10
|
+
MakerActiveIlks,
|
|
10
11
|
MorphoBlueMarkets,
|
|
11
|
-
MorphoMidnightMarkets,
|
|
12
12
|
SparkMarkets,
|
|
13
13
|
} from '../markets';
|
|
14
14
|
import { _getMorphoBlueAccountData, _getMorphoBluePortfolioMarketData, getMorphoEarn } from '../morphoBlue';
|
|
15
|
-
import { _getMorphoMidnightAccountData, _getMorphoMidnightMarketData } from '../morphoMidnight';
|
|
16
15
|
import {
|
|
17
|
-
AaveV2MarketData,
|
|
18
|
-
AaveV3MarketData,
|
|
19
|
-
AaveV4SpokeData,
|
|
20
16
|
AaveVersions,
|
|
21
17
|
CdpInfo,
|
|
22
|
-
CompoundV2MarketsData,
|
|
23
|
-
CompoundV3MarketsData,
|
|
24
18
|
CompoundVersions,
|
|
25
|
-
CrvUSDGlobalMarketData,
|
|
26
19
|
LiquityV2MarketData,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
LiquityV2Versions,
|
|
21
|
+
PortfolioData,
|
|
22
|
+
PortfolioDataOptions,
|
|
23
|
+
PortfolioLiquityV2Troves,
|
|
30
24
|
PortfolioMarketsData,
|
|
25
|
+
PortfolioMarketsErrors,
|
|
26
|
+
PortfolioMarketsResult,
|
|
31
27
|
PortfolioPositionsData,
|
|
32
|
-
|
|
28
|
+
PortfolioPositionsDataForAddress,
|
|
29
|
+
PortfolioRewardsData,
|
|
30
|
+
PortfolioStakingPositionsData,
|
|
31
|
+
PortfolioUserData,
|
|
32
|
+
ShifterPortfolioMarketsData,
|
|
33
33
|
} from '../types';
|
|
34
34
|
import { _getCompoundV3AccountData, _getCompoundV3MarketsData } from '../compoundV3';
|
|
35
35
|
import { _getSparkAccountData, _getSparkMarketsData } from '../spark';
|
|
36
36
|
import { _getCurveUsdGlobalData, _getCurveUsdUserData } from '../curveUsd';
|
|
37
37
|
import { _getLlamaLendGlobalData, _getLlamaLendUserData } from '../llamaLend';
|
|
38
38
|
import { _getAaveV3AccountData, _getAaveV3MarketData, getStakeAaveData } from '../aaveV3';
|
|
39
|
+
import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
|
|
39
40
|
import { ZERO_ADDRESS } from '../constants';
|
|
40
|
-
import { _getMakerCdpData, _getUserCdps } from '../maker';
|
|
41
|
+
import { _getMakerCdpData, _getMakerIlksData, _getUserCdps } from '../maker';
|
|
41
42
|
import { _getAaveV2AccountData, _getAaveV2MarketsData } from '../aaveV2';
|
|
42
43
|
import { _getCompoundV2AccountData, _getCompoundV2MarketsData } from '../compoundV2';
|
|
43
44
|
import { getViemProvider } from '../services/viem';
|
|
44
45
|
import { _getLiquityTroveInfo, getLiquityStakingData } from '../liquity';
|
|
45
|
-
import {
|
|
46
|
-
|
|
46
|
+
import {
|
|
47
|
+
_getLiquityV2MarketData, _getLiquityV2TroveData, _getLiquityV2UsersTroveIds, getLiquitySAndYBold, getLiquityV2Staking,
|
|
48
|
+
} from '../liquityV2';
|
|
49
|
+
import { _getAllFluidMarketDataPortfolio, _getAllUserEarnPositionsWithFTokens, _getUserPositionsPortfolio } from '../fluid';
|
|
47
50
|
import { getUmbrellaData } from '../umbrella';
|
|
48
51
|
import { getMerklUnclaimedRewards, getUnclaimedRewardsForAllMarkets } from '../claiming/aaveV3';
|
|
49
52
|
import { fetchSparkAirdropRewards, fetchSparkRewards } from '../claiming/spark';
|
|
50
53
|
import { getKingRewards } from '../claiming/king';
|
|
51
54
|
import { fetchEthenaAirdropRewards } from '../claiming/ethena';
|
|
52
|
-
import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
|
|
53
55
|
import { getUniswapRewards } from '../claiming/uniswap';
|
|
56
|
+
import {
|
|
57
|
+
aaveV3Lending, LENDING_PROTOCOLS, LendingKey, morphoBlueLending, requireMarket, sparkLending,
|
|
58
|
+
} from './lendingProtocols';
|
|
54
59
|
|
|
55
|
-
|
|
56
|
-
positions: PortfolioPositionsData;
|
|
57
|
-
stakingPositions: any;
|
|
58
|
-
rewardsData: any;
|
|
59
|
-
markets: PortfolioMarketsData;
|
|
60
|
-
}> {
|
|
61
|
-
const isMainnet = network === NetworkNumber.Eth;
|
|
62
|
-
const isFluidSupported = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Base, NetworkNumber.Plasma].includes(network);
|
|
63
|
-
|
|
64
|
-
const morphoMarkets = Object.values(MorphoBlueMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
65
|
-
const morphoMidnightMarkets = Object.values(MorphoMidnightMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
66
|
-
const compoundV3Markets = Object.values(CompoundMarkets(network)).filter((market) => market.chainIds.includes(network) && market.value !== CompoundVersions.CompoundV2);
|
|
67
|
-
const sparkMarkets = Object.values(SparkMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
68
|
-
const aaveV3Markets = [AaveVersions.AaveV3, AaveVersions.AaveV3Lido, AaveVersions.AaveV3Etherfi].map((version) => AaveMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
|
|
69
|
-
const aaveV2Markets = [AaveVersions.AaveV2].map((version) => AaveMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
|
|
70
|
-
const compoundV2Markets = [CompoundVersions.CompoundV2].map((version) => CompoundMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
|
|
71
|
-
const crvUsdMarkets = Object.values(CrvUsdMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
72
|
-
const llamaLendMarkets = [NetworkNumber.Eth, NetworkNumber.Arb].includes(network) ? Object.values(LlamaLendMarkets(network)).filter((market) => market.chainIds.includes(network)) : [];
|
|
73
|
-
const liquityV2Markets = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)) : [];
|
|
74
|
-
const liquityV2MarketsStaking = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)).filter(market => !market.isLegacy) : [];
|
|
75
|
-
const aaveV4Spokes = Object.values(AaveV4Spokes(network)).filter((market) => market.chainIds.includes(network));
|
|
60
|
+
type PortfolioClient = ReturnType<typeof getViemProvider>;
|
|
76
61
|
|
|
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
|
+
});
|
|
77
67
|
|
|
68
|
+
const getPortfolioClients = (provider: EthereumProvider, defaultProvider: EthereumProvider, network: NetworkNumber, isSim: boolean) => {
|
|
78
69
|
// batchSize is viem's cap on a batch's raw subcall calldata (bytes); the JSON-RPC body ends up
|
|
79
70
|
// ~4x larger (hex + aggregate3 ABI + JSON overhead) and RPC providers reject bodies over
|
|
80
71
|
// ~2.5MB with HTTP 413, so keep this small enough that no single body gets near that.
|
|
81
72
|
// 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
|
|
82
73
|
// (e.g. adding new markets may result in body size going over alchemy body size limit)
|
|
83
74
|
const args: [NetworkNumber, any?] = [network, { batch: { multicall: { batchSize: isSim ? 2_000 : 250_000 } } }];
|
|
84
|
-
|
|
85
|
-
|
|
75
|
+
return {
|
|
76
|
+
client: getViemProvider(provider, ...args),
|
|
77
|
+
defaultClient: getViemProvider(defaultProvider, ...args),
|
|
78
|
+
};
|
|
79
|
+
};
|
|
86
80
|
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
const compoundV3MarketsData: Record<string, CompoundV3MarketsData> = {};
|
|
90
|
-
const sparkMarketsData: Record<string, SparkMarketsData> = {};
|
|
91
|
-
const aaveV3MarketsData: Record<string, AaveV3MarketData> = {};
|
|
92
|
-
const makerCdps: Record<string, CdpInfo[]> = {};
|
|
93
|
-
const aaveV2MarketsData: Record<string, AaveV2MarketData> = {};
|
|
94
|
-
const compoundV2MarketsData: Record<string, CompoundV2MarketsData> = {};
|
|
95
|
-
const crvUsdMarketsData: Record<string, CrvUSDGlobalMarketData> = {};
|
|
96
|
-
const llamaLendMarketsData: Record<string, LlamaLendGlobalMarketData> = {};
|
|
97
|
-
const liquityV2MarketsData: Record<string, LiquityV2MarketData> = {};
|
|
98
|
-
const aaveV4SpokesData: Record<string, AaveV4SpokeData> = {};
|
|
81
|
+
const _getPortfolioMarketsData = async (client: PortfolioClient, defaultClient: PortfolioClient, network: NetworkNumber): Promise<PortfolioMarketsResult> => {
|
|
82
|
+
const { liquityV2Markets } = getPortfolioMarkets(network);
|
|
99
83
|
|
|
100
84
|
const markets: PortfolioMarketsData = {
|
|
101
|
-
morphoMarketsData,
|
|
102
|
-
morphoMidnightMarketsData,
|
|
103
|
-
compoundV3MarketsData,
|
|
104
|
-
sparkMarketsData,
|
|
105
|
-
aaveV3MarketsData,
|
|
106
|
-
aaveV2MarketsData,
|
|
107
|
-
compoundV2MarketsData,
|
|
108
|
-
crvUsdMarketsData,
|
|
109
|
-
llamaLendMarketsData,
|
|
110
|
-
liquityV2MarketsData,
|
|
111
|
-
aaveV4SpokesData,
|
|
85
|
+
morphoMarketsData: {},
|
|
86
|
+
morphoMidnightMarketsData: {},
|
|
87
|
+
compoundV3MarketsData: {},
|
|
88
|
+
sparkMarketsData: {},
|
|
89
|
+
aaveV3MarketsData: {},
|
|
90
|
+
aaveV2MarketsData: {},
|
|
91
|
+
compoundV2MarketsData: {},
|
|
92
|
+
crvUsdMarketsData: {},
|
|
93
|
+
llamaLendMarketsData: {},
|
|
94
|
+
liquityV2MarketsData: {},
|
|
95
|
+
aaveV4SpokesData: {},
|
|
112
96
|
};
|
|
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);
|
|
113
142
|
|
|
114
143
|
const positions: PortfolioPositionsData = {};
|
|
115
|
-
const stakingPositions:
|
|
116
|
-
const rewardsData:
|
|
117
|
-
const
|
|
144
|
+
const stakingPositions: PortfolioStakingPositionsData = {};
|
|
145
|
+
const rewardsData: PortfolioRewardsData = {};
|
|
146
|
+
const makerCdpsErrors: Record<EthAddress, string> = {};
|
|
118
147
|
|
|
119
|
-
for (const address of
|
|
148
|
+
for (const address of positionsAddresses) {
|
|
120
149
|
positions[address.toLowerCase() as EthAddress] = {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
morphoBlue: {},
|
|
124
|
-
morphoMidnight: {},
|
|
125
|
-
compoundV3: {},
|
|
126
|
-
spark: {},
|
|
150
|
+
// Every registry protocol, keyed by market as its reads fill it in.
|
|
151
|
+
...Object.fromEntries(LENDING_PROTOCOLS.map((protocol) => [protocol.key, {}])) as Pick<PortfolioPositionsDataForAddress, LendingKey>,
|
|
127
152
|
maker: {},
|
|
128
|
-
aaveV2: {},
|
|
129
|
-
compoundV2: {},
|
|
130
153
|
liquity: {},
|
|
131
|
-
crvUsd: {},
|
|
132
|
-
llamaLend: {},
|
|
133
154
|
fluid: {
|
|
134
155
|
error: '',
|
|
135
156
|
data: {},
|
|
136
157
|
},
|
|
158
|
+
...(includeLiquityV2 ? { liquityV2: {} } : {}),
|
|
137
159
|
};
|
|
138
160
|
}
|
|
139
161
|
|
|
140
162
|
// TODO: check default values, probably needed when fetching portfolio on unsupported networks
|
|
141
|
-
for (const address of
|
|
163
|
+
for (const address of stakingAddresses) {
|
|
142
164
|
stakingPositions[address.toLowerCase() as EthAddress] = {
|
|
143
165
|
aaveV3: {},
|
|
144
166
|
morphoBlue: {},
|
|
@@ -152,72 +174,52 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
152
174
|
error: '',
|
|
153
175
|
data: {},
|
|
154
176
|
},
|
|
177
|
+
// Set by the sBOLD/yBOLD fetch below, whether it succeeds or fails.
|
|
178
|
+
liquityV2SBoldYBold: { error: '', data: null },
|
|
155
179
|
};
|
|
180
|
+
}
|
|
156
181
|
|
|
182
|
+
for (const address of rewardsAddresses) {
|
|
183
|
+
// merkl, spk, king and uniswap are set by their fetches below, whether they succeed or fail.
|
|
157
184
|
rewardsData[address.toLowerCase() as EthAddress] = {
|
|
158
|
-
merkl: {},
|
|
185
|
+
merkl: { error: '', data: null },
|
|
159
186
|
aaveV3: {},
|
|
160
187
|
spark: {},
|
|
161
|
-
spk: {},
|
|
162
|
-
king: {},
|
|
188
|
+
spk: { error: '', data: null },
|
|
189
|
+
king: { error: '', data: null },
|
|
163
190
|
ethena: {},
|
|
191
|
+
uniswap: { error: '', data: null },
|
|
164
192
|
};
|
|
165
193
|
}
|
|
166
194
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
...morphoMidnightMarkets.map(async (market) => {
|
|
174
|
-
const marketData = await _getMorphoMidnightMarketData(client, network, market);
|
|
175
|
-
morphoMidnightMarketsData[market.value] = marketData;
|
|
176
|
-
}),
|
|
177
|
-
...compoundV3Markets.map(async (market) => {
|
|
178
|
-
const marketData = await _getCompoundV3MarketsData(client, network, market, defaultClient);
|
|
179
|
-
compoundV3MarketsData[market.value] = marketData;
|
|
180
|
-
}),
|
|
181
|
-
...sparkMarkets.map(async (market) => {
|
|
182
|
-
const marketData = await _getSparkMarketsData(client, network, market);
|
|
183
|
-
sparkMarketsData[market.value] = marketData;
|
|
184
|
-
}),
|
|
185
|
-
...aaveV3Markets.map(async (market) => {
|
|
186
|
-
const marketData = await _getAaveV3MarketData(client, network, market);
|
|
187
|
-
aaveV3MarketsData[market.value] = marketData;
|
|
188
|
-
}),
|
|
189
|
-
...aaveV4Spokes.map(async (spoke) => {
|
|
190
|
-
const spokeData = await _getAaveV4SpokeData(client, network, spoke);
|
|
191
|
-
aaveV4SpokesData[spoke.value] = spokeData;
|
|
192
|
-
}),
|
|
193
|
-
...aaveV2Markets.map(async (market) => {
|
|
194
|
-
const marketData = await _getAaveV2MarketsData(client, network, market);
|
|
195
|
-
aaveV2MarketsData[market.value] = marketData;
|
|
196
|
-
}),
|
|
197
|
-
...compoundV2Markets.map(async (market) => {
|
|
198
|
-
const marketData = await _getCompoundV2MarketsData(client, network);
|
|
199
|
-
compoundV2MarketsData[market.value] = marketData;
|
|
200
|
-
}),
|
|
201
|
-
...crvUsdMarkets.map(async (market) => {
|
|
202
|
-
const marketData = await _getCurveUsdGlobalData(client, network, market);
|
|
203
|
-
crvUsdMarketsData[market.value] = marketData;
|
|
204
|
-
}),
|
|
205
|
-
...llamaLendMarkets.map(async (market) => {
|
|
206
|
-
const marketData = await _getLlamaLendGlobalData(client, network, market);
|
|
207
|
-
llamaLendMarketsData[market.value] = marketData;
|
|
208
|
-
}),
|
|
209
|
-
...liquityV2Markets.map(async (market) => {
|
|
210
|
-
const marketData = await _getLiquityV2MarketData(client, network, market);
|
|
211
|
-
liquityV2MarketsData[market.value] = marketData;
|
|
212
|
-
}),
|
|
213
|
-
|
|
214
|
-
// === INDEPENDENT USER DATA (doesn't depend on market data) ===
|
|
215
|
-
...addresses.map(async (address) => {
|
|
195
|
+
// Everything starts at once; a read that needs a market's data waits for marketsData.
|
|
196
|
+
await Promise.all([
|
|
197
|
+
// === POSITIONS THAT DO NOT NEED MARKET DATA ===
|
|
198
|
+
// Maker: the address's vault list, then each vault. A failed list is reported in errors.makerCdps: maker stays {},
|
|
199
|
+
// which would otherwise read as "no vaults".
|
|
200
|
+
...positionsAddresses.map(async (address) => {
|
|
216
201
|
if (!isMainnet) return; // Maker CDPs are only available on mainnet
|
|
217
|
-
|
|
218
|
-
|
|
202
|
+
let cdps: CdpInfo[];
|
|
203
|
+
try {
|
|
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
|
+
}));
|
|
219
221
|
}),
|
|
220
|
-
...
|
|
222
|
+
...positionsAddresses.map(async (address) => {
|
|
221
223
|
try {
|
|
222
224
|
if (!isFluidSupported) return; // Fluid is not available on Optimism
|
|
223
225
|
const userPositions = (await _getUserPositionsPortfolio(client, network, address));
|
|
@@ -234,71 +236,94 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
234
236
|
};
|
|
235
237
|
}
|
|
236
238
|
}),
|
|
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
|
+
}),
|
|
237
249
|
|
|
238
|
-
// === STAKING DATA (independent of market data) ===
|
|
239
|
-
...
|
|
250
|
+
// === STAKING DATA (independent of market data; Morpho Earn is with the positions below) ===
|
|
251
|
+
...stakingAddresses.map(async (address) => {
|
|
240
252
|
try {
|
|
241
253
|
if (!isFluidSupported) return;
|
|
242
|
-
stakingPositions[address.toLowerCase()].fluid = await _getAllUserEarnPositionsWithFTokens(client, network, address);
|
|
254
|
+
stakingPositions[address.toLowerCase() as EthAddress].fluid = await _getAllUserEarnPositionsWithFTokens(client, network, address);
|
|
243
255
|
} catch (error) {
|
|
244
256
|
console.error(`Error fetching Fluid lend data for address ${address}:`, error);
|
|
245
|
-
stakingPositions[address.toLowerCase()].fluid = { error: `Error fetching Fluid lend data for address ${address}`, data: null };
|
|
257
|
+
stakingPositions[address.toLowerCase() as EthAddress].fluid = { error: `Error fetching Fluid lend data for address ${address}`, data: null };
|
|
246
258
|
}
|
|
247
259
|
}),
|
|
248
|
-
...
|
|
260
|
+
...stakingAddresses.map(async (address) => {
|
|
249
261
|
try {
|
|
250
262
|
if (!isMainnet) return;
|
|
251
|
-
stakingPositions[address.toLowerCase()].liquity = await getLiquityStakingData(client, network, address);
|
|
263
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquity = await getLiquityStakingData(client, network, address);
|
|
252
264
|
} catch (error) {
|
|
253
265
|
console.error(`Error fetching Liquity staking data for address ${address}:`, error);
|
|
254
|
-
stakingPositions[address.toLowerCase()].liquity = { error: `Error fetching Liquity staking data for address ${address}`, data: null };
|
|
266
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquity = { error: `Error fetching Liquity staking data for address ${address}`, data: null };
|
|
255
267
|
}
|
|
256
268
|
}),
|
|
257
|
-
...
|
|
269
|
+
...stakingAddresses.map(async (address) => {
|
|
258
270
|
try {
|
|
259
271
|
if (!isMainnet) return;
|
|
260
|
-
stakingPositions[address.toLowerCase()].aaveV3 = await getStakeAaveData(client, network, address);
|
|
272
|
+
stakingPositions[address.toLowerCase() as EthAddress].aaveV3 = await getStakeAaveData(client, network, address);
|
|
261
273
|
} catch (error) {
|
|
262
274
|
console.error(`Error fetching Aave V3 staking data for address ${address}:`, error);
|
|
263
|
-
stakingPositions[address.toLowerCase()].aaveV3 = { error: `Error fetching Aave V3 staking data for address ${address}`, data: null };
|
|
275
|
+
stakingPositions[address.toLowerCase() as EthAddress].aaveV3 = { error: `Error fetching Aave V3 staking data for address ${address}`, data: null };
|
|
264
276
|
}
|
|
265
277
|
}),
|
|
266
|
-
...
|
|
278
|
+
...stakingAddresses.map(async (address) => {
|
|
267
279
|
try {
|
|
268
280
|
if (!isMainnet) return;
|
|
269
|
-
stakingPositions[address.toLowerCase()].umbrella = await getUmbrellaData(client, network, address);
|
|
281
|
+
stakingPositions[address.toLowerCase() as EthAddress].umbrella = await getUmbrellaData(client, network, address);
|
|
270
282
|
} catch (error) {
|
|
271
283
|
console.error(`Error fetching Umbrella staking data for address ${address}:`, error);
|
|
272
|
-
stakingPositions[address.toLowerCase()].umbrella = { error: `Error fetching Umbrella staking data for address ${address}`, data: null };
|
|
284
|
+
stakingPositions[address.toLowerCase() as EthAddress].umbrella = { error: `Error fetching Umbrella staking data for address ${address}`, data: null };
|
|
273
285
|
}
|
|
274
286
|
}),
|
|
275
|
-
// Liquity V2
|
|
276
|
-
...
|
|
287
|
+
// Liquity V2 stability pools, then sBOLD/yBOLD, which reads them.
|
|
288
|
+
...stakingAddresses.map(async (address) => {
|
|
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
|
+
}));
|
|
277
302
|
try {
|
|
278
303
|
if (!isMainnet) {
|
|
279
|
-
stakingPositions[address.toLowerCase()].
|
|
304
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data: null };
|
|
280
305
|
return;
|
|
281
306
|
}
|
|
282
|
-
const
|
|
283
|
-
stakingPositions[address.toLowerCase()].
|
|
307
|
+
const data = await getLiquitySAndYBold(client, network, stakingPositions[address.toLowerCase() as EthAddress].liquityV2, address);
|
|
308
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data };
|
|
284
309
|
} catch (error) {
|
|
285
|
-
console.error(`Error fetching
|
|
286
|
-
stakingPositions[address.toLowerCase()].
|
|
310
|
+
console.error(`Error fetching SBold/YBold data for address ${address}:`, error);
|
|
311
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: `Error fetching sBold/yBold data for address ${address}`, data: null };
|
|
287
312
|
}
|
|
288
|
-
})
|
|
313
|
+
}),
|
|
289
314
|
|
|
290
315
|
// === REWARDS DATA (independent of market data) ===
|
|
291
316
|
// Batch King rewards
|
|
292
317
|
(async () => {
|
|
293
318
|
try {
|
|
294
319
|
if (!isMainnet) {
|
|
295
|
-
for (const address of
|
|
296
|
-
rewardsData[address.toLowerCase()].king = { error: '', data: [] };
|
|
320
|
+
for (const address of rewardsAddresses) {
|
|
321
|
+
rewardsData[address.toLowerCase() as EthAddress].king = { error: '', data: [] };
|
|
297
322
|
}
|
|
298
323
|
return;
|
|
299
324
|
}
|
|
300
|
-
const kingRewards = await getKingRewards(client, network,
|
|
301
|
-
for (const address of
|
|
325
|
+
const kingRewards = await getKingRewards(client, network, rewardsAddresses);
|
|
326
|
+
for (const address of rewardsAddresses) {
|
|
302
327
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
303
328
|
rewardsData[lowerAddress].king = {
|
|
304
329
|
error: '',
|
|
@@ -307,7 +332,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
307
332
|
}
|
|
308
333
|
} catch (error) {
|
|
309
334
|
console.error('Error fetching King rewards data in batch:', error);
|
|
310
|
-
for (const address of
|
|
335
|
+
for (const address of rewardsAddresses) {
|
|
311
336
|
rewardsData[address.toLowerCase() as EthAddress].king = {
|
|
312
337
|
error: 'Error fetching King rewards data in batch',
|
|
313
338
|
data: null,
|
|
@@ -319,13 +344,13 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
319
344
|
(async () => {
|
|
320
345
|
try {
|
|
321
346
|
if (!isMainnet) {
|
|
322
|
-
for (const address of
|
|
323
|
-
rewardsData[address.toLowerCase()].uniswap = { error: '', data: [] };
|
|
347
|
+
for (const address of rewardsAddresses) {
|
|
348
|
+
rewardsData[address.toLowerCase() as EthAddress].uniswap = { error: '', data: [] };
|
|
324
349
|
}
|
|
325
350
|
return;
|
|
326
351
|
}
|
|
327
|
-
const uniswapRewards = await getUniswapRewards(client, network,
|
|
328
|
-
for (const address of
|
|
352
|
+
const uniswapRewards = await getUniswapRewards(client, network, rewardsAddresses);
|
|
353
|
+
for (const address of rewardsAddresses) {
|
|
329
354
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
330
355
|
rewardsData[lowerAddress].uniswap = {
|
|
331
356
|
error: '',
|
|
@@ -334,7 +359,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
334
359
|
}
|
|
335
360
|
} catch (error) {
|
|
336
361
|
console.error('Error fetching Uniswap rewards data in batch:', error);
|
|
337
|
-
for (const address of
|
|
362
|
+
for (const address of rewardsAddresses) {
|
|
338
363
|
rewardsData[address.toLowerCase() as EthAddress].uniswap = {
|
|
339
364
|
error: 'Error fetching Uniswap rewards data in batch',
|
|
340
365
|
data: null,
|
|
@@ -342,10 +367,10 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
342
367
|
}
|
|
343
368
|
}
|
|
344
369
|
})(),
|
|
345
|
-
...
|
|
370
|
+
...sparkLending.markets(network).map((market) => rewardsAddresses.map(async address => {
|
|
346
371
|
try {
|
|
347
372
|
if (!isMainnet) {
|
|
348
|
-
rewardsData[address.toLowerCase()].spark[market.value] = { error: '', data: [] };
|
|
373
|
+
rewardsData[address.toLowerCase() as EthAddress].spark[market.value] = { error: '', data: [] };
|
|
349
374
|
return;
|
|
350
375
|
}
|
|
351
376
|
const sparkData = await fetchSparkRewards(client, network, address, market.providerAddress);
|
|
@@ -355,7 +380,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
355
380
|
rewardsData[address.toLowerCase() as EthAddress].spark[market.value] = { error: `Error fetching Spark rewards data for address ${address}`, data: null };
|
|
356
381
|
}
|
|
357
382
|
})).flat(),
|
|
358
|
-
...
|
|
383
|
+
...rewardsAddresses.map(async (address) => {
|
|
359
384
|
try {
|
|
360
385
|
const merklData = await getMerklUnclaimedRewards(address, network);
|
|
361
386
|
rewardsData[address.toLowerCase() as EthAddress].merkl = { error: '', data: merklData };
|
|
@@ -364,27 +389,28 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
364
389
|
rewardsData[address.toLowerCase() as EthAddress].merkl = { error: `Error fetching Merkl rewards data for address ${address}`, data: null };
|
|
365
390
|
}
|
|
366
391
|
}),
|
|
367
|
-
...
|
|
392
|
+
...aaveV3Lending.markets(network).map(market => rewardsAddresses.map(async (address) => {
|
|
368
393
|
try {
|
|
369
394
|
const aaveData = await getUnclaimedRewardsForAllMarkets(client, network, address, market.providerAddress);
|
|
370
395
|
rewardsData[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: '', data: aaveData };
|
|
371
396
|
} catch (error) {
|
|
372
397
|
console.error(`Error fetching Aave V3 Merit rewards data for address ${address}:`, error);
|
|
373
|
-
|
|
398
|
+
// This market only: the other markets keep their rewards.
|
|
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
400
|
}
|
|
375
401
|
})).flat(),
|
|
376
402
|
// Batch Spark Airdrop rewards
|
|
377
403
|
(async () => {
|
|
378
404
|
try {
|
|
379
405
|
if (!isMainnet) {
|
|
380
|
-
for (const address of
|
|
381
|
-
rewardsData[address.toLowerCase()].spk = { error: '', data: [] };
|
|
406
|
+
for (const address of rewardsAddresses) {
|
|
407
|
+
rewardsData[address.toLowerCase() as EthAddress].spk = { error: '', data: [] };
|
|
382
408
|
}
|
|
383
409
|
return;
|
|
384
410
|
}
|
|
385
411
|
|
|
386
|
-
const sparkAirdropRewards = await fetchSparkAirdropRewards(client, network,
|
|
387
|
-
for (const address of
|
|
412
|
+
const sparkAirdropRewards = await fetchSparkAirdropRewards(client, network, rewardsAddresses);
|
|
413
|
+
for (const address of rewardsAddresses) {
|
|
388
414
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
389
415
|
rewardsData[lowerAddress].spk = {
|
|
390
416
|
error: '',
|
|
@@ -393,7 +419,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
393
419
|
}
|
|
394
420
|
} catch (error) {
|
|
395
421
|
console.error('Error fetching Spark Airdrop rewards data in batch:', error);
|
|
396
|
-
for (const address of
|
|
422
|
+
for (const address of rewardsAddresses) {
|
|
397
423
|
rewardsData[address.toLowerCase() as EthAddress].spk = {
|
|
398
424
|
error: 'Error fetching Spark Airdrop rewards data in batch',
|
|
399
425
|
data: null,
|
|
@@ -407,8 +433,8 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
407
433
|
return;
|
|
408
434
|
}
|
|
409
435
|
|
|
410
|
-
const ethenaAirdropRewards = await fetchEthenaAirdropRewards(
|
|
411
|
-
for (const address of
|
|
436
|
+
const ethenaAirdropRewards = await fetchEthenaAirdropRewards(rewardsAddresses);
|
|
437
|
+
for (const address of rewardsAddresses) {
|
|
412
438
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
413
439
|
rewardsData[lowerAddress].ethena = {
|
|
414
440
|
error: '',
|
|
@@ -417,7 +443,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
417
443
|
}
|
|
418
444
|
} catch (error) {
|
|
419
445
|
console.error('Error fetching Ethena Airdrop rewards data:', error);
|
|
420
|
-
for (const address of
|
|
446
|
+
for (const address of rewardsAddresses) {
|
|
421
447
|
rewardsData[address.toLowerCase() as EthAddress].ethena = {
|
|
422
448
|
error: 'Error fetching Ethena Airdrop rewards data in batch',
|
|
423
449
|
data: null,
|
|
@@ -425,21 +451,274 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
425
451
|
}
|
|
426
452
|
}
|
|
427
453
|
})(),
|
|
454
|
+
|
|
455
|
+
// === POSITIONS ON MARKETS (each waits for its market's data) ===
|
|
456
|
+
...LENDING_PROTOCOLS.flatMap((protocol) => protocol.readPositions({ client, network }, positionsAddresses, marketsData, positions)),
|
|
457
|
+
// Morpho Blue Earn: a supply without a borrow, kept as staking, read on the same markets as Morpho Blue's positions.
|
|
458
|
+
...morphoBlueLending.markets(network).flatMap((market) => stakingAddresses.map(async (address) => {
|
|
459
|
+
try {
|
|
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
|
+
}
|
|
465
|
+
} catch (error) {
|
|
466
|
+
console.error(`Error fetching MorphoBlue earn data for address ${address} on market ${market.value}:`, error);
|
|
467
|
+
stakingPositions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue earn data for address ${address} on market ${market.value}`, data: null };
|
|
468
|
+
}
|
|
469
|
+
})),
|
|
470
|
+
// Liquity V2 troves: one market's trove ids for every address at once, then each trove's data. A failure is the
|
|
471
|
+
// market's error; the troves that did load stay in its data.
|
|
472
|
+
...(liquityV2Addresses.length ? liquityV2Markets : []).map(async (market) => {
|
|
473
|
+
const setEntry = (address: EthAddress, entry: PortfolioLiquityV2Troves) => {
|
|
474
|
+
positions[address.toLowerCase() as EthAddress].liquityV2![market.value] = entry;
|
|
475
|
+
};
|
|
476
|
+
const failAll = (error: string) => liquityV2Addresses.forEach((address) => setEntry(address, { error, data: {} }));
|
|
477
|
+
|
|
478
|
+
const allMarketsData = (await marketsData).liquityV2MarketsData;
|
|
479
|
+
const marketData = allMarketsData[market.value];
|
|
480
|
+
if (!marketData) {
|
|
481
|
+
failAll(`Error fetching Liquity V2 market data for market ${market.value}`);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
let troveIds: Record<EthAddress, string[]>;
|
|
485
|
+
try {
|
|
486
|
+
troveIds = await _getLiquityV2UsersTroveIds(client, network, market, marketData.marketData.troveNFTAddress, isSim, liquityV2Addresses);
|
|
487
|
+
} catch (error) {
|
|
488
|
+
console.error(`Error fetching Liquity V2 trove ids on market ${market.value}:`, error);
|
|
489
|
+
failAll(`Error fetching Liquity V2 troves on market ${market.value}`);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
await Promise.all(liquityV2Addresses.map(async (address) => {
|
|
493
|
+
const entry: PortfolioLiquityV2Troves = { error: '', data: {} };
|
|
494
|
+
const failed: string[] = [];
|
|
495
|
+
await Promise.all((troveIds[address] ?? []).map(async (troveId) => {
|
|
496
|
+
try {
|
|
497
|
+
entry.data[troveId] = await _getLiquityV2TroveData(client, network, {
|
|
498
|
+
selectedMarket: market,
|
|
499
|
+
assetsData: marketData.assetsData,
|
|
500
|
+
troveId,
|
|
501
|
+
allMarketsData: allMarketsData as Record<LiquityV2Versions, LiquityV2MarketData>,
|
|
502
|
+
});
|
|
503
|
+
} catch (error) {
|
|
504
|
+
console.error(`Error fetching Liquity V2 trove ${troveId} for address ${address} on market ${market.value}:`, error);
|
|
505
|
+
failed.push(troveId);
|
|
506
|
+
}
|
|
507
|
+
}));
|
|
508
|
+
if (failed.length) entry.error = `Error fetching Liquity V2 troves ${failed.join(', ')} for address ${address} on market ${market.value}`;
|
|
509
|
+
setEntry(address, entry);
|
|
510
|
+
}));
|
|
511
|
+
}),
|
|
512
|
+
]);
|
|
513
|
+
|
|
514
|
+
return {
|
|
515
|
+
positions,
|
|
516
|
+
stakingPositions,
|
|
517
|
+
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
|
+
markets,
|
|
564
|
+
errors: {
|
|
565
|
+
...(Object.keys(marketsErrors).length ? { markets: marketsErrors } : {}),
|
|
566
|
+
...userData.errors,
|
|
567
|
+
},
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
export async function getShifterPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], isSim = false): Promise<{
|
|
573
|
+
positions: PortfolioPositionsData;
|
|
574
|
+
markets: ShifterPortfolioMarketsData;
|
|
575
|
+
}> {
|
|
576
|
+
const isMainnet = network === NetworkNumber.Eth;
|
|
577
|
+
const isFluidSupported = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Base, NetworkNumber.Plasma].includes(network);
|
|
578
|
+
|
|
579
|
+
const morphoMarkets = Object.values(MorphoBlueMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
580
|
+
const compoundV3Markets = Object.values(CompoundMarkets(network)).filter((market) => market.chainIds.includes(network) && market.value !== CompoundVersions.CompoundV2);
|
|
581
|
+
const sparkMarkets = Object.values(SparkMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
582
|
+
const aaveV3Markets = [AaveVersions.AaveV3, AaveVersions.AaveV3Lido, AaveVersions.AaveV3Etherfi].map((version) => AaveMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
|
|
583
|
+
const aaveV2Markets = [AaveVersions.AaveV2].map((version) => AaveMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
|
|
584
|
+
const compoundV2Markets = [CompoundVersions.CompoundV2].map((version) => CompoundMarkets(network)[version]).filter((market) => market.chainIds.includes(network));
|
|
585
|
+
const crvUsdMarkets = Object.values(CrvUsdMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
586
|
+
const llamaLendMarkets = [NetworkNumber.Eth, NetworkNumber.Arb].includes(network) ? Object.values(LlamaLendMarkets(network)).filter((market) => market.chainIds.includes(network)) : [];
|
|
587
|
+
const liquityV2Markets = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)) : [];
|
|
588
|
+
const aaveV4Spokes = Object.values(AaveV4Spokes(network)).filter((market) => market.chainIds.includes(network));
|
|
589
|
+
|
|
590
|
+
const args: [NetworkNumber, any?] = [network, { batch: { multicall: { batchSize: isSim ? 2_000 : 2_500_000 } } }];
|
|
591
|
+
const client = getViemProvider(provider, ...args);
|
|
592
|
+
const defaultClient = getViemProvider(defaultProvider, ...args);
|
|
593
|
+
|
|
594
|
+
const markets: ShifterPortfolioMarketsData = {
|
|
595
|
+
morphoMarketsData: {},
|
|
596
|
+
morphoMidnightMarketsData: {}, // Morpho Midnight is not fetched for the shifter; key kept so the payload shape is stable
|
|
597
|
+
compoundV3MarketsData: {},
|
|
598
|
+
sparkMarketsData: {},
|
|
599
|
+
aaveV3MarketsData: {},
|
|
600
|
+
aaveV2MarketsData: {},
|
|
601
|
+
compoundV2MarketsData: {},
|
|
602
|
+
crvUsdMarketsData: {},
|
|
603
|
+
llamaLendMarketsData: {},
|
|
604
|
+
liquityV2MarketsData: {},
|
|
605
|
+
aaveV4SpokesData: {},
|
|
606
|
+
fluidMarketsData: {},
|
|
607
|
+
makerMarketsData: {},
|
|
608
|
+
};
|
|
609
|
+
const makerCdps: Record<string, CdpInfo[]> = {};
|
|
610
|
+
|
|
611
|
+
const positions: PortfolioPositionsData = {};
|
|
612
|
+
|
|
613
|
+
for (const address of addresses) {
|
|
614
|
+
positions[address.toLowerCase() as EthAddress] = {
|
|
615
|
+
aaveV3: {},
|
|
616
|
+
aaveV4: {},
|
|
617
|
+
morphoBlue: {},
|
|
618
|
+
morphoMidnight: {}, // not fetched for the shifter (see markets above)
|
|
619
|
+
compoundV3: {},
|
|
620
|
+
spark: {},
|
|
621
|
+
maker: {},
|
|
622
|
+
aaveV2: {},
|
|
623
|
+
compoundV2: {},
|
|
624
|
+
liquity: {},
|
|
625
|
+
crvUsd: {},
|
|
626
|
+
llamaLend: {},
|
|
627
|
+
fluid: {
|
|
628
|
+
error: '',
|
|
629
|
+
data: {},
|
|
630
|
+
},
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
await Promise.allSettled([
|
|
635
|
+
// === MARKET DATA (needs to be fetched first) ===
|
|
636
|
+
...morphoMarkets.map(async (market) => {
|
|
637
|
+
markets.morphoMarketsData[market.value] = await _getMorphoBluePortfolioMarketData(client, network, market);
|
|
638
|
+
}),
|
|
639
|
+
...compoundV3Markets.map(async (market) => {
|
|
640
|
+
markets.compoundV3MarketsData[market.value] = await _getCompoundV3MarketsData(client, network, market, defaultClient);
|
|
641
|
+
}),
|
|
642
|
+
...sparkMarkets.map(async (market) => {
|
|
643
|
+
markets.sparkMarketsData[market.value] = await _getSparkMarketsData(client, network, market);
|
|
644
|
+
}),
|
|
645
|
+
...aaveV3Markets.map(async (market) => {
|
|
646
|
+
markets.aaveV3MarketsData[market.value] = await _getAaveV3MarketData(client, network, market);
|
|
647
|
+
}),
|
|
648
|
+
...aaveV4Spokes.map(async (spoke) => {
|
|
649
|
+
markets.aaveV4SpokesData[spoke.value] = await _getAaveV4SpokeData(client, network, spoke);
|
|
650
|
+
}),
|
|
651
|
+
...aaveV2Markets.map(async (market) => {
|
|
652
|
+
markets.aaveV2MarketsData[market.value] = await _getAaveV2MarketsData(client, network, market);
|
|
653
|
+
}),
|
|
654
|
+
...compoundV2Markets.map(async (market) => {
|
|
655
|
+
markets.compoundV2MarketsData[market.value] = await _getCompoundV2MarketsData(client, network);
|
|
656
|
+
}),
|
|
657
|
+
...crvUsdMarkets.map(async (market) => {
|
|
658
|
+
markets.crvUsdMarketsData[market.value] = await _getCurveUsdGlobalData(client, network, market);
|
|
659
|
+
}),
|
|
660
|
+
...llamaLendMarkets.map(async (market) => {
|
|
661
|
+
markets.llamaLendMarketsData[market.value] = await _getLlamaLendGlobalData(client, network, market);
|
|
662
|
+
}),
|
|
663
|
+
...liquityV2Markets.map(async (market) => {
|
|
664
|
+
markets.liquityV2MarketsData[market.value] = await _getLiquityV2MarketData(client, network, market);
|
|
665
|
+
}),
|
|
666
|
+
(async () => {
|
|
667
|
+
if (!isFluidSupported) return;
|
|
668
|
+
try {
|
|
669
|
+
markets.fluidMarketsData = await _getAllFluidMarketDataPortfolio(client, network);
|
|
670
|
+
} catch (error) {
|
|
671
|
+
console.error('Error fetching Fluid markets data:', error);
|
|
672
|
+
}
|
|
673
|
+
})(),
|
|
674
|
+
(async () => {
|
|
675
|
+
if (!isMainnet) return; // Maker CDPs are only available on mainnet
|
|
676
|
+
try {
|
|
677
|
+
markets.makerMarketsData = await _getMakerIlksData(client, network, MakerActiveIlks);
|
|
678
|
+
} catch (error) {
|
|
679
|
+
console.error('Error fetching Maker ilks data:', error);
|
|
680
|
+
}
|
|
681
|
+
})(),
|
|
682
|
+
|
|
683
|
+
// === INDEPENDENT USER DATA (doesn't depend on market data) ===
|
|
684
|
+
...addresses.map(async (address) => {
|
|
685
|
+
if (!isMainnet) return; // Maker CDPs are only available on mainnet
|
|
686
|
+
const makerCdp = await _getUserCdps(client, network, address);
|
|
687
|
+
makerCdps[address.toLowerCase() as EthAddress] = makerCdp;
|
|
688
|
+
}),
|
|
689
|
+
...addresses.map(async (address) => {
|
|
690
|
+
try {
|
|
691
|
+
if (!isFluidSupported) return; // Fluid is not available on Optimism
|
|
692
|
+
const userPositions = (await _getUserPositionsPortfolio(client, network, address));
|
|
693
|
+
for (const position of userPositions) {
|
|
694
|
+
if (position.userData && new Dec(position.userData.suppliedUsd).gt(0)) {
|
|
695
|
+
positions[address.toLowerCase() as EthAddress].fluid.data[position.userData.nftId] = position.userData;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
} catch (error) {
|
|
699
|
+
console.error(`Error fetching Fluid positions for address ${address}:`, error);
|
|
700
|
+
positions[address.toLowerCase() as EthAddress].fluid = {
|
|
701
|
+
error: `Error fetching Fluid positions for address ${address}`,
|
|
702
|
+
data: {},
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
}),
|
|
428
706
|
]);
|
|
429
707
|
|
|
430
708
|
await Promise.all([
|
|
431
|
-
...aaveV3Markets.map((market) =>
|
|
709
|
+
...aaveV3Markets.map((market) => addresses.map(async (address) => {
|
|
432
710
|
try {
|
|
433
|
-
const accData = await _getAaveV3AccountData(client, network, address, { selectedMarket: market, ...aaveV3MarketsData[market.value] });
|
|
434
|
-
|
|
711
|
+
const accData = await _getAaveV3AccountData(client, network, address, { selectedMarket: market, ...markets.aaveV3MarketsData[market.value] });
|
|
712
|
+
// Always store the position (even with no supply) so the eMode category is available to the shifter
|
|
713
|
+
positions[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: '', data: accData };
|
|
435
714
|
} catch (error) {
|
|
436
715
|
console.error(`Error fetching AaveV3 account data for address ${address} on market ${market.value}:`, error);
|
|
437
716
|
positions[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: `Error fetching AaveV3 account data for address ${address} on market ${market.value}`, data: null };
|
|
438
717
|
}
|
|
439
718
|
})).flat(),
|
|
440
|
-
...aaveV4Spokes.map((spoke) =>
|
|
719
|
+
...aaveV4Spokes.map((spoke) => addresses.map(async (address) => {
|
|
441
720
|
try {
|
|
442
|
-
const accData = await _getAaveV4AccountData(client, network, aaveV4SpokesData[spoke.value], address);
|
|
721
|
+
const accData = await _getAaveV4AccountData(client, network, markets.aaveV4SpokesData[spoke.value], address);
|
|
443
722
|
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].aaveV4[spoke.value] = { error: '', data: accData };
|
|
444
723
|
} catch (error) {
|
|
445
724
|
console.error(`Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}:`, error);
|
|
@@ -448,70 +727,36 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
448
727
|
})).flat(),
|
|
449
728
|
...morphoMarkets.map((market) => addresses.map(async (address) => {
|
|
450
729
|
try {
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
getMorphoEarn(client, network, address, market, morphoMarketsData[market.value]),
|
|
454
|
-
]);
|
|
455
|
-
if (accDataPromise.status === 'rejected') {
|
|
456
|
-
console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, accDataPromise.reason);
|
|
457
|
-
positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
|
|
458
|
-
}
|
|
459
|
-
if (earnDataPromise.status === 'rejected') {
|
|
460
|
-
console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, earnDataPromise.reason);
|
|
461
|
-
positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
|
|
462
|
-
}
|
|
463
|
-
if (accDataPromise.status !== 'rejected') {
|
|
464
|
-
const accData = accDataPromise.value;
|
|
465
|
-
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: '', data: accData };
|
|
466
|
-
}
|
|
467
|
-
if (earnDataPromise.status !== 'rejected') {
|
|
468
|
-
const earnData = earnDataPromise.value;
|
|
469
|
-
if (earnData && new Dec(earnData.amount).gt(0)) {
|
|
470
|
-
stakingPositions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = {
|
|
471
|
-
error: '',
|
|
472
|
-
data: earnData,
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
}
|
|
730
|
+
const accData = await _getMorphoBlueAccountData(client, network, address, market, markets.morphoMarketsData[market.value]);
|
|
731
|
+
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: '', data: accData };
|
|
476
732
|
} catch (error) {
|
|
477
733
|
console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, error);
|
|
478
734
|
positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
|
|
479
735
|
}
|
|
480
736
|
})).flat(),
|
|
481
|
-
...morphoMidnightMarkets.map((market) => addresses.map(async (address) => {
|
|
482
|
-
try {
|
|
483
|
-
// Markets are created lazily on the first position, so an uncreated one can hold no position.
|
|
484
|
-
if (!morphoMidnightMarketsData[market.value]?.isCreated) return;
|
|
485
|
-
const accData = await _getMorphoMidnightAccountData(client, network, address, market, morphoMidnightMarketsData[market.value]);
|
|
486
|
-
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].morphoMidnight[market.value] = { error: '', data: accData };
|
|
487
|
-
} catch (error) {
|
|
488
|
-
console.error(`Error fetching MorphoMidnight account data for address ${address} on market ${market.value}:`, error);
|
|
489
|
-
positions[address.toLowerCase() as EthAddress].morphoMidnight[market.value] = { error: `Error fetching MorphoMidnight account data for address ${address} on market ${market.value}`, data: null };
|
|
490
|
-
}
|
|
491
|
-
})).flat(),
|
|
492
737
|
...compoundV3Markets.map((market) => addresses.map(async (address) => {
|
|
493
738
|
try {
|
|
494
|
-
const accData = await _getCompoundV3AccountData(client, network, address, ZERO_ADDRESS, { selectedMarket: market, assetsData: compoundV3MarketsData[market.value].assetsData });
|
|
739
|
+
const accData = await _getCompoundV3AccountData(client, network, address, ZERO_ADDRESS, { selectedMarket: market, assetsData: markets.compoundV3MarketsData[market.value].assetsData });
|
|
495
740
|
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].compoundV3[market.value] = { error: '', data: accData };
|
|
496
741
|
} catch (error) {
|
|
497
742
|
console.error(`Error fetching CompoundV3 account data for address ${address} on market ${market.value}:`, error);
|
|
498
743
|
positions[address.toLowerCase() as EthAddress].compoundV3[market.value] = { error: `Error fetching CompoundV3 account data for address ${address} on market ${market.value}`, data: null };
|
|
499
744
|
}
|
|
500
745
|
})).flat(),
|
|
501
|
-
...sparkMarkets.map((market) =>
|
|
746
|
+
...sparkMarkets.map((market) => addresses.map(async (address) => {
|
|
502
747
|
try {
|
|
503
|
-
const accData = await _getSparkAccountData(client, network, address, { selectedMarket: market, assetsData: sparkMarketsData[market.value].assetsData, eModeCategoriesData: sparkMarketsData[market.value].eModeCategoriesData });
|
|
504
|
-
|
|
748
|
+
const accData = await _getSparkAccountData(client, network, address, { selectedMarket: market, assetsData: markets.sparkMarketsData[market.value].assetsData, eModeCategoriesData: markets.sparkMarketsData[market.value].eModeCategoriesData });
|
|
749
|
+
// Always store the position (even with no supply) so the eMode category is available to the shifter
|
|
750
|
+
positions[address.toLowerCase() as EthAddress].spark[market.value] = { error: '', data: accData };
|
|
505
751
|
} catch (error) {
|
|
506
752
|
console.error(`Error fetching Spark account data for address ${address} on market ${market.value}:`, error);
|
|
507
753
|
positions[address.toLowerCase() as EthAddress].spark[market.value] = { error: `Error fetching Spark account data for address ${address} on market ${market.value}`, data: null };
|
|
508
754
|
}
|
|
509
755
|
})).flat(),
|
|
510
|
-
|
|
511
|
-
// before a vault's data lands and the vault is missing from the result.
|
|
512
|
-
...addresses.flatMap((address) => (makerCdps[address.toLowerCase() as EthAddress] ?? []).map(async (cdpInfo) => {
|
|
756
|
+
...addresses.map((address) => (makerCdps[address.toLowerCase() as EthAddress] || []).map(async (cdpInfo) => {
|
|
513
757
|
try {
|
|
514
|
-
|
|
758
|
+
// reuse ilk data fetched for the markets payload; ilks outside the active set are fetched on demand
|
|
759
|
+
const cdpData = await _getMakerCdpData(client, network, cdpInfo, markets.makerMarketsData[cdpInfo.ilkLabel]);
|
|
515
760
|
if (cdpData) {
|
|
516
761
|
positions[address.toLowerCase() as EthAddress].maker[cdpInfo.id] = { error: '', data: cdpData };
|
|
517
762
|
}
|
|
@@ -519,10 +764,10 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
519
764
|
console.error(`Error fetching Maker CDP data for address ${address} with ID ${cdpInfo.id}:`, error);
|
|
520
765
|
positions[address.toLowerCase() as EthAddress].maker[cdpInfo.id] = { error: `Error fetching Maker CDP data for address ${address} with ID ${cdpInfo.id}`, data: null };
|
|
521
766
|
}
|
|
522
|
-
})),
|
|
767
|
+
})).flat(),
|
|
523
768
|
...aaveV2Markets.map((market) => addresses.map(async (address) => {
|
|
524
769
|
try {
|
|
525
|
-
const accData = await _getAaveV2AccountData(client, network, address, aaveV2MarketsData[market.value].assetsData, market);
|
|
770
|
+
const accData = await _getAaveV2AccountData(client, network, address, markets.aaveV2MarketsData[market.value].assetsData, market);
|
|
526
771
|
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].aaveV2[market.value] = { error: '', data: accData };
|
|
527
772
|
} catch (error) {
|
|
528
773
|
console.error(`Error fetching AaveV2 account data for address ${address}:`, error);
|
|
@@ -531,7 +776,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
531
776
|
})).flat(),
|
|
532
777
|
...compoundV2Markets.map((market) => addresses.map(async (address) => {
|
|
533
778
|
try {
|
|
534
|
-
const accData = await _getCompoundV2AccountData(client, network, address, compoundV2MarketsData[market.value].assetsData);
|
|
779
|
+
const accData = await _getCompoundV2AccountData(client, network, address, markets.compoundV2MarketsData[market.value].assetsData);
|
|
535
780
|
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].compoundV2[market.value] = { error: '', data: accData };
|
|
536
781
|
} catch (error) {
|
|
537
782
|
console.error(`Error fetching CompoundV2 account data for address ${address}:`, error);
|
|
@@ -550,9 +795,9 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
550
795
|
}),
|
|
551
796
|
...crvUsdMarkets.map((market) => addresses.map(async (address) => {
|
|
552
797
|
try {
|
|
553
|
-
const accData = await _getCurveUsdUserData(client, network, address, market, crvUsdMarketsData[market.value].activeBand);
|
|
798
|
+
const accData = await _getCurveUsdUserData(client, network, address, market, markets.crvUsdMarketsData[market.value].activeBand, markets.crvUsdMarketsData[market.value].borrowRate);
|
|
554
799
|
if (new Dec(accData.suppliedUsd).gt(0) || new Dec(accData.borrowedUsd).gt(0)) {
|
|
555
|
-
positions[address.toLowerCase() as EthAddress].crvUsd[market.value] = { error: '', data:
|
|
800
|
+
positions[address.toLowerCase() as EthAddress].crvUsd[market.value] = { error: '', data: accData };
|
|
556
801
|
}
|
|
557
802
|
} catch (error) {
|
|
558
803
|
console.error(`Error fetching Curve USD account data for address ${address} on market ${market.value}:`, error);
|
|
@@ -561,35 +806,19 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
561
806
|
})).flat(),
|
|
562
807
|
...llamaLendMarkets.map((market) => addresses.map(async (address) => {
|
|
563
808
|
try {
|
|
564
|
-
const accData = await _getLlamaLendUserData(client, network, address, market, llamaLendMarketsData[market.value]);
|
|
809
|
+
const accData = await _getLlamaLendUserData(client, network, address, market, markets.llamaLendMarketsData[market.value]);
|
|
565
810
|
if (new Dec(accData.suppliedUsd).gt(0) || new Dec(accData.borrowedUsd).gt(0)) {
|
|
566
|
-
positions[address.toLowerCase() as EthAddress].llamaLend[market.value] = { error: '', data: { ...accData, borrowRate: llamaLendMarketsData[market.value].borrowRate } };
|
|
811
|
+
positions[address.toLowerCase() as EthAddress].llamaLend[market.value] = { error: '', data: { ...accData, borrowRate: markets.llamaLendMarketsData[market.value].borrowRate } };
|
|
567
812
|
}
|
|
568
813
|
} catch (error) {
|
|
569
814
|
console.error(`Error fetching LlamaLend account data for address ${address} on market ${market.value}:`, error);
|
|
570
815
|
positions[address.toLowerCase() as EthAddress].llamaLend[market.value] = { error: `Error fetching LlamaLend account data for address ${address} on market ${market.value}`, data: null };
|
|
571
816
|
}
|
|
572
817
|
})).flat(),
|
|
573
|
-
// liquity sBold/yBold and staking options
|
|
574
|
-
...addresses.map(async (address) => {
|
|
575
|
-
try {
|
|
576
|
-
if (!isMainnet) {
|
|
577
|
-
stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data: null };
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
const data = await getLiquitySAndYBold(client, network, stakingPositions[address.toLowerCase()].liquityV2, address);
|
|
581
|
-
stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data };
|
|
582
|
-
} catch (error) {
|
|
583
|
-
console.error(`Error fetching SBold/YBold data for address ${address}:`, error);
|
|
584
|
-
stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: `Error fetching sBold/yBold data for address ${address}`, data: null };
|
|
585
|
-
}
|
|
586
|
-
}),
|
|
587
818
|
]);
|
|
588
819
|
|
|
589
820
|
return {
|
|
590
821
|
positions,
|
|
591
|
-
stakingPositions,
|
|
592
|
-
rewardsData,
|
|
593
822
|
markets,
|
|
594
823
|
};
|
|
595
824
|
}
|