@defisaver/positions-sdk 2.1.161-portfolio-api-dev → 2.1.161-portfolio-api-2-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/aaveV3/index.d.ts +2 -12
- package/cjs/aaveV3/sgho.d.ts +2 -16
- 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/fluid/index.d.ts +5 -49
- package/cjs/liquity/index.d.ts +2 -12
- package/cjs/liquityV2/index.d.ts +3 -23
- package/cjs/portfolio/index.d.ts +5 -5
- package/cjs/portfolio/index.js +46 -33
- package/cjs/types/aave.d.ts +83 -0
- package/cjs/types/fluid.d.ts +13 -0
- package/cjs/types/index.d.ts +1 -0
- package/cjs/types/index.js +1 -0
- package/cjs/types/liquity.d.ts +12 -0
- package/cjs/types/liquityV2.d.ts +24 -0
- package/cjs/types/portfolio.d.ts +88 -10
- package/cjs/umbrella/index.d.ts +2 -3
- package/esm/aaveV3/index.d.ts +2 -12
- package/esm/aaveV3/sgho.d.ts +2 -16
- 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/fluid/index.d.ts +5 -49
- package/esm/liquity/index.d.ts +2 -12
- package/esm/liquityV2/index.d.ts +3 -23
- package/esm/portfolio/index.d.ts +5 -5
- package/esm/portfolio/index.js +46 -33
- package/esm/types/aave.d.ts +83 -0
- package/esm/types/fluid.d.ts +13 -0
- package/esm/types/index.d.ts +1 -0
- package/esm/types/index.js +1 -0
- package/esm/types/liquity.d.ts +12 -0
- package/esm/types/liquityV2.d.ts +24 -0
- package/esm/types/portfolio.d.ts +88 -10
- package/esm/umbrella/index.d.ts +2 -3
- package/package.json +1 -1
- package/src/aaveV3/index.ts +2 -1
- package/src/aaveV3/sgho.ts +2 -17
- 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/fluid/index.ts +4 -3
- package/src/liquity/index.ts +2 -2
- package/src/liquityV2/index.ts +3 -3
- package/src/portfolio/index.ts +72 -55
- package/src/types/aave.ts +86 -0
- package/src/types/fluid.ts +14 -0
- package/src/types/index.ts +2 -1
- package/src/types/liquity.ts +13 -1
- package/src/types/liquityV2.ts +26 -1
- package/src/types/portfolio.ts +111 -11
- package/src/umbrella/index.ts +2 -1
package/src/portfolio/index.ts
CHANGED
|
@@ -27,7 +27,11 @@ import {
|
|
|
27
27
|
LlamaLendGlobalMarketData,
|
|
28
28
|
MorphoBlueMarketInfo,
|
|
29
29
|
MorphoMidnightMarketInfo,
|
|
30
|
+
PortfolioDataOptions,
|
|
31
|
+
PortfolioMarketsData,
|
|
30
32
|
PortfolioPositionsData,
|
|
33
|
+
PortfolioRewardsData,
|
|
34
|
+
PortfolioStakingPositionsData,
|
|
31
35
|
SparkMarketsData,
|
|
32
36
|
} from '../types';
|
|
33
37
|
import { _getCompoundV3AccountData, _getCompoundV3MarketsData } from '../compoundV3';
|
|
@@ -51,13 +55,18 @@ import { fetchEthenaAirdropRewards } from '../claiming/ethena';
|
|
|
51
55
|
import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
|
|
52
56
|
import { getUniswapRewards } from '../claiming/uniswap';
|
|
53
57
|
|
|
54
|
-
export async function getPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], isSim = false): Promise<{
|
|
58
|
+
export async function getPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], isSim = false, options: PortfolioDataOptions = {}): Promise<{
|
|
55
59
|
positions: PortfolioPositionsData;
|
|
56
|
-
stakingPositions:
|
|
57
|
-
rewardsData:
|
|
58
|
-
markets:
|
|
60
|
+
stakingPositions: PortfolioStakingPositionsData;
|
|
61
|
+
rewardsData: PortfolioRewardsData;
|
|
62
|
+
markets: PortfolioMarketsData;
|
|
59
63
|
}> {
|
|
60
64
|
const isMainnet = network === NetworkNumber.Eth;
|
|
65
|
+
const includeStaking = options.staking !== false;
|
|
66
|
+
const includeRewards = options.rewards !== false;
|
|
67
|
+
// The addresses staking and rewards are read for: none when left out, so their fetches below make no calls.
|
|
68
|
+
const stakingAddresses = includeStaking ? addresses : [];
|
|
69
|
+
const rewardsAddresses = includeRewards ? addresses : [];
|
|
61
70
|
const isFluidSupported = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Base, NetworkNumber.Plasma].includes(network);
|
|
62
71
|
|
|
63
72
|
const morphoMarkets = Object.values(MorphoBlueMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
@@ -96,7 +105,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
96
105
|
const liquityV2MarketsData: Record<string, LiquityV2MarketData> = {};
|
|
97
106
|
const aaveV4SpokesData: Record<string, AaveV4SpokeData> = {};
|
|
98
107
|
|
|
99
|
-
const markets = {
|
|
108
|
+
const markets: PortfolioMarketsData = {
|
|
100
109
|
morphoMarketsData,
|
|
101
110
|
morphoMidnightMarketsData,
|
|
102
111
|
compoundV3MarketsData,
|
|
@@ -111,8 +120,8 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
111
120
|
};
|
|
112
121
|
|
|
113
122
|
const positions: PortfolioPositionsData = {};
|
|
114
|
-
const stakingPositions:
|
|
115
|
-
const rewardsData:
|
|
123
|
+
const stakingPositions: PortfolioStakingPositionsData = {};
|
|
124
|
+
const rewardsData: PortfolioRewardsData = {};
|
|
116
125
|
const allAddresses = [...addresses];
|
|
117
126
|
|
|
118
127
|
for (const address of allAddresses) {
|
|
@@ -137,7 +146,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
137
146
|
}
|
|
138
147
|
|
|
139
148
|
// TODO: check default values, probably needed when fetching portfolio on unsupported networks
|
|
140
|
-
for (const address of
|
|
149
|
+
for (const address of stakingAddresses) {
|
|
141
150
|
stakingPositions[address.toLowerCase() as EthAddress] = {
|
|
142
151
|
aaveV3: {},
|
|
143
152
|
morphoBlue: {},
|
|
@@ -151,15 +160,21 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
151
160
|
error: '',
|
|
152
161
|
data: {},
|
|
153
162
|
},
|
|
163
|
+
// Set by the sBOLD/yBOLD fetch below, whether it succeeds or fails.
|
|
164
|
+
liquityV2SBoldYBold: { error: '', data: null },
|
|
154
165
|
};
|
|
166
|
+
}
|
|
155
167
|
|
|
168
|
+
for (const address of rewardsAddresses) {
|
|
169
|
+
// merkl, spk, king and uniswap are set by their fetches below, whether they succeed or fail.
|
|
156
170
|
rewardsData[address.toLowerCase() as EthAddress] = {
|
|
157
|
-
merkl: {},
|
|
171
|
+
merkl: { error: '', data: null },
|
|
158
172
|
aaveV3: {},
|
|
159
173
|
spark: {},
|
|
160
|
-
spk: {},
|
|
161
|
-
king: {},
|
|
174
|
+
spk: { error: '', data: null },
|
|
175
|
+
king: { error: '', data: null },
|
|
162
176
|
ethena: {},
|
|
177
|
+
uniswap: { error: '', data: null },
|
|
163
178
|
};
|
|
164
179
|
}
|
|
165
180
|
|
|
@@ -235,54 +250,54 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
235
250
|
}),
|
|
236
251
|
|
|
237
252
|
// === STAKING DATA (independent of market data) ===
|
|
238
|
-
...
|
|
253
|
+
...stakingAddresses.map(async (address) => {
|
|
239
254
|
try {
|
|
240
255
|
if (!isFluidSupported) return;
|
|
241
|
-
stakingPositions[address.toLowerCase()].fluid = await _getAllUserEarnPositionsWithFTokens(client, network, address);
|
|
256
|
+
stakingPositions[address.toLowerCase() as EthAddress].fluid = await _getAllUserEarnPositionsWithFTokens(client, network, address);
|
|
242
257
|
} catch (error) {
|
|
243
258
|
console.error(`Error fetching Fluid lend data for address ${address}:`, error);
|
|
244
|
-
stakingPositions[address.toLowerCase()].fluid = { error: `Error fetching Fluid lend data for address ${address}`, data: null };
|
|
259
|
+
stakingPositions[address.toLowerCase() as EthAddress].fluid = { error: `Error fetching Fluid lend data for address ${address}`, data: null };
|
|
245
260
|
}
|
|
246
261
|
}),
|
|
247
|
-
...
|
|
262
|
+
...stakingAddresses.map(async (address) => {
|
|
248
263
|
try {
|
|
249
264
|
if (!isMainnet) return;
|
|
250
|
-
stakingPositions[address.toLowerCase()].liquity = await getLiquityStakingData(client, network, address);
|
|
265
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquity = await getLiquityStakingData(client, network, address);
|
|
251
266
|
} catch (error) {
|
|
252
267
|
console.error(`Error fetching Liquity staking data for address ${address}:`, error);
|
|
253
|
-
stakingPositions[address.toLowerCase()].liquity = { error: `Error fetching Liquity staking data for address ${address}`, data: null };
|
|
268
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquity = { error: `Error fetching Liquity staking data for address ${address}`, data: null };
|
|
254
269
|
}
|
|
255
270
|
}),
|
|
256
|
-
...
|
|
271
|
+
...stakingAddresses.map(async (address) => {
|
|
257
272
|
try {
|
|
258
273
|
if (!isMainnet) return;
|
|
259
|
-
stakingPositions[address.toLowerCase()].aaveV3 = await getStakeAaveData(client, network, address);
|
|
274
|
+
stakingPositions[address.toLowerCase() as EthAddress].aaveV3 = await getStakeAaveData(client, network, address);
|
|
260
275
|
} catch (error) {
|
|
261
276
|
console.error(`Error fetching Aave V3 staking data for address ${address}:`, error);
|
|
262
|
-
stakingPositions[address.toLowerCase()].aaveV3 = { error: `Error fetching Aave V3 staking data for address ${address}`, data: null };
|
|
277
|
+
stakingPositions[address.toLowerCase() as EthAddress].aaveV3 = { error: `Error fetching Aave V3 staking data for address ${address}`, data: null };
|
|
263
278
|
}
|
|
264
279
|
}),
|
|
265
|
-
...
|
|
280
|
+
...stakingAddresses.map(async (address) => {
|
|
266
281
|
try {
|
|
267
282
|
if (!isMainnet) return;
|
|
268
|
-
stakingPositions[address.toLowerCase()].umbrella = await getUmbrellaData(client, network, address);
|
|
283
|
+
stakingPositions[address.toLowerCase() as EthAddress].umbrella = await getUmbrellaData(client, network, address);
|
|
269
284
|
} catch (error) {
|
|
270
285
|
console.error(`Error fetching Umbrella staking data for address ${address}:`, error);
|
|
271
|
-
stakingPositions[address.toLowerCase()].umbrella = { error: `Error fetching Umbrella staking data for address ${address}`, data: null };
|
|
286
|
+
stakingPositions[address.toLowerCase() as EthAddress].umbrella = { error: `Error fetching Umbrella staking data for address ${address}`, data: null };
|
|
272
287
|
}
|
|
273
288
|
}),
|
|
274
289
|
// Liquity V2 staking
|
|
275
|
-
...liquityV2MarketsStaking.map(market =>
|
|
290
|
+
...liquityV2MarketsStaking.map(market => stakingAddresses.map(async (address) => {
|
|
276
291
|
try {
|
|
277
292
|
if (!isMainnet) {
|
|
278
|
-
stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: '', data: null };
|
|
293
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: '', data: null };
|
|
279
294
|
return;
|
|
280
295
|
}
|
|
281
296
|
const liquityV2StakingData = await getLiquityV2Staking(client, network, market.value, address);
|
|
282
|
-
stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: '', data: liquityV2StakingData };
|
|
297
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: '', data: liquityV2StakingData };
|
|
283
298
|
} catch (error) {
|
|
284
299
|
console.error(`Error fetching Liquity V2 staking data for address ${address}, market ${market.value}:`, error);
|
|
285
|
-
stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: `Error fetching Liquity V2 staking data for address ${address}`, data: null };
|
|
300
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: `Error fetching Liquity V2 staking data for address ${address}`, data: null };
|
|
286
301
|
}
|
|
287
302
|
})).flat(),
|
|
288
303
|
|
|
@@ -291,13 +306,13 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
291
306
|
(async () => {
|
|
292
307
|
try {
|
|
293
308
|
if (!isMainnet) {
|
|
294
|
-
for (const address of
|
|
295
|
-
rewardsData[address.toLowerCase()].king = { error: '', data: [] };
|
|
309
|
+
for (const address of rewardsAddresses) {
|
|
310
|
+
rewardsData[address.toLowerCase() as EthAddress].king = { error: '', data: [] };
|
|
296
311
|
}
|
|
297
312
|
return;
|
|
298
313
|
}
|
|
299
|
-
const kingRewards = await getKingRewards(client, network,
|
|
300
|
-
for (const address of
|
|
314
|
+
const kingRewards = await getKingRewards(client, network, rewardsAddresses);
|
|
315
|
+
for (const address of rewardsAddresses) {
|
|
301
316
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
302
317
|
rewardsData[lowerAddress].king = {
|
|
303
318
|
error: '',
|
|
@@ -306,7 +321,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
306
321
|
}
|
|
307
322
|
} catch (error) {
|
|
308
323
|
console.error('Error fetching King rewards data in batch:', error);
|
|
309
|
-
for (const address of
|
|
324
|
+
for (const address of rewardsAddresses) {
|
|
310
325
|
rewardsData[address.toLowerCase() as EthAddress].king = {
|
|
311
326
|
error: 'Error fetching King rewards data in batch',
|
|
312
327
|
data: null,
|
|
@@ -318,13 +333,13 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
318
333
|
(async () => {
|
|
319
334
|
try {
|
|
320
335
|
if (!isMainnet) {
|
|
321
|
-
for (const address of
|
|
322
|
-
rewardsData[address.toLowerCase()].uniswap = { error: '', data: [] };
|
|
336
|
+
for (const address of rewardsAddresses) {
|
|
337
|
+
rewardsData[address.toLowerCase() as EthAddress].uniswap = { error: '', data: [] };
|
|
323
338
|
}
|
|
324
339
|
return;
|
|
325
340
|
}
|
|
326
|
-
const uniswapRewards = await getUniswapRewards(client, network,
|
|
327
|
-
for (const address of
|
|
341
|
+
const uniswapRewards = await getUniswapRewards(client, network, rewardsAddresses);
|
|
342
|
+
for (const address of rewardsAddresses) {
|
|
328
343
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
329
344
|
rewardsData[lowerAddress].uniswap = {
|
|
330
345
|
error: '',
|
|
@@ -333,7 +348,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
333
348
|
}
|
|
334
349
|
} catch (error) {
|
|
335
350
|
console.error('Error fetching Uniswap rewards data in batch:', error);
|
|
336
|
-
for (const address of
|
|
351
|
+
for (const address of rewardsAddresses) {
|
|
337
352
|
rewardsData[address.toLowerCase() as EthAddress].uniswap = {
|
|
338
353
|
error: 'Error fetching Uniswap rewards data in batch',
|
|
339
354
|
data: null,
|
|
@@ -341,10 +356,10 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
341
356
|
}
|
|
342
357
|
}
|
|
343
358
|
})(),
|
|
344
|
-
...sparkMarkets.map((market) =>
|
|
359
|
+
...sparkMarkets.map((market) => rewardsAddresses.map(async address => {
|
|
345
360
|
try {
|
|
346
361
|
if (!isMainnet) {
|
|
347
|
-
rewardsData[address.toLowerCase()].spark[market.value] = { error: '', data: [] };
|
|
362
|
+
rewardsData[address.toLowerCase() as EthAddress].spark[market.value] = { error: '', data: [] };
|
|
348
363
|
return;
|
|
349
364
|
}
|
|
350
365
|
const sparkData = await fetchSparkRewards(client, network, address, market.providerAddress);
|
|
@@ -354,7 +369,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
354
369
|
rewardsData[address.toLowerCase() as EthAddress].spark[market.value] = { error: `Error fetching Spark rewards data for address ${address}`, data: null };
|
|
355
370
|
}
|
|
356
371
|
})).flat(),
|
|
357
|
-
...
|
|
372
|
+
...rewardsAddresses.map(async (address) => {
|
|
358
373
|
try {
|
|
359
374
|
const merklData = await getMerklUnclaimedRewards(address, network);
|
|
360
375
|
rewardsData[address.toLowerCase() as EthAddress].merkl = { error: '', data: merklData };
|
|
@@ -363,27 +378,28 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
363
378
|
rewardsData[address.toLowerCase() as EthAddress].merkl = { error: `Error fetching Merkl rewards data for address ${address}`, data: null };
|
|
364
379
|
}
|
|
365
380
|
}),
|
|
366
|
-
...aaveV3Markets.map(market =>
|
|
381
|
+
...aaveV3Markets.map(market => rewardsAddresses.map(async (address) => {
|
|
367
382
|
try {
|
|
368
383
|
const aaveData = await getUnclaimedRewardsForAllMarkets(client, network, address, market.providerAddress);
|
|
369
384
|
rewardsData[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: '', data: aaveData };
|
|
370
385
|
} catch (error) {
|
|
371
386
|
console.error(`Error fetching Aave V3 Merit rewards data for address ${address}:`, error);
|
|
372
|
-
|
|
387
|
+
// This market only: the other markets keep their rewards.
|
|
388
|
+
rewardsData[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: `Error fetching Aave V3 rewards data for address ${address} on market ${market.value}`, data: null };
|
|
373
389
|
}
|
|
374
390
|
})).flat(),
|
|
375
391
|
// Batch Spark Airdrop rewards
|
|
376
392
|
(async () => {
|
|
377
393
|
try {
|
|
378
394
|
if (!isMainnet) {
|
|
379
|
-
for (const address of
|
|
380
|
-
rewardsData[address.toLowerCase()].spk = { error: '', data: [] };
|
|
395
|
+
for (const address of rewardsAddresses) {
|
|
396
|
+
rewardsData[address.toLowerCase() as EthAddress].spk = { error: '', data: [] };
|
|
381
397
|
}
|
|
382
398
|
return;
|
|
383
399
|
}
|
|
384
400
|
|
|
385
|
-
const sparkAirdropRewards = await fetchSparkAirdropRewards(client, network,
|
|
386
|
-
for (const address of
|
|
401
|
+
const sparkAirdropRewards = await fetchSparkAirdropRewards(client, network, rewardsAddresses);
|
|
402
|
+
for (const address of rewardsAddresses) {
|
|
387
403
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
388
404
|
rewardsData[lowerAddress].spk = {
|
|
389
405
|
error: '',
|
|
@@ -392,7 +408,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
392
408
|
}
|
|
393
409
|
} catch (error) {
|
|
394
410
|
console.error('Error fetching Spark Airdrop rewards data in batch:', error);
|
|
395
|
-
for (const address of
|
|
411
|
+
for (const address of rewardsAddresses) {
|
|
396
412
|
rewardsData[address.toLowerCase() as EthAddress].spk = {
|
|
397
413
|
error: 'Error fetching Spark Airdrop rewards data in batch',
|
|
398
414
|
data: null,
|
|
@@ -406,8 +422,8 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
406
422
|
return;
|
|
407
423
|
}
|
|
408
424
|
|
|
409
|
-
const ethenaAirdropRewards = await fetchEthenaAirdropRewards(
|
|
410
|
-
for (const address of
|
|
425
|
+
const ethenaAirdropRewards = await fetchEthenaAirdropRewards(rewardsAddresses);
|
|
426
|
+
for (const address of rewardsAddresses) {
|
|
411
427
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
412
428
|
rewardsData[lowerAddress].ethena = {
|
|
413
429
|
error: '',
|
|
@@ -416,7 +432,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
416
432
|
}
|
|
417
433
|
} catch (error) {
|
|
418
434
|
console.error('Error fetching Ethena Airdrop rewards data:', error);
|
|
419
|
-
for (const address of
|
|
435
|
+
for (const address of rewardsAddresses) {
|
|
420
436
|
rewardsData[address.toLowerCase() as EthAddress].ethena = {
|
|
421
437
|
error: 'Error fetching Ethena Airdrop rewards data in batch',
|
|
422
438
|
data: null,
|
|
@@ -449,15 +465,16 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
449
465
|
try {
|
|
450
466
|
const [accDataPromise, earnDataPromise] = await Promise.allSettled([
|
|
451
467
|
_getMorphoBlueAccountData(client, network, address, market, morphoMarketsData[market.value]),
|
|
452
|
-
getMorphoEarn(client, network, address, market, morphoMarketsData[market.value]),
|
|
468
|
+
includeStaking ? getMorphoEarn(client, network, address, market, morphoMarketsData[market.value]) : null,
|
|
453
469
|
]);
|
|
454
470
|
if (accDataPromise.status === 'rejected') {
|
|
455
471
|
console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, accDataPromise.reason);
|
|
456
472
|
positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
|
|
457
473
|
}
|
|
458
474
|
if (earnDataPromise.status === 'rejected') {
|
|
459
|
-
|
|
460
|
-
|
|
475
|
+
// An Earn failure belongs to the staking entry: the lending position is not the one that failed.
|
|
476
|
+
console.error(`Error fetching MorphoBlue earn data for address ${address} on market ${market.value}:`, earnDataPromise.reason);
|
|
477
|
+
stakingPositions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue earn data for address ${address} on market ${market.value}`, data: null };
|
|
461
478
|
}
|
|
462
479
|
if (accDataPromise.status !== 'rejected') {
|
|
463
480
|
const accData = accDataPromise.value;
|
|
@@ -570,13 +587,13 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
570
587
|
}
|
|
571
588
|
})).flat(),
|
|
572
589
|
// liquity sBold/yBold and staking options
|
|
573
|
-
...
|
|
590
|
+
...stakingAddresses.map(async (address) => {
|
|
574
591
|
try {
|
|
575
592
|
if (!isMainnet) {
|
|
576
593
|
stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data: null };
|
|
577
594
|
return;
|
|
578
595
|
}
|
|
579
|
-
const data = await getLiquitySAndYBold(client, network, stakingPositions[address.toLowerCase()].liquityV2, address);
|
|
596
|
+
const data = await getLiquitySAndYBold(client, network, stakingPositions[address.toLowerCase() as EthAddress].liquityV2, address);
|
|
580
597
|
stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data };
|
|
581
598
|
} catch (error) {
|
|
582
599
|
console.error(`Error fetching SBold/YBold data for address ${address}:`, error);
|
package/src/types/aave.ts
CHANGED
|
@@ -219,3 +219,89 @@ export interface AaveHelperCommon {
|
|
|
219
219
|
selectedMarket: Partial<AaveMarketInfo>,
|
|
220
220
|
network?: NetworkNumber,
|
|
221
221
|
}
|
|
222
|
+
|
|
223
|
+
export interface SghoUserData {
|
|
224
|
+
shares: string;
|
|
225
|
+
balance: string;
|
|
226
|
+
maxDeposit: string;
|
|
227
|
+
maxWithdraw: string;
|
|
228
|
+
underlyingBalance: string;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface SghoData {
|
|
232
|
+
totalAssets: string;
|
|
233
|
+
totalSupply: string;
|
|
234
|
+
supplyCap: string;
|
|
235
|
+
/** Target savings rate as an APY percent (e.g. "4.25" for 4.25%). */
|
|
236
|
+
targetRate: string;
|
|
237
|
+
paused: boolean;
|
|
238
|
+
user: SghoUserData;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** stkAAVE, stkGHO and sGHO of an address, from getStakeAaveData. */
|
|
242
|
+
export interface StakeAaveData {
|
|
243
|
+
activatedCooldown: string,
|
|
244
|
+
activatedCooldownAmount: string,
|
|
245
|
+
stkAaveRewardsBalance: string,
|
|
246
|
+
aaveRewardsBalance: string,
|
|
247
|
+
stkAaveBalance: string,
|
|
248
|
+
stkGhoBalance: string,
|
|
249
|
+
ghoMeritApy: string,
|
|
250
|
+
stkAaveApy: string,
|
|
251
|
+
sgho: SghoData,
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface UmbrellaTokenInfo {
|
|
255
|
+
token: EthAddress,
|
|
256
|
+
/** USD, converted from the oracle's 8 decimals. */
|
|
257
|
+
price: string,
|
|
258
|
+
name: string,
|
|
259
|
+
symbol: string,
|
|
260
|
+
decimals: number,
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export interface UmbrellaRewardTokenData {
|
|
264
|
+
rewardTokenData: UmbrellaTokenInfo,
|
|
265
|
+
maxEmissionPerSecond: bigint,
|
|
266
|
+
distributionEnd: bigint,
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface UmbrellaStakingAccountData {
|
|
270
|
+
stakeUserBalance: string,
|
|
271
|
+
rewardsTokenUserData: { reward: EthAddress, currentReward: string }[],
|
|
272
|
+
userCooldownAmount: string,
|
|
273
|
+
// Passed through unconverted: the view's bigint, or '0' when the view returns none.
|
|
274
|
+
userEndOfCooldown: bigint | string,
|
|
275
|
+
userWithdrawalWindow: bigint | string,
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* One Umbrella stake token of an address, from getUmbrellaData: the view contracts' structs merged, with the amounts
|
|
280
|
+
* the app reads converted to decimal strings. The bigint fields are the views' raw values, as the SDK passes them.
|
|
281
|
+
*/
|
|
282
|
+
export interface UmbrellaStakingTokenData {
|
|
283
|
+
stakeTokenData: UmbrellaTokenInfo,
|
|
284
|
+
totalAssets: string,
|
|
285
|
+
targetLiquidity: string,
|
|
286
|
+
isStakeConfigured: boolean,
|
|
287
|
+
rewardsTokenData: UmbrellaRewardTokenData[],
|
|
288
|
+
stakeToken: EthAddress,
|
|
289
|
+
stakeUserBalance: bigint,
|
|
290
|
+
rewardsTokenUserData: { reward: EthAddress, currentReward: bigint }[],
|
|
291
|
+
stkToken: EthAddress,
|
|
292
|
+
totalShares: string,
|
|
293
|
+
stkUnderlyingToken: EthAddress,
|
|
294
|
+
aToken: EthAddress,
|
|
295
|
+
cooldownPeriod: bigint,
|
|
296
|
+
unstakeWindow: bigint,
|
|
297
|
+
stkTokenToWaTokenRate: string,
|
|
298
|
+
waTokenToATokenRate: string,
|
|
299
|
+
rewardsEmissionRates: string[],
|
|
300
|
+
userCooldownAmount: bigint,
|
|
301
|
+
userEndOfCooldown: bigint,
|
|
302
|
+
userWithdrawalWindow: bigint,
|
|
303
|
+
account: UmbrellaStakingAccountData,
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** Keyed by Umbrella stake (UmbrellaGHO, UmbrellaUSDC, UmbrellaUSDT, UmbrellaETH). */
|
|
307
|
+
export type UmbrellaStakingData = Record<string, UmbrellaStakingTokenData>;
|
package/src/types/fluid.ts
CHANGED
|
@@ -487,3 +487,17 @@ export interface FluidFTokenDataStructOutput {
|
|
|
487
487
|
rewardsRate: bigint;
|
|
488
488
|
withdrawable: bigint;
|
|
489
489
|
}
|
|
490
|
+
|
|
491
|
+
/** One Fluid lending (fToken) deposit of an address, from getAllUserEarnPositionsWithFTokens and getFluidDepositData. */
|
|
492
|
+
export interface FluidEarnPositionData {
|
|
493
|
+
fTokenAddress: string | undefined,
|
|
494
|
+
fTokenSymbol: string,
|
|
495
|
+
decimals: string,
|
|
496
|
+
totalDeposited: string,
|
|
497
|
+
withdrawable: string,
|
|
498
|
+
apy: string,
|
|
499
|
+
depositRate: string,
|
|
500
|
+
withdrawRate: string,
|
|
501
|
+
deposited: string,
|
|
502
|
+
depositedShares: string,
|
|
503
|
+
}
|
package/src/types/index.ts
CHANGED
package/src/types/liquity.ts
CHANGED
|
@@ -29,4 +29,16 @@ export interface LiquityTroveInfo {
|
|
|
29
29
|
priceForRecovery: string,
|
|
30
30
|
debtInFront: string,
|
|
31
31
|
exposure: string,
|
|
32
|
-
}
|
|
32
|
+
}
|
|
33
|
+
/** LQTY staking and the LUSD stability pool deposit of an address, from getLiquityStakingData. */
|
|
34
|
+
export interface LiquityStakingData {
|
|
35
|
+
totalLUSDDeposited: string,
|
|
36
|
+
totalLQTYStaked: string,
|
|
37
|
+
stakedLQTY: string,
|
|
38
|
+
stakedLUSDBalance: string,
|
|
39
|
+
rewardETH: string,
|
|
40
|
+
rewardLUSD: string,
|
|
41
|
+
stabilityRewardETH: string,
|
|
42
|
+
stabilityRewardLQTY: string,
|
|
43
|
+
showStakingBalances: boolean,
|
|
44
|
+
}
|
package/src/types/liquityV2.ts
CHANGED
|
@@ -131,4 +131,29 @@ export interface LiquityV2TroveData {
|
|
|
131
131
|
debtInFront: string,
|
|
132
132
|
lastInterestRateAdjTime: string,
|
|
133
133
|
exposure: string,
|
|
134
|
-
}
|
|
134
|
+
}
|
|
135
|
+
/** The stability pool deposit of an address in one market, from getLiquityV2Staking. */
|
|
136
|
+
export interface LiquityV2StakingData {
|
|
137
|
+
totalBOLDDeposited: string,
|
|
138
|
+
stakedBOLDBalance: string,
|
|
139
|
+
stabilityRewardColl: string,
|
|
140
|
+
stabilityRewardYield: string,
|
|
141
|
+
showStakingBalances: boolean,
|
|
142
|
+
debtTokenBalance: string,
|
|
143
|
+
stabilityPoolApy: string,
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** sBOLD, yBOLD and stYBOLD of an address, from getLiquitySAndYBold. */
|
|
147
|
+
export interface LiquityV2SBoldYBoldData {
|
|
148
|
+
spApy: string,
|
|
149
|
+
yBoldApy: string,
|
|
150
|
+
totalBoldDepositedSBold: string,
|
|
151
|
+
boldRateSBold: string,
|
|
152
|
+
maxWithdrawSBold: string,
|
|
153
|
+
totalBoldDepositedYBold: string,
|
|
154
|
+
boldRateYBold: string,
|
|
155
|
+
maxWithdrawYBold: string,
|
|
156
|
+
sBoldBalance: string,
|
|
157
|
+
yBoldBalance: string,
|
|
158
|
+
stYBoldBalance: string,
|
|
159
|
+
}
|
package/src/types/portfolio.ts
CHANGED
|
@@ -1,22 +1,53 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
AaveV2MarketData, AaveV2PositionData, AaveV3MarketData, AaveV3PositionData, AaveVersions, StakeAaveData, UmbrellaStakingData,
|
|
3
|
+
} from './aave';
|
|
4
|
+
import { AaveV4AccountData, AaveV4SpokeData, AaveV4SpokesType } from './aaveV4';
|
|
5
|
+
import {
|
|
6
|
+
AaveRewardsClaimableToken,
|
|
7
|
+
EthenaAirdropClaimableToken,
|
|
8
|
+
KingRewardsClaimableToken,
|
|
9
|
+
MerklRewardsClaimableToken,
|
|
10
|
+
SparkAirdropClaimableToken,
|
|
11
|
+
SparkRewardsClaimableToken,
|
|
12
|
+
UniswapAirdropClaimableToken,
|
|
13
|
+
} from './claiming';
|
|
3
14
|
import { EthAddress } from './common';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
CompoundV2MarketsData, CompoundV2PositionData, CompoundV3MarketsData, CompoundV3PositionData, CompoundVersions,
|
|
17
|
+
} from './compound';
|
|
18
|
+
import { CrvUSDGlobalMarketData, CrvUSDUserData, CrvUSDVersions } from './curveUsd';
|
|
19
|
+
import { FluidEarnPositionData, FluidVaultData } from './fluid';
|
|
20
|
+
import { LiquityStakingData, LiquityTroveInfo } from './liquity';
|
|
21
|
+
import {
|
|
22
|
+
LiquityV2MarketData, LiquityV2SBoldYBoldData, LiquityV2StakingData, LiquityV2TroveData, LiquityV2Versions,
|
|
23
|
+
} from './liquityV2';
|
|
24
|
+
import { LlamaLendGlobalMarketData, LlamaLendUserData, LlamaLendVersionsType } from './llamaLend';
|
|
10
25
|
import { CdpData } from './maker';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
26
|
+
import {
|
|
27
|
+
MorphoBlueEarnData, MorphoBlueMarketInfo, MorphoBluePositionData, MorphoBlueVersions,
|
|
28
|
+
} from './morphoBlue';
|
|
29
|
+
import { MorphoMidnightMarketInfo, MorphoMidnightPositionData, MorphoMidnightVersions } from './morphoMidnight';
|
|
30
|
+
import { SparkMarketsData, SparkPositionData, SparkVersions } from './spark';
|
|
14
31
|
|
|
15
32
|
export interface PortfolioProtocolData<T> {
|
|
16
33
|
error: string,
|
|
17
34
|
data: T | null,
|
|
18
35
|
}
|
|
19
36
|
|
|
37
|
+
/** A failed fetch of a protocol that, when it succeeds, getPortfolioData returns unwrapped rather than as `{ error, data }`. */
|
|
38
|
+
export interface PortfolioProtocolError {
|
|
39
|
+
error: string,
|
|
40
|
+
data: null,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** What getPortfolioData reads besides positions and markets. Both default to true. */
|
|
44
|
+
export interface PortfolioDataOptions {
|
|
45
|
+
/** stakingPositions; with false it comes back as {} and no staking source is read. */
|
|
46
|
+
staking?: boolean,
|
|
47
|
+
/** rewardsData; with false it comes back as {} and no rewards source is read. */
|
|
48
|
+
rewards?: boolean,
|
|
49
|
+
}
|
|
50
|
+
|
|
20
51
|
export interface PortfolioPositionsDataForAddress {
|
|
21
52
|
aaveV3: {
|
|
22
53
|
[key in AaveVersions]?: PortfolioProtocolData<AaveV3PositionData>;
|
|
@@ -62,4 +93,73 @@ export interface PortfolioPositionsDataForAddress {
|
|
|
62
93
|
|
|
63
94
|
export interface PortfolioPositionsData {
|
|
64
95
|
[key: EthAddress]: PortfolioPositionsDataForAddress;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The staking positions getPortfolioData returns for an address. The shapes are uneven, and the DeFi Saver app
|
|
100
|
+
* parses them as they are: a protocol read once per address comes back unwrapped on success, as
|
|
101
|
+
* `{ error, data: null }` on failure, and as {} (or Fluid's `{ error: '', data: {} }`) on a network it is not read on.
|
|
102
|
+
*/
|
|
103
|
+
export interface PortfolioStakingPositionsDataForAddress {
|
|
104
|
+
/** stkAAVE, stkGHO and sGHO; mainnet only. */
|
|
105
|
+
aaveV3: StakeAaveData | PortfolioProtocolError | {};
|
|
106
|
+
/** Morpho Blue supply without a borrow (Earn), by market; only markets with a deposit or an error. */
|
|
107
|
+
morphoBlue: {
|
|
108
|
+
[key in MorphoBlueVersions]?: PortfolioProtocolData<MorphoBlueEarnData>;
|
|
109
|
+
};
|
|
110
|
+
// Never filled; kept so the shape does not change under the app.
|
|
111
|
+
compoundV3: Record<string, never>;
|
|
112
|
+
spark: Record<string, never>;
|
|
113
|
+
aaveV2: Record<string, never>;
|
|
114
|
+
compoundV2: Record<string, never>;
|
|
115
|
+
/** LQTY staking and the LUSD stability pool; mainnet only. */
|
|
116
|
+
liquity: LiquityStakingData | PortfolioProtocolError | {};
|
|
117
|
+
/** Stability pool deposits, by market; mainnet only, legacy markets left out. */
|
|
118
|
+
liquityV2: {
|
|
119
|
+
[key in LiquityV2Versions]?: PortfolioProtocolData<LiquityV2StakingData>;
|
|
120
|
+
};
|
|
121
|
+
/** Fluid lending (fToken) deposits; `{ error: '', data: {} }` on a network without Fluid. */
|
|
122
|
+
fluid: FluidEarnPositionData[] | PortfolioProtocolError | { error: string, data: Record<string, never> };
|
|
123
|
+
/** Umbrella stakes; absent off mainnet. */
|
|
124
|
+
umbrella?: UmbrellaStakingData | PortfolioProtocolError;
|
|
125
|
+
/** sBOLD, yBOLD and stYBOLD; data is null off mainnet. */
|
|
126
|
+
liquityV2SBoldYBold: PortfolioProtocolData<LiquityV2SBoldYBoldData>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// A Record rather than an interface: the app casts getPortfolioData's result to its own Record<string, …> type, and an
|
|
130
|
+
// interface with an address index signature is not comparable to one.
|
|
131
|
+
export type PortfolioStakingPositionsData = Record<EthAddress, PortfolioStakingPositionsDataForAddress>;
|
|
132
|
+
|
|
133
|
+
/** The claimable rewards getPortfolioData returns for an address. Off mainnet only Merkl and Aave V3 are read. */
|
|
134
|
+
export interface PortfolioRewardsDataForAddress {
|
|
135
|
+
merkl: PortfolioProtocolData<MerklRewardsClaimableToken[]>;
|
|
136
|
+
aaveV3: {
|
|
137
|
+
[key in AaveVersions]?: PortfolioProtocolData<AaveRewardsClaimableToken[]>;
|
|
138
|
+
};
|
|
139
|
+
spark: {
|
|
140
|
+
[key in SparkVersions]?: PortfolioProtocolData<SparkRewardsClaimableToken[]>;
|
|
141
|
+
};
|
|
142
|
+
/** The SPK airdrop. */
|
|
143
|
+
spk: PortfolioProtocolData<SparkAirdropClaimableToken[]>;
|
|
144
|
+
king: PortfolioProtocolData<KingRewardsClaimableToken[]>;
|
|
145
|
+
/** {} off mainnet. */
|
|
146
|
+
ethena: PortfolioProtocolData<EthenaAirdropClaimableToken[]> | {};
|
|
147
|
+
uniswap: PortfolioProtocolData<UniswapAirdropClaimableToken[]>;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export type PortfolioRewardsData = Record<EthAddress, PortfolioRewardsDataForAddress>;
|
|
151
|
+
|
|
152
|
+
/** The `markets` getPortfolioData returns: every market it read, by protocol, keyed by market or spoke. */
|
|
153
|
+
export interface PortfolioMarketsData {
|
|
154
|
+
morphoMarketsData: Record<string, MorphoBlueMarketInfo>;
|
|
155
|
+
morphoMidnightMarketsData: Record<string, MorphoMidnightMarketInfo>;
|
|
156
|
+
compoundV3MarketsData: Record<string, CompoundV3MarketsData>;
|
|
157
|
+
sparkMarketsData: Record<string, SparkMarketsData>;
|
|
158
|
+
aaveV3MarketsData: Record<string, AaveV3MarketData>;
|
|
159
|
+
aaveV2MarketsData: Record<string, AaveV2MarketData>;
|
|
160
|
+
compoundV2MarketsData: Record<string, CompoundV2MarketsData>;
|
|
161
|
+
crvUsdMarketsData: Record<string, CrvUSDGlobalMarketData>;
|
|
162
|
+
llamaLendMarketsData: Record<string, LlamaLendGlobalMarketData>;
|
|
163
|
+
liquityV2MarketsData: Record<string, LiquityV2MarketData>;
|
|
164
|
+
aaveV4SpokesData: Record<string, AaveV4SpokeData>;
|
|
65
165
|
}
|
package/src/umbrella/index.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Client } from 'viem';
|
|
2
2
|
import { AaveUmbrellaViewViem, AaveV3ViewContractViem } from '../contracts';
|
|
3
3
|
import { EthAddress, NetworkNumber } from '../types/common';
|
|
4
|
+
import { UmbrellaStakingData } from '../types/aave';
|
|
4
5
|
import { compareAddresses, convertHybridArraysToObjects, getEthAmountForDecimals } from '../services/utils';
|
|
5
6
|
import { findMatching, tokenEntries } from './umbrellaUtils';
|
|
6
7
|
|
|
7
8
|
const umbrella = '0xD400fc38ED4732893174325693a63C30ee3881a8';
|
|
8
9
|
const aaveOracle = '0x54586bE62E3c3580375aE3723C145253060Ca0C2';
|
|
9
10
|
|
|
10
|
-
export const getUmbrellaData = async (provider: Client, network: NetworkNumber, address: EthAddress) => {
|
|
11
|
+
export const getUmbrellaData = async (provider: Client, network: NetworkNumber, address: EthAddress): Promise<UmbrellaStakingData> => {
|
|
11
12
|
const umbrellaView = AaveUmbrellaViewViem(provider, network);
|
|
12
13
|
const aaveV3View = AaveV3ViewContractViem(provider, network);
|
|
13
14
|
const [tokensAggregatedData, additionalUmbrellaStakingData, userAggregatedData] = await Promise.all([
|