@defisaver/positions-sdk 2.1.71 → 2.1.72-aave-v4-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/aaveV4/index.d.ts +7 -0
- package/cjs/aaveV4/index.js +174 -0
- package/cjs/config/contracts.d.ts +1551 -0
- package/cjs/config/contracts.js +9 -0
- package/cjs/contracts.d.ts +32741 -0
- package/cjs/contracts.js +2 -1
- package/cjs/helpers/aaveV4Helpers/index.d.ts +13 -0
- package/cjs/helpers/aaveV4Helpers/index.js +117 -0
- package/cjs/helpers/index.d.ts +1 -0
- package/cjs/helpers/index.js +2 -1
- package/cjs/index.d.ts +2 -1
- package/cjs/index.js +3 -1
- package/cjs/markets/aave/marketAssets.js +1 -1
- package/cjs/markets/aaveV4/index.d.ts +28 -0
- package/cjs/markets/aaveV4/index.js +140 -0
- package/cjs/markets/index.d.ts +1 -0
- package/cjs/markets/index.js +3 -1
- package/cjs/portfolio/index.js +20 -0
- package/cjs/staking/eligibility.d.ts +6 -0
- package/cjs/staking/eligibility.js +37 -19
- package/cjs/staking/staking.js +4 -2
- package/cjs/types/aaveV4.d.ts +145 -0
- package/cjs/types/aaveV4.js +19 -0
- package/cjs/types/common.d.ts +2 -1
- package/cjs/types/common.js +1 -0
- package/cjs/types/index.d.ts +1 -0
- package/cjs/types/index.js +1 -0
- package/cjs/types/portfolio.d.ts +4 -0
- package/esm/aaveV4/index.d.ts +7 -0
- package/esm/aaveV4/index.js +165 -0
- package/esm/config/contracts.d.ts +1551 -0
- package/esm/config/contracts.js +8 -0
- package/esm/contracts.d.ts +32741 -0
- package/esm/contracts.js +1 -0
- package/esm/helpers/aaveV4Helpers/index.d.ts +13 -0
- package/esm/helpers/aaveV4Helpers/index.js +108 -0
- package/esm/helpers/index.d.ts +1 -0
- package/esm/helpers/index.js +1 -0
- package/esm/index.d.ts +2 -1
- package/esm/index.js +2 -1
- package/esm/markets/aave/marketAssets.js +1 -1
- package/esm/markets/aaveV4/index.d.ts +28 -0
- package/esm/markets/aaveV4/index.js +122 -0
- package/esm/markets/index.d.ts +1 -0
- package/esm/markets/index.js +1 -0
- package/esm/portfolio/index.js +21 -1
- package/esm/staking/eligibility.d.ts +6 -0
- package/esm/staking/eligibility.js +35 -18
- package/esm/staking/staking.js +4 -2
- package/esm/types/aaveV4.d.ts +145 -0
- package/esm/types/aaveV4.js +16 -0
- package/esm/types/common.d.ts +2 -1
- package/esm/types/common.js +1 -0
- package/esm/types/index.d.ts +1 -0
- package/esm/types/index.js +1 -0
- package/esm/types/portfolio.d.ts +4 -0
- package/package.json +2 -2
- package/src/aaveV4/index.ts +176 -0
- package/src/config/contracts.ts +9 -1
- package/src/contracts.ts +3 -1
- package/src/helpers/aaveV4Helpers/index.ts +128 -0
- package/src/helpers/index.ts +1 -0
- package/src/index.ts +2 -0
- package/src/markets/aave/marketAssets.ts +1 -1
- package/src/markets/aaveV4/index.ts +149 -0
- package/src/markets/index.ts +6 -1
- package/src/portfolio/index.ts +20 -0
- package/src/staking/eligibility.ts +67 -15
- package/src/staking/staking.ts +3 -2
- package/src/types/aaveV4.ts +161 -0
- package/src/types/common.ts +1 -0
- package/src/types/index.ts +2 -1
- package/src/types/portfolio.ts +4 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import Dec from 'decimal.js';
|
|
2
|
+
import { calcLeverageLiqPrice, getAssetsTotal, STABLE_ASSETS } from '../../moneymarket';
|
|
3
|
+
import {
|
|
4
|
+
AaveV4AggregatedPositionData, AaveV4AssetsData, AaveV4ReserveAssetData, AaveV4UsedReserveAsset, AaveV4UsedReserveAssets,
|
|
5
|
+
} from '../../types';
|
|
6
|
+
import { LeverageType, NetworkNumber } from '../../types/common';
|
|
7
|
+
|
|
8
|
+
export const aaveV4GetCollateralFactor = (assetData: AaveV4ReserveAssetData, usedAssetData: AaveV4UsedReserveAsset, useUserCollateralFactor: boolean = false): number => (useUserCollateralFactor ? usedAssetData.collateralFactor : assetData.collateralFactor);
|
|
9
|
+
|
|
10
|
+
export const isLeveragedPosAaveV4 = (usedAssets: AaveV4UsedReserveAssets, dustLimit = 5) => {
|
|
11
|
+
let borrowUnstable = 0;
|
|
12
|
+
let supplyStable = 0;
|
|
13
|
+
let borrowStable = 0;
|
|
14
|
+
let supplyUnstable = 0;
|
|
15
|
+
let longAsset = '';
|
|
16
|
+
let shortAsset = '';
|
|
17
|
+
Object.values(usedAssets).forEach(({
|
|
18
|
+
symbol, suppliedUsd, borrowedUsd, collateral, reserveId,
|
|
19
|
+
}) => {
|
|
20
|
+
const spokeAsset = `${symbol}-${reserveId}`;
|
|
21
|
+
const isSupplied = (+suppliedUsd) > dustLimit; // ignore dust like <limit leftover supply
|
|
22
|
+
const isBorrowed = (+borrowedUsd) > dustLimit; // ignore dust like <limit leftover supply
|
|
23
|
+
if (isSupplied && STABLE_ASSETS.includes(symbol) && collateral) supplyStable += 1;
|
|
24
|
+
if (isBorrowed && STABLE_ASSETS.includes(symbol)) borrowStable += 1;
|
|
25
|
+
if (isBorrowed && !STABLE_ASSETS.includes(symbol)) {
|
|
26
|
+
borrowUnstable += 1;
|
|
27
|
+
shortAsset = spokeAsset;
|
|
28
|
+
}
|
|
29
|
+
if (isSupplied && !STABLE_ASSETS.includes(symbol) && collateral) {
|
|
30
|
+
supplyUnstable += 1;
|
|
31
|
+
longAsset = spokeAsset;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const isLong = borrowStable > 0 && borrowUnstable === 0 && supplyUnstable === 1 && supplyStable === 0;
|
|
35
|
+
const isShort = supplyStable > 0 && supplyUnstable === 0 && borrowUnstable === 1 && borrowStable === 0;
|
|
36
|
+
const isVolatilePair = supplyUnstable === 1 && borrowUnstable === 1 && supplyStable === 0 && borrowStable === 0;
|
|
37
|
+
if (isLong) {
|
|
38
|
+
return {
|
|
39
|
+
leveragedType: LeverageType.Long,
|
|
40
|
+
leveragedAsset: longAsset,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (isShort) {
|
|
44
|
+
return {
|
|
45
|
+
leveragedType: LeverageType.Short,
|
|
46
|
+
leveragedAsset: shortAsset,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (isVolatilePair) {
|
|
50
|
+
return {
|
|
51
|
+
leveragedType: LeverageType.VolatilePair,
|
|
52
|
+
leveragedAsset: longAsset,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
leveragedType: LeverageType.None,
|
|
57
|
+
leveragedAsset: '',
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const aaveV4GetAggregatedPositionData = ({
|
|
62
|
+
usedAssets,
|
|
63
|
+
assetsData,
|
|
64
|
+
network,
|
|
65
|
+
useUserCollateralFactor = false,
|
|
66
|
+
}: {
|
|
67
|
+
usedAssets: AaveV4UsedReserveAssets,
|
|
68
|
+
assetsData: AaveV4AssetsData,
|
|
69
|
+
network: NetworkNumber,
|
|
70
|
+
useUserCollateralFactor?: boolean,
|
|
71
|
+
}): AaveV4AggregatedPositionData => {
|
|
72
|
+
const payload = {} as AaveV4AggregatedPositionData;
|
|
73
|
+
payload.suppliedUsd = getAssetsTotal(usedAssets, ({ isSupplied }: { isSupplied: boolean }) => isSupplied, ({ suppliedUsd }: { suppliedUsd: string }) => suppliedUsd);
|
|
74
|
+
payload.suppliedCollateralUsd = getAssetsTotal(usedAssets, ({ isSupplied, collateral }: { isSupplied: boolean, collateral: string }) => isSupplied && collateral, ({ suppliedUsd }: { suppliedUsd: string }) => suppliedUsd);
|
|
75
|
+
payload.borrowLimitUsd = getAssetsTotal(
|
|
76
|
+
usedAssets,
|
|
77
|
+
({ isSupplied, collateral }: { isSupplied: boolean, collateral: string }) => isSupplied && collateral,
|
|
78
|
+
({ symbol, suppliedUsd, reserveId }: { symbol: string, suppliedUsd: string, reserveId: number }) => new Dec(suppliedUsd).mul(aaveV4GetCollateralFactor(assetsData[`${symbol}-${reserveId}`], usedAssets[`${symbol}-${reserveId}`], useUserCollateralFactor)),
|
|
79
|
+
);
|
|
80
|
+
payload.liquidationLimitUsd = payload.borrowLimitUsd;
|
|
81
|
+
payload.borrowedUsd = getAssetsTotal(usedAssets, ({ isBorrowed }: { isBorrowed: boolean }) => isBorrowed, ({ borrowedUsd }: { borrowedUsd: string }) => borrowedUsd);
|
|
82
|
+
payload.drawnUsd = getAssetsTotal(usedAssets, ({ isBorrowed }: { isBorrowed: boolean }) => isBorrowed, ({ drawnUsd }: { drawnUsd: string }) => drawnUsd);
|
|
83
|
+
payload.premiumUsd = getAssetsTotal(usedAssets, ({ isBorrowed }: { isBorrowed: boolean }) => isBorrowed, ({ premiumUsd }: { premiumUsd: string }) => premiumUsd);
|
|
84
|
+
const leftToBorrowUsd = new Dec(payload.borrowLimitUsd).sub(payload.borrowedUsd);
|
|
85
|
+
payload.leftToBorrowUsd = leftToBorrowUsd.lte('0') ? '0' : leftToBorrowUsd.toString();
|
|
86
|
+
payload.ratio = +payload.suppliedUsd ? new Dec(payload.borrowLimitUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
87
|
+
payload.collRatio = +payload.suppliedUsd ? new Dec(payload.suppliedCollateralUsd).div(payload.borrowedUsd).mul(100).toString() : '0';
|
|
88
|
+
payload.liqRatio = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).toString();
|
|
89
|
+
payload.liqPercent = new Dec(payload.borrowLimitUsd).div(payload.liquidationLimitUsd).mul(100).toString();
|
|
90
|
+
const { leveragedType, leveragedAsset } = isLeveragedPosAaveV4(usedAssets);
|
|
91
|
+
payload.leveragedType = leveragedType;
|
|
92
|
+
payload.leveragedAsset = leveragedAsset;
|
|
93
|
+
payload.liquidationPrice = '';
|
|
94
|
+
if (leveragedType !== '') {
|
|
95
|
+
const leveragedAssetData = assetsData[leveragedAsset];
|
|
96
|
+
let assetPrice = leveragedAssetData?.price || '0';
|
|
97
|
+
if (leveragedType === LeverageType.VolatilePair) {
|
|
98
|
+
const borrowedAsset = (Object.values(usedAssets) as AaveV4UsedReserveAsset[]).find(({ borrowedUsd }: { borrowedUsd: string }) => +borrowedUsd > 0);
|
|
99
|
+
const borrowedAssetPrice = assetsData[`${borrowedAsset!.symbol}-${borrowedAsset!.reserveId}`].price;
|
|
100
|
+
const leveragedAssetPrice = assetsData[leveragedAsset].price;
|
|
101
|
+
const isReverse = new Dec(leveragedAssetPrice).lt(borrowedAssetPrice);
|
|
102
|
+
if (isReverse) {
|
|
103
|
+
payload.leveragedType = LeverageType.VolatilePairReverse;
|
|
104
|
+
payload.currentVolatilePairRatio = new Dec(borrowedAssetPrice).div(leveragedAssetPrice).toDP(18).toString();
|
|
105
|
+
assetPrice = new Dec(borrowedAssetPrice).div(assetPrice).toString();
|
|
106
|
+
} else {
|
|
107
|
+
assetPrice = new Dec(assetPrice).div(borrowedAssetPrice).toString();
|
|
108
|
+
payload.currentVolatilePairRatio = new Dec(leveragedAssetPrice).div(borrowedAssetPrice).toDP(18).toString();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
payload.liquidationPrice = calcLeverageLiqPrice(payload.leveragedType, assetPrice, payload.borrowedUsd, payload.liquidationLimitUsd);
|
|
112
|
+
}
|
|
113
|
+
payload.minCollRatio = new Dec(payload.suppliedCollateralUsd).div(payload.borrowLimitUsd).mul(100).toString();
|
|
114
|
+
payload.collLiquidationRatio = new Dec(payload.suppliedCollateralUsd).div(payload.liquidationLimitUsd).mul(100).toString();
|
|
115
|
+
// payload.healthRatio = new Dec(payload.liquidationLimitUsd).div(payload.borrowedUsd).toDP(4).toString();
|
|
116
|
+
payload.minHealthRatio = new Dec(payload.liquidationLimitUsd).div(payload.borrowLimitUsd).toDP(4).toString();
|
|
117
|
+
|
|
118
|
+
// TODO: Re-implement netApy calculation
|
|
119
|
+
// const { netApy, incentiveUsd, totalInterestUsd } = calculateNetApy({
|
|
120
|
+
// usedAssets,
|
|
121
|
+
// assetsData,
|
|
122
|
+
// optionalData: { healthRatio: payload.healthRatio },
|
|
123
|
+
// });
|
|
124
|
+
payload.netApy = '0';
|
|
125
|
+
payload.incentiveUsd = '0';
|
|
126
|
+
payload.totalInterestUsd = '0';
|
|
127
|
+
return payload;
|
|
128
|
+
};
|
package/src/helpers/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import './setup';
|
|
2
2
|
|
|
3
3
|
import * as fluid from './fluid';
|
|
4
|
+
import * as aaveV4 from './aaveV4';
|
|
4
5
|
import * as aaveV3 from './aaveV3';
|
|
5
6
|
import * as aaveV2 from './aaveV2';
|
|
6
7
|
import * as compoundV3 from './compoundV3';
|
|
@@ -27,6 +28,7 @@ export * from './types';
|
|
|
27
28
|
export {
|
|
28
29
|
aaveV2,
|
|
29
30
|
aaveV3,
|
|
31
|
+
aaveV4,
|
|
30
32
|
compoundV2,
|
|
31
33
|
compoundV3,
|
|
32
34
|
spark,
|
|
@@ -20,7 +20,7 @@ export const aaveV3AssetsDefaultMarketBase = ['ETH', 'cbETH', 'USDbC', 'wstETH',
|
|
|
20
20
|
|
|
21
21
|
export const aaveV3AssetsDefaultMarketLinea = ['ETH', 'USDC', 'weETH', 'ezETH', 'USDT', 'wstETH', 'wrsETH', 'WBTC', 'mUSD'];
|
|
22
22
|
|
|
23
|
-
export const aaveV3AssetsDefaultMarketPlasma = ['ETH', 'USDT', 'sUSDe', 'USDe', 'weETH', 'XAUt', 'PT USDe Jan', 'PT sUSDe Jan', 'wrsETH', 'wstETH', 'syrupUSDT', 'XPL', 'PT USDe Apr', 'PT sUSDe Apr'];
|
|
23
|
+
export const aaveV3AssetsDefaultMarketPlasma = ['ETH', 'USDT', 'sUSDe', 'USDe', 'weETH', 'XAUt', 'PT USDe Jan', 'PT sUSDe Jan', 'wrsETH', 'wstETH', 'syrupUSDT', 'XPL', 'PT USDe Apr', 'PT sUSDe Apr', 'GHO'];
|
|
24
24
|
|
|
25
25
|
// @dev Keep assets in array, do not assign directly, so we can parse it and edit it programmatically with `scripts/updateMarkets`
|
|
26
26
|
export const aaveV3AssetsDefaultMarket = {
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AaveV4HubInfo,
|
|
3
|
+
AaveV4HubsType,
|
|
4
|
+
AaveV4SpokeInfo,
|
|
5
|
+
AaveV4SpokesType,
|
|
6
|
+
NetworkNumber,
|
|
7
|
+
} from '../../types';
|
|
8
|
+
|
|
9
|
+
// HUBS
|
|
10
|
+
|
|
11
|
+
export const AAVE_V4_CORE_HUB = (networkId: NetworkNumber): AaveV4HubInfo => ({
|
|
12
|
+
chainIds: [NetworkNumber.Eth],
|
|
13
|
+
label: 'Core Hub',
|
|
14
|
+
value: AaveV4HubsType.AaveV4CoreHub,
|
|
15
|
+
address: '0x3Ed2C9829FBCab6015E331a0352F8ae148217D70',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const AAVE_V4_PLUS_HUB = (networkId: NetworkNumber): AaveV4HubInfo => ({
|
|
19
|
+
chainIds: [NetworkNumber.Eth],
|
|
20
|
+
label: 'Plus Hub',
|
|
21
|
+
value: AaveV4HubsType.AaveV4PlusHub,
|
|
22
|
+
address: '0xcb8C80026248f92c6DE735264c23c8e22922C562',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const AAVE_V4_PRIME_HUB = (networkId: NetworkNumber): AaveV4HubInfo => ({
|
|
26
|
+
chainIds: [NetworkNumber.Eth],
|
|
27
|
+
label: 'Prime Hub',
|
|
28
|
+
value: AaveV4HubsType.AaveV4PrimeHub,
|
|
29
|
+
address: '0xea40581231Ca775e6A3d7c129cF231D292B85f20',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const AaveV4Hubs = (networkId: NetworkNumber) => ({
|
|
33
|
+
[AaveV4HubsType.AaveV4CoreHub]: AAVE_V4_CORE_HUB(networkId),
|
|
34
|
+
[AaveV4HubsType.AaveV4PlusHub]: AAVE_V4_PLUS_HUB(networkId),
|
|
35
|
+
[AaveV4HubsType.AaveV4PrimeHub]: AAVE_V4_PRIME_HUB(networkId),
|
|
36
|
+
}) as const;
|
|
37
|
+
|
|
38
|
+
export const getAaveV4HubTypeInfo = (type: AaveV4HubsType, network?: NetworkNumber) => ({ ...AaveV4Hubs(network ?? NetworkNumber.Eth) }[type]);
|
|
39
|
+
|
|
40
|
+
export const getAaveV4HubByAddress = (networkId: NetworkNumber, address: string): AaveV4HubInfo | undefined => Object.values(AaveV4Hubs(networkId)).find(
|
|
41
|
+
hub => hub.address.toLowerCase() === address.toLowerCase(),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// SPOKES
|
|
45
|
+
|
|
46
|
+
export const AAVE_V4_BLUECHIP_SPOKE = (networkId: NetworkNumber): AaveV4SpokeInfo => ({
|
|
47
|
+
chainIds: [NetworkNumber.Eth],
|
|
48
|
+
label: 'Bluechip Spoke',
|
|
49
|
+
value: AaveV4SpokesType.AaveV4BluechipSpoke,
|
|
50
|
+
url: 'bluechip',
|
|
51
|
+
address: '0x637F9E189332a2821e5B046E2d7EEFae2405d6c5',
|
|
52
|
+
hubs: [
|
|
53
|
+
AAVE_V4_CORE_HUB(NetworkNumber.Eth).address,
|
|
54
|
+
AAVE_V4_PLUS_HUB(NetworkNumber.Eth).address,
|
|
55
|
+
AAVE_V4_PRIME_HUB(NetworkNumber.Eth).address,
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export const AAVE_V4_ETHENA_SPOKE = (networkId: NetworkNumber): AaveV4SpokeInfo => ({
|
|
60
|
+
chainIds: [NetworkNumber.Eth],
|
|
61
|
+
label: 'Ethena Spoke',
|
|
62
|
+
value: AaveV4SpokesType.AaveV4EthenaSpoke,
|
|
63
|
+
url: 'ethena',
|
|
64
|
+
address: '0x4054a9EbfcdB692599a8dF61eb0b3484F2d279D4',
|
|
65
|
+
hubs: [
|
|
66
|
+
AAVE_V4_CORE_HUB(NetworkNumber.Eth).address,
|
|
67
|
+
AAVE_V4_PLUS_HUB(NetworkNumber.Eth).address,
|
|
68
|
+
AAVE_V4_PRIME_HUB(NetworkNumber.Eth).address,
|
|
69
|
+
],
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export const AAVE_V4_ETHERFI_SPOKE = (networkId: NetworkNumber): AaveV4SpokeInfo => ({
|
|
73
|
+
chainIds: [NetworkNumber.Eth],
|
|
74
|
+
label: 'Etherfi Spoke',
|
|
75
|
+
value: AaveV4SpokesType.AaveV4EtherfiSpoke,
|
|
76
|
+
url: 'etherfi',
|
|
77
|
+
address: '0x4054a9EbfcdB692599a8dF61eb0b3484F2d279D4',
|
|
78
|
+
hubs: [
|
|
79
|
+
AAVE_V4_CORE_HUB(NetworkNumber.Eth).address,
|
|
80
|
+
AAVE_V4_PLUS_HUB(NetworkNumber.Eth).address,
|
|
81
|
+
AAVE_V4_PRIME_HUB(NetworkNumber.Eth).address,
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
export const AAVE_V4_GOLD_SPOKE = (networkId: NetworkNumber): AaveV4SpokeInfo => ({
|
|
86
|
+
chainIds: [NetworkNumber.Eth],
|
|
87
|
+
label: 'Gold Spoke',
|
|
88
|
+
value: AaveV4SpokesType.AaveV4GoldSpoke,
|
|
89
|
+
url: 'gold',
|
|
90
|
+
address: '0x0DC7ccE912Afab8B49031A0A95DB74531741C2c4',
|
|
91
|
+
hubs: [
|
|
92
|
+
AAVE_V4_CORE_HUB(NetworkNumber.Eth).address,
|
|
93
|
+
AAVE_V4_PLUS_HUB(NetworkNumber.Eth).address,
|
|
94
|
+
AAVE_V4_PRIME_HUB(NetworkNumber.Eth).address,
|
|
95
|
+
],
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
export const AAVE_V4_KELP_SPOKE = (networkId: NetworkNumber): AaveV4SpokeInfo => ({
|
|
99
|
+
chainIds: [NetworkNumber.Eth],
|
|
100
|
+
label: 'Kelp Spoke',
|
|
101
|
+
value: AaveV4SpokesType.AaveV4KelpSpoke,
|
|
102
|
+
url: 'kelp',
|
|
103
|
+
address: '0x8aC76d950a3D03F9E1d857b5AAFFdA3f86C1e9AA',
|
|
104
|
+
hubs: [
|
|
105
|
+
AAVE_V4_CORE_HUB(NetworkNumber.Eth).address,
|
|
106
|
+
AAVE_V4_PLUS_HUB(NetworkNumber.Eth).address,
|
|
107
|
+
AAVE_V4_PRIME_HUB(NetworkNumber.Eth).address,
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
export const AAVE_V4_LIDO_SPOKE = (networkId: NetworkNumber): AaveV4SpokeInfo => ({
|
|
112
|
+
chainIds: [NetworkNumber.Eth],
|
|
113
|
+
label: 'Lido Spoke',
|
|
114
|
+
value: AaveV4SpokesType.AaveV4LidoSpoke,
|
|
115
|
+
url: 'lido',
|
|
116
|
+
address: '0x4D4a7b3Ce709b4362D7095a4A0105bDFDb5dA2a7',
|
|
117
|
+
hubs: [
|
|
118
|
+
AAVE_V4_CORE_HUB(NetworkNumber.Eth).address,
|
|
119
|
+
AAVE_V4_PLUS_HUB(NetworkNumber.Eth).address,
|
|
120
|
+
AAVE_V4_PRIME_HUB(NetworkNumber.Eth).address,
|
|
121
|
+
],
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
export const AAVE_V4_MAIN_SPOKE = (networkId: NetworkNumber): AaveV4SpokeInfo => ({
|
|
125
|
+
chainIds: [NetworkNumber.Eth],
|
|
126
|
+
label: 'Main Spoke',
|
|
127
|
+
value: AaveV4SpokesType.AaveV4MainSpoke,
|
|
128
|
+
url: 'main',
|
|
129
|
+
address: '0x46539e9123A18c427e6b4DFF114c28CF405Cb023',
|
|
130
|
+
hubs: [
|
|
131
|
+
AAVE_V4_CORE_HUB(NetworkNumber.Eth).address,
|
|
132
|
+
AAVE_V4_PLUS_HUB(NetworkNumber.Eth).address,
|
|
133
|
+
AAVE_V4_PRIME_HUB(NetworkNumber.Eth).address,
|
|
134
|
+
],
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
export const AaveV4Spokes = (networkId: NetworkNumber) => ({
|
|
138
|
+
[AaveV4SpokesType.AaveV4BluechipSpoke]: AAVE_V4_BLUECHIP_SPOKE(networkId),
|
|
139
|
+
[AaveV4SpokesType.AaveV4EthenaSpoke]: AAVE_V4_ETHENA_SPOKE(networkId),
|
|
140
|
+
[AaveV4SpokesType.AaveV4EtherfiSpoke]: AAVE_V4_ETHERFI_SPOKE(networkId),
|
|
141
|
+
[AaveV4SpokesType.AaveV4GoldSpoke]: AAVE_V4_GOLD_SPOKE(networkId),
|
|
142
|
+
[AaveV4SpokesType.AaveV4KelpSpoke]: AAVE_V4_KELP_SPOKE(networkId),
|
|
143
|
+
[AaveV4SpokesType.AaveV4LidoSpoke]: AAVE_V4_LIDO_SPOKE(networkId),
|
|
144
|
+
[AaveV4SpokesType.AaveV4MainSpoke]: AAVE_V4_MAIN_SPOKE(networkId),
|
|
145
|
+
}) as const;
|
|
146
|
+
|
|
147
|
+
export const getAaveV4SpokeTypeInfo = (type: AaveV4SpokesType, network?: NetworkNumber) => ({ ...AaveV4Spokes(network ?? NetworkNumber.Eth) }[type]);
|
|
148
|
+
|
|
149
|
+
|
package/src/markets/index.ts
CHANGED
|
@@ -21,5 +21,10 @@ export { LlamaLendMarkets } from './llamaLend';
|
|
|
21
21
|
export { LiquityV2Markets, findLiquityV2MarketByAddress } from './liquityV2';
|
|
22
22
|
export { EulerV2Markets } from './euler';
|
|
23
23
|
export {
|
|
24
|
-
FluidMarkets,
|
|
24
|
+
FluidMarkets,
|
|
25
|
+
getFluidVersionsDataForNetwork,
|
|
26
|
+
getFluidMarketInfoById,
|
|
27
|
+
getFTokenAddress,
|
|
28
|
+
getFluidMarketInfoByAddress,
|
|
25
29
|
} from './fluid';
|
|
30
|
+
export { AaveV4Spokes } from './aaveV4';
|
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[], isSim = false): 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 args: [NetworkNumber, any?] = [network, { batch: { multicall: { batchSize: isSim ? 500_000 : 2_500_000 } } }];
|
|
76
80
|
const client = getViemProvider(provider, ...args);
|
|
@@ -87,6 +91,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
87
91
|
const crvUsdMarketsData: Record<string, CrvUSDGlobalMarketData> = {};
|
|
88
92
|
const llamaLendMarketsData: Record<string, LlamaLendGlobalMarketData> = {};
|
|
89
93
|
const liquityV2MarketsData: Record<string, LiquityV2MarketData> = {};
|
|
94
|
+
const aaveV4SpokesData: Record<string, AaveV4SpokeData> = {};
|
|
90
95
|
|
|
91
96
|
const markets = {
|
|
92
97
|
morphoMarketsData,
|
|
@@ -99,6 +104,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
99
104
|
crvUsdMarketsData,
|
|
100
105
|
llamaLendMarketsData,
|
|
101
106
|
liquityV2MarketsData,
|
|
107
|
+
aaveV4SpokesData,
|
|
102
108
|
};
|
|
103
109
|
|
|
104
110
|
const positions: PortfolioPositionsData = {};
|
|
@@ -109,6 +115,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
109
115
|
for (const address of allAddresses) {
|
|
110
116
|
positions[address.toLowerCase() as EthAddress] = {
|
|
111
117
|
aaveV3: {},
|
|
118
|
+
aaveV4: {},
|
|
112
119
|
morphoBlue: {},
|
|
113
120
|
compoundV3: {},
|
|
114
121
|
spark: {},
|
|
@@ -177,6 +184,10 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
177
184
|
const marketData = await _getAaveV3MarketData(client, network, market);
|
|
178
185
|
aaveV3MarketsData[market.value] = marketData;
|
|
179
186
|
}),
|
|
187
|
+
...aaveV4Spokes.map(async (spoke) => {
|
|
188
|
+
const spokeData = await _getAaveV4SpokeData(client, network, spoke);
|
|
189
|
+
aaveV4SpokesData[spoke.value] = spokeData;
|
|
190
|
+
}),
|
|
180
191
|
...aaveV2Markets.map(async (market) => {
|
|
181
192
|
const marketData = await _getAaveV2MarketsData(client, network, market);
|
|
182
193
|
aaveV2MarketsData[market.value] = marketData;
|
|
@@ -429,6 +440,15 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
429
440
|
positions[address.toLowerCase() as EthAddress].aaveV3[market.value] = { error: `Error fetching AaveV3 account data for address ${address} on market ${market.value}`, data: null };
|
|
430
441
|
}
|
|
431
442
|
})).flat(),
|
|
443
|
+
...aaveV4Spokes.map((spoke) => allAddresses.map(async (address) => {
|
|
444
|
+
try {
|
|
445
|
+
const accData = await _getAaveV4AccountData(client, network, aaveV4SpokesData[spoke.value], address);
|
|
446
|
+
if (new Dec(accData.suppliedUsd).gt(0)) positions[address.toLowerCase() as EthAddress].aaveV4[spoke.value] = { error: '', data: accData };
|
|
447
|
+
} catch (error) {
|
|
448
|
+
console.error(`Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}:`, error);
|
|
449
|
+
positions[address.toLowerCase() as EthAddress].aaveV4[spoke.value] = { error: `Error fetching AaveV4 account data for address ${address} on spoke ${spoke.value}`, data: null };
|
|
450
|
+
}
|
|
451
|
+
})).flat(),
|
|
432
452
|
...morphoMarkets.map((market) => addresses.map(async (address) => {
|
|
433
453
|
try {
|
|
434
454
|
const [accDataPromise, earnDataPromise] = await Promise.allSettled([
|
|
@@ -1,29 +1,80 @@
|
|
|
1
1
|
import Dec from 'decimal.js';
|
|
2
2
|
import { IncentiveEligibilityId, MMUsedAssets } from '../types/common';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
type EthenaPairEligibilityConfig = {
|
|
5
|
+
baseSymbol: string;
|
|
6
|
+
pairSymbol: string;
|
|
7
|
+
allowedBorrowAssets: string[];
|
|
8
|
+
maxHealthRatio: string; // exclusive upper bound
|
|
9
|
+
rewardSymbol: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const isEligibleForEthenaPairRewards = (
|
|
13
|
+
usedAssets: MMUsedAssets,
|
|
14
|
+
{ healthRatio }: { healthRatio: string },
|
|
15
|
+
{
|
|
16
|
+
baseSymbol,
|
|
17
|
+
pairSymbol,
|
|
18
|
+
allowedBorrowAssets,
|
|
19
|
+
maxHealthRatio,
|
|
20
|
+
rewardSymbol,
|
|
21
|
+
}: EthenaPairEligibilityConfig,
|
|
22
|
+
) => {
|
|
23
|
+
const baseSuppliedUsd = usedAssets[baseSymbol]?.suppliedUsd || '0';
|
|
24
|
+
const pairSuppliedUsd = usedAssets[pairSymbol]?.suppliedUsd || '0';
|
|
25
|
+
|
|
26
|
+
const anythingElseSupplied = Object.values(usedAssets).some(
|
|
27
|
+
(asset) => asset.isSupplied && asset.symbol !== baseSymbol && asset.symbol !== pairSymbol,
|
|
28
|
+
);
|
|
8
29
|
if (anythingElseSupplied) return { isEligible: false, eligibleUSDAmount: '0' };
|
|
9
|
-
const totalAmountSupplied = new Dec(USDeUSDAmountSupplied).add(sUSDeUSDAmountSupplied).toString();
|
|
10
|
-
const percentageInUSDe = new Dec(USDeUSDAmountSupplied).div(totalAmountSupplied).toNumber();
|
|
11
|
-
if (percentageInUSDe < 0.45 || percentageInUSDe > 0.55) return { isEligible: false, eligibleUSDAmount: '0' }; // 45% - 55% of total amount supplied must be in USDe
|
|
12
|
-
const percentageInSUSDe = new Dec(sUSDeUSDAmountSupplied).div(totalAmountSupplied).toNumber();
|
|
13
|
-
if (percentageInSUSDe < 0.45 || percentageInSUSDe > 0.55) return { isEligible: false, eligibleUSDAmount: '0' }; // 45% - 55% of total amount supplied must be in sUSDe
|
|
14
30
|
|
|
15
|
-
const
|
|
16
|
-
|
|
31
|
+
const totalAmountSupplied = new Dec(baseSuppliedUsd).add(pairSuppliedUsd);
|
|
32
|
+
if (totalAmountSupplied.eq(0)) return { isEligible: false, eligibleUSDAmount: '0' };
|
|
33
|
+
|
|
34
|
+
const percentageInBase = new Dec(baseSuppliedUsd).div(totalAmountSupplied).toNumber();
|
|
35
|
+
if (percentageInBase < 0.45 || percentageInBase > 0.55) return { isEligible: false, eligibleUSDAmount: '0' };
|
|
36
|
+
|
|
37
|
+
const percentageInPair = new Dec(pairSuppliedUsd).div(totalAmountSupplied).toNumber();
|
|
38
|
+
if (percentageInPair < 0.45 || percentageInPair > 0.55) return { isEligible: false, eligibleUSDAmount: '0' };
|
|
39
|
+
|
|
40
|
+
const anythingBorrowedNotAllowed = Object.values(usedAssets).some(
|
|
41
|
+
(asset) => asset.isBorrowed && !allowedBorrowAssets.includes(asset.symbol),
|
|
42
|
+
);
|
|
17
43
|
if (anythingBorrowedNotAllowed) return { isEligible: false, eligibleUSDAmount: '0' };
|
|
18
44
|
|
|
19
|
-
if (new Dec(healthRatio).gte(
|
|
45
|
+
if (new Dec(healthRatio).gte(maxHealthRatio)) return { isEligible: false, eligibleUSDAmount: '0' };
|
|
20
46
|
|
|
21
|
-
const halfAmountSupplied =
|
|
22
|
-
const
|
|
47
|
+
const halfAmountSupplied = totalAmountSupplied.div(2);
|
|
48
|
+
const rewardSuppliedUsd = usedAssets[rewardSymbol]?.suppliedUsd || '0';
|
|
49
|
+
const eligibleUSDAmount = Dec.min(rewardSuppliedUsd, halfAmountSupplied).toString();
|
|
23
50
|
|
|
24
|
-
return { isEligible: true, eligibleUSDAmount
|
|
51
|
+
return { isEligible: true, eligibleUSDAmount };
|
|
25
52
|
};
|
|
26
53
|
|
|
54
|
+
export const isEligibleForEthenaUSDeRewards = (usedAssets: MMUsedAssets, { healthRatio }: { healthRatio: string }) => isEligibleForEthenaPairRewards(
|
|
55
|
+
usedAssets,
|
|
56
|
+
{ healthRatio },
|
|
57
|
+
{
|
|
58
|
+
baseSymbol: 'USDe',
|
|
59
|
+
pairSymbol: 'sUSDe',
|
|
60
|
+
allowedBorrowAssets: ['USDC', 'USDT', 'USDS'],
|
|
61
|
+
maxHealthRatio: '2.5',
|
|
62
|
+
rewardSymbol: 'USDe',
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
export const isEligibleForEthenaGHORewards = (usedAssets: MMUsedAssets, { healthRatio }: { healthRatio: string }) => isEligibleForEthenaPairRewards(
|
|
67
|
+
usedAssets,
|
|
68
|
+
{ healthRatio },
|
|
69
|
+
{
|
|
70
|
+
baseSymbol: 'syrupUSDT',
|
|
71
|
+
pairSymbol: 'GHO',
|
|
72
|
+
allowedBorrowAssets: ['USDT'],
|
|
73
|
+
maxHealthRatio: '2',
|
|
74
|
+
rewardSymbol: 'GHO',
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
|
|
27
78
|
export const isEligibleForAaveV3ArbitrumEthSupply = (usedAssets: MMUsedAssets) => {
|
|
28
79
|
const ETHAmountSupplied = usedAssets.ETH?.suppliedUsd || '0';
|
|
29
80
|
const ETHAmountBorrowed = usedAssets.ETH?.borrowedUsd || '0';
|
|
@@ -51,4 +102,5 @@ export const EligibilityMapping: { [key in IncentiveEligibilityId]: (usedAssets:
|
|
|
51
102
|
[IncentiveEligibilityId.AaveV3ArbitrumEthSupply]: isEligibleForAaveV3ArbitrumEthSupply,
|
|
52
103
|
[IncentiveEligibilityId.AaveV3ArbitrumETHLSBorrow]: isEligibleForAaveV3ArbitrumETHLSBorrow,
|
|
53
104
|
[IncentiveEligibilityId.AaveV3EthenaLiquidLeveragePlasma]: isEligibleForEthenaUSDeRewards,
|
|
105
|
+
[IncentiveEligibilityId.AaveV3EthenaLiquidLeveragePlasmaGHO]: isEligibleForEthenaGHORewards,
|
|
54
106
|
};
|
package/src/staking/staking.ts
CHANGED
|
@@ -64,7 +64,7 @@ export const STAKING_ASSETS = [
|
|
|
64
64
|
'cbETH', 'wstETH', 'cbETH', 'rETH', 'sDAI', 'weETH', 'sUSDe', 'osETH',
|
|
65
65
|
'ezETH', 'ETHx', 'rsETH', 'pufETH', 'wrsETH', 'wsuperOETHb', 'sUSDS', 'tETH', 'PT sUSDe Sep', 'PT USDe Sep',
|
|
66
66
|
'PT sUSDe Nov', 'PT USDe Nov', 'PT USDe Jan', 'PT sUSDe Jan', 'wrsETH', 'wstETH', 'syrupUSDT', 'syrupUSDC', 'wstUSR',
|
|
67
|
-
'PT sUSDe Feb', 'PT USDe Feb', 'PT sUSDe Apr', 'PT USDe Apr', 'PT sUSDe May', 'PT USDe May', 'PT srUSDe Apr',
|
|
67
|
+
'PT sUSDe Feb', 'PT USDe Feb', 'PT sUSDe Apr', 'PT USDe Apr', 'PT sUSDe May', 'PT USDe May', 'PT srUSDe Apr', 'GHO',
|
|
68
68
|
];
|
|
69
69
|
|
|
70
70
|
export const getStakingApy = memoize(async (asset: string, network: number = NetworkNumber.Eth) => {
|
|
@@ -89,7 +89,7 @@ export const getStakingApy = memoize(async (asset: string, network: number = Net
|
|
|
89
89
|
if (asset === 'PT sUSDe Sep') return await getApyFromDfsApi('PT sUSDe Sep', network);
|
|
90
90
|
if (asset === 'PT USDe Sep') return await getApyFromDfsApi('PT USDe Sep', network);
|
|
91
91
|
if (asset === 'tETH') return await getApyFromDfsApi('tETH');
|
|
92
|
-
if (asset === 'USDe') return await getApyFromDfsApi('USDe');
|
|
92
|
+
if (asset === 'USDe') return await getApyFromDfsApi('USDe', network);
|
|
93
93
|
if (asset === 'PT sUSDe Nov') return await getApyFromDfsApi('PT sUSDe Nov', network);
|
|
94
94
|
if (asset === 'PT USDe Nov') return await getApyFromDfsApi('PT USDe Nov', network);
|
|
95
95
|
if (asset === 'PT USDe Jan') return await getApyFromDfsApi('PT USDe Jan', network);
|
|
@@ -104,6 +104,7 @@ export const getStakingApy = memoize(async (asset: string, network: number = Net
|
|
|
104
104
|
if (asset === 'PT sUSDe May') return await getApyFromDfsApi('PT sUSDe May', network);
|
|
105
105
|
if (asset === 'PT USDe May') return await getApyFromDfsApi('PT USDe May', network);
|
|
106
106
|
if (asset === 'PT srUSDe Apr') return await getApyFromDfsApi('PT srUSDe Apr', network);
|
|
107
|
+
if (asset === 'GHO') return await getApyFromDfsApi('GHO', network);
|
|
107
108
|
} catch (e) {
|
|
108
109
|
console.error(`Failed to fetch APY for ${asset}`);
|
|
109
110
|
}
|