@defisaver/positions-sdk 2.1.43-aave-v4-dev-1 → 2.1.43-aave-v4-dev-2
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/aaveV4/index.d.ts +2 -2
- package/cjs/aaveV4/index.js +73 -16
- package/cjs/helpers/aaveV4Helpers/index.d.ts +7 -0
- package/cjs/helpers/aaveV4Helpers/index.js +54 -0
- package/cjs/helpers/index.d.ts +1 -0
- package/cjs/helpers/index.js +2 -1
- package/cjs/portfolio/index.js +20 -0
- package/cjs/types/aaveV4.d.ts +49 -2
- package/cjs/types/portfolio.d.ts +4 -0
- package/esm/aaveV4/index.d.ts +2 -2
- package/esm/aaveV4/index.js +73 -16
- package/esm/helpers/aaveV4Helpers/index.d.ts +7 -0
- package/esm/helpers/aaveV4Helpers/index.js +47 -0
- package/esm/helpers/index.d.ts +1 -0
- package/esm/helpers/index.js +1 -0
- package/esm/portfolio/index.js +21 -1
- package/esm/types/aaveV4.d.ts +49 -2
- package/esm/types/portfolio.d.ts +4 -0
- package/package.json +2 -2
- package/src/aaveV4/index.ts +84 -15
- package/src/helpers/aaveV4Helpers/index.ts +66 -0
- package/src/helpers/index.ts +1 -0
- package/src/portfolio/index.ts +20 -0
- package/src/types/aaveV4.ts +54 -2
- package/src/types/portfolio.ts +4 -0
package/cjs/aaveV4/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Client } from 'viem';
|
|
2
|
-
import { AaveV4SpokeData, AaveV4SpokeInfo } from '../types';
|
|
2
|
+
import { AaveV4AccountData, AaveV4SpokeData, AaveV4SpokeInfo } from '../types';
|
|
3
3
|
import { EthAddress, EthereumProvider, NetworkNumber } from '../types/common';
|
|
4
4
|
export declare function _getAaveV4SpokeData(provider: Client, network: NetworkNumber, market: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
|
|
5
5
|
export declare function getAaveV4SpokeData(provider: EthereumProvider, network: NetworkNumber, spoke: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
|
|
6
|
-
export declare function _getAaveV4AccountData(provider: Client, network: NetworkNumber,
|
|
6
|
+
export declare function _getAaveV4AccountData(provider: Client, network: NetworkNumber, spokeData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<AaveV4AccountData>;
|
|
7
7
|
export declare function getAaveV4AccountData(provider: EthereumProvider, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<any>;
|
package/cjs/aaveV4/index.js
CHANGED
|
@@ -19,10 +19,13 @@ exports.getAaveV4AccountData = getAaveV4AccountData;
|
|
|
19
19
|
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
20
20
|
const tokens_1 = require("@defisaver/tokens");
|
|
21
21
|
const viem_1 = require("../services/viem");
|
|
22
|
+
const common_1 = require("../types/common");
|
|
22
23
|
const contracts_1 = require("../contracts");
|
|
24
|
+
const staking_1 = require("../staking");
|
|
25
|
+
const utils_1 = require("../services/utils");
|
|
26
|
+
const aaveV4Helpers_1 = require("../helpers/aaveV4Helpers");
|
|
23
27
|
const fetchHubData = (viewContract, hubAddress) => __awaiter(void 0, void 0, void 0, function* () {
|
|
24
28
|
const hubData = yield viewContract.read.getHubAllAssetsData([hubAddress]);
|
|
25
|
-
console.log('hubData', hubData);
|
|
26
29
|
return {
|
|
27
30
|
assets: hubData.reduce((acc, assetOnChainData) => {
|
|
28
31
|
acc[assetOnChainData.assetId] = {
|
|
@@ -33,10 +36,32 @@ const fetchHubData = (viewContract, hubAddress) => __awaiter(void 0, void 0, voi
|
|
|
33
36
|
}, {}),
|
|
34
37
|
};
|
|
35
38
|
});
|
|
36
|
-
const formatReserveAsset = (reserveAsset, hubAsset, oracleDecimals) => {
|
|
37
|
-
const assetInfo = (0, tokens_1.getAssetInfoByAddress)(reserveAsset.underlying);
|
|
39
|
+
const formatReserveAsset = (reserveAsset, hubAsset, oracleDecimals, network) => __awaiter(void 0, void 0, void 0, function* () {
|
|
40
|
+
const assetInfo = (0, tokens_1.getAssetInfoByAddress)(reserveAsset.underlying, network);
|
|
41
|
+
const symbol = (0, utils_1.wethToEth)(assetInfo.symbol);
|
|
42
|
+
const isStakingAsset = staking_1.STAKING_ASSETS.includes(symbol);
|
|
43
|
+
const supplyIncentives = [];
|
|
44
|
+
const borrowIncentives = [];
|
|
45
|
+
if (isStakingAsset) {
|
|
46
|
+
const yieldApy = yield (0, staking_1.getStakingApy)(symbol, network);
|
|
47
|
+
supplyIncentives.push({
|
|
48
|
+
apy: yieldApy,
|
|
49
|
+
token: symbol,
|
|
50
|
+
incentiveKind: common_1.IncentiveKind.Staking,
|
|
51
|
+
description: `Native ${symbol} yield.`,
|
|
52
|
+
});
|
|
53
|
+
if (reserveAsset.borrowable) {
|
|
54
|
+
// when borrowing assets whose value increases over time
|
|
55
|
+
borrowIncentives.push({
|
|
56
|
+
apy: new decimal_js_1.default(yieldApy).mul(-1).toString(),
|
|
57
|
+
token: symbol,
|
|
58
|
+
incentiveKind: common_1.IncentiveKind.Reward,
|
|
59
|
+
description: `Due to the native yield of ${symbol}, the value of the debt would increase over time.`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
38
63
|
return ({
|
|
39
|
-
symbol
|
|
64
|
+
symbol,
|
|
40
65
|
underlying: reserveAsset.underlying,
|
|
41
66
|
hub: reserveAsset.hub,
|
|
42
67
|
assetId: reserveAsset.assetId,
|
|
@@ -47,17 +72,20 @@ const formatReserveAsset = (reserveAsset, hubAsset, oracleDecimals) => {
|
|
|
47
72
|
collateralFactor: new decimal_js_1.default(reserveAsset.collateralFactor).div(10000).toNumber(),
|
|
48
73
|
liquidationFee: new decimal_js_1.default(reserveAsset.liquidationFee).div(10000).toNumber(),
|
|
49
74
|
price: new decimal_js_1.default(reserveAsset.price).div(new decimal_js_1.default(10).pow(oracleDecimals)).toString(),
|
|
50
|
-
totalSupplied: (0, tokens_1.assetAmountInEth)(reserveAsset.totalSupplied.toString(),
|
|
51
|
-
totalDrawn: (0, tokens_1.assetAmountInEth)(reserveAsset.totalDrawn.toString(),
|
|
52
|
-
totalPremium: (0, tokens_1.assetAmountInEth)(reserveAsset.totalPremium.toString(),
|
|
53
|
-
totalDebt: (0, tokens_1.assetAmountInEth)(reserveAsset.totalDebt.toString(),
|
|
54
|
-
supplyCap: (0, tokens_1.assetAmountInEth)(reserveAsset.supplyCap.toString(),
|
|
55
|
-
borrowCap: (0, tokens_1.assetAmountInEth)(reserveAsset.borrowCap.toString(),
|
|
75
|
+
totalSupplied: (0, tokens_1.assetAmountInEth)(reserveAsset.totalSupplied.toString(), symbol),
|
|
76
|
+
totalDrawn: (0, tokens_1.assetAmountInEth)(reserveAsset.totalDrawn.toString(), symbol),
|
|
77
|
+
totalPremium: (0, tokens_1.assetAmountInEth)(reserveAsset.totalPremium.toString(), symbol),
|
|
78
|
+
totalDebt: (0, tokens_1.assetAmountInEth)(reserveAsset.totalDebt.toString(), symbol),
|
|
79
|
+
supplyCap: (0, tokens_1.assetAmountInEth)(reserveAsset.supplyCap.toString(), symbol),
|
|
80
|
+
borrowCap: (0, tokens_1.assetAmountInEth)(reserveAsset.borrowCap.toString(), symbol),
|
|
56
81
|
spokeActive: reserveAsset.spokeActive,
|
|
57
82
|
spokePaused: reserveAsset.spokePaused,
|
|
58
83
|
drawnRate: new decimal_js_1.default(hubAsset.drawnRate).div(new decimal_js_1.default(10).pow(27)).toString(),
|
|
84
|
+
supplyRate: '0', // To be implemented
|
|
85
|
+
supplyIncentives,
|
|
86
|
+
borrowIncentives,
|
|
59
87
|
});
|
|
60
|
-
};
|
|
88
|
+
});
|
|
61
89
|
function _getAaveV4SpokeData(provider_1, network_1, market_1) {
|
|
62
90
|
return __awaiter(this, arguments, void 0, function* (provider, network, market, blockNumber = 'latest') {
|
|
63
91
|
const viewContract = (0, contracts_1.AaveV4ViewContractViem)(provider, network, blockNumber);
|
|
@@ -68,7 +96,7 @@ function _getAaveV4SpokeData(provider_1, network_1, market_1) {
|
|
|
68
96
|
hubsData[hubAddress] = yield fetchHubData(viewContract, hubAddress);
|
|
69
97
|
})),
|
|
70
98
|
]);
|
|
71
|
-
const reserveAssetsArray = spokeData[1].map((reserveAssetOnChain) => formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], +spokeData[0].oracleDecimals.toString()));
|
|
99
|
+
const reserveAssetsArray = yield Promise.all(spokeData[1].map((reserveAssetOnChain) => __awaiter(this, void 0, void 0, function* () { return formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], +spokeData[0].oracleDecimals.toString(), network); })));
|
|
72
100
|
return {
|
|
73
101
|
assetsData: reserveAssetsArray.reduce((acc, reserveAsset) => {
|
|
74
102
|
acc[reserveAsset.symbol] = reserveAsset;
|
|
@@ -85,11 +113,40 @@ function getAaveV4SpokeData(provider_1, network_1, spoke_1) {
|
|
|
85
113
|
return _getAaveV4SpokeData((0, viem_1.getViemProvider)(provider, network), network, spoke, blockNumber);
|
|
86
114
|
});
|
|
87
115
|
}
|
|
88
|
-
function _getAaveV4AccountData(provider_1, network_1,
|
|
89
|
-
return __awaiter(this, arguments, void 0, function* (provider, network,
|
|
116
|
+
function _getAaveV4AccountData(provider_1, network_1, spokeData_1, address_1) {
|
|
117
|
+
return __awaiter(this, arguments, void 0, function* (provider, network, spokeData, address, blockNumber = 'latest') {
|
|
90
118
|
const viewContract = (0, contracts_1.AaveV4ViewContractViem)(provider, network, blockNumber);
|
|
91
|
-
const loanData = yield viewContract.read.getLoanData([
|
|
92
|
-
|
|
119
|
+
const loanData = yield viewContract.read.getLoanData([spokeData.address, address]);
|
|
120
|
+
const healthFactor = new decimal_js_1.default(loanData.healthFactor).div(1e18).toString();
|
|
121
|
+
const usedAssets = loanData.reserves.reduce((acc, usedReserveAsset) => {
|
|
122
|
+
const reserveData = spokeData.assetsData[(0, utils_1.wethToEth)((0, tokens_1.getAssetInfoByAddress)(usedReserveAsset.underlying, network).symbol)];
|
|
123
|
+
const price = reserveData.price;
|
|
124
|
+
const supplied = (0, tokens_1.assetAmountInEth)(usedReserveAsset.supplied.toString(), reserveData.symbol);
|
|
125
|
+
const drawn = (0, tokens_1.assetAmountInEth)(usedReserveAsset.drawn.toString(), reserveData.symbol);
|
|
126
|
+
const premium = (0, tokens_1.assetAmountInEth)(usedReserveAsset.premium.toString(), reserveData.symbol);
|
|
127
|
+
const borrowed = (0, tokens_1.assetAmountInEth)(usedReserveAsset.totalDebt.toString(), reserveData.symbol);
|
|
128
|
+
acc[reserveData.symbol] = {
|
|
129
|
+
symbol: reserveData.symbol,
|
|
130
|
+
supplied,
|
|
131
|
+
suppliedUsd: new decimal_js_1.default(supplied).mul(price).toString(),
|
|
132
|
+
drawn,
|
|
133
|
+
drawnUsd: new decimal_js_1.default(drawn).mul(price).toString(),
|
|
134
|
+
premium,
|
|
135
|
+
premiumUsd: new decimal_js_1.default(premium).mul(price).toString(),
|
|
136
|
+
borrowed,
|
|
137
|
+
borrowedUsd: new decimal_js_1.default(borrowed).mul(price).toString(),
|
|
138
|
+
isSupplied: !new decimal_js_1.default(supplied).eq(0),
|
|
139
|
+
isBorrowed: usedReserveAsset.isBorrowing,
|
|
140
|
+
collateral: usedReserveAsset.isUsingAsCollateral,
|
|
141
|
+
};
|
|
142
|
+
return acc;
|
|
143
|
+
}, {});
|
|
144
|
+
return Object.assign({ usedAssets,
|
|
145
|
+
healthFactor }, (0, aaveV4Helpers_1.aaveV4GetAggregatedPositionData)({
|
|
146
|
+
usedAssets,
|
|
147
|
+
assetsData: spokeData.assetsData,
|
|
148
|
+
network,
|
|
149
|
+
}));
|
|
93
150
|
});
|
|
94
151
|
}
|
|
95
152
|
function getAaveV4AccountData(provider_1, network_1, marketData_1, address_1) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AaveV4AggregatedPositionData, AaveV4AssetsData, AaveV4UsedReserveAssets } from '../../types';
|
|
2
|
+
import { NetworkNumber } from '../../types/common';
|
|
3
|
+
export declare const aaveV4GetAggregatedPositionData: ({ usedAssets, assetsData, network, }: {
|
|
4
|
+
usedAssets: AaveV4UsedReserveAssets;
|
|
5
|
+
assetsData: AaveV4AssetsData;
|
|
6
|
+
network: NetworkNumber;
|
|
7
|
+
}) => AaveV4AggregatedPositionData;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.aaveV4GetAggregatedPositionData = void 0;
|
|
7
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
8
|
+
const moneymarket_1 = require("../../moneymarket");
|
|
9
|
+
const aaveV4GetAggregatedPositionData = ({ usedAssets, assetsData, network, }) => {
|
|
10
|
+
const payload = {};
|
|
11
|
+
payload.suppliedUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isSupplied }) => isSupplied, ({ suppliedUsd }) => suppliedUsd);
|
|
12
|
+
payload.suppliedCollateralUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral, ({ suppliedUsd }) => suppliedUsd);
|
|
13
|
+
payload.borrowLimitUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral, ({ symbol, suppliedUsd }) => new decimal_js_1.default(suppliedUsd).mul(assetsData[symbol].collateralFactor));
|
|
14
|
+
payload.liquidationLimitUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral,
|
|
15
|
+
// TODO: Verify if liquidation factor is available in Aave V4, currently using collateralFactor as placeholder
|
|
16
|
+
({ symbol, suppliedUsd }) => new decimal_js_1.default(suppliedUsd).mul(assetsData[symbol].collateralFactor));
|
|
17
|
+
payload.borrowedUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isBorrowed }) => isBorrowed, ({ borrowedUsd }) => borrowedUsd);
|
|
18
|
+
payload.drawnUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isBorrowed }) => isBorrowed, ({ drawnUsd }) => drawnUsd);
|
|
19
|
+
payload.premiumUsd = (0, moneymarket_1.getAssetsTotal)(usedAssets, ({ isBorrowed }) => isBorrowed, ({ premiumUsd }) => premiumUsd);
|
|
20
|
+
const leftToBorrowUsd = new decimal_js_1.default(payload.borrowLimitUsd).sub(payload.borrowedUsd);
|
|
21
|
+
payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
|
|
22
|
+
payload.ratio = +payload.suppliedUsd ? new decimal_js_1.default(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
23
|
+
payload.collRatio = +payload.suppliedUsd ? new decimal_js_1.default(payload.suppliedCollateralUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
24
|
+
payload.liqRatio = new decimal_js_1.default(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).toString();
|
|
25
|
+
payload.liqPercent = new decimal_js_1.default(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).mul(100).toString();
|
|
26
|
+
const { leveragedType, leveragedAsset } = (0, moneymarket_1.isLeveragedPos)(usedAssets);
|
|
27
|
+
payload.leveragedType = leveragedType;
|
|
28
|
+
payload.leveragedAsset = leveragedAsset;
|
|
29
|
+
payload.liquidationPrice = '';
|
|
30
|
+
if (leveragedType !== '') {
|
|
31
|
+
let assetPrice = assetsData[leveragedAsset].price;
|
|
32
|
+
if (leveragedType === 'lsd-leverage') {
|
|
33
|
+
// Treat ETH like a stablecoin in a long stETH position
|
|
34
|
+
payload.leveragedLsdAssetRatio = new decimal_js_1.default(assetsData[leveragedAsset].price).div(assetsData.ETH.price).toDP(18).toString();
|
|
35
|
+
assetPrice = new decimal_js_1.default(assetPrice).div(assetsData.ETH.price).toString();
|
|
36
|
+
}
|
|
37
|
+
payload.liquidationPrice = (0, moneymarket_1.calcLeverageLiqPrice)(leveragedType, assetPrice, payload.borrowedUsd, payload.liquidationLimitUsd);
|
|
38
|
+
}
|
|
39
|
+
payload.minCollRatio = new decimal_js_1.default(payload.suppliedCollateralUsd).div(payload.borrowLimitUsd).mul(100).toString();
|
|
40
|
+
payload.collLiquidationRatio = new decimal_js_1.default(payload.suppliedCollateralUsd).div(payload.liquidationLimitUsd).mul(100).toString();
|
|
41
|
+
// payload.healthRatio = new Dec(payload.liquidationLimitUsd).div(payload.borrowedUsd).toDP(4).toString();
|
|
42
|
+
payload.minHealthRatio = new decimal_js_1.default(payload.liquidationLimitUsd).div(payload.borrowLimitUsd).toDP(4).toString();
|
|
43
|
+
// TODO: Re-implement netApy calculation
|
|
44
|
+
// const { netApy, incentiveUsd, totalInterestUsd } = calculateNetApy({
|
|
45
|
+
// usedAssets,
|
|
46
|
+
// assetsData,
|
|
47
|
+
// optionalData: { healthRatio: payload.healthRatio },
|
|
48
|
+
// });
|
|
49
|
+
payload.netApy = '0';
|
|
50
|
+
payload.incentiveUsd = '0';
|
|
51
|
+
payload.totalInterestUsd = '0';
|
|
52
|
+
return payload;
|
|
53
|
+
};
|
|
54
|
+
exports.aaveV4GetAggregatedPositionData = aaveV4GetAggregatedPositionData;
|
package/cjs/helpers/index.d.ts
CHANGED
package/cjs/helpers/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.fluidHelpers = exports.eulerV2Helpers = exports.liquityV2Helpers = exports.llamaLendHelpers = exports.morphoBlueHelpers = exports.makerHelpers = exports.curveUsdHelpers = exports.sparkHelpers = exports.compoundHelpers = exports.aaveHelpers = void 0;
|
|
36
|
+
exports.aaveV4Helpers = exports.fluidHelpers = exports.eulerV2Helpers = exports.liquityV2Helpers = exports.llamaLendHelpers = exports.morphoBlueHelpers = exports.makerHelpers = exports.curveUsdHelpers = exports.sparkHelpers = exports.compoundHelpers = exports.aaveHelpers = void 0;
|
|
37
37
|
exports.aaveHelpers = __importStar(require("./aaveHelpers"));
|
|
38
38
|
exports.compoundHelpers = __importStar(require("./compoundHelpers"));
|
|
39
39
|
exports.sparkHelpers = __importStar(require("./sparkHelpers"));
|
|
@@ -44,3 +44,4 @@ exports.llamaLendHelpers = __importStar(require("./llamaLendHelpers"));
|
|
|
44
44
|
exports.liquityV2Helpers = __importStar(require("./liquityV2Helpers"));
|
|
45
45
|
exports.eulerV2Helpers = __importStar(require("./eulerHelpers"));
|
|
46
46
|
exports.fluidHelpers = __importStar(require("./fluidHelpers"));
|
|
47
|
+
exports.aaveV4Helpers = __importStar(require("./aaveV4Helpers"));
|
package/cjs/portfolio/index.js
CHANGED
|
@@ -40,6 +40,7 @@ const spark_2 = require("../claiming/spark");
|
|
|
40
40
|
const morphoBlue_2 = require("../claiming/morphoBlue");
|
|
41
41
|
const king_1 = require("../claiming/king");
|
|
42
42
|
const ethena_1 = require("../claiming/ethena");
|
|
43
|
+
const aaveV4_1 = require("../aaveV4");
|
|
43
44
|
function getPortfolioData(provider, network, defaultProvider, addresses, summerFiAddresses) {
|
|
44
45
|
return __awaiter(this, void 0, void 0, function* () {
|
|
45
46
|
const isMainnet = network === common_1.NetworkNumber.Eth;
|
|
@@ -56,6 +57,7 @@ function getPortfolioData(provider, network, defaultProvider, addresses, summerF
|
|
|
56
57
|
const llamaLendMarkets = [common_1.NetworkNumber.Eth, common_1.NetworkNumber.Arb].includes(network) ? Object.values((0, markets_1.LlamaLendMarkets)(network)).filter((market) => market.chainIds.includes(network)) : [];
|
|
57
58
|
const liquityV2Markets = [common_1.NetworkNumber.Eth].includes(network) ? Object.values((0, markets_1.LiquityV2Markets)(network)) : [];
|
|
58
59
|
const liquityV2MarketsStaking = [common_1.NetworkNumber.Eth].includes(network) ? Object.values((0, markets_1.LiquityV2Markets)(network)).filter(market => !market.isLegacy) : [];
|
|
60
|
+
const aaveV4Spokes = Object.values((0, markets_1.AaveV4Spokes)(network)).filter((market) => market.chainIds.includes(network));
|
|
59
61
|
const client = (0, viem_1.getViemProvider)(provider, network, {
|
|
60
62
|
batch: {
|
|
61
63
|
multicall: {
|
|
@@ -81,6 +83,7 @@ function getPortfolioData(provider, network, defaultProvider, addresses, summerF
|
|
|
81
83
|
const crvUsdMarketsData = {};
|
|
82
84
|
const llamaLendMarketsData = {};
|
|
83
85
|
const liquityV2MarketsData = {};
|
|
86
|
+
const aaveV4SpokesData = {};
|
|
84
87
|
const markets = {
|
|
85
88
|
morphoMarketsData,
|
|
86
89
|
compoundV3MarketsData,
|
|
@@ -92,6 +95,7 @@ function getPortfolioData(provider, network, defaultProvider, addresses, summerF
|
|
|
92
95
|
crvUsdMarketsData,
|
|
93
96
|
llamaLendMarketsData,
|
|
94
97
|
liquityV2MarketsData,
|
|
98
|
+
aaveV4SpokesData,
|
|
95
99
|
};
|
|
96
100
|
const positions = {};
|
|
97
101
|
const stakingPositions = {};
|
|
@@ -100,6 +104,7 @@ function getPortfolioData(provider, network, defaultProvider, addresses, summerF
|
|
|
100
104
|
for (const address of allAddresses) {
|
|
101
105
|
positions[address.toLowerCase()] = {
|
|
102
106
|
aaveV3: {},
|
|
107
|
+
aaveV4: {},
|
|
103
108
|
morphoBlue: {},
|
|
104
109
|
compoundV3: {},
|
|
105
110
|
spark: {},
|
|
@@ -165,6 +170,10 @@ function getPortfolioData(provider, network, defaultProvider, addresses, summerF
|
|
|
165
170
|
const marketData = yield (0, aaveV3_1._getAaveV3MarketData)(client, network, market);
|
|
166
171
|
aaveV3MarketsData[market.value] = marketData;
|
|
167
172
|
})),
|
|
173
|
+
...aaveV4Spokes.map((spoke) => __awaiter(this, void 0, void 0, function* () {
|
|
174
|
+
const spokeData = yield (0, aaveV4_1._getAaveV4SpokeData)(client, network, spoke);
|
|
175
|
+
aaveV4SpokesData[spoke.value] = spokeData;
|
|
176
|
+
})),
|
|
168
177
|
...aaveV2Markets.map((market) => __awaiter(this, void 0, void 0, function* () {
|
|
169
178
|
const marketData = yield (0, aaveV2_1._getAaveV2MarketsData)(client, network, market);
|
|
170
179
|
aaveV2MarketsData[market.value] = marketData;
|
|
@@ -434,6 +443,17 @@ function getPortfolioData(provider, network, defaultProvider, addresses, summerF
|
|
|
434
443
|
positions[address.toLowerCase()].aaveV3[market.value] = { error: `Error fetching AaveV3 account data for address ${address} on market ${market.value}`, data: null };
|
|
435
444
|
}
|
|
436
445
|
}))).flat(),
|
|
446
|
+
...aaveV4Spokes.map((spoke) => allAddresses.map((address) => __awaiter(this, void 0, void 0, function* () {
|
|
447
|
+
try {
|
|
448
|
+
const accData = yield (0, aaveV4_1._getAaveV4AccountData)(client, network, aaveV4SpokesData[spoke.value], address);
|
|
449
|
+
if (new decimal_js_1.default(accData.suppliedUsd).gt(0))
|
|
450
|
+
positions[address.toLowerCase()].aaveV4[spoke.value] = { error: '', data: accData };
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
console.error(`Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}:`, error);
|
|
454
|
+
positions[address.toLowerCase()].aaveV4[spoke.value] = { error: `Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}`, data: null };
|
|
455
|
+
}
|
|
456
|
+
}))).flat(),
|
|
437
457
|
...morphoMarkets.map((market) => addresses.map((address) => __awaiter(this, void 0, void 0, function* () {
|
|
438
458
|
try {
|
|
439
459
|
const [accDataPromise, earnDataPromise] = yield Promise.allSettled([
|
package/cjs/types/aaveV4.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EthAddress, NetworkNumber } from './common';
|
|
1
|
+
import { EthAddress, IncentiveData, NetworkNumber } from './common';
|
|
2
2
|
export declare enum AaveV4SpokesType {
|
|
3
3
|
AaveV4CoreSpoke = "aave_v4_core_spoke"
|
|
4
4
|
}
|
|
@@ -64,10 +64,57 @@ export interface AaveV4ReserveAssetData {
|
|
|
64
64
|
spokeActive: boolean;
|
|
65
65
|
spokePaused: boolean;
|
|
66
66
|
drawnRate: string;
|
|
67
|
+
supplyRate: string;
|
|
68
|
+
supplyIncentives: IncentiveData[];
|
|
69
|
+
borrowIncentives: IncentiveData[];
|
|
67
70
|
}
|
|
71
|
+
export type AaveV4AssetsData = Record<string, AaveV4ReserveAssetData>;
|
|
68
72
|
export interface AaveV4SpokeData {
|
|
69
|
-
assetsData:
|
|
73
|
+
assetsData: AaveV4AssetsData;
|
|
70
74
|
oracle: EthAddress;
|
|
71
75
|
oracleDecimals: number;
|
|
72
76
|
address: EthAddress;
|
|
73
77
|
}
|
|
78
|
+
export interface AaveV4UsedReserveAsset {
|
|
79
|
+
symbol: string;
|
|
80
|
+
supplied: string;
|
|
81
|
+
suppliedUsd: string;
|
|
82
|
+
drawn: string;
|
|
83
|
+
drawnUsd: string;
|
|
84
|
+
premium: string;
|
|
85
|
+
premiumUsd: string;
|
|
86
|
+
borrowed: string;
|
|
87
|
+
borrowedUsd: string;
|
|
88
|
+
isSupplied: boolean;
|
|
89
|
+
isBorrowed: boolean;
|
|
90
|
+
collateral: boolean;
|
|
91
|
+
}
|
|
92
|
+
export interface AaveV4AggregatedPositionData {
|
|
93
|
+
suppliedUsd: string;
|
|
94
|
+
suppliedCollateralUsd: string;
|
|
95
|
+
borrowLimitUsd: string;
|
|
96
|
+
liquidationLimitUsd: string;
|
|
97
|
+
borrowedUsd: string;
|
|
98
|
+
drawnUsd: string;
|
|
99
|
+
premiumUsd: string;
|
|
100
|
+
leftToBorrowUsd: string;
|
|
101
|
+
ratio: string;
|
|
102
|
+
collRatio: string;
|
|
103
|
+
liqRatio: string;
|
|
104
|
+
liqPercent: string;
|
|
105
|
+
leveragedType: string;
|
|
106
|
+
leveragedAsset: string;
|
|
107
|
+
liquidationPrice: string;
|
|
108
|
+
leveragedLsdAssetRatio?: string;
|
|
109
|
+
minCollRatio: string;
|
|
110
|
+
collLiquidationRatio: string;
|
|
111
|
+
minHealthRatio: string;
|
|
112
|
+
netApy: string;
|
|
113
|
+
incentiveUsd: string;
|
|
114
|
+
totalInterestUsd: string;
|
|
115
|
+
}
|
|
116
|
+
export type AaveV4UsedReserveAssets = Record<string, AaveV4UsedReserveAsset>;
|
|
117
|
+
export interface AaveV4AccountData extends AaveV4AggregatedPositionData {
|
|
118
|
+
usedAssets: AaveV4UsedReserveAssets;
|
|
119
|
+
healthFactor: string;
|
|
120
|
+
}
|
package/cjs/types/portfolio.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AaveV2PositionData, AaveV3PositionData, AaveVersions } from './aave';
|
|
2
|
+
import { AaveV4AccountData, AaveV4SpokesType } from './aaveV4';
|
|
2
3
|
import { EthAddress } from './common';
|
|
3
4
|
import { CompoundV2PositionData, CompoundV3PositionData, CompoundVersions } from './compound';
|
|
4
5
|
import { CrvUSDUserData, CrvUSDVersions } from './curveUsd';
|
|
@@ -51,6 +52,9 @@ export interface PortfolioPositionsDataForAddress {
|
|
|
51
52
|
[key: string]: FluidVaultData;
|
|
52
53
|
};
|
|
53
54
|
};
|
|
55
|
+
aaveV4: {
|
|
56
|
+
[key in AaveV4SpokesType]?: PortfolioProtocolData<AaveV4AccountData>;
|
|
57
|
+
};
|
|
54
58
|
}
|
|
55
59
|
export interface PortfolioPositionsData {
|
|
56
60
|
[key: EthAddress]: PortfolioPositionsDataForAddress;
|
package/esm/aaveV4/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Client } from 'viem';
|
|
2
|
-
import { AaveV4SpokeData, AaveV4SpokeInfo } from '../types';
|
|
2
|
+
import { AaveV4AccountData, AaveV4SpokeData, AaveV4SpokeInfo } from '../types';
|
|
3
3
|
import { EthAddress, EthereumProvider, NetworkNumber } from '../types/common';
|
|
4
4
|
export declare function _getAaveV4SpokeData(provider: Client, network: NetworkNumber, market: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
|
|
5
5
|
export declare function getAaveV4SpokeData(provider: EthereumProvider, network: NetworkNumber, spoke: AaveV4SpokeInfo, blockNumber?: 'latest' | number): Promise<AaveV4SpokeData>;
|
|
6
|
-
export declare function _getAaveV4AccountData(provider: Client, network: NetworkNumber,
|
|
6
|
+
export declare function _getAaveV4AccountData(provider: Client, network: NetworkNumber, spokeData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<AaveV4AccountData>;
|
|
7
7
|
export declare function getAaveV4AccountData(provider: EthereumProvider, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber?: 'latest' | number): Promise<any>;
|
package/esm/aaveV4/index.js
CHANGED
|
@@ -10,10 +10,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import Dec from 'decimal.js';
|
|
11
11
|
import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
|
|
12
12
|
import { getViemProvider } from '../services/viem';
|
|
13
|
+
import { IncentiveKind, } from '../types/common';
|
|
13
14
|
import { AaveV4ViewContractViem } from '../contracts';
|
|
15
|
+
import { getStakingApy, STAKING_ASSETS } from '../staking';
|
|
16
|
+
import { wethToEth } from '../services/utils';
|
|
17
|
+
import { aaveV4GetAggregatedPositionData } from '../helpers/aaveV4Helpers';
|
|
14
18
|
const fetchHubData = (viewContract, hubAddress) => __awaiter(void 0, void 0, void 0, function* () {
|
|
15
19
|
const hubData = yield viewContract.read.getHubAllAssetsData([hubAddress]);
|
|
16
|
-
console.log('hubData', hubData);
|
|
17
20
|
return {
|
|
18
21
|
assets: hubData.reduce((acc, assetOnChainData) => {
|
|
19
22
|
acc[assetOnChainData.assetId] = {
|
|
@@ -24,10 +27,32 @@ const fetchHubData = (viewContract, hubAddress) => __awaiter(void 0, void 0, voi
|
|
|
24
27
|
}, {}),
|
|
25
28
|
};
|
|
26
29
|
});
|
|
27
|
-
const formatReserveAsset = (reserveAsset, hubAsset, oracleDecimals) => {
|
|
28
|
-
const assetInfo = getAssetInfoByAddress(reserveAsset.underlying);
|
|
30
|
+
const formatReserveAsset = (reserveAsset, hubAsset, oracleDecimals, network) => __awaiter(void 0, void 0, void 0, function* () {
|
|
31
|
+
const assetInfo = getAssetInfoByAddress(reserveAsset.underlying, network);
|
|
32
|
+
const symbol = wethToEth(assetInfo.symbol);
|
|
33
|
+
const isStakingAsset = STAKING_ASSETS.includes(symbol);
|
|
34
|
+
const supplyIncentives = [];
|
|
35
|
+
const borrowIncentives = [];
|
|
36
|
+
if (isStakingAsset) {
|
|
37
|
+
const yieldApy = yield getStakingApy(symbol, network);
|
|
38
|
+
supplyIncentives.push({
|
|
39
|
+
apy: yieldApy,
|
|
40
|
+
token: symbol,
|
|
41
|
+
incentiveKind: IncentiveKind.Staking,
|
|
42
|
+
description: `Native ${symbol} yield.`,
|
|
43
|
+
});
|
|
44
|
+
if (reserveAsset.borrowable) {
|
|
45
|
+
// when borrowing assets whose value increases over time
|
|
46
|
+
borrowIncentives.push({
|
|
47
|
+
apy: new Dec(yieldApy).mul(-1).toString(),
|
|
48
|
+
token: symbol,
|
|
49
|
+
incentiveKind: IncentiveKind.Reward,
|
|
50
|
+
description: `Due to the native yield of ${symbol}, the value of the debt would increase over time.`,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
29
54
|
return ({
|
|
30
|
-
symbol
|
|
55
|
+
symbol,
|
|
31
56
|
underlying: reserveAsset.underlying,
|
|
32
57
|
hub: reserveAsset.hub,
|
|
33
58
|
assetId: reserveAsset.assetId,
|
|
@@ -38,17 +63,20 @@ const formatReserveAsset = (reserveAsset, hubAsset, oracleDecimals) => {
|
|
|
38
63
|
collateralFactor: new Dec(reserveAsset.collateralFactor).div(10000).toNumber(),
|
|
39
64
|
liquidationFee: new Dec(reserveAsset.liquidationFee).div(10000).toNumber(),
|
|
40
65
|
price: new Dec(reserveAsset.price).div(new Dec(10).pow(oracleDecimals)).toString(),
|
|
41
|
-
totalSupplied: assetAmountInEth(reserveAsset.totalSupplied.toString(),
|
|
42
|
-
totalDrawn: assetAmountInEth(reserveAsset.totalDrawn.toString(),
|
|
43
|
-
totalPremium: assetAmountInEth(reserveAsset.totalPremium.toString(),
|
|
44
|
-
totalDebt: assetAmountInEth(reserveAsset.totalDebt.toString(),
|
|
45
|
-
supplyCap: assetAmountInEth(reserveAsset.supplyCap.toString(),
|
|
46
|
-
borrowCap: assetAmountInEth(reserveAsset.borrowCap.toString(),
|
|
66
|
+
totalSupplied: assetAmountInEth(reserveAsset.totalSupplied.toString(), symbol),
|
|
67
|
+
totalDrawn: assetAmountInEth(reserveAsset.totalDrawn.toString(), symbol),
|
|
68
|
+
totalPremium: assetAmountInEth(reserveAsset.totalPremium.toString(), symbol),
|
|
69
|
+
totalDebt: assetAmountInEth(reserveAsset.totalDebt.toString(), symbol),
|
|
70
|
+
supplyCap: assetAmountInEth(reserveAsset.supplyCap.toString(), symbol),
|
|
71
|
+
borrowCap: assetAmountInEth(reserveAsset.borrowCap.toString(), symbol),
|
|
47
72
|
spokeActive: reserveAsset.spokeActive,
|
|
48
73
|
spokePaused: reserveAsset.spokePaused,
|
|
49
74
|
drawnRate: new Dec(hubAsset.drawnRate).div(new Dec(10).pow(27)).toString(),
|
|
75
|
+
supplyRate: '0', // To be implemented
|
|
76
|
+
supplyIncentives,
|
|
77
|
+
borrowIncentives,
|
|
50
78
|
});
|
|
51
|
-
};
|
|
79
|
+
});
|
|
52
80
|
export function _getAaveV4SpokeData(provider_1, network_1, market_1) {
|
|
53
81
|
return __awaiter(this, arguments, void 0, function* (provider, network, market, blockNumber = 'latest') {
|
|
54
82
|
const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
|
|
@@ -59,7 +87,7 @@ export function _getAaveV4SpokeData(provider_1, network_1, market_1) {
|
|
|
59
87
|
hubsData[hubAddress] = yield fetchHubData(viewContract, hubAddress);
|
|
60
88
|
})),
|
|
61
89
|
]);
|
|
62
|
-
const reserveAssetsArray = spokeData[1].map((reserveAssetOnChain) => formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], +spokeData[0].oracleDecimals.toString()));
|
|
90
|
+
const reserveAssetsArray = yield Promise.all(spokeData[1].map((reserveAssetOnChain) => __awaiter(this, void 0, void 0, function* () { return formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], +spokeData[0].oracleDecimals.toString(), network); })));
|
|
63
91
|
return {
|
|
64
92
|
assetsData: reserveAssetsArray.reduce((acc, reserveAsset) => {
|
|
65
93
|
acc[reserveAsset.symbol] = reserveAsset;
|
|
@@ -76,11 +104,40 @@ export function getAaveV4SpokeData(provider_1, network_1, spoke_1) {
|
|
|
76
104
|
return _getAaveV4SpokeData(getViemProvider(provider, network), network, spoke, blockNumber);
|
|
77
105
|
});
|
|
78
106
|
}
|
|
79
|
-
export function _getAaveV4AccountData(provider_1, network_1,
|
|
80
|
-
return __awaiter(this, arguments, void 0, function* (provider, network,
|
|
107
|
+
export function _getAaveV4AccountData(provider_1, network_1, spokeData_1, address_1) {
|
|
108
|
+
return __awaiter(this, arguments, void 0, function* (provider, network, spokeData, address, blockNumber = 'latest') {
|
|
81
109
|
const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
|
|
82
|
-
const loanData = yield viewContract.read.getLoanData([
|
|
83
|
-
|
|
110
|
+
const loanData = yield viewContract.read.getLoanData([spokeData.address, address]);
|
|
111
|
+
const healthFactor = new Dec(loanData.healthFactor).div(1e18).toString();
|
|
112
|
+
const usedAssets = loanData.reserves.reduce((acc, usedReserveAsset) => {
|
|
113
|
+
const reserveData = spokeData.assetsData[wethToEth(getAssetInfoByAddress(usedReserveAsset.underlying, network).symbol)];
|
|
114
|
+
const price = reserveData.price;
|
|
115
|
+
const supplied = assetAmountInEth(usedReserveAsset.supplied.toString(), reserveData.symbol);
|
|
116
|
+
const drawn = assetAmountInEth(usedReserveAsset.drawn.toString(), reserveData.symbol);
|
|
117
|
+
const premium = assetAmountInEth(usedReserveAsset.premium.toString(), reserveData.symbol);
|
|
118
|
+
const borrowed = assetAmountInEth(usedReserveAsset.totalDebt.toString(), reserveData.symbol);
|
|
119
|
+
acc[reserveData.symbol] = {
|
|
120
|
+
symbol: reserveData.symbol,
|
|
121
|
+
supplied,
|
|
122
|
+
suppliedUsd: new Dec(supplied).mul(price).toString(),
|
|
123
|
+
drawn,
|
|
124
|
+
drawnUsd: new Dec(drawn).mul(price).toString(),
|
|
125
|
+
premium,
|
|
126
|
+
premiumUsd: new Dec(premium).mul(price).toString(),
|
|
127
|
+
borrowed,
|
|
128
|
+
borrowedUsd: new Dec(borrowed).mul(price).toString(),
|
|
129
|
+
isSupplied: !new Dec(supplied).eq(0),
|
|
130
|
+
isBorrowed: usedReserveAsset.isBorrowing,
|
|
131
|
+
collateral: usedReserveAsset.isUsingAsCollateral,
|
|
132
|
+
};
|
|
133
|
+
return acc;
|
|
134
|
+
}, {});
|
|
135
|
+
return Object.assign({ usedAssets,
|
|
136
|
+
healthFactor }, aaveV4GetAggregatedPositionData({
|
|
137
|
+
usedAssets,
|
|
138
|
+
assetsData: spokeData.assetsData,
|
|
139
|
+
network,
|
|
140
|
+
}));
|
|
84
141
|
});
|
|
85
142
|
}
|
|
86
143
|
export function getAaveV4AccountData(provider_1, network_1, marketData_1, address_1) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AaveV4AggregatedPositionData, AaveV4AssetsData, AaveV4UsedReserveAssets } from '../../types';
|
|
2
|
+
import { NetworkNumber } from '../../types/common';
|
|
3
|
+
export declare const aaveV4GetAggregatedPositionData: ({ usedAssets, assetsData, network, }: {
|
|
4
|
+
usedAssets: AaveV4UsedReserveAssets;
|
|
5
|
+
assetsData: AaveV4AssetsData;
|
|
6
|
+
network: NetworkNumber;
|
|
7
|
+
}) => AaveV4AggregatedPositionData;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import Dec from 'decimal.js';
|
|
2
|
+
import { calcLeverageLiqPrice, getAssetsTotal, isLeveragedPos } from '../../moneymarket';
|
|
3
|
+
export const aaveV4GetAggregatedPositionData = ({ usedAssets, assetsData, network, }) => {
|
|
4
|
+
const payload = {};
|
|
5
|
+
payload.suppliedUsd = getAssetsTotal(usedAssets, ({ isSupplied }) => isSupplied, ({ suppliedUsd }) => suppliedUsd);
|
|
6
|
+
payload.suppliedCollateralUsd = getAssetsTotal(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral, ({ suppliedUsd }) => suppliedUsd);
|
|
7
|
+
payload.borrowLimitUsd = getAssetsTotal(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral, ({ symbol, suppliedUsd }) => new Dec(suppliedUsd).mul(assetsData[symbol].collateralFactor));
|
|
8
|
+
payload.liquidationLimitUsd = getAssetsTotal(usedAssets, ({ isSupplied, collateral }) => isSupplied && collateral,
|
|
9
|
+
// TODO: Verify if liquidation factor is available in Aave V4, currently using collateralFactor as placeholder
|
|
10
|
+
({ symbol, suppliedUsd }) => new Dec(suppliedUsd).mul(assetsData[symbol].collateralFactor));
|
|
11
|
+
payload.borrowedUsd = getAssetsTotal(usedAssets, ({ isBorrowed }) => isBorrowed, ({ borrowedUsd }) => borrowedUsd);
|
|
12
|
+
payload.drawnUsd = getAssetsTotal(usedAssets, ({ isBorrowed }) => isBorrowed, ({ drawnUsd }) => drawnUsd);
|
|
13
|
+
payload.premiumUsd = getAssetsTotal(usedAssets, ({ isBorrowed }) => isBorrowed, ({ premiumUsd }) => premiumUsd);
|
|
14
|
+
const leftToBorrowUsd = new Dec(payload.borrowLimitUsd).sub(payload.borrowedUsd);
|
|
15
|
+
payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
|
|
16
|
+
payload.ratio = +payload.suppliedUsd ? new Dec(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
17
|
+
payload.collRatio = +payload.suppliedUsd ? new Dec(payload.suppliedCollateralUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
18
|
+
payload.liqRatio = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).toString();
|
|
19
|
+
payload.liqPercent = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).mul(100).toString();
|
|
20
|
+
const { leveragedType, leveragedAsset } = isLeveragedPos(usedAssets);
|
|
21
|
+
payload.leveragedType = leveragedType;
|
|
22
|
+
payload.leveragedAsset = leveragedAsset;
|
|
23
|
+
payload.liquidationPrice = '';
|
|
24
|
+
if (leveragedType !== '') {
|
|
25
|
+
let assetPrice = assetsData[leveragedAsset].price;
|
|
26
|
+
if (leveragedType === 'lsd-leverage') {
|
|
27
|
+
// Treat ETH like a stablecoin in a long stETH position
|
|
28
|
+
payload.leveragedLsdAssetRatio = new Dec(assetsData[leveragedAsset].price).div(assetsData.ETH.price).toDP(18).toString();
|
|
29
|
+
assetPrice = new Dec(assetPrice).div(assetsData.ETH.price).toString();
|
|
30
|
+
}
|
|
31
|
+
payload.liquidationPrice = calcLeverageLiqPrice(leveragedType, assetPrice, payload.borrowedUsd, payload.liquidationLimitUsd);
|
|
32
|
+
}
|
|
33
|
+
payload.minCollRatio = new Dec(payload.suppliedCollateralUsd).div(payload.borrowLimitUsd).mul(100).toString();
|
|
34
|
+
payload.collLiquidationRatio = new Dec(payload.suppliedCollateralUsd).div(payload.liquidationLimitUsd).mul(100).toString();
|
|
35
|
+
// payload.healthRatio = new Dec(payload.liquidationLimitUsd).div(payload.borrowedUsd).toDP(4).toString();
|
|
36
|
+
payload.minHealthRatio = new Dec(payload.liquidationLimitUsd).div(payload.borrowLimitUsd).toDP(4).toString();
|
|
37
|
+
// TODO: Re-implement netApy calculation
|
|
38
|
+
// const { netApy, incentiveUsd, totalInterestUsd } = calculateNetApy({
|
|
39
|
+
// usedAssets,
|
|
40
|
+
// assetsData,
|
|
41
|
+
// optionalData: { healthRatio: payload.healthRatio },
|
|
42
|
+
// });
|
|
43
|
+
payload.netApy = '0';
|
|
44
|
+
payload.incentiveUsd = '0';
|
|
45
|
+
payload.totalInterestUsd = '0';
|
|
46
|
+
return payload;
|
|
47
|
+
};
|
package/esm/helpers/index.d.ts
CHANGED
package/esm/helpers/index.js
CHANGED
package/esm/portfolio/index.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import Dec from 'decimal.js';
|
|
11
11
|
import { NetworkNumber } from '../types/common';
|
|
12
|
-
import { AaveMarkets, CompoundMarkets, CrvUsdMarkets, EulerV2Markets, LiquityV2Markets, LlamaLendMarkets, MorphoBlueMarkets, SparkMarkets, } from '../markets';
|
|
12
|
+
import { AaveMarkets, AaveV4Spokes, CompoundMarkets, CrvUsdMarkets, EulerV2Markets, LiquityV2Markets, LlamaLendMarkets, MorphoBlueMarkets, SparkMarkets, } from '../markets';
|
|
13
13
|
import { _getMorphoBlueAccountData, _getMorphoBlueMarketData, getMorphoEarn } from '../morphoBlue';
|
|
14
14
|
import { AaveVersions, CompoundVersions, } from '../types';
|
|
15
15
|
import { _getCompoundV3AccountData, _getCompoundV3MarketsData } from '../compoundV3';
|
|
@@ -34,6 +34,7 @@ import { fetchSparkAirdropRewards, fetchSparkRewards } from '../claiming/spark';
|
|
|
34
34
|
import { fetchMorphoBlueRewards } from '../claiming/morphoBlue';
|
|
35
35
|
import { getKingRewards } from '../claiming/king';
|
|
36
36
|
import { fetchEthenaAirdropRewards } from '../claiming/ethena';
|
|
37
|
+
import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
|
|
37
38
|
export function getPortfolioData(provider, network, defaultProvider, addresses, summerFiAddresses) {
|
|
38
39
|
return __awaiter(this, void 0, void 0, function* () {
|
|
39
40
|
const isMainnet = network === NetworkNumber.Eth;
|
|
@@ -50,6 +51,7 @@ export function getPortfolioData(provider, network, defaultProvider, addresses,
|
|
|
50
51
|
const llamaLendMarkets = [NetworkNumber.Eth, NetworkNumber.Arb].includes(network) ? Object.values(LlamaLendMarkets(network)).filter((market) => market.chainIds.includes(network)) : [];
|
|
51
52
|
const liquityV2Markets = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)) : [];
|
|
52
53
|
const liquityV2MarketsStaking = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)).filter(market => !market.isLegacy) : [];
|
|
54
|
+
const aaveV4Spokes = Object.values(AaveV4Spokes(network)).filter((market) => market.chainIds.includes(network));
|
|
53
55
|
const client = getViemProvider(provider, network, {
|
|
54
56
|
batch: {
|
|
55
57
|
multicall: {
|
|
@@ -75,6 +77,7 @@ export function getPortfolioData(provider, network, defaultProvider, addresses,
|
|
|
75
77
|
const crvUsdMarketsData = {};
|
|
76
78
|
const llamaLendMarketsData = {};
|
|
77
79
|
const liquityV2MarketsData = {};
|
|
80
|
+
const aaveV4SpokesData = {};
|
|
78
81
|
const markets = {
|
|
79
82
|
morphoMarketsData,
|
|
80
83
|
compoundV3MarketsData,
|
|
@@ -86,6 +89,7 @@ export function getPortfolioData(provider, network, defaultProvider, addresses,
|
|
|
86
89
|
crvUsdMarketsData,
|
|
87
90
|
llamaLendMarketsData,
|
|
88
91
|
liquityV2MarketsData,
|
|
92
|
+
aaveV4SpokesData,
|
|
89
93
|
};
|
|
90
94
|
const positions = {};
|
|
91
95
|
const stakingPositions = {};
|
|
@@ -94,6 +98,7 @@ export function getPortfolioData(provider, network, defaultProvider, addresses,
|
|
|
94
98
|
for (const address of allAddresses) {
|
|
95
99
|
positions[address.toLowerCase()] = {
|
|
96
100
|
aaveV3: {},
|
|
101
|
+
aaveV4: {},
|
|
97
102
|
morphoBlue: {},
|
|
98
103
|
compoundV3: {},
|
|
99
104
|
spark: {},
|
|
@@ -159,6 +164,10 @@ export function getPortfolioData(provider, network, defaultProvider, addresses,
|
|
|
159
164
|
const marketData = yield _getAaveV3MarketData(client, network, market);
|
|
160
165
|
aaveV3MarketsData[market.value] = marketData;
|
|
161
166
|
})),
|
|
167
|
+
...aaveV4Spokes.map((spoke) => __awaiter(this, void 0, void 0, function* () {
|
|
168
|
+
const spokeData = yield _getAaveV4SpokeData(client, network, spoke);
|
|
169
|
+
aaveV4SpokesData[spoke.value] = spokeData;
|
|
170
|
+
})),
|
|
162
171
|
...aaveV2Markets.map((market) => __awaiter(this, void 0, void 0, function* () {
|
|
163
172
|
const marketData = yield _getAaveV2MarketsData(client, network, market);
|
|
164
173
|
aaveV2MarketsData[market.value] = marketData;
|
|
@@ -428,6 +437,17 @@ export function getPortfolioData(provider, network, defaultProvider, addresses,
|
|
|
428
437
|
positions[address.toLowerCase()].aaveV3[market.value] = { error: `Error fetching AaveV3 account data for address ${address} on market ${market.value}`, data: null };
|
|
429
438
|
}
|
|
430
439
|
}))).flat(),
|
|
440
|
+
...aaveV4Spokes.map((spoke) => allAddresses.map((address) => __awaiter(this, void 0, void 0, function* () {
|
|
441
|
+
try {
|
|
442
|
+
const accData = yield _getAaveV4AccountData(client, network, aaveV4SpokesData[spoke.value], address);
|
|
443
|
+
if (new Dec(accData.suppliedUsd).gt(0))
|
|
444
|
+
positions[address.toLowerCase()].aaveV4[spoke.value] = { error: '', data: accData };
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
console.error(`Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}:`, error);
|
|
448
|
+
positions[address.toLowerCase()].aaveV4[spoke.value] = { error: `Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}`, data: null };
|
|
449
|
+
}
|
|
450
|
+
}))).flat(),
|
|
431
451
|
...morphoMarkets.map((market) => addresses.map((address) => __awaiter(this, void 0, void 0, function* () {
|
|
432
452
|
try {
|
|
433
453
|
const [accDataPromise, earnDataPromise] = yield Promise.allSettled([
|
package/esm/types/aaveV4.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EthAddress, NetworkNumber } from './common';
|
|
1
|
+
import { EthAddress, IncentiveData, NetworkNumber } from './common';
|
|
2
2
|
export declare enum AaveV4SpokesType {
|
|
3
3
|
AaveV4CoreSpoke = "aave_v4_core_spoke"
|
|
4
4
|
}
|
|
@@ -64,10 +64,57 @@ export interface AaveV4ReserveAssetData {
|
|
|
64
64
|
spokeActive: boolean;
|
|
65
65
|
spokePaused: boolean;
|
|
66
66
|
drawnRate: string;
|
|
67
|
+
supplyRate: string;
|
|
68
|
+
supplyIncentives: IncentiveData[];
|
|
69
|
+
borrowIncentives: IncentiveData[];
|
|
67
70
|
}
|
|
71
|
+
export type AaveV4AssetsData = Record<string, AaveV4ReserveAssetData>;
|
|
68
72
|
export interface AaveV4SpokeData {
|
|
69
|
-
assetsData:
|
|
73
|
+
assetsData: AaveV4AssetsData;
|
|
70
74
|
oracle: EthAddress;
|
|
71
75
|
oracleDecimals: number;
|
|
72
76
|
address: EthAddress;
|
|
73
77
|
}
|
|
78
|
+
export interface AaveV4UsedReserveAsset {
|
|
79
|
+
symbol: string;
|
|
80
|
+
supplied: string;
|
|
81
|
+
suppliedUsd: string;
|
|
82
|
+
drawn: string;
|
|
83
|
+
drawnUsd: string;
|
|
84
|
+
premium: string;
|
|
85
|
+
premiumUsd: string;
|
|
86
|
+
borrowed: string;
|
|
87
|
+
borrowedUsd: string;
|
|
88
|
+
isSupplied: boolean;
|
|
89
|
+
isBorrowed: boolean;
|
|
90
|
+
collateral: boolean;
|
|
91
|
+
}
|
|
92
|
+
export interface AaveV4AggregatedPositionData {
|
|
93
|
+
suppliedUsd: string;
|
|
94
|
+
suppliedCollateralUsd: string;
|
|
95
|
+
borrowLimitUsd: string;
|
|
96
|
+
liquidationLimitUsd: string;
|
|
97
|
+
borrowedUsd: string;
|
|
98
|
+
drawnUsd: string;
|
|
99
|
+
premiumUsd: string;
|
|
100
|
+
leftToBorrowUsd: string;
|
|
101
|
+
ratio: string;
|
|
102
|
+
collRatio: string;
|
|
103
|
+
liqRatio: string;
|
|
104
|
+
liqPercent: string;
|
|
105
|
+
leveragedType: string;
|
|
106
|
+
leveragedAsset: string;
|
|
107
|
+
liquidationPrice: string;
|
|
108
|
+
leveragedLsdAssetRatio?: string;
|
|
109
|
+
minCollRatio: string;
|
|
110
|
+
collLiquidationRatio: string;
|
|
111
|
+
minHealthRatio: string;
|
|
112
|
+
netApy: string;
|
|
113
|
+
incentiveUsd: string;
|
|
114
|
+
totalInterestUsd: string;
|
|
115
|
+
}
|
|
116
|
+
export type AaveV4UsedReserveAssets = Record<string, AaveV4UsedReserveAsset>;
|
|
117
|
+
export interface AaveV4AccountData extends AaveV4AggregatedPositionData {
|
|
118
|
+
usedAssets: AaveV4UsedReserveAssets;
|
|
119
|
+
healthFactor: string;
|
|
120
|
+
}
|
package/esm/types/portfolio.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AaveV2PositionData, AaveV3PositionData, AaveVersions } from './aave';
|
|
2
|
+
import { AaveV4AccountData, AaveV4SpokesType } from './aaveV4';
|
|
2
3
|
import { EthAddress } from './common';
|
|
3
4
|
import { CompoundV2PositionData, CompoundV3PositionData, CompoundVersions } from './compound';
|
|
4
5
|
import { CrvUSDUserData, CrvUSDVersions } from './curveUsd';
|
|
@@ -51,6 +52,9 @@ export interface PortfolioPositionsDataForAddress {
|
|
|
51
52
|
[key: string]: FluidVaultData;
|
|
52
53
|
};
|
|
53
54
|
};
|
|
55
|
+
aaveV4: {
|
|
56
|
+
[key in AaveV4SpokesType]?: PortfolioProtocolData<AaveV4AccountData>;
|
|
57
|
+
};
|
|
54
58
|
}
|
|
55
59
|
export interface PortfolioPositionsData {
|
|
56
60
|
[key: EthAddress]: PortfolioPositionsDataForAddress;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defisaver/positions-sdk",
|
|
3
|
-
"version": "2.1.43-aave-v4-dev-
|
|
3
|
+
"version": "2.1.43-aave-v4-dev-2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./cjs/index.js",
|
|
6
6
|
"module": "./esm/index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"dev": "tsc -p tsconfig.json --watch",
|
|
13
13
|
"lint": "eslint src/ --fix",
|
|
14
14
|
"lint-check": "eslint src/",
|
|
15
|
-
"test": "mocha tests/
|
|
15
|
+
"test": "mocha tests/portfolio.ts",
|
|
16
16
|
"test-single": "mocha ./tests/$npm_config_name.ts",
|
|
17
17
|
"test:debugger": "mocha --inspect-brk tests/*",
|
|
18
18
|
"version-bump": "git commit -am \"Version bump to $(npm version patch | cut -c 2-)\""
|
package/src/aaveV4/index.ts
CHANGED
|
@@ -3,16 +3,22 @@ import Dec from 'decimal.js';
|
|
|
3
3
|
import { assetAmountInEth, getAssetInfoByAddress } from '@defisaver/tokens';
|
|
4
4
|
import { getViemProvider } from '../services/viem';
|
|
5
5
|
import {
|
|
6
|
+
AaveV4AccountData,
|
|
6
7
|
AaveV4HubAssetOnChainData,
|
|
7
8
|
AaveV4HubOnChainData,
|
|
8
9
|
AaveV4ReserveAssetData, AaveV4ReserveAssetOnChain, AaveV4SpokeData, AaveV4SpokeInfo,
|
|
10
|
+
AaveV4UsedReserveAssets,
|
|
9
11
|
} from '../types';
|
|
10
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
EthAddress, EthereumProvider, IncentiveData, IncentiveKind, NetworkNumber,
|
|
14
|
+
} from '../types/common';
|
|
11
15
|
import { AaveV4ViewContractViem } from '../contracts';
|
|
16
|
+
import { getStakingApy, STAKING_ASSETS } from '../staking';
|
|
17
|
+
import { wethToEth } from '../services/utils';
|
|
18
|
+
import { aaveV4GetAggregatedPositionData } from '../helpers/aaveV4Helpers';
|
|
12
19
|
|
|
13
20
|
const fetchHubData = async (viewContract: ReturnType<typeof AaveV4ViewContractViem>, hubAddress: EthAddress): Promise<AaveV4HubOnChainData> => {
|
|
14
21
|
const hubData = await viewContract.read.getHubAllAssetsData([hubAddress]);
|
|
15
|
-
console.log('hubData', hubData);
|
|
16
22
|
return {
|
|
17
23
|
assets: hubData.reduce((acc: Record<number, AaveV4HubAssetOnChainData>, assetOnChainData) => {
|
|
18
24
|
acc[assetOnChainData.assetId] = {
|
|
@@ -24,10 +30,35 @@ const fetchHubData = async (viewContract: ReturnType<typeof AaveV4ViewContractVi
|
|
|
24
30
|
};
|
|
25
31
|
};
|
|
26
32
|
|
|
27
|
-
const formatReserveAsset = (reserveAsset: AaveV4ReserveAssetOnChain, hubAsset: AaveV4HubAssetOnChainData, oracleDecimals: number): AaveV4ReserveAssetData => {
|
|
28
|
-
const assetInfo = getAssetInfoByAddress(reserveAsset.underlying);
|
|
33
|
+
const formatReserveAsset = async (reserveAsset: AaveV4ReserveAssetOnChain, hubAsset: AaveV4HubAssetOnChainData, oracleDecimals: number, network: NetworkNumber): Promise<AaveV4ReserveAssetData> => {
|
|
34
|
+
const assetInfo = getAssetInfoByAddress(reserveAsset.underlying, network);
|
|
35
|
+
const symbol = wethToEth(assetInfo.symbol);
|
|
36
|
+
|
|
37
|
+
const isStakingAsset = STAKING_ASSETS.includes(symbol);
|
|
38
|
+
const supplyIncentives: IncentiveData[] = [];
|
|
39
|
+
const borrowIncentives: IncentiveData[] = [];
|
|
40
|
+
|
|
41
|
+
if (isStakingAsset) {
|
|
42
|
+
const yieldApy = await getStakingApy(symbol, network as NetworkNumber);
|
|
43
|
+
supplyIncentives.push({
|
|
44
|
+
apy: yieldApy,
|
|
45
|
+
token: symbol,
|
|
46
|
+
incentiveKind: IncentiveKind.Staking,
|
|
47
|
+
description: `Native ${symbol} yield.`,
|
|
48
|
+
});
|
|
49
|
+
if (reserveAsset.borrowable) {
|
|
50
|
+
// when borrowing assets whose value increases over time
|
|
51
|
+
borrowIncentives.push({
|
|
52
|
+
apy: new Dec(yieldApy).mul(-1).toString(),
|
|
53
|
+
token: symbol,
|
|
54
|
+
incentiveKind: IncentiveKind.Reward,
|
|
55
|
+
description: `Due to the native yield of ${symbol}, the value of the debt would increase over time.`,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
29
60
|
return ({
|
|
30
|
-
symbol
|
|
61
|
+
symbol,
|
|
31
62
|
underlying: reserveAsset.underlying,
|
|
32
63
|
hub: reserveAsset.hub,
|
|
33
64
|
assetId: reserveAsset.assetId,
|
|
@@ -38,15 +69,18 @@ const formatReserveAsset = (reserveAsset: AaveV4ReserveAssetOnChain, hubAsset: A
|
|
|
38
69
|
collateralFactor: new Dec(reserveAsset.collateralFactor).div(10000).toNumber(),
|
|
39
70
|
liquidationFee: new Dec(reserveAsset.liquidationFee).div(10000).toNumber(),
|
|
40
71
|
price: new Dec(reserveAsset.price).div(new Dec(10).pow(oracleDecimals)).toString(),
|
|
41
|
-
totalSupplied: assetAmountInEth(reserveAsset.totalSupplied.toString(),
|
|
42
|
-
totalDrawn: assetAmountInEth(reserveAsset.totalDrawn.toString(),
|
|
43
|
-
totalPremium: assetAmountInEth(reserveAsset.totalPremium.toString(),
|
|
44
|
-
totalDebt: assetAmountInEth(reserveAsset.totalDebt.toString(),
|
|
45
|
-
supplyCap: assetAmountInEth(reserveAsset.supplyCap.toString(),
|
|
46
|
-
borrowCap: assetAmountInEth(reserveAsset.borrowCap.toString(),
|
|
72
|
+
totalSupplied: assetAmountInEth(reserveAsset.totalSupplied.toString(), symbol),
|
|
73
|
+
totalDrawn: assetAmountInEth(reserveAsset.totalDrawn.toString(), symbol),
|
|
74
|
+
totalPremium: assetAmountInEth(reserveAsset.totalPremium.toString(), symbol),
|
|
75
|
+
totalDebt: assetAmountInEth(reserveAsset.totalDebt.toString(), symbol),
|
|
76
|
+
supplyCap: assetAmountInEth(reserveAsset.supplyCap.toString(), symbol),
|
|
77
|
+
borrowCap: assetAmountInEth(reserveAsset.borrowCap.toString(), symbol),
|
|
47
78
|
spokeActive: reserveAsset.spokeActive,
|
|
48
79
|
spokePaused: reserveAsset.spokePaused,
|
|
49
80
|
drawnRate: new Dec(hubAsset.drawnRate).div(new Dec(10).pow(27)).toString(),
|
|
81
|
+
supplyRate: '0', // To be implemented
|
|
82
|
+
supplyIncentives,
|
|
83
|
+
borrowIncentives,
|
|
50
84
|
});
|
|
51
85
|
};
|
|
52
86
|
|
|
@@ -61,7 +95,7 @@ export async function _getAaveV4SpokeData(provider: Client, network: NetworkNumb
|
|
|
61
95
|
}),
|
|
62
96
|
]);
|
|
63
97
|
|
|
64
|
-
const reserveAssetsArray = spokeData[1].map((reserveAssetOnChain: AaveV4ReserveAssetOnChain) => formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], +spokeData[0].oracleDecimals.toString()));
|
|
98
|
+
const reserveAssetsArray = await Promise.all(spokeData[1].map(async (reserveAssetOnChain: AaveV4ReserveAssetOnChain) => formatReserveAsset(reserveAssetOnChain, hubsData[reserveAssetOnChain.hub].assets[reserveAssetOnChain.assetId], +spokeData[0].oracleDecimals.toString(), network)));
|
|
65
99
|
|
|
66
100
|
return {
|
|
67
101
|
assetsData: reserveAssetsArray.reduce((acc: Record<string, AaveV4ReserveAssetData>, reserveAsset: AaveV4ReserveAssetData) => {
|
|
@@ -78,11 +112,46 @@ export async function getAaveV4SpokeData(provider: EthereumProvider, network: Ne
|
|
|
78
112
|
return _getAaveV4SpokeData(getViemProvider(provider, network), network, spoke, blockNumber);
|
|
79
113
|
}
|
|
80
114
|
|
|
81
|
-
export async function _getAaveV4AccountData(provider: Client, network: NetworkNumber,
|
|
115
|
+
export async function _getAaveV4AccountData(provider: Client, network: NetworkNumber, spokeData: AaveV4SpokeData, address: EthAddress, blockNumber: 'latest' | number = 'latest'): Promise<AaveV4AccountData> {
|
|
82
116
|
const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
|
|
83
117
|
|
|
84
|
-
const loanData = await viewContract.read.getLoanData([
|
|
85
|
-
|
|
118
|
+
const loanData = await viewContract.read.getLoanData([spokeData.address, address]);
|
|
119
|
+
|
|
120
|
+
const healthFactor = new Dec(loanData.healthFactor).div(1e18).toString();
|
|
121
|
+
|
|
122
|
+
const usedAssets = loanData.reserves.reduce((acc: AaveV4UsedReserveAssets, usedReserveAsset) => {
|
|
123
|
+
const reserveData = spokeData.assetsData[wethToEth(getAssetInfoByAddress(usedReserveAsset.underlying, network).symbol)];
|
|
124
|
+
const price = reserveData.price;
|
|
125
|
+
const supplied = assetAmountInEth(usedReserveAsset.supplied.toString(), reserveData.symbol);
|
|
126
|
+
const drawn = assetAmountInEth(usedReserveAsset.drawn.toString(), reserveData.symbol);
|
|
127
|
+
const premium = assetAmountInEth(usedReserveAsset.premium.toString(), reserveData.symbol);
|
|
128
|
+
const borrowed = assetAmountInEth(usedReserveAsset.totalDebt.toString(), reserveData.symbol);
|
|
129
|
+
acc[reserveData.symbol] = {
|
|
130
|
+
symbol: reserveData.symbol,
|
|
131
|
+
supplied,
|
|
132
|
+
suppliedUsd: new Dec(supplied).mul(price).toString(),
|
|
133
|
+
drawn,
|
|
134
|
+
drawnUsd: new Dec(drawn).mul(price).toString(),
|
|
135
|
+
premium,
|
|
136
|
+
premiumUsd: new Dec(premium).mul(price).toString(),
|
|
137
|
+
borrowed,
|
|
138
|
+
borrowedUsd: new Dec(borrowed).mul(price).toString(),
|
|
139
|
+
isSupplied: !new Dec(supplied).eq(0),
|
|
140
|
+
isBorrowed: usedReserveAsset.isBorrowing,
|
|
141
|
+
collateral: usedReserveAsset.isUsingAsCollateral,
|
|
142
|
+
};
|
|
143
|
+
return acc;
|
|
144
|
+
}, {});
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
usedAssets,
|
|
148
|
+
healthFactor,
|
|
149
|
+
...aaveV4GetAggregatedPositionData({
|
|
150
|
+
usedAssets,
|
|
151
|
+
assetsData: spokeData.assetsData,
|
|
152
|
+
network,
|
|
153
|
+
}),
|
|
154
|
+
};
|
|
86
155
|
}
|
|
87
156
|
|
|
88
157
|
export async function getAaveV4AccountData(provider: EthereumProvider, network: NetworkNumber, marketData: AaveV4SpokeData, address: EthAddress, blockNumber: 'latest' | number = 'latest'): Promise<any> {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import Dec from 'decimal.js';
|
|
2
|
+
import { calcLeverageLiqPrice, getAssetsTotal, isLeveragedPos } from '../../moneymarket';
|
|
3
|
+
import { AaveV4AggregatedPositionData, AaveV4AssetsData, AaveV4UsedReserveAssets } from '../../types';
|
|
4
|
+
import { NetworkNumber } from '../../types/common';
|
|
5
|
+
|
|
6
|
+
export const aaveV4GetAggregatedPositionData = ({
|
|
7
|
+
usedAssets,
|
|
8
|
+
assetsData,
|
|
9
|
+
network,
|
|
10
|
+
}: {
|
|
11
|
+
usedAssets: AaveV4UsedReserveAssets,
|
|
12
|
+
assetsData: AaveV4AssetsData,
|
|
13
|
+
network: NetworkNumber,
|
|
14
|
+
}): AaveV4AggregatedPositionData => {
|
|
15
|
+
const payload = {} as AaveV4AggregatedPositionData;
|
|
16
|
+
payload.suppliedUsd = getAssetsTotal(usedAssets, ({ isSupplied }: { isSupplied: boolean }) => isSupplied, ({ suppliedUsd }: { suppliedUsd: string }) => suppliedUsd);
|
|
17
|
+
payload.suppliedCollateralUsd = getAssetsTotal(usedAssets, ({ isSupplied, collateral }: { isSupplied: boolean, collateral: string }) => isSupplied && collateral, ({ suppliedUsd }: { suppliedUsd: string }) => suppliedUsd);
|
|
18
|
+
payload.borrowLimitUsd = getAssetsTotal(
|
|
19
|
+
usedAssets,
|
|
20
|
+
({ isSupplied, collateral }: { isSupplied: boolean, collateral: string }) => isSupplied && collateral,
|
|
21
|
+
({ symbol, suppliedUsd }: { symbol: string, suppliedUsd: string }) => new Dec(suppliedUsd).mul(assetsData[symbol].collateralFactor),
|
|
22
|
+
);
|
|
23
|
+
payload.liquidationLimitUsd = getAssetsTotal(
|
|
24
|
+
usedAssets,
|
|
25
|
+
({ isSupplied, collateral }: { isSupplied: boolean, collateral: string }) => isSupplied && collateral,
|
|
26
|
+
// TODO: Verify if liquidation factor is available in Aave V4, currently using collateralFactor as placeholder
|
|
27
|
+
({ symbol, suppliedUsd }: { symbol: string, suppliedUsd: string }) => new Dec(suppliedUsd).mul(assetsData[symbol].collateralFactor),
|
|
28
|
+
);
|
|
29
|
+
payload.borrowedUsd = getAssetsTotal(usedAssets, ({ isBorrowed }: { isBorrowed: boolean }) => isBorrowed, ({ borrowedUsd }: { borrowedUsd: string }) => borrowedUsd);
|
|
30
|
+
payload.drawnUsd = getAssetsTotal(usedAssets, ({ isBorrowed }: { isBorrowed: boolean }) => isBorrowed, ({ drawnUsd }: { drawnUsd: string }) => drawnUsd);
|
|
31
|
+
payload.premiumUsd = getAssetsTotal(usedAssets, ({ isBorrowed }: { isBorrowed: boolean }) => isBorrowed, ({ premiumUsd }: { premiumUsd: string }) => premiumUsd);
|
|
32
|
+
const leftToBorrowUsd = new Dec(payload.borrowLimitUsd).sub(payload.borrowedUsd);
|
|
33
|
+
payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
|
|
34
|
+
payload.ratio = +payload.suppliedUsd ? new Dec(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
35
|
+
payload.collRatio = +payload.suppliedUsd ? new Dec(payload.suppliedCollateralUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
36
|
+
payload.liqRatio = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).toString();
|
|
37
|
+
payload.liqPercent = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).mul(100).toString();
|
|
38
|
+
const { leveragedType, leveragedAsset } = isLeveragedPos(usedAssets);
|
|
39
|
+
payload.leveragedType = leveragedType;
|
|
40
|
+
payload.leveragedAsset = leveragedAsset;
|
|
41
|
+
payload.liquidationPrice = '';
|
|
42
|
+
if (leveragedType !== '') {
|
|
43
|
+
let assetPrice = assetsData[leveragedAsset].price;
|
|
44
|
+
if (leveragedType === 'lsd-leverage') {
|
|
45
|
+
// Treat ETH like a stablecoin in a long stETH position
|
|
46
|
+
payload.leveragedLsdAssetRatio = new Dec(assetsData[leveragedAsset].price).div(assetsData.ETH.price).toDP(18).toString();
|
|
47
|
+
assetPrice = new Dec(assetPrice).div(assetsData.ETH.price).toString();
|
|
48
|
+
}
|
|
49
|
+
payload.liquidationPrice = calcLeverageLiqPrice(leveragedType, assetPrice, payload.borrowedUsd, payload.liquidationLimitUsd);
|
|
50
|
+
}
|
|
51
|
+
payload.minCollRatio = new Dec(payload.suppliedCollateralUsd).div(payload.borrowLimitUsd).mul(100).toString();
|
|
52
|
+
payload.collLiquidationRatio = new Dec(payload.suppliedCollateralUsd).div(payload.liquidationLimitUsd).mul(100).toString();
|
|
53
|
+
// payload.healthRatio = new Dec(payload.liquidationLimitUsd).div(payload.borrowedUsd).toDP(4).toString();
|
|
54
|
+
payload.minHealthRatio = new Dec(payload.liquidationLimitUsd).div(payload.borrowLimitUsd).toDP(4).toString();
|
|
55
|
+
|
|
56
|
+
// TODO: Re-implement netApy calculation
|
|
57
|
+
// const { netApy, incentiveUsd, totalInterestUsd } = calculateNetApy({
|
|
58
|
+
// usedAssets,
|
|
59
|
+
// assetsData,
|
|
60
|
+
// optionalData: { healthRatio: payload.healthRatio },
|
|
61
|
+
// });
|
|
62
|
+
payload.netApy = '0';
|
|
63
|
+
payload.incentiveUsd = '0';
|
|
64
|
+
payload.totalInterestUsd = '0';
|
|
65
|
+
return payload;
|
|
66
|
+
};
|
package/src/helpers/index.ts
CHANGED
package/src/portfolio/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ import Dec from 'decimal.js';
|
|
|
2
2
|
import { EthAddress, EthereumProvider, NetworkNumber } from '../types/common';
|
|
3
3
|
import {
|
|
4
4
|
AaveMarkets,
|
|
5
|
+
AaveV4Spokes,
|
|
5
6
|
CompoundMarkets,
|
|
6
7
|
CrvUsdMarkets,
|
|
7
8
|
EulerV2Markets,
|
|
@@ -14,6 +15,7 @@ import { _getMorphoBlueAccountData, _getMorphoBlueMarketData, getMorphoEarn } fr
|
|
|
14
15
|
import {
|
|
15
16
|
AaveV2MarketData,
|
|
16
17
|
AaveV3MarketData,
|
|
18
|
+
AaveV4SpokeData,
|
|
17
19
|
AaveVersions,
|
|
18
20
|
CdpInfo,
|
|
19
21
|
CompoundV2MarketsData,
|
|
@@ -49,6 +51,7 @@ import { fetchSparkAirdropRewards, fetchSparkRewards } from '../claiming/spark';
|
|
|
49
51
|
import { fetchMorphoBlueRewards } from '../claiming/morphoBlue';
|
|
50
52
|
import { getKingRewards } from '../claiming/king';
|
|
51
53
|
import { fetchEthenaAirdropRewards } from '../claiming/ethena';
|
|
54
|
+
import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
|
|
52
55
|
|
|
53
56
|
export async function getPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], summerFiAddresses: EthAddress[]): Promise<{
|
|
54
57
|
positions: PortfolioPositionsData;
|
|
@@ -71,6 +74,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
71
74
|
const llamaLendMarkets = [NetworkNumber.Eth, NetworkNumber.Arb].includes(network) ? Object.values(LlamaLendMarkets(network)).filter((market) => market.chainIds.includes(network)) : [];
|
|
72
75
|
const liquityV2Markets = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)) : [];
|
|
73
76
|
const liquityV2MarketsStaking = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)).filter(market => !market.isLegacy) : [];
|
|
77
|
+
const aaveV4Spokes = Object.values(AaveV4Spokes(network)).filter((market) => market.chainIds.includes(network));
|
|
74
78
|
|
|
75
79
|
const client = getViemProvider(provider, network, {
|
|
76
80
|
batch: {
|
|
@@ -98,6 +102,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
98
102
|
const crvUsdMarketsData: Record<string, CrvUSDGlobalMarketData> = {};
|
|
99
103
|
const llamaLendMarketsData: Record<string, LlamaLendGlobalMarketData> = {};
|
|
100
104
|
const liquityV2MarketsData: Record<string, LiquityV2MarketData> = {};
|
|
105
|
+
const aaveV4SpokesData: Record<string, AaveV4SpokeData> = {};
|
|
101
106
|
|
|
102
107
|
const markets = {
|
|
103
108
|
morphoMarketsData,
|
|
@@ -110,6 +115,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
110
115
|
crvUsdMarketsData,
|
|
111
116
|
llamaLendMarketsData,
|
|
112
117
|
liquityV2MarketsData,
|
|
118
|
+
aaveV4SpokesData,
|
|
113
119
|
};
|
|
114
120
|
|
|
115
121
|
const positions: PortfolioPositionsData = {};
|
|
@@ -120,6 +126,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
120
126
|
for (const address of allAddresses) {
|
|
121
127
|
positions[address.toLowerCase() as EthAddress] = {
|
|
122
128
|
aaveV3: {},
|
|
129
|
+
aaveV4: {},
|
|
123
130
|
morphoBlue: {},
|
|
124
131
|
compoundV3: {},
|
|
125
132
|
spark: {},
|
|
@@ -188,6 +195,10 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
188
195
|
const marketData = await _getAaveV3MarketData(client, network, market);
|
|
189
196
|
aaveV3MarketsData[market.value] = marketData;
|
|
190
197
|
}),
|
|
198
|
+
...aaveV4Spokes.map(async (spoke) => {
|
|
199
|
+
const spokeData = await _getAaveV4SpokeData(client, network, spoke);
|
|
200
|
+
aaveV4SpokesData[spoke.value] = spokeData;
|
|
201
|
+
}),
|
|
191
202
|
...aaveV2Markets.map(async (market) => {
|
|
192
203
|
const marketData = await _getAaveV2MarketsData(client, network, market);
|
|
193
204
|
aaveV2MarketsData[market.value] = marketData;
|
|
@@ -441,6 +452,15 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
441
452
|
positions[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: `Error fetching AaveV3 account data for address ${address} on market ${market.value}`, data: null };
|
|
442
453
|
}
|
|
443
454
|
})).flat(),
|
|
455
|
+
...aaveV4Spokes.map((spoke) => allAddresses.map(async (address) => {
|
|
456
|
+
try {
|
|
457
|
+
const accData = await _getAaveV4AccountData(client, network, aaveV4SpokesData[spoke.value], address);
|
|
458
|
+
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].aaveV4[spoke.value] = { error: '', data: accData };
|
|
459
|
+
} catch (error) {
|
|
460
|
+
console.error(`Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}:`, error);
|
|
461
|
+
positions[address.toLowerCase() as EthAddress].aaveV4[spoke.value] = { error: `Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}`, data: null };
|
|
462
|
+
}
|
|
463
|
+
})).flat(),
|
|
444
464
|
...morphoMarkets.map((market) => addresses.map(async (address) => {
|
|
445
465
|
try {
|
|
446
466
|
const [accDataPromise, earnDataPromise] = await Promise.allSettled([
|
package/src/types/aaveV4.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EthAddress, NetworkNumber } from './common';
|
|
1
|
+
import { EthAddress, IncentiveData, NetworkNumber } from './common';
|
|
2
2
|
|
|
3
3
|
export enum AaveV4SpokesType {
|
|
4
4
|
AaveV4CoreSpoke = 'aave_v4_core_spoke',
|
|
@@ -71,11 +71,63 @@ export interface AaveV4ReserveAssetData {
|
|
|
71
71
|
spokeActive: boolean,
|
|
72
72
|
spokePaused: boolean,
|
|
73
73
|
drawnRate: string,
|
|
74
|
+
supplyRate: string,
|
|
75
|
+
supplyIncentives: IncentiveData[];
|
|
76
|
+
borrowIncentives: IncentiveData[];
|
|
74
77
|
}
|
|
75
78
|
|
|
79
|
+
export type AaveV4AssetsData = Record<string, AaveV4ReserveAssetData>;
|
|
80
|
+
|
|
76
81
|
export interface AaveV4SpokeData {
|
|
77
|
-
assetsData:
|
|
82
|
+
assetsData: AaveV4AssetsData,
|
|
78
83
|
oracle: EthAddress,
|
|
79
84
|
oracleDecimals: number,
|
|
80
85
|
address: EthAddress,
|
|
81
86
|
}
|
|
87
|
+
|
|
88
|
+
export interface AaveV4UsedReserveAsset {
|
|
89
|
+
symbol: string,
|
|
90
|
+
supplied: string,
|
|
91
|
+
suppliedUsd: string,
|
|
92
|
+
drawn: string,
|
|
93
|
+
drawnUsd: string,
|
|
94
|
+
premium: string,
|
|
95
|
+
premiumUsd: string,
|
|
96
|
+
borrowed: string,
|
|
97
|
+
borrowedUsd: string,
|
|
98
|
+
isSupplied: boolean,
|
|
99
|
+
isBorrowed: boolean,
|
|
100
|
+
collateral: boolean,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface AaveV4AggregatedPositionData {
|
|
104
|
+
suppliedUsd: string,
|
|
105
|
+
suppliedCollateralUsd: string,
|
|
106
|
+
borrowLimitUsd: string,
|
|
107
|
+
liquidationLimitUsd: string,
|
|
108
|
+
borrowedUsd: string,
|
|
109
|
+
drawnUsd: string,
|
|
110
|
+
premiumUsd: string,
|
|
111
|
+
leftToBorrowUsd: string,
|
|
112
|
+
ratio: string,
|
|
113
|
+
collRatio: string,
|
|
114
|
+
liqRatio: string,
|
|
115
|
+
liqPercent: string,
|
|
116
|
+
leveragedType: string,
|
|
117
|
+
leveragedAsset: string,
|
|
118
|
+
liquidationPrice: string,
|
|
119
|
+
leveragedLsdAssetRatio?: string,
|
|
120
|
+
minCollRatio: string,
|
|
121
|
+
collLiquidationRatio: string,
|
|
122
|
+
minHealthRatio: string,
|
|
123
|
+
netApy: string,
|
|
124
|
+
incentiveUsd: string,
|
|
125
|
+
totalInterestUsd: string,
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export type AaveV4UsedReserveAssets = Record<string, AaveV4UsedReserveAsset>;
|
|
129
|
+
|
|
130
|
+
export interface AaveV4AccountData extends AaveV4AggregatedPositionData {
|
|
131
|
+
usedAssets: AaveV4UsedReserveAssets,
|
|
132
|
+
healthFactor: string,
|
|
133
|
+
}
|
package/src/types/portfolio.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AaveV2PositionData, AaveV3PositionData, AaveVersions } from './aave';
|
|
2
|
+
import { AaveV4AccountData, AaveV4SpokesType } from './aaveV4';
|
|
2
3
|
import { EthAddress } from './common';
|
|
3
4
|
import { CompoundV2PositionData, CompoundV3PositionData, CompoundVersions } from './compound';
|
|
4
5
|
import { CrvUSDUserData, CrvUSDVersions } from './curveUsd';
|
|
@@ -54,6 +55,9 @@ export interface PortfolioPositionsDataForAddress {
|
|
|
54
55
|
[key: string]: FluidVaultData;
|
|
55
56
|
};
|
|
56
57
|
};
|
|
58
|
+
aaveV4: {
|
|
59
|
+
[key in AaveV4SpokesType]?: PortfolioProtocolData<AaveV4AccountData>;
|
|
60
|
+
};
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
export interface PortfolioPositionsData {
|