Skip to content

Commit 67aefa8

Browse files
committed
feat: added UiPoolDataProvider V2 with eth markets oracle case
1 parent e558db4 commit 67aefa8

File tree

12 files changed

+413
-8
lines changed

12 files changed

+413
-8
lines changed

contracts/interfaces/IChainlinkAggregator.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
pragma solidity 0.6.12;
33

44
interface IChainlinkAggregator {
5+
function decimals() external view returns (uint8);
6+
57
function latestAnswer() external view returns (int256);
68

79
function latestTimestamp() external view returns (uint256);
@@ -14,4 +16,4 @@ interface IChainlinkAggregator {
1416

1517
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp);
1618
event NewRound(uint256 indexed roundId, address indexed startedBy);
17-
}
19+
}
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
// SPDX-License-Identifier: agpl-3.0
2+
pragma solidity 0.6.12;
3+
pragma experimental ABIEncoderV2;
4+
5+
import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
6+
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
7+
import {IUiPoolDataProviderV2} from './interfaces/IUiPoolDataProviderV2.sol';
8+
import {ILendingPool} from '../interfaces/ILendingPool.sol';
9+
import {IAaveOracle} from './interfaces/IAaveOracle.sol';
10+
import {IAToken} from '../interfaces/IAToken.sol';
11+
import {IVariableDebtToken} from '../interfaces/IVariableDebtToken.sol';
12+
import {IStableDebtToken} from '../interfaces/IStableDebtToken.sol';
13+
import {WadRayMath} from '../protocol/libraries/math/WadRayMath.sol';
14+
import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol';
15+
import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol';
16+
import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
17+
import {IChainlinkAggregator} from '../interfaces/IChainlinkAggregator.sol';
18+
import {DefaultReserveInterestRateStrategy} from '../protocol/lendingpool/DefaultReserveInterestRateStrategy.sol';
19+
import {IERC20DetailedBytes} from './interfaces/IERC20DetailedBytes.sol';
20+
21+
contract UiPoolDataProviderV2 is IUiPoolDataProviderV2 {
22+
using WadRayMath for uint256;
23+
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
24+
using UserConfiguration for DataTypes.UserConfigurationMap;
25+
26+
IChainlinkAggregator public immutable networkBaseTokenPriceInUsdProxyAggregator;
27+
IChainlinkAggregator public immutable marketReferenceCurrencyPriceInUsdProxyAggregator;
28+
uint256 public constant ETH_CURRENCY_UNIT = 1 ether;
29+
address public constant MKRAddress = 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2;
30+
31+
constructor(
32+
IChainlinkAggregator _networkBaseTokenPriceInUsdProxyAggregator,
33+
IChainlinkAggregator _marketReferenceCurrencyPriceInUsdProxyAggregator
34+
) public {
35+
networkBaseTokenPriceInUsdProxyAggregator = _networkBaseTokenPriceInUsdProxyAggregator;
36+
marketReferenceCurrencyPriceInUsdProxyAggregator = _marketReferenceCurrencyPriceInUsdProxyAggregator;
37+
}
38+
39+
function getInterestRateStrategySlopes(DefaultReserveInterestRateStrategy interestRateStrategy)
40+
internal
41+
view
42+
returns (
43+
uint256,
44+
uint256,
45+
uint256,
46+
uint256
47+
)
48+
{
49+
return (
50+
interestRateStrategy.variableRateSlope1(),
51+
interestRateStrategy.variableRateSlope2(),
52+
interestRateStrategy.stableRateSlope1(),
53+
interestRateStrategy.stableRateSlope2()
54+
);
55+
}
56+
57+
function getReservesList(ILendingPoolAddressesProvider provider)
58+
public
59+
view
60+
override
61+
returns (address[] memory)
62+
{
63+
ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
64+
return lendingPool.getReservesList();
65+
}
66+
67+
function getReservesData(ILendingPoolAddressesProvider provider)
68+
public
69+
view
70+
override
71+
returns (AggregatedReserveData[] memory, BaseCurrencyInfo memory)
72+
{
73+
IAaveOracle oracle = IAaveOracle(provider.getPriceOracle());
74+
ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
75+
address[] memory reserves = lendingPool.getReservesList();
76+
AggregatedReserveData[] memory reservesData = new AggregatedReserveData[](reserves.length);
77+
78+
for (uint256 i = 0; i < reserves.length; i++) {
79+
AggregatedReserveData memory reserveData = reservesData[i];
80+
reserveData.underlyingAsset = reserves[i];
81+
82+
// reserve current state
83+
DataTypes.ReserveData memory baseData = lendingPool.getReserveData(
84+
reserveData.underlyingAsset
85+
);
86+
reserveData.liquidityIndex = baseData.liquidityIndex;
87+
reserveData.variableBorrowIndex = baseData.variableBorrowIndex;
88+
reserveData.liquidityRate = baseData.currentLiquidityRate;
89+
reserveData.variableBorrowRate = baseData.currentVariableBorrowRate;
90+
reserveData.stableBorrowRate = baseData.currentStableBorrowRate;
91+
reserveData.lastUpdateTimestamp = baseData.lastUpdateTimestamp;
92+
reserveData.aTokenAddress = baseData.aTokenAddress;
93+
reserveData.stableDebtTokenAddress = baseData.stableDebtTokenAddress;
94+
reserveData.variableDebtTokenAddress = baseData.variableDebtTokenAddress;
95+
reserveData.interestRateStrategyAddress = baseData.interestRateStrategyAddress;
96+
reserveData.priceInMarketReferenceCurrency = oracle.getAssetPrice(
97+
reserveData.underlyingAsset
98+
);
99+
100+
reserveData.availableLiquidity = IERC20Detailed(reserveData.underlyingAsset).balanceOf(
101+
reserveData.aTokenAddress
102+
);
103+
(
104+
reserveData.totalPrincipalStableDebt,
105+
,
106+
reserveData.averageStableRate,
107+
reserveData.stableDebtLastUpdateTimestamp
108+
) = IStableDebtToken(reserveData.stableDebtTokenAddress).getSupplyData();
109+
reserveData.totalScaledVariableDebt = IVariableDebtToken(reserveData.variableDebtTokenAddress)
110+
.scaledTotalSupply();
111+
112+
if (address(reserveData.underlyingAsset) == address(MKRAddress)) {
113+
bytes32 symbol = IERC20DetailedBytes(reserveData.underlyingAsset).symbol();
114+
reserveData.symbol = bytes32ToString(symbol);
115+
} else {
116+
reserveData.symbol = IERC20Detailed(reserveData.underlyingAsset).symbol();
117+
}
118+
119+
(
120+
reserveData.baseLTVasCollateral,
121+
reserveData.reserveLiquidationThreshold,
122+
reserveData.reserveLiquidationBonus,
123+
reserveData.decimals,
124+
reserveData.reserveFactor
125+
) = baseData.configuration.getParamsMemory();
126+
(
127+
reserveData.isActive,
128+
reserveData.isFrozen,
129+
reserveData.borrowingEnabled,
130+
reserveData.stableBorrowRateEnabled
131+
) = baseData.configuration.getFlagsMemory();
132+
reserveData.usageAsCollateralEnabled = reserveData.baseLTVasCollateral != 0;
133+
(
134+
reserveData.variableRateSlope1,
135+
reserveData.variableRateSlope2,
136+
reserveData.stableRateSlope1,
137+
reserveData.stableRateSlope2
138+
) = getInterestRateStrategySlopes(
139+
DefaultReserveInterestRateStrategy(reserveData.interestRateStrategyAddress)
140+
);
141+
}
142+
143+
BaseCurrencyInfo memory baseCurrencyInfo;
144+
baseCurrencyInfo.networkBaseTokenPriceInUsd = networkBaseTokenPriceInUsdProxyAggregator
145+
.latestAnswer();
146+
baseCurrencyInfo.networkBaseTokenPriceDecimals = networkBaseTokenPriceInUsdProxyAggregator
147+
.decimals();
148+
149+
try oracle.BASE_CURRENCY_UNIT() returns (uint256 baseCurrencyUnit) {
150+
if (ETH_CURRENCY_UNIT == baseCurrencyUnit) {
151+
baseCurrencyInfo.marketReferenceCurrencyUnit = ETH_CURRENCY_UNIT;
152+
baseCurrencyInfo
153+
.marketReferenceCurrencyPriceInUsd = marketReferenceCurrencyPriceInUsdProxyAggregator
154+
.latestAnswer();
155+
} else {
156+
baseCurrencyInfo.marketReferenceCurrencyUnit = baseCurrencyUnit;
157+
baseCurrencyInfo.marketReferenceCurrencyPriceInUsd = int256(baseCurrencyUnit);
158+
}
159+
} catch (
160+
bytes memory /*lowLevelData*/
161+
) {
162+
baseCurrencyInfo.marketReferenceCurrencyUnit = ETH_CURRENCY_UNIT;
163+
baseCurrencyInfo
164+
.marketReferenceCurrencyPriceInUsd = marketReferenceCurrencyPriceInUsdProxyAggregator
165+
.latestAnswer();
166+
}
167+
168+
return (reservesData, baseCurrencyInfo);
169+
}
170+
171+
function getUserReservesData(ILendingPoolAddressesProvider provider, address user)
172+
external
173+
view
174+
override
175+
returns (UserReserveData[] memory)
176+
{
177+
ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
178+
address[] memory reserves = lendingPool.getReservesList();
179+
DataTypes.UserConfigurationMap memory userConfig = lendingPool.getUserConfiguration(user);
180+
181+
UserReserveData[] memory userReservesData = new UserReserveData[](
182+
user != address(0) ? reserves.length : 0
183+
);
184+
185+
for (uint256 i = 0; i < reserves.length; i++) {
186+
DataTypes.ReserveData memory baseData = lendingPool.getReserveData(reserves[i]);
187+
188+
// user reserve data
189+
userReservesData[i].underlyingAsset = reserves[i];
190+
userReservesData[i].scaledATokenBalance = IAToken(baseData.aTokenAddress).scaledBalanceOf(
191+
user
192+
);
193+
userReservesData[i].usageAsCollateralEnabledOnUser = userConfig.isUsingAsCollateral(i);
194+
195+
if (userConfig.isBorrowing(i)) {
196+
userReservesData[i].scaledVariableDebt = IVariableDebtToken(
197+
baseData.variableDebtTokenAddress
198+
).scaledBalanceOf(user);
199+
userReservesData[i].principalStableDebt = IStableDebtToken(baseData.stableDebtTokenAddress)
200+
.principalBalanceOf(user);
201+
if (userReservesData[i].principalStableDebt != 0) {
202+
userReservesData[i].stableBorrowRate = IStableDebtToken(baseData.stableDebtTokenAddress)
203+
.getUserStableRate(user);
204+
userReservesData[i].stableBorrowLastUpdateTimestamp = IStableDebtToken(
205+
baseData.stableDebtTokenAddress
206+
).getUserLastUpdated(user);
207+
}
208+
}
209+
}
210+
211+
return (userReservesData);
212+
}
213+
214+
function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) {
215+
uint8 i = 0;
216+
while (i < 32 && _bytes32[i] != 0) {
217+
i++;
218+
}
219+
bytes memory bytesArray = new bytes(i);
220+
for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
221+
bytesArray[i] = _bytes32[i];
222+
}
223+
return string(bytesArray);
224+
}
225+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// SPDX-License-Identifier: agpl-3.0
2+
pragma solidity 0.6.12;
3+
4+
/**
5+
* @title IAaveOracle interface
6+
* @notice Interface for the Aave oracle.
7+
**/
8+
9+
interface IAaveOracle {
10+
function BASE_CURRENCY() external view returns (address); // if usd returns 0x0, if eth returns weth address
11+
function BASE_CURRENCY_UNIT() external view returns (uint256);
12+
13+
/***********
14+
@dev returns the asset price in ETH
15+
*/
16+
function getAssetPrice(address asset) external view returns (uint256);
17+
}
Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
// SPDX-License-Identifier: agpl-3.0
22
pragma solidity 0.6.12;
33

