@defisaver/positions-sdk 2.1.161-portfolio-api-2-dev → 2.1.161-portfolio-api-3-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/aaveV2/index.js +2 -0
- package/cjs/aaveV3/index.d.ts +39 -0
- package/cjs/aaveV3/index.js +237 -2
- package/cjs/aaveV4/lend.d.ts +2 -43
- package/cjs/curveUsd/index.d.ts +5 -2
- package/cjs/curveUsd/index.js +30 -15
- package/cjs/fluid/index.d.ts +5 -0
- package/cjs/fluid/index.js +36 -1
- package/cjs/helpers/aaveHelpers/index.d.ts +9 -1
- package/cjs/helpers/aaveHelpers/index.js +26 -6
- package/cjs/helpers/aaveV4Helpers/index.js +1 -0
- package/cjs/helpers/compoundHelpers/index.js +2 -0
- package/cjs/helpers/curveUsdHelpers/index.d.ts +3 -1
- package/cjs/helpers/curveUsdHelpers/index.js +15 -2
- package/cjs/helpers/fluidHelpers/index.js +1 -0
- package/cjs/helpers/liquityV2Helpers/index.js +1 -0
- package/cjs/helpers/llamaLendHelpers/index.js +1 -0
- package/cjs/helpers/makerHelpers/index.d.ts +1 -1
- package/cjs/helpers/makerHelpers/index.js +2 -2
- package/cjs/helpers/morphoBlueHelpers/index.js +2 -0
- package/cjs/helpers/morphoMidnightHelpers/index.js +2 -0
- package/cjs/helpers/sparkHelpers/index.js +1 -0
- package/cjs/liquity/index.js +15 -2
- package/cjs/liquityV2/index.d.ts +5 -0
- package/cjs/liquityV2/index.js +37 -19
- package/cjs/maker/index.d.ts +7 -2
- package/cjs/maker/index.js +44 -12
- package/cjs/markets/index.d.ts +1 -0
- package/cjs/markets/index.js +3 -1
- package/cjs/markets/maker/index.d.ts +1 -0
- package/cjs/markets/maker/index.js +13 -0
- package/cjs/moneymarket/moneymarketCommonService.d.ts +9 -0
- package/cjs/moneymarket/moneymarketCommonService.js +13 -1
- package/cjs/portfolio/index.d.ts +17 -5
- package/cjs/portfolio/index.js +583 -394
- package/cjs/portfolio/lendingProtocols.d.ts +184 -0
- package/cjs/portfolio/lendingProtocols.js +196 -0
- package/cjs/savings/index.d.ts +8 -2
- package/cjs/savings/index.js +37 -5
- package/cjs/savings/morphoVaults/index.d.ts +1 -1
- package/cjs/savings/morphoVaults/index.js +56 -18
- package/cjs/savings/morphoVaults/options.d.ts +2 -0
- package/cjs/savings/morphoVaults/options.js +61 -1
- package/cjs/spark/index.js +1 -0
- package/cjs/staking/staking.d.ts +20 -0
- package/cjs/staking/staking.js +19 -1
- package/cjs/types/aave.d.ts +3 -0
- package/cjs/types/aaveV4.d.ts +45 -0
- package/cjs/types/compound.d.ts +2 -0
- package/cjs/types/curveUsd.d.ts +6 -0
- package/cjs/types/fluid.d.ts +1 -0
- package/cjs/types/liquity.d.ts +4 -0
- package/cjs/types/liquityV2.d.ts +2 -0
- package/cjs/types/maker.d.ts +4 -0
- package/cjs/types/morphoBlue.d.ts +2 -0
- package/cjs/types/morphoMidnight.d.ts +1 -0
- package/cjs/types/portfolio.d.ts +64 -6
- package/cjs/types/savings/index.d.ts +9 -1
- package/cjs/types/savings/morphoVaults.d.ts +10 -2
- package/cjs/types/savings/morphoVaults.js +9 -1
- package/cjs/types/spark.d.ts +2 -0
- package/esm/aaveV2/index.js +2 -0
- package/esm/aaveV3/index.d.ts +39 -0
- package/esm/aaveV3/index.js +231 -2
- package/esm/aaveV4/lend.d.ts +2 -43
- package/esm/curveUsd/index.d.ts +5 -2
- package/esm/curveUsd/index.js +30 -15
- package/esm/fluid/index.d.ts +5 -0
- package/esm/fluid/index.js +33 -0
- package/esm/helpers/aaveHelpers/index.d.ts +9 -1
- package/esm/helpers/aaveHelpers/index.js +23 -5
- package/esm/helpers/aaveV4Helpers/index.js +1 -0
- package/esm/helpers/compoundHelpers/index.js +2 -0
- package/esm/helpers/curveUsdHelpers/index.d.ts +3 -1
- package/esm/helpers/curveUsdHelpers/index.js +16 -3
- package/esm/helpers/fluidHelpers/index.js +1 -0
- package/esm/helpers/liquityV2Helpers/index.js +1 -0
- package/esm/helpers/llamaLendHelpers/index.js +2 -1
- package/esm/helpers/makerHelpers/index.d.ts +1 -1
- package/esm/helpers/makerHelpers/index.js +2 -2
- package/esm/helpers/morphoBlueHelpers/index.js +2 -0
- package/esm/helpers/morphoMidnightHelpers/index.js +2 -0
- package/esm/helpers/sparkHelpers/index.js +1 -0
- package/esm/liquity/index.js +16 -3
- package/esm/liquityV2/index.d.ts +5 -0
- package/esm/liquityV2/index.js +35 -18
- package/esm/maker/index.d.ts +7 -2
- package/esm/maker/index.js +44 -14
- package/esm/markets/index.d.ts +1 -0
- package/esm/markets/index.js +1 -0
- package/esm/markets/maker/index.d.ts +1 -0
- package/esm/markets/maker/index.js +10 -0
- package/esm/moneymarket/moneymarketCommonService.d.ts +9 -0
- package/esm/moneymarket/moneymarketCommonService.js +11 -0
- package/esm/portfolio/index.d.ts +17 -5
- package/esm/portfolio/index.js +584 -398
- package/esm/portfolio/lendingProtocols.d.ts +184 -0
- package/esm/portfolio/lendingProtocols.js +189 -0
- package/esm/savings/index.d.ts +8 -2
- package/esm/savings/index.js +35 -4
- package/esm/savings/morphoVaults/index.d.ts +1 -1
- package/esm/savings/morphoVaults/index.js +57 -19
- package/esm/savings/morphoVaults/options.d.ts +2 -0
- package/esm/savings/morphoVaults/options.js +61 -1
- package/esm/spark/index.js +1 -0
- package/esm/staking/staking.d.ts +20 -0
- package/esm/staking/staking.js +17 -0
- package/esm/types/aave.d.ts +3 -0
- package/esm/types/aaveV4.d.ts +45 -0
- package/esm/types/compound.d.ts +2 -0
- package/esm/types/curveUsd.d.ts +6 -0
- package/esm/types/fluid.d.ts +1 -0
- package/esm/types/liquity.d.ts +4 -0
- package/esm/types/liquityV2.d.ts +2 -0
- package/esm/types/maker.d.ts +4 -0
- package/esm/types/morphoBlue.d.ts +2 -0
- package/esm/types/morphoMidnight.d.ts +1 -0
- package/esm/types/portfolio.d.ts +64 -6
- package/esm/types/savings/index.d.ts +9 -1
- package/esm/types/savings/morphoVaults.d.ts +10 -2
- package/esm/types/savings/morphoVaults.js +8 -0
- package/esm/types/spark.d.ts +2 -0
- package/package.json +3 -2
- package/src/aaveV2/index.ts +2 -0
- package/src/aaveV3/index.ts +291 -1
- package/src/aaveV4/lend.ts +2 -48
- package/src/curveUsd/index.ts +33 -16
- package/src/fluid/index.ts +40 -0
- package/src/helpers/aaveHelpers/index.ts +27 -7
- package/src/helpers/aaveV4Helpers/index.ts +1 -0
- package/src/helpers/compoundHelpers/index.ts +2 -0
- package/src/helpers/curveUsdHelpers/index.ts +23 -3
- package/src/helpers/fluidHelpers/index.ts +1 -0
- package/src/helpers/liquityV2Helpers/index.ts +1 -0
- package/src/helpers/llamaLendHelpers/index.ts +2 -1
- package/src/helpers/makerHelpers/index.ts +2 -1
- package/src/helpers/morphoBlueHelpers/index.ts +2 -0
- package/src/helpers/morphoMidnightHelpers/index.ts +2 -0
- package/src/helpers/sparkHelpers/index.ts +1 -0
- package/src/liquity/index.ts +17 -3
- package/src/liquityV2/index.ts +45 -19
- package/src/maker/index.ts +77 -30
- package/src/markets/index.ts +2 -1
- package/src/markets/maker/index.ts +10 -0
- package/src/moneymarket/moneymarketCommonService.ts +15 -0
- package/src/portfolio/index.ts +428 -215
- package/src/portfolio/lendingProtocols.ts +281 -0
- package/src/savings/index.ts +45 -5
- package/src/savings/morphoVaults/index.ts +73 -21
- package/src/savings/morphoVaults/options.ts +67 -1
- package/src/spark/index.ts +1 -0
- package/src/staking/staking.ts +28 -0
- package/src/types/aave.ts +4 -0
- package/src/types/aaveV4.ts +50 -0
- package/src/types/compound.ts +2 -0
- package/src/types/curveUsd.ts +7 -0
- package/src/types/fluid.ts +1 -0
- package/src/types/liquity.ts +6 -0
- package/src/types/liquityV2.ts +2 -0
- package/src/types/maker.ts +6 -0
- package/src/types/morphoBlue.ts +2 -0
- package/src/types/morphoMidnight.ts +1 -0
- package/src/types/portfolio.ts +71 -6
- package/src/types/savings/index.ts +12 -1
- package/src/types/savings/morphoVaults.ts +11 -1
- package/src/types/spark.ts +2 -0
package/src/aaveV3/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
AaveIncentivesControllerViem,
|
|
7
7
|
AaveV3ViewContractViem,
|
|
8
8
|
createViemContractFromConfigFunc,
|
|
9
|
+
getConfigContractAbi,
|
|
9
10
|
StkAAVEViem,
|
|
10
11
|
} from '../contracts';
|
|
11
12
|
import { aaveAnyGetAggregatedPositionData, aaveV3IsInIsolationMode, aaveV3IsInSiloedMode } from '../helpers/aaveHelpers';
|
|
@@ -62,7 +63,10 @@ export const aaveV3EmodeCategoriesMapping = (extractedState: any, usedAssets: Aa
|
|
|
62
63
|
...extractedState, usedAssets, eModeCategory: e.id,
|
|
63
64
|
});
|
|
64
65
|
const willStayOverCollateralized = new Dec(afterEnteringCategory.ratio === 'NaN' ? '0' : afterEnteringCategory.ratio).eq(0) || new Dec(afterEnteringCategory.ratio).gt(afterEnteringCategory.liqPercent);
|
|
65
|
-
const
|
|
66
|
+
const collateralOnlyFromCategory = e.id === 0 || !e.isolated
|
|
67
|
+
? true
|
|
68
|
+
: !usedAssetsValues.filter(u => u.isSupplied && u.collateral && !e.collateralAssets.includes(u.symbol)).length;
|
|
69
|
+
const enteringTerms = [borrowingOnlyFromCategory, willStayOverCollateralized, collateralOnlyFromCategory];
|
|
66
70
|
categoriesMapping[e.id] = {
|
|
67
71
|
enteringTerms,
|
|
68
72
|
canEnterCategory: !enteringTerms.includes(false),
|
|
@@ -119,6 +123,7 @@ export async function _getAaveV3MarketData(provider: Client, network: NetworkNum
|
|
|
119
123
|
borrowableBitmap: eModesInfo[i].borrowableBitmap.toString(),
|
|
120
124
|
collateralBitmap: eModesInfo[i].collateralBitmap.toString(),
|
|
121
125
|
ltvzeroBitmap: eModesInfo[i].ltvzeroBitmap.toString(),
|
|
126
|
+
isolated: eModesInfo[i].isolated ?? false,
|
|
122
127
|
borrowAssets: [],
|
|
123
128
|
collateralAssets: [],
|
|
124
129
|
ltvZeroAssets: [],
|
|
@@ -346,6 +351,7 @@ export async function _getAaveV3MarketData(provider: Client, network: NetworkNum
|
|
|
346
351
|
collateralAssets: assetsData.map((a) => a.symbol),
|
|
347
352
|
borrowAssets: assetsData.map((a) => a.symbol),
|
|
348
353
|
ltvZeroAssets: [],
|
|
354
|
+
isolated: false,
|
|
349
355
|
};
|
|
350
356
|
|
|
351
357
|
return { assetsData: payload, eModeCategoriesData };
|
|
@@ -431,6 +437,290 @@ export const _getAaveV3AccountBalances = async (provider: Client, network: Netwo
|
|
|
431
437
|
|
|
432
438
|
export const getAaveV3AccountBalances = async (provider: EthereumProvider, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress): Promise<PositionBalances> => _getAaveV3AccountBalances(getViemProvider(provider, network), network, block, addressMapping, address);
|
|
433
439
|
|
|
440
|
+
/**
|
|
441
|
+
* Historical net-balance helpers that bypass the AaveV3View contract.
|
|
442
|
+
*
|
|
443
|
+
* The View contract (and therefore `getAaveV3AccountData` / `getAaveV3AccountBalances`) can only be
|
|
444
|
+
* queried from its deployment block onwards, so it cannot read balances for positions older than that.
|
|
445
|
+
* The aTokens/debt tokens, the ProtocolDataProvider and the Aave price oracle all exist from Aave v3
|
|
446
|
+
* launch, so reading `balanceOf` on those tokens + the oracle price directly reaches much further back
|
|
447
|
+
* and costs ~1 multicall per point. Used to build a position balance-history chart.
|
|
448
|
+
*/
|
|
449
|
+
|
|
450
|
+
// matches morphoVaults' batchSize; stays under the 500k portfolio discovery allows on simulation fork RPCs
|
|
451
|
+
const HISTORICAL_MULTICALL_BATCH_SIZE = 250_000;
|
|
452
|
+
|
|
453
|
+
// Minimal Aave price oracle ABI (getAssetPrice returns the asset price in the market base currency).
|
|
454
|
+
const AAVE_ORACLE_ABI = [
|
|
455
|
+
{
|
|
456
|
+
inputs: [{ internalType: 'address', name: 'asset', type: 'address' }],
|
|
457
|
+
name: 'getAssetPrice',
|
|
458
|
+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
|
459
|
+
stateMutability: 'view',
|
|
460
|
+
type: 'function',
|
|
461
|
+
},
|
|
462
|
+
] as const;
|
|
463
|
+
|
|
464
|
+
export interface AaveV3ReserveTokenAddresses {
|
|
465
|
+
[symbol: string]: {
|
|
466
|
+
symbol: string,
|
|
467
|
+
underlyingAddress: EthAddress,
|
|
468
|
+
aTokenAddress: EthAddress,
|
|
469
|
+
stableDebtTokenAddress: EthAddress,
|
|
470
|
+
variableDebtTokenAddress: EthAddress,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export interface AaveV3HistoricalBalance {
|
|
475
|
+
block: number,
|
|
476
|
+
suppliedUsd: string,
|
|
477
|
+
borrowedUsd: string,
|
|
478
|
+
netUsd: string,
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Fetches the aToken / stable-debt / variable-debt token addresses for every asset in the market.
|
|
483
|
+
* These are effectively immutable per reserve, so fetch once and reuse across all history points.
|
|
484
|
+
*/
|
|
485
|
+
export const _getAaveV3ReserveTokenAddresses = async (provider: Client, network: NetworkNumber, market: AaveMarketInfo): Promise<AaveV3ReserveTokenAddresses> => {
|
|
486
|
+
const symbols = market.assets;
|
|
487
|
+
const underlyingAddresses = symbols.map((a: string) => getAssetInfo(getWrappedNativeAssetFromUnwrapped(a), network).address as EthAddress);
|
|
488
|
+
// @ts-ignore market.protocolData is a valid config key at runtime
|
|
489
|
+
const dataProviderAbi = getConfigContractAbi(market.protocolData, network);
|
|
490
|
+
|
|
491
|
+
const contracts = underlyingAddresses.map((underlying) => ({
|
|
492
|
+
address: market.protocolDataAddress as EthAddress,
|
|
493
|
+
abi: dataProviderAbi,
|
|
494
|
+
functionName: 'getReserveTokensAddresses',
|
|
495
|
+
args: [underlying],
|
|
496
|
+
}));
|
|
497
|
+
|
|
498
|
+
// @ts-ignore
|
|
499
|
+
const results = await provider.multicall({ contracts, allowFailure: true, batchSize: HISTORICAL_MULTICALL_BATCH_SIZE });
|
|
500
|
+
|
|
501
|
+
const mapping: AaveV3ReserveTokenAddresses = {};
|
|
502
|
+
results.forEach((res: any, i: number) => {
|
|
503
|
+
if (res.status !== 'success' || !res.result) return;
|
|
504
|
+
// outputs order: [aTokenAddress, stableDebtTokenAddress, variableDebtTokenAddress]
|
|
505
|
+
const [aTokenAddress, stableDebtTokenAddress, variableDebtTokenAddress] = res.result as [EthAddress, EthAddress, EthAddress];
|
|
506
|
+
mapping[symbols[i]] = {
|
|
507
|
+
symbol: symbols[i],
|
|
508
|
+
underlyingAddress: underlyingAddresses[i],
|
|
509
|
+
aTokenAddress,
|
|
510
|
+
stableDebtTokenAddress,
|
|
511
|
+
variableDebtTokenAddress,
|
|
512
|
+
};
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
return mapping;
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
export const getAaveV3ReserveTokenAddresses = async (provider: EthereumProvider, network: NetworkNumber, market: AaveMarketInfo): Promise<AaveV3ReserveTokenAddresses> => _getAaveV3ReserveTokenAddresses(getViemProvider(provider, network, { batch: { multicall: true } }), network, market);
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Resolves the price oracle the market's addresses provider points at (at `block`, or latest).
|
|
522
|
+
* Aave can rotate the oracle over time, so a caller hoisting one address across many historical
|
|
523
|
+
* points must resolve it at both ends of its block range and only reuse it when the two agree —
|
|
524
|
+
* otherwise leave `oracleAddress` unset on _getAaveV3HistoricalBalance and it resolves per block.
|
|
525
|
+
*/
|
|
526
|
+
export const _getAaveV3OracleAddress = async (provider: Client, network: NetworkNumber, market: AaveMarketInfo, block: Blockish = 'latest'): Promise<EthAddress> => {
|
|
527
|
+
// @ts-ignore market.provider is a valid config key at runtime
|
|
528
|
+
const providerAbi = getConfigContractAbi(market.provider, network);
|
|
529
|
+
// @ts-ignore readContract exists on the public client returned by getViemProvider
|
|
530
|
+
const oracleAddress = await provider.readContract({
|
|
531
|
+
address: market.providerAddress as EthAddress,
|
|
532
|
+
abi: providerAbi as any,
|
|
533
|
+
functionName: 'getPriceOracle',
|
|
534
|
+
...setViemBlockNumber(block),
|
|
535
|
+
});
|
|
536
|
+
if (!oracleAddress) throw new Error(`AaveV3 oracle unavailable at block ${block}`);
|
|
537
|
+
return oracleAddress as EthAddress;
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
export const getAaveV3OracleAddress = async (provider: EthereumProvider, network: NetworkNumber, market: AaveMarketInfo, block: Blockish = 'latest'): Promise<EthAddress> => _getAaveV3OracleAddress(getViemProvider(provider, network, { batch: { multicall: true } }), network, market, block);
|
|
541
|
+
|
|
542
|
+
type AaveV3ReserveTokenEntry = AaveV3ReserveTokenAddresses[string];
|
|
543
|
+
type AaveV3ActiveHistoricalAsset = AaveV3ReserveTokenEntry & { supplied: string, debt: string };
|
|
544
|
+
// raw getAssetPrice multicall result per asset symbol
|
|
545
|
+
type AaveV3PriceResultsBySymbol = Record<string, any>;
|
|
546
|
+
|
|
547
|
+
// supply = aToken.balanceOf, debt = variableDebtToken.balanceOf + stableDebtToken.balanceOf — three reads per reserve
|
|
548
|
+
const getHistoricalBalanceContracts = (entries: AaveV3ReserveTokenEntry[], address: EthAddress) => {
|
|
549
|
+
const erc20Abi = getConfigContractAbi('Erc20');
|
|
550
|
+
return entries.flatMap((e) => ([
|
|
551
|
+
{
|
|
552
|
+
address: e.aTokenAddress, abi: erc20Abi, functionName: 'balanceOf', args: [address],
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
address: e.variableDebtTokenAddress, abi: erc20Abi, functionName: 'balanceOf', args: [address],
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
address: e.stableDebtTokenAddress, abi: erc20Abi, functionName: 'balanceOf', args: [address],
|
|
559
|
+
},
|
|
560
|
+
]));
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
const getAssetPriceContract = (oracle: EthAddress, underlyingAddress: EthAddress) => ({
|
|
564
|
+
address: oracle,
|
|
565
|
+
abi: AAVE_ORACLE_ABI,
|
|
566
|
+
functionName: 'getAssetPrice',
|
|
567
|
+
args: [underlyingAddress],
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Pairs each reserve with its three balance reads and keeps the assets the wallet actually held.
|
|
572
|
+
* Throws when every read failed — a totally failed fetch must not masquerade as a real $0 balance,
|
|
573
|
+
* so the caller can distinguish "fetch failed" (gap) from "position was empty" (genuine 0).
|
|
574
|
+
*/
|
|
575
|
+
const toActiveHistoricalAssets = (entries: AaveV3ReserveTokenEntry[], balanceResults: any[], block: number): AaveV3ActiveHistoricalAsset[] => {
|
|
576
|
+
const anyBalanceRead = balanceResults.some((r) => r?.status === 'success');
|
|
577
|
+
if (!anyBalanceRead) throw new Error(`AaveV3 historical balance: all balance reads failed at block ${block}`);
|
|
578
|
+
|
|
579
|
+
return entries.map((e, i) => {
|
|
580
|
+
const supplyRes = balanceResults[i * 3];
|
|
581
|
+
const varDebtRes = balanceResults[(i * 3) + 1];
|
|
582
|
+
const stableDebtRes = balanceResults[(i * 3) + 2];
|
|
583
|
+
const supplied = supplyRes?.status === 'success' ? (supplyRes.result as bigint).toString() : '0';
|
|
584
|
+
const varDebt = varDebtRes?.status === 'success' ? (varDebtRes.result as bigint).toString() : '0';
|
|
585
|
+
const stableDebt = stableDebtRes?.status === 'success' ? (stableDebtRes.result as bigint).toString() : '0';
|
|
586
|
+
const debt = new Dec(varDebt).add(stableDebt).toString();
|
|
587
|
+
return { ...e, supplied, debt };
|
|
588
|
+
}).filter((a) => a.supplied !== '0' || a.debt !== '0');
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Sums the active assets into the point's USD totals. Throws when every price read failed — the
|
|
593
|
+
* oracle couldn't serve this block (pruned state, or a hoisted oracle that wasn't deployed yet back
|
|
594
|
+
* then), and totalling it up anyway would return a fake near-$0 balance. A single asset's price
|
|
595
|
+
* failure still just skips that asset, matching the live data path.
|
|
596
|
+
*/
|
|
597
|
+
const sumHistoricalUsd = (activeAssets: AaveV3ActiveHistoricalAsset[], priceResBySymbol: AaveV3PriceResultsBySymbol, block: number): AaveV3HistoricalBalance => {
|
|
598
|
+
const anyPriceRead = activeAssets.some((a) => priceResBySymbol[a.symbol]?.status === 'success');
|
|
599
|
+
if (!anyPriceRead) throw new Error(`AaveV3 historical balance: all price reads failed at block ${block}`);
|
|
600
|
+
|
|
601
|
+
let suppliedUsd = new Dec(0);
|
|
602
|
+
let borrowedUsd = new Dec(0);
|
|
603
|
+
activeAssets.forEach((a) => {
|
|
604
|
+
const priceRes = priceResBySymbol[a.symbol];
|
|
605
|
+
if (priceRes?.status !== 'success') return;
|
|
606
|
+
const priceUsd = new Dec((priceRes.result as bigint).toString()).div(1e8); // Aave v3 base currency is USD with 8 decimals
|
|
607
|
+
if (a.supplied !== '0') suppliedUsd = suppliedUsd.add(new Dec(assetAmountInEth(a.supplied, a.symbol)).mul(priceUsd));
|
|
608
|
+
if (a.debt !== '0') borrowedUsd = borrowedUsd.add(new Dec(assetAmountInEth(a.debt, a.symbol)).mul(priceUsd));
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
return {
|
|
612
|
+
block,
|
|
613
|
+
suppliedUsd: suppliedUsd.toString(),
|
|
614
|
+
borrowedUsd: borrowedUsd.toString(),
|
|
615
|
+
netUsd: suppliedUsd.minus(borrowedUsd).toString(),
|
|
616
|
+
};
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Fast path, ONE multicall: balances plus a price read for every reserve through the caller-hoisted
|
|
621
|
+
* oracle. Prices are read for all reserves because which assets are active isn't known until the
|
|
622
|
+
* balances come back — extra reads inside an already-paid multicall are nearly free.
|
|
623
|
+
*/
|
|
624
|
+
const fetchHistoricalPointWithOracle = async (provider: Client, entries: AaveV3ReserveTokenEntry[], address: EthAddress, block: number, oracleAddress: EthAddress) => {
|
|
625
|
+
const balanceContracts = getHistoricalBalanceContracts(entries, address);
|
|
626
|
+
const priceContracts = entries.map((e) => getAssetPriceContract(oracleAddress, e.underlyingAddress));
|
|
627
|
+
|
|
628
|
+
// @ts-ignore
|
|
629
|
+
const results = await provider.multicall({
|
|
630
|
+
contracts: [...balanceContracts, ...priceContracts], allowFailure: true, blockNumber: BigInt(block), batchSize: HISTORICAL_MULTICALL_BATCH_SIZE,
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
const activeAssets = toActiveHistoricalAssets(entries, results.slice(0, balanceContracts.length), block);
|
|
634
|
+
const priceResBySymbol: AaveV3PriceResultsBySymbol = {};
|
|
635
|
+
results.slice(balanceContracts.length).forEach((res: any, i: number) => {
|
|
636
|
+
priceResBySymbol[entries[i].symbol] = res;
|
|
637
|
+
});
|
|
638
|
+
return { activeAssets, priceResBySymbol };
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Fallback path, TWO multicalls: balances plus getPriceOracle (resolving the oracle that was active
|
|
643
|
+
* AT the block — Aave can rotate it over time), then prices for just the active assets. The oracle
|
|
644
|
+
* read has no fallback: if it fails (bad archive node, rate limit, ...) the failure must surface
|
|
645
|
+
* rather than silently returning $0 — the caller renders a gap instead of a misleading zero.
|
|
646
|
+
*/
|
|
647
|
+
const fetchHistoricalPointResolvingOracle = async (provider: Client, network: NetworkNumber, market: AaveMarketInfo, entries: AaveV3ReserveTokenEntry[], address: EthAddress, block: number) => {
|
|
648
|
+
const balanceContracts = getHistoricalBalanceContracts(entries, address);
|
|
649
|
+
// @ts-ignore market.provider is a valid config key at runtime
|
|
650
|
+
const providerAbi = getConfigContractAbi(market.provider, network);
|
|
651
|
+
const oracleContract = {
|
|
652
|
+
address: market.providerAddress as EthAddress,
|
|
653
|
+
abi: providerAbi,
|
|
654
|
+
functionName: 'getPriceOracle',
|
|
655
|
+
};
|
|
656
|
+
const blockNumber = BigInt(block);
|
|
657
|
+
|
|
658
|
+
// @ts-ignore
|
|
659
|
+
const results = await provider.multicall({
|
|
660
|
+
contracts: [...balanceContracts, oracleContract], allowFailure: true, blockNumber, batchSize: HISTORICAL_MULTICALL_BATCH_SIZE,
|
|
661
|
+
});
|
|
662
|
+
const oracleRes = results[balanceContracts.length];
|
|
663
|
+
if (oracleRes?.status !== 'success' || !oracleRes.result) throw new Error(`AaveV3 historical balance: oracle unavailable at block ${block}`);
|
|
664
|
+
|
|
665
|
+
const activeAssets = toActiveHistoricalAssets(entries, results.slice(0, balanceContracts.length), block);
|
|
666
|
+
if (!activeAssets.length) return { activeAssets, priceResBySymbol: {} };
|
|
667
|
+
|
|
668
|
+
const priceContracts = activeAssets.map((a) => getAssetPriceContract(oracleRes.result as EthAddress, a.underlyingAddress));
|
|
669
|
+
// @ts-ignore
|
|
670
|
+
const priceResults = await provider.multicall({
|
|
671
|
+
contracts: priceContracts, allowFailure: true, blockNumber, batchSize: HISTORICAL_MULTICALL_BATCH_SIZE,
|
|
672
|
+
});
|
|
673
|
+
const priceResBySymbol: AaveV3PriceResultsBySymbol = {};
|
|
674
|
+
priceResults.forEach((res: any, i: number) => {
|
|
675
|
+
priceResBySymbol[activeAssets[i].symbol] = res;
|
|
676
|
+
});
|
|
677
|
+
return { activeAssets, priceResBySymbol };
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Computes a user's Aave v3 net USD balance (supplied collateral - borrowed debt) at a historical block,
|
|
682
|
+
* without touching the AaveV3View contract. Pass `reserveTokens` (from getAaveV3ReserveTokenAddresses)
|
|
683
|
+
* to avoid refetching token addresses for every point, and `oracleAddress` (from getAaveV3OracleAddress,
|
|
684
|
+
* verified identical at both ends of the sampled range) to collapse the point into a single multicall —
|
|
685
|
+
* with it the balances and all prices ride one request; without it the oracle is resolved at the block
|
|
686
|
+
* inside the balance multicall and the active assets are priced in a second one.
|
|
687
|
+
*/
|
|
688
|
+
export const _getAaveV3HistoricalBalance = async (
|
|
689
|
+
provider: Client,
|
|
690
|
+
network: NetworkNumber,
|
|
691
|
+
market: AaveMarketInfo,
|
|
692
|
+
address: EthAddress,
|
|
693
|
+
block: number,
|
|
694
|
+
reserveTokens?: AaveV3ReserveTokenAddresses,
|
|
695
|
+
oracleAddress?: EthAddress,
|
|
696
|
+
): Promise<AaveV3HistoricalBalance> => {
|
|
697
|
+
const empty: AaveV3HistoricalBalance = {
|
|
698
|
+
block, suppliedUsd: '0', borrowedUsd: '0', netUsd: '0',
|
|
699
|
+
};
|
|
700
|
+
if (!address) return empty;
|
|
701
|
+
|
|
702
|
+
const tokens = reserveTokens || await _getAaveV3ReserveTokenAddresses(provider, network, market);
|
|
703
|
+
const entries = Object.values(tokens);
|
|
704
|
+
if (!entries.length) return empty;
|
|
705
|
+
|
|
706
|
+
const { activeAssets, priceResBySymbol } = oracleAddress
|
|
707
|
+
? await fetchHistoricalPointWithOracle(provider, entries, address, block, oracleAddress)
|
|
708
|
+
: await fetchHistoricalPointResolvingOracle(provider, network, market, entries, address, block);
|
|
709
|
+
|
|
710
|
+
if (!activeAssets.length) return empty;
|
|
711
|
+
return sumHistoricalUsd(activeAssets, priceResBySymbol, block);
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
export const getAaveV3HistoricalBalance = async (
|
|
715
|
+
provider: EthereumProvider,
|
|
716
|
+
network: NetworkNumber,
|
|
717
|
+
market: AaveMarketInfo,
|
|
718
|
+
address: EthAddress,
|
|
719
|
+
block: number,
|
|
720
|
+
reserveTokens?: AaveV3ReserveTokenAddresses,
|
|
721
|
+
oracleAddress?: EthAddress,
|
|
722
|
+
): Promise<AaveV3HistoricalBalance> => _getAaveV3HistoricalBalance(getViemProvider(provider, network, { batch: { multicall: true } }), network, market, address, block, reserveTokens, oracleAddress);
|
|
723
|
+
|
|
434
724
|
export const _getAaveV3AccountData = async (provider: Client, network: NetworkNumber, address: EthAddress, extractedState: any, blockNumber: 'latest' | number = 'latest'): Promise<AaveV3PositionData> => {
|
|
435
725
|
const {
|
|
436
726
|
selectedMarket: market, assetsData, eModeCategoriesData,
|
package/src/aaveV4/lend.ts
CHANGED
|
@@ -7,26 +7,9 @@ import {
|
|
|
7
7
|
EthereumProvider,
|
|
8
8
|
NetworkNumber,
|
|
9
9
|
} from '../types/common';
|
|
10
|
+
import { AaveV4TokenizationSpokeDataParsed } from '../types/aaveV4';
|
|
10
11
|
|
|
11
|
-
export
|
|
12
|
-
underlyingAsset: EthAddress;
|
|
13
|
-
assetId: string;
|
|
14
|
-
decimals: number;
|
|
15
|
-
spoke: EthAddress;
|
|
16
|
-
spokeActive: boolean;
|
|
17
|
-
spokeHalted: boolean;
|
|
18
|
-
spokeDepositCap: string;
|
|
19
|
-
spokeTotalAssets: string;
|
|
20
|
-
spokeTotalShares: string;
|
|
21
|
-
hub: EthAddress;
|
|
22
|
-
hubLiquidity: string;
|
|
23
|
-
hubDrawnRate: string;
|
|
24
|
-
convertToShares: string;
|
|
25
|
-
convertToAssets: string;
|
|
26
|
-
user: EthAddress;
|
|
27
|
-
userSuppliedAssets: string;
|
|
28
|
-
userSuppliedShares: string;
|
|
29
|
-
}
|
|
12
|
+
export type { AaveV4TokenizationSpokeData, AaveV4TokenizationSpokeDataParsed } from '../types/aaveV4';
|
|
30
13
|
|
|
31
14
|
export const AAVE_V4_TOKENIZED_SPOKES: Record<string, EthAddress> = {
|
|
32
15
|
EURC_CORE: '0x6D9e2Cdd61CaF69af99b275704B6e272C41c6718',
|
|
@@ -102,34 +85,6 @@ export const aaveV4GetTokenizedVaultAddress = (
|
|
|
102
85
|
return AAVE_V4_TOKENIZED_SPOKES[key];
|
|
103
86
|
};
|
|
104
87
|
|
|
105
|
-
/** Parsed tokenization spoke data with human-readable supplied amounts for display */
|
|
106
|
-
export interface AaveV4TokenizationSpokeDataParsed {
|
|
107
|
-
vaultAddress: EthAddress;
|
|
108
|
-
key: string | null;
|
|
109
|
-
symbol: string;
|
|
110
|
-
hubKey: string;
|
|
111
|
-
// ---- Spoke ----
|
|
112
|
-
spokeActive: boolean;
|
|
113
|
-
spokeHalted: boolean;
|
|
114
|
-
/** Deposit cap in asset units (wei string) */
|
|
115
|
-
spokeDepositCap: string;
|
|
116
|
-
/** Total assets currently in spoke in asset units (wei string) */
|
|
117
|
-
spokeTotalAssets: string;
|
|
118
|
-
// ---- Hub ----
|
|
119
|
-
/** Available hub liquidity in asset units (wei string) */
|
|
120
|
-
hubLiquidity: string;
|
|
121
|
-
/** The conversion rate from assets to shares expressed in asset units. */
|
|
122
|
-
convertToShares: string;
|
|
123
|
-
// ---- User ----
|
|
124
|
-
userSuppliedAssetsEth: string;
|
|
125
|
-
userSuppliedSharesEth: string;
|
|
126
|
-
userSuppliedAssets: string;
|
|
127
|
-
userSuppliedShares: string;
|
|
128
|
-
underlyingAsset: EthAddress;
|
|
129
|
-
spoke: EthAddress;
|
|
130
|
-
decimals: number;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
88
|
const AAVE_V4_TOKENIZED_SPOKE_ADDRESS_TO_KEY: Record<string, string> = Object.entries(
|
|
134
89
|
AAVE_V4_TOKENIZED_SPOKES,
|
|
135
90
|
).reduce((acc, [k, v]) => {
|
|
@@ -189,4 +144,3 @@ export async function getAaveV4TokenizationSpokesData(
|
|
|
189
144
|
}).filter(item => item != null);
|
|
190
145
|
}
|
|
191
146
|
|
|
192
|
-
|
package/src/curveUsd/index.ts
CHANGED
|
@@ -14,6 +14,13 @@ import { getCrvUsdAggregatedData } from '../helpers/curveUsdHelpers';
|
|
|
14
14
|
import { CrvUsdMarkets } from '../markets';
|
|
15
15
|
import { wethToEth } from '../services/utils';
|
|
16
16
|
import { getViemProvider, setViemBlockNumber } from '../services/viem';
|
|
17
|
+
import { getStakingApy, STAKING_ASSETS } from '../staking';
|
|
18
|
+
|
|
19
|
+
/** Converts a per-second AMM rate (wei, 18 decimals) into a yearly compounded borrow APY in %. */
|
|
20
|
+
const ammRateToBorrowApy = (rateWei: string) => {
|
|
21
|
+
const exponent = new Dec(assetAmountInEth(rateWei)).mul(365).mul(86400);
|
|
22
|
+
return new Dec(new Dec(2.718281828459).pow(exponent).minus(1)).mul(100).toString();
|
|
23
|
+
};
|
|
17
24
|
|
|
18
25
|
const getAndFormatBands = async (provider: Client, network: NetworkNumber, selectedMarket: CrvUSDMarketData, _minBand: string, _maxBand: string) => {
|
|
19
26
|
const contract = CrvUSDViewContractViem(provider, network);
|
|
@@ -69,15 +76,8 @@ export const _getCurveUsdGlobalData = async (provider: Client, network: NetworkN
|
|
|
69
76
|
const totalDebt = assetAmountInEth(data.totalDebt.toString(), debtAsset);
|
|
70
77
|
const ammPrice = assetAmountInEth(data.ammPrice.toString(), debtAsset);
|
|
71
78
|
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
const exponentRate = new Dec(rate).mul(365).mul(86400);
|
|
76
|
-
const exponentFutureRate = new Dec(futureRate).mul(365).mul(86400);
|
|
77
|
-
const borrowRate = new Dec(new Dec(2.718281828459).pow(exponentRate).minus(1)).mul(100)
|
|
78
|
-
.toString();
|
|
79
|
-
const futureBorrowRate = new Dec(new Dec(2.718281828459).pow(exponentFutureRate).minus(1)).mul(100)
|
|
80
|
-
.toString();
|
|
79
|
+
const borrowRate = ammRateToBorrowApy(data.ammRate.toString());
|
|
80
|
+
const futureBorrowRate = ammRateToBorrowApy(data.monetaryPolicyRate.toString());
|
|
81
81
|
|
|
82
82
|
const bandsData = await getAndFormatBands(provider, network, selectedMarket, data.minBand.toString(), data.maxBand.toString());
|
|
83
83
|
|
|
@@ -164,13 +164,22 @@ export const getCrvUsdAccountBalances = async (
|
|
|
164
164
|
controllerAddress: EthAddress,
|
|
165
165
|
): Promise<PositionBalances> => _getCrvUsdAccountBalances(getViemProvider(provider, network), network, block, addressMapping, address, controllerAddress);
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
/**
|
|
168
|
+
* @param borrowRate market borrow APY in % (`CrvUSDGlobalMarketData.borrowRate`); when omitted it is read from the view contract
|
|
169
|
+
*/
|
|
170
|
+
export const _getCurveUsdUserData = async (provider: Client, network: NetworkNumber, address: EthAddress, selectedMarket: CrvUSDMarketData, activeBand: string, borrowRate?: string): Promise<CrvUSDUserData> => {
|
|
168
171
|
const contract = CrvUSDViewContractViem(provider, network);
|
|
169
|
-
|
|
170
|
-
const data = await contract.read.userData([selectedMarket.controllerAddress, address]);
|
|
171
172
|
const collAsset = selectedMarket.collAsset;
|
|
172
173
|
const debtAsset = selectedMarket.baseAsset;
|
|
173
174
|
|
|
175
|
+
const [data, collStakingApy, marketBorrowRate] = await Promise.all([
|
|
176
|
+
contract.read.userData([selectedMarket.controllerAddress, address]),
|
|
177
|
+
STAKING_ASSETS.includes(collAsset) ? getStakingApy(collAsset) : Promise.resolve('0'),
|
|
178
|
+
borrowRate !== undefined
|
|
179
|
+
? Promise.resolve(borrowRate)
|
|
180
|
+
: contract.read.globalData([selectedMarket.controllerAddress]).then((globalData) => ammRateToBorrowApy(globalData.ammRate.toString())),
|
|
181
|
+
]);
|
|
182
|
+
|
|
174
183
|
const health = assetAmountInEth(data.health.toString());
|
|
175
184
|
const healthPercent = new Dec(health).mul(100).toString();
|
|
176
185
|
const collPrice = assetAmountInEth(data.collateralPrice.toString(), debtAsset);
|
|
@@ -202,7 +211,7 @@ export const _getCurveUsdUserData = async (provider: Client, network: NetworkNum
|
|
|
202
211
|
isBorrowed: new Dec(debtBorrowed).gt('0'),
|
|
203
212
|
symbol: 'crvUSD',
|
|
204
213
|
price: '1',
|
|
205
|
-
interestRate:
|
|
214
|
+
interestRate: marketBorrowRate,
|
|
206
215
|
},
|
|
207
216
|
} : {};
|
|
208
217
|
|
|
@@ -232,8 +241,15 @@ export const _getCurveUsdUserData = async (provider: Client, network: NetworkNum
|
|
|
232
241
|
numOfBands: data.N.toString(),
|
|
233
242
|
usedAssets,
|
|
234
243
|
status,
|
|
244
|
+
borrowRate: marketBorrowRate,
|
|
235
245
|
...getCrvUsdAggregatedData({
|
|
236
|
-
loanExists: data.loanExists,
|
|
246
|
+
loanExists: data.loanExists,
|
|
247
|
+
usedAssets,
|
|
248
|
+
network: NetworkNumber.Eth,
|
|
249
|
+
selectedMarket,
|
|
250
|
+
numOfBands: data.N.toString(),
|
|
251
|
+
borrowRate: marketBorrowRate,
|
|
252
|
+
collStakingApy,
|
|
237
253
|
}),
|
|
238
254
|
userBands,
|
|
239
255
|
};
|
|
@@ -245,10 +261,11 @@ export const getCurveUsdUserData = async (
|
|
|
245
261
|
address: EthAddress,
|
|
246
262
|
selectedMarket: CrvUSDMarketData,
|
|
247
263
|
activeBand: string,
|
|
248
|
-
|
|
264
|
+
borrowRate?: string,
|
|
265
|
+
): Promise<CrvUSDUserData> => _getCurveUsdUserData(getViemProvider(provider, network), network, address, selectedMarket, activeBand, borrowRate);
|
|
249
266
|
|
|
250
267
|
export const getCurveUsdFullPositionData = async (provider: EthereumProvider, network: NetworkNumber, address: EthAddress, selectedMarket: CrvUSDMarketData): Promise<CrvUSDUserData> => {
|
|
251
268
|
const marketData = await getCurveUsdGlobalData(provider, network, selectedMarket);
|
|
252
|
-
const positionData = await getCurveUsdUserData(provider, network, address, selectedMarket, marketData.activeBand);
|
|
269
|
+
const positionData = await getCurveUsdUserData(provider, network, address, selectedMarket, marketData.activeBand, marketData.borrowRate);
|
|
253
270
|
return positionData;
|
|
254
271
|
};
|
package/src/fluid/index.ts
CHANGED
|
@@ -1831,3 +1831,43 @@ export const _getUserPositionsPortfolio = async (provider: PublicClient, network
|
|
|
1831
1831
|
userData: userData[i],
|
|
1832
1832
|
})).filter(md => md.marketData !== undefined);
|
|
1833
1833
|
};
|
|
1834
|
+
|
|
1835
|
+
|
|
1836
|
+
export const _getAllFluidMarketDataPortfolio = async (provider: PublicClient, network: NetworkNumber): Promise<Record<string, FluidMarketData>> => {
|
|
1837
|
+
const versions = getFluidVersionsDataForNetwork(network);
|
|
1838
|
+
if (versions.length === 0) return {};
|
|
1839
|
+
|
|
1840
|
+
const view = FluidViewContractViem(provider, network);
|
|
1841
|
+
const vaultsData = await Promise.all(versions.map((version) => view.read.getVaultData([version.marketAddress])));
|
|
1842
|
+
|
|
1843
|
+
const tokens = Array.from(new Set(vaultsData.map((vaultData) => {
|
|
1844
|
+
const vaultTokens = [getAssetInfoByAddress(vaultData.supplyToken0, network).symbol, getAssetInfoByAddress(vaultData.borrowToken0, network).symbol];
|
|
1845
|
+
if (vaultData.supplyToken1 && !compareAddresses(ZERO_ADDRESS, vaultData.supplyToken1)) vaultTokens.push(getAssetInfoByAddress(vaultData.supplyToken1, network).symbol);
|
|
1846
|
+
if (vaultData.borrowToken1 && !compareAddresses(ZERO_ADDRESS, vaultData.borrowToken1)) vaultTokens.push(getAssetInfoByAddress(vaultData.borrowToken1, network).symbol);
|
|
1847
|
+
return vaultTokens;
|
|
1848
|
+
}).flat()));
|
|
1849
|
+
|
|
1850
|
+
// ETH and WBTC needed for other tokens prices
|
|
1851
|
+
if (!tokens.includes('ETH')) tokens.push('ETH');
|
|
1852
|
+
if (!tokens.includes('WBTC')) tokens.push('WBTC');
|
|
1853
|
+
|
|
1854
|
+
const [tokenPrices, merklCampaigns] = await Promise.all([
|
|
1855
|
+
getTokensPricesForPortfolio(tokens, provider, network),
|
|
1856
|
+
getFluidMerklCampaigns(network),
|
|
1857
|
+
]);
|
|
1858
|
+
|
|
1859
|
+
const parsedMarketsData = await Promise.all(vaultsData.map(async (vaultData) => parseMarketData(provider, vaultData, network, tokenPrices)));
|
|
1860
|
+
|
|
1861
|
+
const marketsData: Record<string, FluidMarketData> = {};
|
|
1862
|
+
parsedMarketsData.forEach((marketData, i) => {
|
|
1863
|
+
if (!marketData) return;
|
|
1864
|
+
marketsData[versions[i].value] = attachFluidMerklIncentives(marketData, merklCampaigns);
|
|
1865
|
+
});
|
|
1866
|
+
|
|
1867
|
+
return marketsData;
|
|
1868
|
+
};
|
|
1869
|
+
|
|
1870
|
+
export const getAllFluidMarketDataPortfolio = async (
|
|
1871
|
+
provider: EthereumProvider,
|
|
1872
|
+
network: NetworkNumber,
|
|
1873
|
+
): Promise<Record<string, FluidMarketData>> => _getAllFluidMarketDataPortfolio(getViemProvider(provider, network, { batch: { multicall: true } }), network);
|
|
@@ -2,7 +2,7 @@ import Dec from 'decimal.js';
|
|
|
2
2
|
import { assetAmountInWei, getAssetInfo, getAssetInfoByAddress } from '@defisaver/tokens';
|
|
3
3
|
import { Client } from 'viem';
|
|
4
4
|
import {
|
|
5
|
-
AaveAssetData, AaveHelperCommon, AaveMarketInfo, AaveV3AggregatedPositionData, AaveV3AssetsData, AaveV3UsedAsset, AaveV3UsedAssets, AaveVersions,
|
|
5
|
+
AaveAssetData, AaveHelperCommon, AaveMarketInfo, AaveV3AggregatedPositionData, AaveV3AssetsData, AaveV3UsedAsset, AaveV3UsedAssets, AaveVersions, EModeCategoriesData,
|
|
6
6
|
} from '../../types';
|
|
7
7
|
import { getNativeAssetFromWrapped, getWrappedNativeAssetFromUnwrapped } from '../../services/utils';
|
|
8
8
|
import {
|
|
@@ -24,6 +24,13 @@ export const isAaveV3 = ({ selectedMarket }: { selectedMarket: Partial<AaveMarke
|
|
|
24
24
|
export const aaveV3IsInIsolationMode = ({ usedAssets, assetsData }: { usedAssets: AaveV3UsedAssets, assetsData: AaveV3AssetsData }) => Object.values(usedAssets).some(({ symbol, collateral }) => collateral && assetsData[symbol].isIsolated);
|
|
25
25
|
export const aaveV3IsInSiloedMode = ({ usedAssets, assetsData }: { usedAssets: AaveV3UsedAssets, assetsData: AaveV3AssetsData }) => Object.values(usedAssets).some(({ symbol, debt }) => debt && assetsData[symbol].isSiloed);
|
|
26
26
|
|
|
27
|
+
export const aaveV3IsEmodeIsolated = ({ eModeCategory, eModeCategoriesData }: { eModeCategory: number, eModeCategoriesData?: EModeCategoriesData }) => eModeCategory !== 0 && !!eModeCategoriesData?.[eModeCategory]?.isolated;
|
|
28
|
+
|
|
29
|
+
export const aaveV3CanBeCollateralInEmode = ({ eModeCategory, eModeCategoriesData }: { eModeCategory: number, eModeCategoriesData?: EModeCategoriesData }, _asset: string) => {
|
|
30
|
+
if (!aaveV3IsEmodeIsolated({ eModeCategory, eModeCategoriesData })) return true;
|
|
31
|
+
return eModeCategoriesData![eModeCategory].collateralAssets.includes(getNativeAssetFromWrapped(_asset));
|
|
32
|
+
};
|
|
33
|
+
|
|
27
34
|
export const aaveAnyGetCollSuppliedAssets = ({ usedAssets }: { usedAssets: AaveV3UsedAssets }) => Object.values(usedAssets)
|
|
28
35
|
.filter(({ isSupplied, collateral }: { isSupplied: boolean, collateral: boolean }) => isSupplied && collateral);
|
|
29
36
|
|
|
@@ -37,14 +44,18 @@ export const aaveAnyGetSuppliableAssets = ({
|
|
|
37
44
|
const collAccountAssets = aaveAnyGetCollSuppliedAssets(data);
|
|
38
45
|
const marketAssets = Object.values(assetsData) as AaveAssetData[];
|
|
39
46
|
|
|
40
|
-
if (
|
|
47
|
+
if (!isAaveV3(data)) return marketAssets.filter(({ canBeSupplied }) => canBeSupplied).map(({ symbol }) => ({ symbol, canBeCollateral: true }));
|
|
48
|
+
|
|
49
|
+
const canBeCollateralInEmode = (symbol: string) => aaveV3CanBeCollateralInEmode(data, symbol);
|
|
50
|
+
|
|
51
|
+
if (collAccountAssets.length === 0) return marketAssets.filter(({ canBeSupplied }) => canBeSupplied).map(({ symbol }) => ({ symbol, canBeCollateral: canBeCollateralInEmode(symbol) }));
|
|
41
52
|
|
|
42
53
|
if (aaveV3IsInIsolationMode(data)) {
|
|
43
54
|
const collAsset = collAccountAssets[0].symbol;
|
|
44
|
-
return marketAssets.filter(d => d.canBeSupplied).map(({ symbol }) => ({ symbol, canBeCollateral: symbol === collAsset }));
|
|
55
|
+
return marketAssets.filter(d => d.canBeSupplied).map(({ symbol }) => ({ symbol, canBeCollateral: symbol === collAsset && canBeCollateralInEmode(symbol) }));
|
|
45
56
|
}
|
|
46
57
|
|
|
47
|
-
return marketAssets.filter(d => d.canBeSupplied).map(({ symbol, isIsolated }) => ({ symbol, canBeCollateral: !isIsolated }));
|
|
58
|
+
return marketAssets.filter(d => d.canBeSupplied).map(({ symbol, isIsolated }) => ({ symbol, canBeCollateral: !isIsolated && canBeCollateralInEmode(symbol) }));
|
|
48
59
|
};
|
|
49
60
|
|
|
50
61
|
export const aaveAnyGetSuppliableAsCollAssets = ({
|
|
@@ -62,8 +73,8 @@ export const aaveAnyGetEmodeMutableProps = (
|
|
|
62
73
|
const asset = getNativeAssetFromWrapped(_asset);
|
|
63
74
|
|
|
64
75
|
const assetData = assetsData[asset];
|
|
65
|
-
const eModeCategoryData: { collateralAssets: string[], collateralFactor: string, liquidationRatio: string, ltvZeroAssets: string[] } = eModeCategoriesData?.[eModeCategory] || {
|
|
66
|
-
collateralAssets: [], ltvZeroAssets: [], collateralFactor: '0', liquidationRatio: '0',
|
|
76
|
+
const eModeCategoryData: { collateralAssets: string[], collateralFactor: string, liquidationRatio: string, ltvZeroAssets: string[], isolated?: boolean } = eModeCategoriesData?.[eModeCategory] || {
|
|
77
|
+
collateralAssets: [], ltvZeroAssets: [], collateralFactor: '0', liquidationRatio: '0', isolated: false,
|
|
67
78
|
};
|
|
68
79
|
|
|
69
80
|
if (
|
|
@@ -72,6 +83,9 @@ export const aaveAnyGetEmodeMutableProps = (
|
|
|
72
83
|
|| new Dec(eModeCategoryData.collateralFactor || 0).eq(0)
|
|
73
84
|
) {
|
|
74
85
|
const { liquidationRatio, collateralFactor } = assetData;
|
|
86
|
+
if (eModeCategory !== 0 && eModeCategoryData.isolated && !eModeCategoryData.collateralAssets.includes(asset)) {
|
|
87
|
+
return ({ liquidationRatio, collateralFactor: '0' });
|
|
88
|
+
}
|
|
75
89
|
return ({ liquidationRatio, collateralFactor });
|
|
76
90
|
}
|
|
77
91
|
if (eModeCategoryData.ltvZeroAssets.includes(asset)) return ({ liquidationRatio: '0', collateralFactor: '0' });
|
|
@@ -118,7 +132,10 @@ export const aaveAnyGetUserReserveLtvAndLltv = (
|
|
|
118
132
|
|| !eModeCategoryData.collateralAssets.includes(asset)
|
|
119
133
|
|| new Dec(eModeCategoryData.collateralFactor || 0).eq(0)
|
|
120
134
|
) {
|
|
121
|
-
|
|
135
|
+
const ltv = eModeCategory !== 0 && eModeCategoryData?.isolated && !eModeCategoryData.collateralAssets.includes(asset)
|
|
136
|
+
? '0'
|
|
137
|
+
: assetData.collateralFactor;
|
|
138
|
+
return { ltv, lltv: assetData.liquidationRatio };
|
|
122
139
|
}
|
|
123
140
|
|
|
124
141
|
const ltv = eModeCategoryData.ltvZeroAssets.includes(asset) ? '0' : eModeCategoryData.collateralFactor;
|
|
@@ -169,6 +186,9 @@ export const aaveAnyGetAggregatedPositionData = ({
|
|
|
169
186
|
},
|
|
170
187
|
);
|
|
171
188
|
payload.safetyRatioWithLtvZeroFallback = +payload.suppliedUsd ? new Dec(payload.borrowLimitWithLtvZeroFallbackUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
189
|
+
// Normalised safety ratio (100 = liquidation on every protocol): the automation ratio above, falling back
|
|
190
|
+
// to the regular ratio when the fallback carries its '0' placeholder.
|
|
191
|
+
payload.safetyRatio = +payload.safetyRatioWithLtvZeroFallback ? payload.safetyRatioWithLtvZeroFallback : payload.ratio;
|
|
172
192
|
payload.liqRatio = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).toString();
|
|
173
193
|
payload.liqPercent = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).mul(100).toString();
|
|
174
194
|
const { leveragedType, leveragedAsset } = isLeveragedPos(usedAssets);
|
|
@@ -270,6 +270,7 @@ export const aaveV4GetAggregatedPositionData = ({
|
|
|
270
270
|
payload.ratio = +payload.suppliedUsd ? new Dec(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
271
271
|
payload.minRatio = '100';
|
|
272
272
|
payload.collRatio = +payload.suppliedUsd ? new Dec(payload.suppliedCollateralUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
273
|
+
payload.safetyRatio = payload.ratio;
|
|
273
274
|
payload.liqRatio = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).toString();
|
|
274
275
|
payload.liqPercent = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).mul(100).toString();
|
|
275
276
|
const { leveragedType, leveragedAsset } = isLeveragedPosAaveV4(usedAssets);
|
|
@@ -139,6 +139,7 @@ export const getCompoundV2AggregatedData = ({
|
|
|
139
139
|
payload.collRatio = payload.borrowedUsd && payload.borrowedUsd !== '0'
|
|
140
140
|
? new Dec(payload.suppliedCollateralUsd).div(payload.borrowedUsd).mul(100).toString()
|
|
141
141
|
: '0';
|
|
142
|
+
payload.safetyRatio = payload.ratio;
|
|
142
143
|
|
|
143
144
|
// Calculate borrow limits per asset
|
|
144
145
|
Object.values(usedAssets).forEach((item) => {
|
|
@@ -185,6 +186,7 @@ export const getCompoundV3AggregatedData = ({
|
|
|
185
186
|
payload.incentiveUsd = incentiveUsd;
|
|
186
187
|
payload.totalInterestUsd = totalInterestUsd;
|
|
187
188
|
payload.minRatio = '100';
|
|
189
|
+
payload.safetyRatio = payload.ratio;
|
|
188
190
|
payload.liqRatio = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).toString();
|
|
189
191
|
payload.liqPercent = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).mul(100).toString();
|
|
190
192
|
payload.minDebt = assetsData[selectedMarket.baseAsset].minDebt;
|