@defisaver/positions-sdk 2.1.161-portfolio-api-1-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 +4 -4
- 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 +68 -5
- 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 +4 -4
- 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 +68 -5
- 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 +69 -53
- 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 +86 -5
- package/src/umbrella/index.ts +2 -1
package/src/portfolio/index.ts
CHANGED
|
@@ -27,8 +27,11 @@ import {
|
|
|
27
27
|
LlamaLendGlobalMarketData,
|
|
28
28
|
MorphoBlueMarketInfo,
|
|
29
29
|
MorphoMidnightMarketInfo,
|
|
30
|
+
PortfolioDataOptions,
|
|
30
31
|
PortfolioMarketsData,
|
|
31
32
|
PortfolioPositionsData,
|
|
33
|
+
PortfolioRewardsData,
|
|
34
|
+
PortfolioStakingPositionsData,
|
|
32
35
|
SparkMarketsData,
|
|
33
36
|
} from '../types';
|
|
34
37
|
import { _getCompoundV3AccountData, _getCompoundV3MarketsData } from '../compoundV3';
|
|
@@ -52,13 +55,18 @@ import { fetchEthenaAirdropRewards } from '../claiming/ethena';
|
|
|
52
55
|
import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
|
|
53
56
|
import { getUniswapRewards } from '../claiming/uniswap';
|
|
54
57
|
|
|
55
|
-
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<{
|
|
56
59
|
positions: PortfolioPositionsData;
|
|
57
|
-
stakingPositions:
|
|
58
|
-
rewardsData:
|
|
60
|
+
stakingPositions: PortfolioStakingPositionsData;
|
|
61
|
+
rewardsData: PortfolioRewardsData;
|
|
59
62
|
markets: PortfolioMarketsData;
|
|
60
63
|
}> {
|
|
61
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 : [];
|
|
62
70
|
const isFluidSupported = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Base, NetworkNumber.Plasma].includes(network);
|
|
63
71
|
|
|
64
72
|
const morphoMarkets = Object.values(MorphoBlueMarkets(network)).filter((market) => market.chainIds.includes(network));
|
|
@@ -112,8 +120,8 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
112
120
|
};
|
|
113
121
|
|
|
114
122
|
const positions: PortfolioPositionsData = {};
|
|
115
|
-
const stakingPositions:
|
|
116
|
-
const rewardsData:
|
|
123
|
+
const stakingPositions: PortfolioStakingPositionsData = {};
|
|
124
|
+
const rewardsData: PortfolioRewardsData = {};
|
|
117
125
|
const allAddresses = [...addresses];
|
|
118
126
|
|
|
119
127
|
for (const address of allAddresses) {
|
|
@@ -138,7 +146,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
138
146
|
}
|
|
139
147
|
|
|
140
148
|
// TODO: check default values, probably needed when fetching portfolio on unsupported networks
|
|
141
|
-
for (const address of
|
|
149
|
+
for (const address of stakingAddresses) {
|
|
142
150
|
stakingPositions[address.toLowerCase() as EthAddress] = {
|
|
143
151
|
aaveV3: {},
|
|
144
152
|
morphoBlue: {},
|
|
@@ -152,15 +160,21 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
152
160
|
error: '',
|
|
153
161
|
data: {},
|
|
154
162
|
},
|
|
163
|
+
// Set by the sBOLD/yBOLD fetch below, whether it succeeds or fails.
|
|
164
|
+
liquityV2SBoldYBold: { error: '', data: null },
|
|
155
165
|
};
|
|
166
|
+
}
|
|
156
167
|
|
|
168
|
+
for (const address of rewardsAddresses) {
|
|
169
|
+
// merkl, spk, king and uniswap are set by their fetches below, whether they succeed or fail.
|
|
157
170
|
rewardsData[address.toLowerCase() as EthAddress] = {
|
|
158
|
-
merkl: {},
|
|
171
|
+
merkl: { error: '', data: null },
|
|
159
172
|
aaveV3: {},
|
|
160
173
|
spark: {},
|
|
161
|
-
spk: {},
|
|
162
|
-
king: {},
|
|
174
|
+
spk: { error: '', data: null },
|
|
175
|
+
king: { error: '', data: null },
|
|
163
176
|
ethena: {},
|
|
177
|
+
uniswap: { error: '', data: null },
|
|
164
178
|
};
|
|
165
179
|
}
|
|
166
180
|
|
|
@@ -236,54 +250,54 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
236
250
|
}),
|
|
237
251
|
|
|
238
252
|
// === STAKING DATA (independent of market data) ===
|
|
239
|
-
...
|
|
253
|
+
...stakingAddresses.map(async (address) => {
|
|
240
254
|
try {
|
|
241
255
|
if (!isFluidSupported) return;
|
|
242
|
-
stakingPositions[address.toLowerCase()].fluid = await _getAllUserEarnPositionsWithFTokens(client, network, address);
|
|
256
|
+
stakingPositions[address.toLowerCase() as EthAddress].fluid = await _getAllUserEarnPositionsWithFTokens(client, network, address);
|
|
243
257
|
} catch (error) {
|
|
244
258
|
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 };
|
|
259
|
+
stakingPositions[address.toLowerCase() as EthAddress].fluid = { error: `Error fetching Fluid lend data for address ${address}`, data: null };
|
|
246
260
|
}
|
|
247
261
|
}),
|
|
248
|
-
...
|
|
262
|
+
...stakingAddresses.map(async (address) => {
|
|
249
263
|
try {
|
|
250
264
|
if (!isMainnet) return;
|
|
251
|
-
stakingPositions[address.toLowerCase()].liquity = await getLiquityStakingData(client, network, address);
|
|
265
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquity = await getLiquityStakingData(client, network, address);
|
|
252
266
|
} catch (error) {
|
|
253
267
|
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 };
|
|
268
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquity = { error: `Error fetching Liquity staking data for address ${address}`, data: null };
|
|
255
269
|
}
|
|
256
270
|
}),
|
|
257
|
-
...
|
|
271
|
+
...stakingAddresses.map(async (address) => {
|
|
258
272
|
try {
|
|
259
273
|
if (!isMainnet) return;
|
|
260
|
-
stakingPositions[address.toLowerCase()].aaveV3 = await getStakeAaveData(client, network, address);
|
|
274
|
+
stakingPositions[address.toLowerCase() as EthAddress].aaveV3 = await getStakeAaveData(client, network, address);
|
|
261
275
|
} catch (error) {
|
|
262
276
|
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 };
|
|
277
|
+
stakingPositions[address.toLowerCase() as EthAddress].aaveV3 = { error: `Error fetching Aave V3 staking data for address ${address}`, data: null };
|
|
264
278
|
}
|
|
265
279
|
}),
|
|
266
|
-
...
|
|
280
|
+
...stakingAddresses.map(async (address) => {
|
|
267
281
|
try {
|
|
268
282
|
if (!isMainnet) return;
|
|
269
|
-
stakingPositions[address.toLowerCase()].umbrella = await getUmbrellaData(client, network, address);
|
|
283
|
+
stakingPositions[address.toLowerCase() as EthAddress].umbrella = await getUmbrellaData(client, network, address);
|
|
270
284
|
} catch (error) {
|
|
271
285
|
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 };
|
|
286
|
+
stakingPositions[address.toLowerCase() as EthAddress].umbrella = { error: `Error fetching Umbrella staking data for address ${address}`, data: null };
|
|
273
287
|
}
|
|
274
288
|
}),
|
|
275
289
|
// Liquity V2 staking
|
|
276
|
-
...liquityV2MarketsStaking.map(market =>
|
|
290
|
+
...liquityV2MarketsStaking.map(market => stakingAddresses.map(async (address) => {
|
|
277
291
|
try {
|
|
278
292
|
if (!isMainnet) {
|
|
279
|
-
stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: '', data: null };
|
|
293
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: '', data: null };
|
|
280
294
|
return;
|
|
281
295
|
}
|
|
282
296
|
const liquityV2StakingData = await getLiquityV2Staking(client, network, market.value, address);
|
|
283
|
-
stakingPositions[address.toLowerCase()].liquityV2[market.value] = { error: '', data: liquityV2StakingData };
|
|
297
|
+
stakingPositions[address.toLowerCase() as EthAddress].liquityV2[market.value] = { error: '', data: liquityV2StakingData };
|
|
284
298
|
} catch (error) {
|
|
285
299
|
console.error(`Error fetching Liquity V2 staking data for address ${address}, market ${market.value}:`, error);
|
|
286
|
-
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 };
|
|
287
301
|
}
|
|
288
302
|
})).flat(),
|
|
289
303
|
|
|
@@ -292,13 +306,13 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
292
306
|
(async () => {
|
|
293
307
|
try {
|
|
294
308
|
if (!isMainnet) {
|
|
295
|
-
for (const address of
|
|
296
|
-
rewardsData[address.toLowerCase()].king = { error: '', data: [] };
|
|
309
|
+
for (const address of rewardsAddresses) {
|
|
310
|
+
rewardsData[address.toLowerCase() as EthAddress].king = { error: '', data: [] };
|
|
297
311
|
}
|
|
298
312
|
return;
|
|
299
313
|
}
|
|
300
|
-
const kingRewards = await getKingRewards(client, network,
|
|
301
|
-
for (const address of
|
|
314
|
+
const kingRewards = await getKingRewards(client, network, rewardsAddresses);
|
|
315
|
+
for (const address of rewardsAddresses) {
|
|
302
316
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
303
317
|
rewardsData[lowerAddress].king = {
|
|
304
318
|
error: '',
|
|
@@ -307,7 +321,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
307
321
|
}
|
|
308
322
|
} catch (error) {
|
|
309
323
|
console.error('Error fetching King rewards data in batch:', error);
|
|
310
|
-
for (const address of
|
|
324
|
+
for (const address of rewardsAddresses) {
|
|
311
325
|
rewardsData[address.toLowerCase() as EthAddress].king = {
|
|
312
326
|
error: 'Error fetching King rewards data in batch',
|
|
313
327
|
data: null,
|
|
@@ -319,13 +333,13 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
319
333
|
(async () => {
|
|
320
334
|
try {
|
|
321
335
|
if (!isMainnet) {
|
|
322
|
-
for (const address of
|
|
323
|
-
rewardsData[address.toLowerCase()].uniswap = { error: '', data: [] };
|
|
336
|
+
for (const address of rewardsAddresses) {
|
|
337
|
+
rewardsData[address.toLowerCase() as EthAddress].uniswap = { error: '', data: [] };
|
|
324
338
|
}
|
|
325
339
|
return;
|
|
326
340
|
}
|
|
327
|
-
const uniswapRewards = await getUniswapRewards(client, network,
|
|
328
|
-
for (const address of
|
|
341
|
+
const uniswapRewards = await getUniswapRewards(client, network, rewardsAddresses);
|
|
342
|
+
for (const address of rewardsAddresses) {
|
|
329
343
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
330
344
|
rewardsData[lowerAddress].uniswap = {
|
|
331
345
|
error: '',
|
|
@@ -334,7 +348,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
334
348
|
}
|
|
335
349
|
} catch (error) {
|
|
336
350
|
console.error('Error fetching Uniswap rewards data in batch:', error);
|
|
337
|
-
for (const address of
|
|
351
|
+
for (const address of rewardsAddresses) {
|
|
338
352
|
rewardsData[address.toLowerCase() as EthAddress].uniswap = {
|
|
339
353
|
error: 'Error fetching Uniswap rewards data in batch',
|
|
340
354
|
data: null,
|
|
@@ -342,10 +356,10 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
342
356
|
}
|
|
343
357
|
}
|
|
344
358
|
})(),
|
|
345
|
-
...sparkMarkets.map((market) =>
|
|
359
|
+
...sparkMarkets.map((market) => rewardsAddresses.map(async address => {
|
|
346
360
|
try {
|
|
347
361
|
if (!isMainnet) {
|
|
348
|
-
rewardsData[address.toLowerCase()].spark[market.value] = { error: '', data: [] };
|
|
362
|
+
rewardsData[address.toLowerCase() as EthAddress].spark[market.value] = { error: '', data: [] };
|
|
349
363
|
return;
|
|
350
364
|
}
|
|
351
365
|
const sparkData = await fetchSparkRewards(client, network, address, market.providerAddress);
|
|
@@ -355,7 +369,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
355
369
|
rewardsData[address.toLowerCase() as EthAddress].spark[market.value] = { error: `Error fetching Spark rewards data for address ${address}`, data: null };
|
|
356
370
|
}
|
|
357
371
|
})).flat(),
|
|
358
|
-
...
|
|
372
|
+
...rewardsAddresses.map(async (address) => {
|
|
359
373
|
try {
|
|
360
374
|
const merklData = await getMerklUnclaimedRewards(address, network);
|
|
361
375
|
rewardsData[address.toLowerCase() as EthAddress].merkl = { error: '', data: merklData };
|
|
@@ -364,27 +378,28 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
364
378
|
rewardsData[address.toLowerCase() as EthAddress].merkl = { error: `Error fetching Merkl rewards data for address ${address}`, data: null };
|
|
365
379
|
}
|
|
366
380
|
}),
|
|
367
|
-
...aaveV3Markets.map(market =>
|
|
381
|
+
...aaveV3Markets.map(market => rewardsAddresses.map(async (address) => {
|
|
368
382
|
try {
|
|
369
383
|
const aaveData = await getUnclaimedRewardsForAllMarkets(client, network, address, market.providerAddress);
|
|
370
384
|
rewardsData[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: '', data: aaveData };
|
|
371
385
|
} catch (error) {
|
|
372
386
|
console.error(`Error fetching Aave V3 Merit rewards data for address ${address}:`, error);
|
|
373
|
-
|
|
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 };
|
|
374
389
|
}
|
|
375
390
|
})).flat(),
|
|
376
391
|
// Batch Spark Airdrop rewards
|
|
377
392
|
(async () => {
|
|
378
393
|
try {
|
|
379
394
|
if (!isMainnet) {
|
|
380
|
-
for (const address of
|
|
381
|
-
rewardsData[address.toLowerCase()].spk = { error: '', data: [] };
|
|
395
|
+
for (const address of rewardsAddresses) {
|
|
396
|
+
rewardsData[address.toLowerCase() as EthAddress].spk = { error: '', data: [] };
|
|
382
397
|
}
|
|
383
398
|
return;
|
|
384
399
|
}
|
|
385
400
|
|
|
386
|
-
const sparkAirdropRewards = await fetchSparkAirdropRewards(client, network,
|
|
387
|
-
for (const address of
|
|
401
|
+
const sparkAirdropRewards = await fetchSparkAirdropRewards(client, network, rewardsAddresses);
|
|
402
|
+
for (const address of rewardsAddresses) {
|
|
388
403
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
389
404
|
rewardsData[lowerAddress].spk = {
|
|
390
405
|
error: '',
|
|
@@ -393,7 +408,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
393
408
|
}
|
|
394
409
|
} catch (error) {
|
|
395
410
|
console.error('Error fetching Spark Airdrop rewards data in batch:', error);
|
|
396
|
-
for (const address of
|
|
411
|
+
for (const address of rewardsAddresses) {
|
|
397
412
|
rewardsData[address.toLowerCase() as EthAddress].spk = {
|
|
398
413
|
error: 'Error fetching Spark Airdrop rewards data in batch',
|
|
399
414
|
data: null,
|
|
@@ -407,8 +422,8 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
407
422
|
return;
|
|
408
423
|
}
|
|
409
424
|
|
|
410
|
-
const ethenaAirdropRewards = await fetchEthenaAirdropRewards(
|
|
411
|
-
for (const address of
|
|
425
|
+
const ethenaAirdropRewards = await fetchEthenaAirdropRewards(rewardsAddresses);
|
|
426
|
+
for (const address of rewardsAddresses) {
|
|
412
427
|
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
413
428
|
rewardsData[lowerAddress].ethena = {
|
|
414
429
|
error: '',
|
|
@@ -417,7 +432,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
417
432
|
}
|
|
418
433
|
} catch (error) {
|
|
419
434
|
console.error('Error fetching Ethena Airdrop rewards data:', error);
|
|
420
|
-
for (const address of
|
|
435
|
+
for (const address of rewardsAddresses) {
|
|
421
436
|
rewardsData[address.toLowerCase() as EthAddress].ethena = {
|
|
422
437
|
error: 'Error fetching Ethena Airdrop rewards data in batch',
|
|
423
438
|
data: null,
|
|
@@ -450,15 +465,16 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
450
465
|
try {
|
|
451
466
|
const [accDataPromise, earnDataPromise] = await Promise.allSettled([
|
|
452
467
|
_getMorphoBlueAccountData(client, network, address, market, morphoMarketsData[market.value]),
|
|
453
|
-
getMorphoEarn(client, network, address, market, morphoMarketsData[market.value]),
|
|
468
|
+
includeStaking ? getMorphoEarn(client, network, address, market, morphoMarketsData[market.value]) : null,
|
|
454
469
|
]);
|
|
455
470
|
if (accDataPromise.status === 'rejected') {
|
|
456
471
|
console.error(`Error fetching MorphoBlue account data for address ${address} on market ${market.value}:`, accDataPromise.reason);
|
|
457
472
|
positions[address.toLowerCase() as EthAddress].morphoBlue[market.value] = { error: `Error fetching MorphoBlue account data for address ${address} on market ${market.value}`, data: null };
|
|
458
473
|
}
|
|
459
474
|
if (earnDataPromise.status === 'rejected') {
|
|
460
|
-
|
|
461
|
-
|
|
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 };
|
|
462
478
|
}
|
|
463
479
|
if (accDataPromise.status !== 'rejected') {
|
|
464
480
|
const accData = accDataPromise.value;
|
|
@@ -571,13 +587,13 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
571
587
|
}
|
|
572
588
|
})).flat(),
|
|
573
589
|
// liquity sBold/yBold and staking options
|
|
574
|
-
...
|
|
590
|
+
...stakingAddresses.map(async (address) => {
|
|
575
591
|
try {
|
|
576
592
|
if (!isMainnet) {
|
|
577
593
|
stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data: null };
|
|
578
594
|
return;
|
|
579
595
|
}
|
|
580
|
-
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);
|
|
581
597
|
stakingPositions[address.toLowerCase() as EthAddress].liquityV2SBoldYBold = { error: '', data };
|
|
582
598
|
} catch (error) {
|
|
583
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,18 +1,31 @@
|
|
|
1
1
|
import {
|
|
2
|
-
AaveV2MarketData, AaveV2PositionData, AaveV3MarketData, AaveV3PositionData, AaveVersions,
|
|
2
|
+
AaveV2MarketData, AaveV2PositionData, AaveV3MarketData, AaveV3PositionData, AaveVersions, StakeAaveData, UmbrellaStakingData,
|
|
3
3
|
} from './aave';
|
|
4
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';
|
|
5
14
|
import { EthAddress } from './common';
|
|
6
15
|
import {
|
|
7
16
|
CompoundV2MarketsData, CompoundV2PositionData, CompoundV3MarketsData, CompoundV3PositionData, CompoundVersions,
|
|
8
17
|
} from './compound';
|
|
9
18
|
import { CrvUSDGlobalMarketData, CrvUSDUserData, CrvUSDVersions } from './curveUsd';
|
|
10
|
-
import { FluidVaultData } from './fluid';
|
|
11
|
-
import { LiquityTroveInfo } from './liquity';
|
|
12
|
-
import {
|
|
19
|
+
import { FluidEarnPositionData, FluidVaultData } from './fluid';
|
|
20
|
+
import { LiquityStakingData, LiquityTroveInfo } from './liquity';
|
|
21
|
+
import {
|
|
22
|
+
LiquityV2MarketData, LiquityV2SBoldYBoldData, LiquityV2StakingData, LiquityV2TroveData, LiquityV2Versions,
|
|
23
|
+
} from './liquityV2';
|
|
13
24
|
import { LlamaLendGlobalMarketData, LlamaLendUserData, LlamaLendVersionsType } from './llamaLend';
|
|
14
25
|
import { CdpData } from './maker';
|
|
15
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
MorphoBlueEarnData, MorphoBlueMarketInfo, MorphoBluePositionData, MorphoBlueVersions,
|
|
28
|
+
} from './morphoBlue';
|
|
16
29
|
import { MorphoMidnightMarketInfo, MorphoMidnightPositionData, MorphoMidnightVersions } from './morphoMidnight';
|
|
17
30
|
import { SparkMarketsData, SparkPositionData, SparkVersions } from './spark';
|
|
18
31
|
|
|
@@ -21,6 +34,20 @@ export interface PortfolioProtocolData<T> {
|
|
|
21
34
|
data: T | null,
|
|
22
35
|
}
|
|
23
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
|
+
|
|
24
51
|
export interface PortfolioPositionsDataForAddress {
|
|
25
52
|
aaveV3: {
|
|
26
53
|
[key in AaveVersions]?: PortfolioProtocolData<AaveV3PositionData>;
|
|
@@ -68,6 +95,60 @@ export interface PortfolioPositionsData {
|
|
|
68
95
|
[key: EthAddress]: PortfolioPositionsDataForAddress;
|
|
69
96
|
}
|
|
70
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
|
+
|
|
71
152
|
/** The `markets` getPortfolioData returns: every market it read, by protocol, keyed by market or spoke. */
|
|
72
153
|
export interface PortfolioMarketsData {
|
|
73
154
|
morphoMarketsData: Record<string, MorphoBlueMarketInfo>;
|
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([
|