4-
contract IERC20DetailedBytes {
5-
bytes32 public name;
6-
bytes32 public symbol;
7-
uint256 public decimals;
8-
}
4+
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
5+
6+
interface IERC20DetailedBytes is IERC20 {
7+
function name() external view returns (bytes32);
8+
9+
function symbol() external view returns (bytes32);
10+
11+
function decimals() external view returns (uint8);
12+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// SPDX-License-Identifier: agpl-3.0
2+
pragma solidity 0.6.12;
3+
pragma experimental ABIEncoderV2;
4+
5+
import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
6+
7+
interface IUiPoolDataProviderV2 {
8+
struct AggregatedReserveData {
9+
address underlyingAsset;
10+
string name;
11+
string symbol;
12+
uint256 decimals;
13+
uint256 baseLTVasCollateral;
14+
uint256 reserveLiquidationThreshold;
15+
uint256 reserveLiquidationBonus;
16+
uint256 reserveFactor;
17+
bool usageAsCollateralEnabled;
18+
bool borrowingEnabled;
19+
bool stableBorrowRateEnabled;
20+
bool isActive;
21+
bool isFrozen;
22+
// base data
23+
uint128 liquidityIndex;
24+
uint128 variableBorrowIndex;
25+
uint128 liquidityRate;
26+
uint128 variableBorrowRate;
27+
uint128 stableBorrowRate;
28+
uint40 lastUpdateTimestamp;
29+
address aTokenAddress;
30+
address stableDebtTokenAddress;
31+
address variableDebtTokenAddress;
32+
address interestRateStrategyAddress;
33+
//
34+
uint256 availableLiquidity;
35+
uint256 totalPrincipalStableDebt;
36+
uint256 averageStableRate;
37+
uint256 stableDebtLastUpdateTimestamp;
38+
uint256 totalScaledVariableDebt;
39+
uint256 priceInMarketReferenceCurrency;
40+
uint256 variableRateSlope1;
41+
uint256 variableRateSlope2;
42+
uint256 stableRateSlope1;
43+
uint256 stableRateSlope2;
44+
}
45+
46+
struct UserReserveData {
47+
address underlyingAsset;
48+
uint256 scaledATokenBalance;
49+
bool usageAsCollateralEnabledOnUser;
50+
uint256 stableBorrowRate;
51+
uint256 scaledVariableDebt;
52+
uint256 principalStableDebt;
53+
uint256 stableBorrowLastUpdateTimestamp;
54+
}
55+
56+
struct BaseCurrencyInfo {
57+
uint256 marketReferenceCurrencyUnit;
58+
int256 marketReferenceCurrencyPriceInUsd;
59+
int256 networkBaseTokenPriceInUsd;
60+
uint8 networkBaseTokenPriceDecimals;
61+
}
62+
63+
function getReservesList(ILendingPoolAddressesProvider provider)
64+
external
65+
view
66+
returns (address[] memory);
67+
68+
function getReservesData(ILendingPoolAddressesProvider provider)
69+
external
70+
view
71+
returns (
72+
AggregatedReserveData[] memory,
73+
BaseCurrencyInfo memory
74+
);
75+
76+
function getUserReservesData(ILendingPoolAddressesProvider provider, address user)
77+
external
78+
view
79+
returns (
80+
UserReserveData[] memory
81+
);
82+
}

helper-hardhat-config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export const NETWORKS_RPC_URL: iParamsPerNetwork<string> = {
5858
};
5959

6060
export const NETWORKS_DEFAULT_GAS: iParamsPerNetwork<number> = {
61-
[eEthereumNetwork.kovan]: 1 * GWEI,
61+
[eEthereumNetwork.kovan]: 3 * GWEI,
6262
[eEthereumNetwork.ropsten]: 65 * GWEI,
6363
[eEthereumNetwork.main]: 65 * GWEI,
6464
[eEthereumNetwork.coverage]: 65 * GWEI,

helpers/constants.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,23 @@ export const MOCK_CHAINLINK_AGGREGATORS_PRICES = {
7575
WAVAX: oneEther.multipliedBy('0.006051936629').toFixed(),
7676
USD: '5848466240000000',
7777
};
78+
79+
export const chainlinkAggregatorProxy = {
80+
main: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419',
81+
kovan: '0x9326BFA02ADD2366b30bacB125260Af641031331',
82+
matic: '0xAB594600376Ec9fD91F8e885dADF0CE036862dE0',
83+
mumbai: '0xd0D5e3DB44DE05E9F294BB0a3bEEaF030DE24Ada',
84+
avalanche: '0x0A77230d17318075983913bC2145DB16C7366156',
85+
fuji: '0x5498BB86BC934c8D34FDA08E81D444153d0D06aD',
86+
tenderly: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419',
87+
};
88+
89+
export const chainlinkEthUsdAggregatorProxy = {
90+
main: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419',
91+
kovan: '0x9326BFA02ADD2366b30bacB125260Af641031331',
92+
matic: '0xF9680D99D6C9589e2a93a78A04A279e509205945',
93+
mumbai: '0x0715A7794a1dc8e42615F059dD6e406A6594651A',
94+
avalanche: '0x976B3D034E162d8bD72D6b9C989d545b839003b0',
95+
fuji: '0x86d67c3D38D2bCeE722E601025C25a575021c6EA',
96+
tenderly: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419',
97+
};

0 commit comments

Comments
 (0)