@defisaver/positions-sdk 2.0.13 → 2.0.14-dev-portfolio
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/CLAUDE.md +32 -0
- package/cjs/aaveV2/index.js +1 -0
- package/cjs/aaveV3/index.d.ts +12 -0
- package/cjs/aaveV3/index.js +93 -1
- package/cjs/claiming/aaveV3.d.ts +9 -0
- package/cjs/claiming/aaveV3.js +148 -0
- package/cjs/claiming/compV3.d.ts +15 -0
- package/cjs/claiming/compV3.js +34 -0
- package/cjs/claiming/index.d.ts +6 -0
- package/cjs/claiming/index.js +46 -0
- package/cjs/claiming/king.d.ts +4 -0
- package/cjs/claiming/king.js +72 -0
- package/cjs/claiming/morphoBlue.d.ts +6 -0
- package/cjs/claiming/morphoBlue.js +113 -0
- package/cjs/claiming/spark.d.ts +6 -0
- package/cjs/claiming/spark.js +188 -0
- package/cjs/config/contracts.d.ts +2667 -0
- package/cjs/config/contracts.js +103 -2
- package/cjs/constants/index.d.ts +4 -0
- package/cjs/constants/index.js +6 -2
- package/cjs/contracts.d.ts +2882 -23
- package/cjs/contracts.js +10 -1
- package/cjs/index.d.ts +2 -1
- package/cjs/index.js +3 -1
- package/cjs/liquity/index.d.ts +11 -0
- package/cjs/liquity/index.js +39 -1
- package/cjs/liquityV2/index.d.ts +8 -0
- package/cjs/liquityV2/index.js +161 -0
- package/cjs/markets/aave/marketAssets.js +1 -1
- package/cjs/markets/compound/marketsAssets.js +2 -2
- package/cjs/markets/spark/marketAssets.js +1 -1
- package/cjs/morphoBlue/index.d.ts +5 -0
- package/cjs/morphoBlue/index.js +38 -4
- package/cjs/portfolio/index.d.ts +6 -1
- package/cjs/portfolio/index.js +256 -10
- package/cjs/services/utils.d.ts +5 -0
- package/cjs/services/utils.js +33 -1
- package/cjs/services/viem.d.ts +12 -12
- package/cjs/spark/index.js +1 -0
- package/cjs/staking/staking.js +3 -1
- package/cjs/types/claiming.d.ts +93 -0
- package/cjs/types/claiming.js +27 -0
- package/cjs/umbrella/index.d.ts +5 -0
- package/cjs/umbrella/index.js +50 -0
- package/cjs/umbrella/umbrellaUtils.d.ts +22 -0
- package/cjs/umbrella/umbrellaUtils.js +34 -0
- package/esm/aaveV2/index.js +1 -0
- package/esm/aaveV3/index.d.ts +12 -0
- package/esm/aaveV3/index.js +91 -1
- package/esm/claiming/aaveV3.d.ts +9 -0
- package/esm/claiming/aaveV3.js +139 -0
- package/esm/claiming/compV3.d.ts +15 -0
- package/esm/claiming/compV3.js +30 -0
- package/esm/claiming/index.d.ts +6 -0
- package/esm/claiming/index.js +6 -0
- package/esm/claiming/king.d.ts +4 -0
- package/esm/claiming/king.js +64 -0
- package/esm/claiming/morphoBlue.d.ts +6 -0
- package/esm/claiming/morphoBlue.js +104 -0
- package/esm/claiming/spark.d.ts +6 -0
- package/esm/claiming/spark.js +179 -0
- package/esm/config/contracts.d.ts +2667 -0
- package/esm/config/contracts.js +102 -1
- package/esm/constants/index.d.ts +4 -0
- package/esm/constants/index.js +5 -1
- package/esm/contracts.d.ts +2882 -23
- package/esm/contracts.js +9 -0
- package/esm/index.d.ts +2 -1
- package/esm/index.js +2 -1
- package/esm/liquity/index.d.ts +11 -0
- package/esm/liquity/index.js +38 -1
- package/esm/liquityV2/index.d.ts +8 -0
- package/esm/liquityV2/index.js +162 -1
- package/esm/markets/aave/marketAssets.js +1 -1
- package/esm/markets/compound/marketsAssets.js +2 -2
- package/esm/markets/spark/marketAssets.js +1 -1
- package/esm/morphoBlue/index.d.ts +5 -0
- package/esm/morphoBlue/index.js +38 -5
- package/esm/portfolio/index.d.ts +6 -1
- package/esm/portfolio/index.js +260 -14
- package/esm/services/utils.d.ts +5 -0
- package/esm/services/utils.js +31 -0
- package/esm/services/viem.d.ts +12 -12
- package/esm/spark/index.js +1 -0
- package/esm/staking/staking.js +3 -1
- package/esm/types/claiming.d.ts +93 -0
- package/esm/types/claiming.js +24 -0
- package/esm/umbrella/index.d.ts +5 -0
- package/esm/umbrella/index.js +46 -0
- package/esm/umbrella/umbrellaUtils.d.ts +22 -0
- package/esm/umbrella/umbrellaUtils.js +28 -0
- package/package.json +2 -2
- package/src/aaveV2/index.ts +2 -1
- package/src/aaveV3/index.ts +100 -2
- package/src/claiming/aaveV3.ts +163 -0
- package/src/claiming/compV3.ts +23 -0
- package/src/claiming/index.ts +13 -0
- package/src/claiming/king.ts +66 -0
- package/src/claiming/morphoBlue.ts +119 -0
- package/src/claiming/spark.ts +226 -0
- package/src/config/contracts.ts +105 -5
- package/src/constants/index.ts +5 -1
- package/src/contracts.ts +14 -1
- package/src/index.ts +2 -0
- package/src/liquity/index.ts +57 -2
- package/src/liquityV2/index.ts +177 -2
- package/src/markets/aave/marketAssets.ts +1 -1
- package/src/markets/compound/marketsAssets.ts +2 -2
- package/src/markets/spark/marketAssets.ts +1 -1
- package/src/morphoBlue/index.ts +43 -6
- package/src/portfolio/index.ts +263 -15
- package/src/services/utils.ts +37 -1
- package/src/spark/index.ts +2 -1
- package/src/staking/staking.ts +2 -1
- package/src/types/claiming.ts +109 -0
- package/src/umbrella/index.ts +70 -0
- package/src/umbrella/umbrellaUtils.ts +30 -0
package/src/portfolio/index.ts
CHANGED
|
@@ -2,9 +2,14 @@ import Dec from 'decimal.js';
|
|
|
2
2
|
import { EthAddress, EthereumProvider, NetworkNumber } from '../types/common';
|
|
3
3
|
import {
|
|
4
4
|
AaveMarkets,
|
|
5
|
-
CompoundMarkets,
|
|
5
|
+
CompoundMarkets,
|
|
6
|
+
CrvUsdMarkets,
|
|
7
|
+
EulerV2Markets,
|
|
8
|
+
LlamaLendMarkets,
|
|
9
|
+
MorphoBlueMarkets,
|
|
10
|
+
SparkMarkets,
|
|
6
11
|
} from '../markets';
|
|
7
|
-
import { _getMorphoBlueAccountData, _getMorphoBlueMarketData } from '../morphoBlue';
|
|
12
|
+
import { _getMorphoBlueAccountData, _getMorphoBlueMarketData, getMorphoEarn } from '../morphoBlue';
|
|
8
13
|
import {
|
|
9
14
|
AaveV2MarketData,
|
|
10
15
|
AaveV3MarketData,
|
|
@@ -25,18 +30,28 @@ import { _getSparkAccountData, _getSparkMarketsData } from '../spark';
|
|
|
25
30
|
import { _getEulerV2AccountData, _getEulerV2MarketsData } from '../eulerV2';
|
|
26
31
|
import { _getCurveUsdGlobalData, _getCurveUsdUserData } from '../curveUsd';
|
|
27
32
|
import { _getLlamaLendGlobalData, _getLlamaLendUserData } from '../llamaLend';
|
|
28
|
-
import { _getAaveV3AccountData, _getAaveV3MarketData } from '../aaveV3';
|
|
33
|
+
import { _getAaveV3AccountData, _getAaveV3MarketData, getStakeAaveData } from '../aaveV3';
|
|
29
34
|
import { ZERO_ADDRESS } from '../constants';
|
|
30
35
|
import { _getMakerCdpData, _getUserCdps } from '../maker';
|
|
31
36
|
import { _getAaveV2AccountData, _getAaveV2MarketsData } from '../aaveV2';
|
|
32
37
|
import { _getCompoundV2AccountData, _getCompoundV2MarketsData } from '../compoundV2';
|
|
33
38
|
import { getViemProvider } from '../services/viem';
|
|
34
|
-
import { _getLiquityTroveInfo } from '../liquity';
|
|
35
|
-
import {
|
|
36
|
-
import { _getUserPositionsPortfolio } from '../fluid';
|
|
39
|
+
import { _getLiquityTroveInfo, getLiquityStakingData } from '../liquity';
|
|
40
|
+
import { _getAllUserEarnPositionsWithFTokens, _getUserPositionsPortfolio } from '../fluid';
|
|
37
41
|
import { getEulerV2SubAccounts } from '../helpers/eulerHelpers';
|
|
42
|
+
import { getUmbrellaData } from '../umbrella';
|
|
43
|
+
import { getMeritUnclaimedRewards, getUnclaimedRewardsForAllMarkets } from '../claiming/aaveV3';
|
|
44
|
+
import { getCompoundV3Rewards } from '../claiming/compV3';
|
|
45
|
+
import { fetchSparkAirdropRewards, fetchSparkRewards } from '../claiming/spark';
|
|
46
|
+
import { fetchMorphoBlueRewards } from '../claiming/morphoBlue';
|
|
47
|
+
import { getKingRewards } from '../claiming/king';
|
|
38
48
|
|
|
39
|
-
export async function getPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], summerFiAddresses: EthAddress[]): Promise<
|
|
49
|
+
export async function getPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], summerFiAddresses: EthAddress[]): Promise<{
|
|
50
|
+
positions: PortfolioPositionsData;
|
|
51
|
+
stakingPositions: any;
|
|
52
|
+
rewardsData: any;
|
|
53
|
+
markets: any;
|
|
54
|
+
}> {
|
|
40
55
|
const isMainnet = network === NetworkNumber.Eth;
|
|
41
56
|
const isFluidSupported = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Base].includes(network);
|
|
42
57
|
|
|
@@ -76,8 +91,23 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
76
91
|
const crvUsdMarketsData: Record<string, CrvUSDGlobalMarketData> = {};
|
|
77
92
|
const llamaLendMarketsData: Record<string, LlamaLendGlobalMarketData> = {};
|
|
78
93
|
|
|
94
|
+
const markets = {
|
|
95
|
+
morphoMarketsData,
|
|
96
|
+
compoundV3MarketsData,
|
|
97
|
+
sparkMarketsData,
|
|
98
|
+
eulerV2MarketsData,
|
|
99
|
+
aaveV3MarketsData,
|
|
100
|
+
aaveV2MarketsData,
|
|
101
|
+
compoundV2MarketsData,
|
|
102
|
+
crvUsdMarketsData,
|
|
103
|
+
llamaLendMarketsData,
|
|
104
|
+
};
|
|
105
|
+
|
|
79
106
|
const positions: PortfolioPositionsData = {};
|
|
107
|
+
const stakingPositions: any = {};
|
|
108
|
+
const rewardsData: any = {};
|
|
80
109
|
const allAddresses = [...addresses, ...summerFiAddresses];
|
|
110
|
+
|
|
81
111
|
for (const address of allAddresses) {
|
|
82
112
|
positions[address.toLowerCase() as EthAddress] = {
|
|
83
113
|
aaveV3: {},
|
|
@@ -98,7 +128,35 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
98
128
|
};
|
|
99
129
|
}
|
|
100
130
|
|
|
131
|
+
// TODO: check default values, probably needed when fetching portfolio on unsupported networks
|
|
132
|
+
for (const address of addresses) {
|
|
133
|
+
stakingPositions[address.toLowerCase() as EthAddress] = {
|
|
134
|
+
aaveV3: {},
|
|
135
|
+
morphoBlue: {},
|
|
136
|
+
compoundV3: {},
|
|
137
|
+
spark: {},
|
|
138
|
+
aaveV2: {},
|
|
139
|
+
compoundV2: {},
|
|
140
|
+
liquity: {},
|
|
141
|
+
fluid: {
|
|
142
|
+
error: '',
|
|
143
|
+
data: {},
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
rewardsData[address.toLowerCase() as EthAddress] = {
|
|
148
|
+
aaveV3merit: {},
|
|
149
|
+
aaveV3: {},
|
|
150
|
+
compV3: {},
|
|
151
|
+
spark: {},
|
|
152
|
+
spk: {},
|
|
153
|
+
king: {},
|
|
154
|
+
morpho: {},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
101
158
|
await Promise.allSettled([
|
|
159
|
+
// === MARKET DATA (needs to be fetched first) ===
|
|
102
160
|
...morphoMarkets.map(async (market) => {
|
|
103
161
|
const marketData = await _getMorphoBlueMarketData(client, network, market);
|
|
104
162
|
morphoMarketsData[market.value] = marketData;
|
|
@@ -119,11 +177,6 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
119
177
|
const marketData = await _getAaveV3MarketData(client, network, market);
|
|
120
178
|
aaveV3MarketsData[market.value] = marketData;
|
|
121
179
|
}),
|
|
122
|
-
...addresses.map(async (address) => {
|
|
123
|
-
if (!isMainnet) return; // Maker CDPs are only available on mainnet
|
|
124
|
-
const makerCdp = await _getUserCdps(client, network, address);
|
|
125
|
-
makerCdps[address.toLowerCase() as EthAddress] = makerCdp;
|
|
126
|
-
}),
|
|
127
180
|
...aaveV2Markets.map(async (market) => {
|
|
128
181
|
const marketData = await _getAaveV2MarketsData(client, network, market);
|
|
129
182
|
aaveV2MarketsData[market.value] = marketData;
|
|
@@ -140,6 +193,13 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
140
193
|
const marketData = await _getLlamaLendGlobalData(client, network, market);
|
|
141
194
|
llamaLendMarketsData[market.value] = marketData;
|
|
142
195
|
}),
|
|
196
|
+
|
|
197
|
+
// === INDEPENDENT USER DATA (doesn't depend on market data) ===
|
|
198
|
+
...addresses.map(async (address) => {
|
|
199
|
+
if (!isMainnet) return; // Maker CDPs are only available on mainnet
|
|
200
|
+
const makerCdp = await _getUserCdps(client, network, address);
|
|
201
|
+
makerCdps[address.toLowerCase() as EthAddress] = makerCdp;
|
|
202
|
+
}),
|
|
143
203
|
...addresses.map(async (address) => {
|
|
144
204
|
try {
|
|
145
205
|
if (!isFluidSupported) return; // Fluid is not available on Optimism
|
|
@@ -157,6 +217,166 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
157
217
|
};
|
|
158
218
|
}
|
|
159
219
|
}),
|
|
220
|
+
|
|
221
|
+
// === STAKING DATA (independent of market data) ===
|
|
222
|
+
...addresses.map(async (address) => {
|
|
223
|
+
try {
|
|
224
|
+
if (!isFluidSupported) return;
|
|
225
|
+
const fluidLendData = await _getAllUserEarnPositionsWithFTokens(client, network, address);
|
|
226
|
+
stakingPositions[address.toLowerCase()].fluid = fluidLendData;
|
|
227
|
+
} catch (error) {
|
|
228
|
+
console.error(`Error fetching Fluid lend data for address ${address}:`, error);
|
|
229
|
+
stakingPositions[address.toLowerCase()].fluid = { error: `Error fetching Fluid lend data for address ${address}`, data: null };
|
|
230
|
+
}
|
|
231
|
+
}),
|
|
232
|
+
...addresses.map(async (address) => {
|
|
233
|
+
try {
|
|
234
|
+
if (!isMainnet) return;
|
|
235
|
+
const liquityStakingData = await getLiquityStakingData(client, network, address);
|
|
236
|
+
stakingPositions[address.toLowerCase()].liquity = liquityStakingData;
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.error(`Error fetching Liquity staking data for address ${address}:`, error);
|
|
239
|
+
stakingPositions[address.toLowerCase()].liquity = { error: `Error fetching Liquity staking data for address ${address}`, data: null };
|
|
240
|
+
}
|
|
241
|
+
}),
|
|
242
|
+
...addresses.map(async (address) => {
|
|
243
|
+
try {
|
|
244
|
+
if (!isMainnet) return;
|
|
245
|
+
stakingPositions[address.toLowerCase()].aaveV3 = await getStakeAaveData(client, network, address);
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error(`Error fetching Aave V3 staking data for address ${address}:`, error);
|
|
248
|
+
stakingPositions[address.toLowerCase()].aaveV3 = { error: `Error fetching Aave V3 staking data for address ${address}`, data: null };
|
|
249
|
+
}
|
|
250
|
+
}),
|
|
251
|
+
...addresses.map(async (address) => {
|
|
252
|
+
try {
|
|
253
|
+
if (!isMainnet) return;
|
|
254
|
+
const umbrellaStakingData = await getUmbrellaData(client, network, address);
|
|
255
|
+
stakingPositions[address.toLowerCase()].umbrella = umbrellaStakingData;
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error(`Error fetching Umbrella staking data for address ${address}:`, error);
|
|
258
|
+
stakingPositions[address.toLowerCase()].umbrella = { error: `Error fetching Umbrella staking data for address ${address}`, data: null };
|
|
259
|
+
}
|
|
260
|
+
}),
|
|
261
|
+
|
|
262
|
+
// === REWARDS DATA (independent of market data) ===
|
|
263
|
+
// Batch King rewards
|
|
264
|
+
(async () => {
|
|
265
|
+
try {
|
|
266
|
+
if (!isMainnet) {
|
|
267
|
+
for (const address of addresses) {
|
|
268
|
+
rewardsData[address.toLowerCase()].king = { error: '', data: [] };
|
|
269
|
+
}
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const kingRewards = await getKingRewards(client, network, addresses);
|
|
273
|
+
for (const address of addresses) {
|
|
274
|
+
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
275
|
+
rewardsData[lowerAddress].king = {
|
|
276
|
+
error: '',
|
|
277
|
+
data: kingRewards[lowerAddress] || [],
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
} catch (error) {
|
|
281
|
+
console.error('Error fetching King rewards data in batch:', error);
|
|
282
|
+
for (const address of addresses) {
|
|
283
|
+
rewardsData[address.toLowerCase() as EthAddress].king = {
|
|
284
|
+
error: 'Error fetching King rewards data in batch',
|
|
285
|
+
data: null,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
})(),
|
|
290
|
+
...sparkMarkets.map((market) => addresses.map(async address => {
|
|
291
|
+
try {
|
|
292
|
+
if (!isMainnet) {
|
|
293
|
+
rewardsData[address.toLowerCase()].spark[market.value] = { error: '', data: [] };
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const sparkData = await fetchSparkRewards(client, network, address, market.providerAddress);
|
|
297
|
+
rewardsData[address.toLowerCase() as EthAddress].spark[market.value] = { error: '', data: sparkData };
|
|
298
|
+
} catch (error) {
|
|
299
|
+
console.error(`Error fetching Spark rewards data for address ${address}, market ${market.value}:`, error);
|
|
300
|
+
rewardsData[address.toLowerCase() as EthAddress].spark[market.value] = { error: `Error fetching Spark rewards data for address ${address}`, data: null };
|
|
301
|
+
}
|
|
302
|
+
})).flat(),
|
|
303
|
+
// CompV3 rewards
|
|
304
|
+
...compoundV3Markets.map(market => addresses.map(async (address) => {
|
|
305
|
+
try {
|
|
306
|
+
const compV3Rewards = await getCompoundV3Rewards(client, network, address, market.baseMarketAddress);
|
|
307
|
+
rewardsData[address.toLowerCase() as EthAddress].compV3[market.value] = { error: '', data: compV3Rewards };
|
|
308
|
+
} catch (error) {
|
|
309
|
+
console.error(`Error fetching Compound V3 rewards data for address ${address}:`, error);
|
|
310
|
+
rewardsData[address.toLowerCase() as EthAddress].compV3[market.value] = { error: `Error fetching Compound V3 rewards data for address ${address}`, data: null };
|
|
311
|
+
}
|
|
312
|
+
})).flat(),
|
|
313
|
+
...addresses.map(async (address) => {
|
|
314
|
+
try {
|
|
315
|
+
const aaveMeritData = await getMeritUnclaimedRewards(address, network);
|
|
316
|
+
rewardsData[address.toLowerCase() as EthAddress].aaveV3merit = { error: '', data: aaveMeritData };
|
|
317
|
+
} catch (error) {
|
|
318
|
+
console.error(`Error fetching Aave V3 Merit rewards data for address ${address}:`, error);
|
|
319
|
+
rewardsData[address.toLowerCase() as EthAddress].aaveV3merit = { error: `Error fetching Aave V3 Merit rewards data for address ${address}`, data: null };
|
|
320
|
+
}
|
|
321
|
+
}),
|
|
322
|
+
...aaveV3Markets.map(market => addresses.map(async (address) => {
|
|
323
|
+
try {
|
|
324
|
+
const aaveData = await getUnclaimedRewardsForAllMarkets(client, network, address, market.providerAddress);
|
|
325
|
+
rewardsData[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: '', data: aaveData };
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.error(`Error fetching Aave V3 Merit rewards data for address ${address}:`, error);
|
|
328
|
+
rewardsData[address.toLowerCase() as EthAddress].aaveV3 = { error: `Error fetching Aave V3 rewards data for address ${address}`, data: null };
|
|
329
|
+
}
|
|
330
|
+
})).flat(),
|
|
331
|
+
// Batch Morpho Blue rewards
|
|
332
|
+
(async () => {
|
|
333
|
+
try {
|
|
334
|
+
const morphoRewards = await fetchMorphoBlueRewards(client, network, addresses);
|
|
335
|
+
for (const address of addresses) {
|
|
336
|
+
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
337
|
+
rewardsData[lowerAddress].morpho = {
|
|
338
|
+
error: '',
|
|
339
|
+
data: morphoRewards[lowerAddress] || [],
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
} catch (error) {
|
|
343
|
+
console.error('Error fetching Morpho Blue rewards data in batch:', error);
|
|
344
|
+
for (const address of addresses) {
|
|
345
|
+
rewardsData[address.toLowerCase() as EthAddress].morpho = {
|
|
346
|
+
error: 'Error fetching Morpho Blue rewards data in batch',
|
|
347
|
+
data: null,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
})(),
|
|
352
|
+
// Batch Spark Airdrop rewards
|
|
353
|
+
(async () => {
|
|
354
|
+
try {
|
|
355
|
+
if (!isMainnet) {
|
|
356
|
+
for (const address of addresses) {
|
|
357
|
+
rewardsData[address.toLowerCase()].king = { error: '', data: [] };
|
|
358
|
+
}
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const sparkAirdropRewards = await fetchSparkAirdropRewards(client, network, addresses);
|
|
363
|
+
for (const address of addresses) {
|
|
364
|
+
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
365
|
+
rewardsData[lowerAddress].spk = {
|
|
366
|
+
error: '',
|
|
367
|
+
data: sparkAirdropRewards[lowerAddress] || [],
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.error('Error fetching Spark Airdrop rewards data in batch:', error);
|
|
372
|
+
for (const address of addresses) {
|
|
373
|
+
rewardsData[address.toLowerCase() as EthAddress].spk = {
|
|
374
|
+
error: 'Error fetching Spark Airdrop rewards data in batch',
|
|
375
|
+
data: null,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
})(),
|
|
160
380
|
]);
|
|
161
381
|
|
|
162
382
|
|
|
@@ -172,8 +392,31 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
172
392
|
})).flat(),
|
|
173
393
|
...morphoMarkets.map((market) => addresses.map(async (address) => {
|
|
174
394
|
try {
|
|
175
|
-
const
|
|
176
|
-
|
|
395
|
+
const [accDataPromise, earnDataPromise] = await Promise.allSettled([
|
|
396
|
+
_getMorphoBlueAccountData(client, network, address, market, morphoMarketsData[market.value]),
|
|
397
|
+
getMorphoEarn(client, network, address, market, morphoMarketsData[market.value]),
|
|
398
|
+
]);
|
|
399
|
+
if (accDataPromise.status === 'rejected') {
|
|
400
|
+
console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, accDataPromise.reason);
|
|
401
|
+
positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
|
|
402
|
+
}
|
|
403
|
+
if (earnDataPromise.status === 'rejected') {
|
|
404
|
+
console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, earnDataPromise.reason);
|
|
405
|
+
positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
|
|
406
|
+
}
|
|
407
|
+
if (accDataPromise.status !== 'rejected') {
|
|
408
|
+
const accData = accDataPromise.value;
|
|
409
|
+
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: '', data: accData };
|
|
410
|
+
}
|
|
411
|
+
if (earnDataPromise.status !== 'rejected') {
|
|
412
|
+
const earnData = earnDataPromise.value;
|
|
413
|
+
if (earnData && new Dec(earnData.amount).gt(0)) {
|
|
414
|
+
stakingPositions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = {
|
|
415
|
+
error: '',
|
|
416
|
+
data: earnData,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
}
|
|
177
420
|
} catch (error) {
|
|
178
421
|
console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, error);
|
|
179
422
|
positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
|
|
@@ -281,5 +524,10 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
281
524
|
})).flat(),
|
|
282
525
|
]);
|
|
283
526
|
|
|
284
|
-
return
|
|
527
|
+
return {
|
|
528
|
+
positions,
|
|
529
|
+
stakingPositions,
|
|
530
|
+
rewardsData,
|
|
531
|
+
markets,
|
|
532
|
+
};
|
|
285
533
|
}
|
package/src/services/utils.ts
CHANGED
|
@@ -61,4 +61,40 @@ export const isMaxuint = (amount: string) => compareAddresses(MAXUINT, amount);
|
|
|
61
61
|
|
|
62
62
|
export const isMainnetNetwork = (network: NetworkNumber) => network === NetworkNumber.Eth;
|
|
63
63
|
|
|
64
|
-
export const DEFAULT_TIMEOUT = 2000; // 2 seconds
|
|
64
|
+
export const DEFAULT_TIMEOUT = 2000; // 2 seconds
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Converts web3 hybrid response (that can be used as objects and arrays, so has duplicated values) to objects.
|
|
68
|
+
* @param value
|
|
69
|
+
*/
|
|
70
|
+
export const convertHybridArraysToObjects = (value: any): any => {
|
|
71
|
+
if (Array.isArray(value)) {
|
|
72
|
+
const keys = Object.keys(value);
|
|
73
|
+
const hasNamedKeys = keys.some((key) => Number.isNaN(Number(key)));
|
|
74
|
+
|
|
75
|
+
// If array has named keys, treat it as an object and keep only those
|
|
76
|
+
if (hasNamedKeys) {
|
|
77
|
+
const result: Record<string, any> = {};
|
|
78
|
+
for (const [key, val] of Object.entries(value)) {
|
|
79
|
+
if (Number.isNaN(Number(key))) {
|
|
80
|
+
result[key] = convertHybridArraysToObjects(val);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Else, treat as a regular array
|
|
87
|
+
return value.map(convertHybridArraysToObjects);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// If it's an object (but not an array), recurse into its values
|
|
91
|
+
if (value && typeof value === 'object') {
|
|
92
|
+
const result: Record<string, any> = {};
|
|
93
|
+
for (const [key, val] of Object.entries(value)) {
|
|
94
|
+
result[key] = convertHybridArraysToObjects(val);
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return value;
|
|
100
|
+
};
|
package/src/spark/index.ts
CHANGED
|
@@ -286,7 +286,8 @@ export const _getSparkAccountBalances = async (provider: Client, network: Networ
|
|
|
286
286
|
// @ts-ignore
|
|
287
287
|
const protocolDataProviderContract = createViemContractFromConfigFunc(market.protocolData, market.protocolDataAddress)(provider, network);
|
|
288
288
|
|
|
289
|
-
|
|
289
|
+
// @ts-ignore
|
|
290
|
+
const reserveTokens = await protocolDataProviderContract.read.getAllReservesTokens(setViemBlockNumber(block)) as { symbol: string, tokenAddress: EthAddress }[];
|
|
290
291
|
const symbols = reserveTokens.map(({ symbol }: { symbol: string }) => symbol);
|
|
291
292
|
const _addresses = reserveTokens.map(({ tokenAddress }: { tokenAddress: EthAddress }) => tokenAddress);
|
|
292
293
|
|
package/src/staking/staking.ts
CHANGED
|
@@ -54,7 +54,7 @@ const getApyFromDfsApi = async (asset: string) => {
|
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
export const STAKING_ASSETS = ['cbETH', 'wstETH', 'cbETH', 'rETH', 'sDAI', 'weETH', 'sUSDe', 'osETH', 'ezETH', 'ETHx', 'rsETH', 'pufETH', 'wrsETH', 'wsuperOETHb', 'sUSDS', 'tETH', 'PT sUSDe Sep', 'PT USDe Sep', 'PT sUSDe Nov'];
|
|
57
|
+
export const STAKING_ASSETS = ['cbETH', 'wstETH', 'cbETH', 'rETH', 'sDAI', 'weETH', 'sUSDe', 'osETH', 'ezETH', 'ETHx', 'rsETH', 'pufETH', 'wrsETH', 'wsuperOETHb', 'sUSDS', 'tETH', 'PT sUSDe Sep', 'PT USDe Sep', 'PT sUSDe Nov', 'PT USDe Nov'];
|
|
58
58
|
|
|
59
59
|
export const getStakingApy = memoize(async (asset: string) => {
|
|
60
60
|
try {
|
|
@@ -80,6 +80,7 @@ export const getStakingApy = memoize(async (asset: string) => {
|
|
|
80
80
|
if (asset === 'tETH') return await getApyFromDfsApi('tETH');
|
|
81
81
|
if (asset === 'USDe') return await getApyFromDfsApi('USDe');
|
|
82
82
|
if (asset === 'PT sUSDe Nov') return await getApyFromDfsApi('PT sUSDe Nov');
|
|
83
|
+
if (asset === 'PT USDe Nov') return await getApyFromDfsApi('PT USDe Nov');
|
|
83
84
|
} catch (e) {
|
|
84
85
|
console.error(`Failed to fetch APY for ${asset}`);
|
|
85
86
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { EthAddress } from './common';
|
|
2
|
+
|
|
3
|
+
export enum ClaimType {
|
|
4
|
+
/** Rewards from AAVE - various tokens & aTokens (supplied to protocol) */
|
|
5
|
+
AAVE_REWARDS = 'AAVE_REWARDS',
|
|
6
|
+
/** Merit rewards from AAVE (various tokens) */
|
|
7
|
+
AAVE_MERIT_REWARDS = 'AAVE_MERIT_REWARDS',
|
|
8
|
+
/** Rewards from Compound V3 (only in COMP) */
|
|
9
|
+
COMPOUND_V3_COMP = 'COMPOUND_V3_COMP',
|
|
10
|
+
/** Rewards from Spark (wstETH only for now) */
|
|
11
|
+
SPARK_REWARDS = 'SPARK_REWARDS',
|
|
12
|
+
/** Rewards from Morpho */
|
|
13
|
+
MORPHO = 'MORPHO',
|
|
14
|
+
/** Rewards from King (prev LTR^2 - received for weETH holding) */
|
|
15
|
+
KING_REWARDS = 'KING_REWARDS',
|
|
16
|
+
/** Spark Airdrop */
|
|
17
|
+
SPARK_AIRDROP = 'SPARK_AIRDROP',
|
|
18
|
+
/** Spark Airdrop */
|
|
19
|
+
SPARK_WST_ETH_REWARDS = 'SPARK_WST_ETH_REWARDS', // TODO: This will be removed once we fully refactor spark rewards
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type _ClaimableTokenPartial = {
|
|
23
|
+
symbol: string;
|
|
24
|
+
underlyingSymbol: string;
|
|
25
|
+
tokenAddress: EthAddress;
|
|
26
|
+
amount: string;
|
|
27
|
+
walletAddress: EthAddress;
|
|
28
|
+
label: string;
|
|
29
|
+
};
|
|
30
|
+
export type AaveRewardsClaimableToken = _ClaimableTokenPartial & {
|
|
31
|
+
claimType: ClaimType.AAVE_REWARDS;
|
|
32
|
+
additionalClaimFields: {
|
|
33
|
+
marketAddress: EthAddress;
|
|
34
|
+
aTokenAddresses: string[];
|
|
35
|
+
isAaveToken: boolean;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
export type AaveMeritRewardsClaimableToken = _ClaimableTokenPartial & {
|
|
39
|
+
claimType: ClaimType.AAVE_MERIT_REWARDS;
|
|
40
|
+
additionalClaimFields: {
|
|
41
|
+
accumulated: string;
|
|
42
|
+
proof: string[];
|
|
43
|
+
decimals: string;
|
|
44
|
+
unclaimed: string;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type SparkRewardsClaimableToken = _ClaimableTokenPartial & {
|
|
49
|
+
claimType: ClaimType.SPARK_REWARDS;
|
|
50
|
+
additionalClaimFields: {
|
|
51
|
+
sparkAssetAddresses: string[];
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type KingRewardsClaimableToken = _ClaimableTokenPartial & {
|
|
56
|
+
claimType: ClaimType.KING_REWARDS;
|
|
57
|
+
additionalClaimFields: {
|
|
58
|
+
allRewardsAmount: string;
|
|
59
|
+
merkleRoot: string;
|
|
60
|
+
merkleProofs: string[];
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type MorphoClaimableToken = _ClaimableTokenPartial & {
|
|
65
|
+
claimType: ClaimType.MORPHO,
|
|
66
|
+
additionalClaimFields: {
|
|
67
|
+
originalAmount: string,
|
|
68
|
+
merkleProofs: string[];
|
|
69
|
+
distributor: EthAddress;
|
|
70
|
+
isLegacy: boolean;
|
|
71
|
+
txData: string;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
export type CompoundV3CompClaimableToken = _ClaimableTokenPartial & {
|
|
77
|
+
claimType: ClaimType.COMPOUND_V3_COMP,
|
|
78
|
+
additionalClaimFields: {
|
|
79
|
+
marketAddress: EthAddress;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export enum SparkAirdropType {
|
|
84
|
+
SPARK_IGNITION = 'spark-ignition',
|
|
85
|
+
PRE_FARMING_AND_SOCIAL = 'pre-farming-and-social',
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export type SparkAirdropClaimableToken = _ClaimableTokenPartial & {
|
|
89
|
+
claimType: ClaimType.SPARK_AIRDROP,
|
|
90
|
+
additionalClaimFields: {
|
|
91
|
+
merkleRoot: string;
|
|
92
|
+
merkleProofs: string[];
|
|
93
|
+
epoch: number;
|
|
94
|
+
rewardType: SparkAirdropType;
|
|
95
|
+
allRewardsAmount: string;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export type SparkWstEthRewardsClaimableToken = _ClaimableTokenPartial & { claimType: ClaimType.SPARK_WST_ETH_REWARDS };
|
|
100
|
+
|
|
101
|
+
export type ClaimableToken =
|
|
102
|
+
AaveRewardsClaimableToken
|
|
103
|
+
| AaveMeritRewardsClaimableToken
|
|
104
|
+
| CompoundV3CompClaimableToken
|
|
105
|
+
| MorphoClaimableToken
|
|
106
|
+
| SparkRewardsClaimableToken
|
|
107
|
+
| KingRewardsClaimableToken
|
|
108
|
+
| SparkAirdropClaimableToken
|
|
109
|
+
| SparkWstEthRewardsClaimableToken;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Client } from 'viem';
|
|
2
|
+
import { AaveUmbrellaViewViem, AaveV3ViewContractViem } from '../contracts';
|
|
3
|
+
import { EthAddress, NetworkNumber } from '../types/common';
|
|
4
|
+
import { compareAddresses, convertHybridArraysToObjects, getEthAmountForDecimals } from '../services/utils';
|
|
5
|
+
import { findMatching, tokenEntries } from './umbrellaUtils';
|
|
6
|
+
|
|
7
|
+
const umbrella = '0xD400fc38ED4732893174325693a63C30ee3881a8';
|
|
8
|
+
const aaveOracle = '0x54586bE62E3c3580375aE3723C145253060Ca0C2';
|
|
9
|
+
|
|
10
|
+
export const getUmbrellaData = async (provider: Client, network: NetworkNumber, address: EthAddress) => {
|
|
11
|
+
const umbrellaView = AaveUmbrellaViewViem(provider, network);
|
|
12
|
+
const aaveV3View = AaveV3ViewContractViem(provider, network);
|
|
13
|
+
const [tokensAggregatedData, additionalUmbrellaStakingData, userAggregatedData] = await Promise.all([
|
|
14
|
+
umbrellaView.read.getTokensAggregatedData([umbrella, aaveOracle]),
|
|
15
|
+
aaveV3View.read.getAdditionalUmbrellaStakingData([umbrella, address]),
|
|
16
|
+
umbrellaView.read.getUserAggregatedData([umbrella, address]),
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
return Object.fromEntries(
|
|
20
|
+
tokenEntries.map(([symbol, tokenAddr]) => {
|
|
21
|
+
// @ts-ignore
|
|
22
|
+
const fromTokens = convertHybridArraysToObjects(findMatching(tokensAggregatedData, tokenAddr));
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
const fromUser = convertHybridArraysToObjects(findMatching(userAggregatedData, tokenAddr));
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
const fromAdditionalData = convertHybridArraysToObjects(findMatching(additionalUmbrellaStakingData, tokenAddr));
|
|
27
|
+
const data = {
|
|
28
|
+
...fromTokens,
|
|
29
|
+
...fromUser,
|
|
30
|
+
...fromAdditionalData,
|
|
31
|
+
account: {
|
|
32
|
+
stakeUserBalance: fromUser?.stakeUserBalance || '0',
|
|
33
|
+
rewardsTokenUserData: convertHybridArraysToObjects(fromUser?.rewardsTokenUserData) || [],
|
|
34
|
+
userCooldownAmount: fromAdditionalData?.userCooldownAmount || '0',
|
|
35
|
+
userEndOfCooldown: fromAdditionalData?.userEndOfCooldown || '0',
|
|
36
|
+
userWithdrawalWindow: fromAdditionalData?.userWithdrawalWindow || '0',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const stakeTokenData = data.stakeTokenData;
|
|
41
|
+
|
|
42
|
+
return [symbol, {
|
|
43
|
+
...data,
|
|
44
|
+
stakeTokenData: { ...stakeTokenData, price: getEthAmountForDecimals(stakeTokenData.price, 8) },
|
|
45
|
+
stkTokenToWaTokenRate: getEthAmountForDecimals(data.stkTokenToWaTokenRate, stakeTokenData.decimals),
|
|
46
|
+
waTokenToATokenRate: getEthAmountForDecimals(data.waTokenToATokenRate, stakeTokenData.decimals),
|
|
47
|
+
totalShares: getEthAmountForDecimals(data.totalShares, stakeTokenData.decimals),
|
|
48
|
+
totalAssets: getEthAmountForDecimals(data.totalAssets, stakeTokenData.decimals),
|
|
49
|
+
targetLiquidity: getEthAmountForDecimals(data.targetLiquidity, stakeTokenData.decimals),
|
|
50
|
+
rewardsTokenData: data.rewardsTokenData.map((tokenData: { rewardTokenData: { price: string | number; }; }) => ({ ...tokenData, rewardTokenData: { ...tokenData.rewardTokenData, price: getEthAmountForDecimals(tokenData.rewardTokenData.price, 8) } })),
|
|
51
|
+
rewardsEmissionRates: data.rewardsEmissionRates.map((rate: string, i: number) => {
|
|
52
|
+
const tokenData = data.rewardsTokenData[i].rewardTokenData;
|
|
53
|
+
return getEthAmountForDecimals(rate, tokenData.decimals);
|
|
54
|
+
}),
|
|
55
|
+
account: {
|
|
56
|
+
...data.account,
|
|
57
|
+
stakeUserBalance: getEthAmountForDecimals(data.account.stakeUserBalance, stakeTokenData.decimals),
|
|
58
|
+
userCooldownAmount: getEthAmountForDecimals(data.account.userCooldownAmount, stakeTokenData.decimals),
|
|
59
|
+
rewardsTokenUserData: data.account.rewardsTokenUserData.map((reward: any) => {
|
|
60
|
+
const tokenData = data.rewardsTokenData.find((tknData: { rewardTokenData: { token: string | undefined; }; }) => compareAddresses(tknData.rewardTokenData.token, reward.reward));
|
|
61
|
+
return ({
|
|
62
|
+
...reward,
|
|
63
|
+
currentReward: getEthAmountForDecimals(reward.currentReward, tokenData.rewardTokenData.decimals),
|
|
64
|
+
});
|
|
65
|
+
}),
|
|
66
|
+
},
|
|
67
|
+
}];
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export enum UmbrellaStaking {
|
|
2
|
+
UmbrellaGHO = 'UmbrellaGHO',
|
|
3
|
+
UmbrellaUSDC = 'UmbrellaUSDC',
|
|
4
|
+
UmbrellaUSDT = 'UmbrellaUSDT',
|
|
5
|
+
UmbrellaETH = 'UmbrellaETH',
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const tokenMapping = {
|
|
9
|
+
[UmbrellaStaking.UmbrellaUSDC]: '0x6bf183243FdD1e306ad2C4450BC7dcf6f0bf8Aa6',
|
|
10
|
+
[UmbrellaStaking.UmbrellaUSDT]: '0xA484Ab92fe32B143AEE7019fC1502b1dAA522D31',
|
|
11
|
+
[UmbrellaStaking.UmbrellaETH]: '0xaAFD07D53A7365D3e9fb6F3a3B09EC19676B73Ce',
|
|
12
|
+
[UmbrellaStaking.UmbrellaGHO]: '0x4f827A63755855cDf3e8f3bcD20265C833f15033',
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
// ----- chat gpt code for result mapping ---------
|
|
16
|
+
export const normalize = (addr?: string) => addr?.toLowerCase?.() ?? '';
|
|
17
|
+
|
|
18
|
+
export const tokenEntries = Object.entries(tokenMapping).map(([symbol, address]) => [
|
|
19
|
+
symbol,
|
|
20
|
+
normalize(address),
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
// 👇 Define how to extract the token address from each source type
|
|
24
|
+
export const extractAddress = (item?: { stakeToken?: string, stkToken?: string, stakeTokenData?: { token?: string } }): string => normalize(item?.stakeToken
|
|
25
|
+
|| item?.stkToken
|
|
26
|
+
|| item?.stakeTokenData?.token,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// 👇 Utility to find the matching object in a dataset
|
|
30
|
+
export const findMatching = (dataset: any[], targetAddr: string) => dataset.find((item) => extractAddress(item) === targetAddr);
|