diff --git a/contracts/misc/IUiPoolDataProvider.sol b/contracts/misc/IUiPoolDataProvider.sol new file mode 100644 index 00000000..a09f210a --- /dev/null +++ b/contracts/misc/IUiPoolDataProvider.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.6.8; +pragma experimental ABIEncoderV2; + +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; + +interface IUiPoolDataProvider { + struct AggregatedReserveData { + address underlyingAsset; + string name; + string symbol; + uint256 decimals; + uint256 ltv; + uint256 liquidationThreshold; + uint256 liquidationBonus; + uint256 reserveFactor; + bool usageAsCollateralEnabled; + bool borrowingEnabled; + bool stableBorrowRateEnabled; + bool isActive; + bool isFreezed; + ReserveLogic.ReserveData baseData; + uint256 availableLiquidity; + uint256 totalBorrowsStable; + uint256 totalBorrowsVariable; + uint256 utilizationRate; + uint256 priceInEth; + uint256 variableRateSlope1; + uint256 variableRateSlope2; + uint256 stableRateSlope1; + uint256 stableRateSlope2; + } + // + // struct ReserveData { + // uint256 averageStableBorrowRate; + // uint256 totalLiquidity; + // } + + struct UserReserveData { + address underlyingAsset; + uint256 principalATokenBalance; + bool usageAsCollateralEnabledOnUser; + uint256 stableBorrowRate; + uint256 principalVariableBorrows; + uint256 principalStableBorrows; + uint256 stableBorrowLastUpdateTimestamp; + } + + // + // struct ATokenSupplyData { + // string name; + // string symbol; + // uint8 decimals; + // uint256 totalSupply; + // address aTokenAddress; + // } + + function getReservesData(ILendingPoolAddressesProvider provider, address user) + external + view + returns ( + AggregatedReserveData[] memory, + UserReserveData[] memory, + uint256 + ); + + // function getUserReservesData(ILendingPoolAddressesProvider provider, address user) + // external + // view + // returns (UserReserveData[] memory); + // + // function getAllATokenSupply(ILendingPoolAddressesProvider provider) + // external + // view + // returns (ATokenSupplyData[] memory); + // + // function getATokenSupply(address[] calldata aTokens) + // external + // view + // returns (ATokenSupplyData[] memory); +} diff --git a/contracts/misc/UiPoolDataProvider.sol b/contracts/misc/UiPoolDataProvider.sol new file mode 100644 index 00000000..74a5bb34 --- /dev/null +++ b/contracts/misc/UiPoolDataProvider.sol @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.6.8; +pragma experimental ABIEncoderV2; + +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {IUiPoolDataProvider} from './IUiPoolDataProvider.sol'; +import {ILendingPool} from '../interfaces/ILendingPool.sol'; +import {IERC20Detailed} from '../interfaces/IERC20Detailed.sol'; +import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; +import {IAToken} from '../tokenization/interfaces/IAToken.sol'; +import {IVariableDebtToken} from '../tokenization/interfaces/IVariableDebtToken.sol'; +import {IStableDebtToken} from '../tokenization/interfaces/IStableDebtToken.sol'; + +import {WadRayMath} from '../libraries/math/WadRayMath.sol'; +import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; +import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol'; +import '../lendingpool/DefaultReserveInterestRateStrategy.sol'; + +contract UiPoolDataProvider is IUiPoolDataProvider { + using WadRayMath for uint256; + using ReserveConfiguration for ReserveConfiguration.Map; + using UserConfiguration for UserConfiguration.Map; + + address public constant MOCK_USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; + + function getInterestRateStrategySlopes(DefaultReserveInterestRateStrategy interestRateStrategy) + internal + view + returns ( + uint256, + uint256, + uint256, + uint256 + ) + { + return ( + interestRateStrategy.variableRateSlope1(), + interestRateStrategy.variableRateSlope2(), + interestRateStrategy.stableRateSlope1(), + interestRateStrategy.stableRateSlope2() + ); + } + + function getReservesData(ILendingPoolAddressesProvider provider, address user) + external + override + view + returns ( + AggregatedReserveData[] memory, + UserReserveData[] memory, + uint256 + ) + { + ILendingPool lendingPool = ILendingPool(provider.getLendingPool()); + IPriceOracleGetter oracle = IPriceOracleGetter(provider.getPriceOracle()); + address[] memory reserves = lendingPool.getReservesList(); + UserConfiguration.Map memory userConfig = lendingPool.getUserConfiguration(user); + + AggregatedReserveData[] memory reservesData = new AggregatedReserveData[](reserves.length); + UserReserveData[] memory userReservesData = new UserReserveData[]( + user != address(0) ? reserves.length : 0 + ); + + for (uint256 i = 0; i < reserves.length; i++) { + AggregatedReserveData memory reserveData = reservesData[i]; + reserveData.underlyingAsset = reserves[i]; + + // reserve current state + reserveData.baseData = lendingPool.getReserveData(reserveData.underlyingAsset); + reserveData.priceInEth = oracle.getAssetPrice(reserveData.underlyingAsset); + + reserveData.availableLiquidity = IERC20Detailed(reserveData.underlyingAsset).balanceOf( + reserveData.baseData.aTokenAddress + ); + reserveData.totalBorrowsStable = IERC20Detailed(reserveData.baseData.stableDebtTokenAddress) + .totalSupply(); + reserveData.totalBorrowsVariable = IERC20Detailed( + reserveData + .baseData + .variableDebtTokenAddress + ) + .totalSupply(); + uint256 totalBorrows = reserveData.totalBorrowsStable + reserveData.totalBorrowsVariable; + reserveData.utilizationRate = totalBorrows == 0 + ? 0 + : totalBorrows.rayDiv(totalBorrows + reserveData.availableLiquidity); + + // reserve configuration + + // we're getting this info from the aToken, because some of assets can be not compliant with ETC20Detailed + reserveData.symbol = IERC20Detailed(reserveData.baseData.aTokenAddress).symbol(); + reserveData.name = ''; + + ( + reserveData.ltv, + reserveData.liquidationThreshold, + reserveData.liquidationBonus, + reserveData.decimals, + reserveData.reserveFactor + ) = reserveData.baseData.configuration.getParamsMemory(); + ( + reserveData.isActive, + reserveData.isFreezed, + reserveData.borrowingEnabled, + reserveData.stableBorrowRateEnabled + ) = reserveData.baseData.configuration.getFlagsMemory(); + reserveData.usageAsCollateralEnabled = reserveData.ltv != 0; + ( + reserveData.variableRateSlope1, + reserveData.variableRateSlope2, + reserveData.stableRateSlope1, + reserveData.stableRateSlope2 + ) = getInterestRateStrategySlopes( + DefaultReserveInterestRateStrategy(reserveData.baseData.interestRateStrategyAddress) + ); + + if (user != address(0)) { + // user reserve data + userReservesData[i].underlyingAsset = reserveData.underlyingAsset; + userReservesData[i].principalATokenBalance = IAToken(reserveData.baseData.aTokenAddress) + .scaledBalanceOf(user); + userReservesData[i].usageAsCollateralEnabledOnUser = userConfig.isUsingAsCollateral(i); + + if (userConfig.isBorrowing(i)) { + userReservesData[i].principalVariableBorrows = IVariableDebtToken( + reserveData + .baseData + .variableDebtTokenAddress + ) + .scaledBalanceOf(user); + userReservesData[i].principalStableBorrows = IStableDebtToken( + reserveData + .baseData + .stableDebtTokenAddress + ) + .principalBalanceOf(user); + if (userReservesData[i].principalStableBorrows != 0) { + userReservesData[i].stableBorrowRate = IStableDebtToken( + reserveData + .baseData + .stableDebtTokenAddress + ) + .getUserStableRate(user); + userReservesData[i].stableBorrowLastUpdateTimestamp = IStableDebtToken( + reserveData + .baseData + .stableDebtTokenAddress + ) + .getUserLastUpdated(user); + } + } + } + } + return (reservesData, userReservesData, oracle.getAssetPrice(MOCK_USD_ADDRESS)); + } +}