diff --git a/contracts/misc/UiIncentiveDataProviderV2.sol b/contracts/misc/UiIncentiveDataProviderV2.sol new file mode 100644 index 00000000..5e643df7 --- /dev/null +++ b/contracts/misc/UiIncentiveDataProviderV2.sol @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {IAaveIncentivesController} from '../interfaces/IAaveIncentivesController.sol'; +import {IUiIncentiveDataProviderV3} from './interfaces/IUiIncentiveDataProviderV3.sol'; +import {ILendingPool} from '../interfaces/ILendingPool.sol'; +import {IAToken} from '../interfaces/IAToken.sol'; +import {IVariableDebtToken} from '../interfaces/IVariableDebtToken.sol'; +import {IStableDebtToken} from '../interfaces/IStableDebtToken.sol'; +import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol'; +import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; +import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {IERC20DetailedBytes} from './interfaces/IERC20DetailedBytes.sol'; + +contract UiIncentiveDataProviderV2 is IUiIncentiveDataProviderV3 { + using UserConfiguration for DataTypes.UserConfigurationMap; + + address public constant MKRAddress = 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2; + + constructor() public {} + + function getFullReservesIncentiveData(ILendingPoolAddressesProvider provider, address user) + external + view + override + returns (AggregatedReserveIncentiveData[] memory, UserReserveIncentiveData[] memory) + { + return (_getReservesIncentivesData(provider), _getUserReservesIncentivesData(provider, user)); + } + + function getReservesIncentivesData(ILendingPoolAddressesProvider provider) + external + view + override + returns (AggregatedReserveIncentiveData[] memory) + { + return _getReservesIncentivesData(provider); + } + + function _getReservesIncentivesData(ILendingPoolAddressesProvider provider) + private + view + returns (AggregatedReserveIncentiveData[] memory) + { + ILendingPool lendingPool = ILendingPool(provider.getLendingPool()); + address[] memory reserves = lendingPool.getReservesList(); + AggregatedReserveIncentiveData[] + memory reservesIncentiveData = new AggregatedReserveIncentiveData[](reserves.length); + + for (uint256 i = 0; i < reserves.length; i++) { + AggregatedReserveIncentiveData memory reserveIncentiveData = reservesIncentiveData[i]; + reserveIncentiveData.underlyingAsset = reserves[i]; + + DataTypes.ReserveData memory baseData = lendingPool.getReserveData(reserves[i]); + + try IAToken(baseData.aTokenAddress).getIncentivesController() returns ( + IAaveIncentivesController aTokenIncentiveController + ) { + RewardInfo[] memory aRewardsInformation = new RewardInfo[](1); + if (address(aTokenIncentiveController) != address(0)) { + address aRewardToken = aTokenIncentiveController.REWARD_TOKEN(); + + try aTokenIncentiveController.getAssetData(baseData.aTokenAddress) returns ( + uint256 aTokenIncentivesIndex, + uint256 aEmissionPerSecond, + uint256 aIncentivesLastUpdateTimestamp + ) { + + aRewardsInformation[0] = RewardInfo( + getSymbol(aRewardToken), + aRewardToken, + address(0), + aEmissionPerSecond, + aIncentivesLastUpdateTimestamp, + aTokenIncentivesIndex, + aTokenIncentiveController.DISTRIBUTION_END(), + 0, + IERC20Detailed(aRewardToken).decimals(), + aTokenIncentiveController.PRECISION(), + 0 + ); + reserveIncentiveData.aIncentiveData = IncentiveData( + baseData.aTokenAddress, + address(aTokenIncentiveController), + aRewardsInformation + ); + } catch ( + bytes memory /*lowLevelData*/ + ) { + ( + uint256 aEmissionPerSecond, + uint256 aIncentivesLastUpdateTimestamp, + uint256 aTokenIncentivesIndex + ) = aTokenIncentiveController.assets(baseData.aTokenAddress); + aRewardsInformation[0] = RewardInfo( + getSymbol(aRewardToken), + aRewardToken, + address(0), + aEmissionPerSecond, + aIncentivesLastUpdateTimestamp, + aTokenIncentivesIndex, + aTokenIncentiveController.DISTRIBUTION_END(), + 0, + IERC20Detailed(aRewardToken).decimals(), + aTokenIncentiveController.PRECISION(), + 0 + ); + + reserveIncentiveData.aIncentiveData = IncentiveData( + baseData.aTokenAddress, + address(aTokenIncentiveController), + aRewardsInformation + ); + } + } + } catch ( + bytes memory /*lowLevelData*/ + ) { + // Will not get here + } + + try IStableDebtToken(baseData.stableDebtTokenAddress).getIncentivesController() returns ( + IAaveIncentivesController sTokenIncentiveController + ) { + RewardInfo[] memory sRewardsInformation = new RewardInfo[](1); + if (address(sTokenIncentiveController) != address(0)) { + address sRewardToken = sTokenIncentiveController.REWARD_TOKEN(); + try sTokenIncentiveController.getAssetData(baseData.stableDebtTokenAddress) returns ( + uint256 sTokenIncentivesIndex, + uint256 sEmissionPerSecond, + uint256 sIncentivesLastUpdateTimestamp + ) { + sRewardsInformation[0] = RewardInfo( + getSymbol(sRewardToken), + sRewardToken, + address(0), + sEmissionPerSecond, + sIncentivesLastUpdateTimestamp, + sTokenIncentivesIndex, + sTokenIncentiveController.DISTRIBUTION_END(), + 0, + IERC20Detailed(sRewardToken).decimals(), + sTokenIncentiveController.PRECISION(), + 0 + ); + + reserveIncentiveData.sIncentiveData = IncentiveData( + baseData.stableDebtTokenAddress, + address(sTokenIncentiveController), + sRewardsInformation + ); + } catch ( + bytes memory /*lowLevelData*/ + ) { + ( + uint256 sEmissionPerSecond, + uint256 sIncentivesLastUpdateTimestamp, + uint256 sTokenIncentivesIndex + ) = sTokenIncentiveController.assets(baseData.stableDebtTokenAddress); + + sRewardsInformation[0] = RewardInfo( + getSymbol(sRewardToken), + sRewardToken, + address(0), + sEmissionPerSecond, + sIncentivesLastUpdateTimestamp, + sTokenIncentivesIndex, + sTokenIncentiveController.DISTRIBUTION_END(), + 0, + IERC20Detailed(sRewardToken).decimals(), + sTokenIncentiveController.PRECISION(), + 0 + ); + + reserveIncentiveData.sIncentiveData = IncentiveData( + baseData.stableDebtTokenAddress, + address(sTokenIncentiveController), + sRewardsInformation + ); + } + } + } catch ( + bytes memory /*lowLevelData*/ + ) { + // Will not get here + } + + try IVariableDebtToken(baseData.variableDebtTokenAddress).getIncentivesController() returns ( + IAaveIncentivesController vTokenIncentiveController + ) { + RewardInfo[] memory vRewardsInformation = new RewardInfo[](1); + if (address(vTokenIncentiveController) != address(0)) { + address vRewardToken = vTokenIncentiveController.REWARD_TOKEN(); + + try vTokenIncentiveController.getAssetData(baseData.variableDebtTokenAddress) returns ( + uint256 vTokenIncentivesIndex, + uint256 vEmissionPerSecond, + uint256 vIncentivesLastUpdateTimestamp + ) { + vRewardsInformation[0] = RewardInfo( + getSymbol(vRewardToken), + vRewardToken, + address(0), + vEmissionPerSecond, + vIncentivesLastUpdateTimestamp, + vTokenIncentivesIndex, + vTokenIncentiveController.DISTRIBUTION_END(), + 0, + IERC20Detailed(vRewardToken).decimals(), + vTokenIncentiveController.PRECISION(), + 0 + ); + + reserveIncentiveData.vIncentiveData = IncentiveData( + baseData.variableDebtTokenAddress, + address(vTokenIncentiveController), + vRewardsInformation + ); + } catch ( + bytes memory /*lowLevelData*/ + ) { + ( + uint256 vEmissionPerSecond, + uint256 vIncentivesLastUpdateTimestamp, + uint256 vTokenIncentivesIndex + ) = vTokenIncentiveController.assets(baseData.variableDebtTokenAddress); + + vRewardsInformation[0] = RewardInfo( + getSymbol(vRewardToken), + vRewardToken, + address(0), + vEmissionPerSecond, + vIncentivesLastUpdateTimestamp, + vTokenIncentivesIndex, + vTokenIncentiveController.DISTRIBUTION_END(), + 0, + IERC20Detailed(vRewardToken).decimals(), + vTokenIncentiveController.PRECISION(), + 0 + ); + + reserveIncentiveData.vIncentiveData = IncentiveData( + baseData.variableDebtTokenAddress, + address(vTokenIncentiveController), + vRewardsInformation + ); + } + } + } catch ( + bytes memory /*lowLevelData*/ + ) { + // Will not get here + } + } + return (reservesIncentiveData); + } + + function getUserReservesIncentivesData(ILendingPoolAddressesProvider provider, address user) + external + view + override + returns (UserReserveIncentiveData[] memory) + { + return _getUserReservesIncentivesData(provider, user); + } + + function _getUserReservesIncentivesData(ILendingPoolAddressesProvider provider, address user) + private + view + returns (UserReserveIncentiveData[] memory) + { + ILendingPool lendingPool = ILendingPool(provider.getLendingPool()); + address[] memory reserves = lendingPool.getReservesList(); + + UserReserveIncentiveData[] memory userReservesIncentivesData = new UserReserveIncentiveData[]( + user != address(0) ? reserves.length : 0 + ); + + for (uint256 i = 0; i < reserves.length; i++) { + DataTypes.ReserveData memory baseData = lendingPool.getReserveData(reserves[i]); + + // user reserve data + userReservesIncentivesData[i].underlyingAsset = reserves[i]; + + try IAToken(baseData.aTokenAddress).getIncentivesController() returns ( + IAaveIncentivesController aTokenIncentiveController + ) { + if (address(aTokenIncentiveController) != address(0)) { + UserRewardInfo[] memory aUserRewardsInformation = new UserRewardInfo[](1); + + address aRewardToken = aTokenIncentiveController.REWARD_TOKEN(); + + aUserRewardsInformation[0] = UserRewardInfo( + getSymbol(aRewardToken), + address(0), + aRewardToken, + aTokenIncentiveController.getUserUnclaimedRewards(user), + aTokenIncentiveController.getUserAssetData(user, baseData.aTokenAddress), + 0, + 0, + IERC20Detailed(aRewardToken).decimals() + ); + + userReservesIncentivesData[i].aTokenIncentivesUserData = UserIncentiveData( + baseData.aTokenAddress, + address(aTokenIncentiveController), + aUserRewardsInformation + ); + } + } catch ( + bytes memory /*lowLevelData*/ + ) {} + + try IVariableDebtToken(baseData.variableDebtTokenAddress).getIncentivesController() returns ( + IAaveIncentivesController vTokenIncentiveController + ) { + if (address(vTokenIncentiveController) != address(0)) { + UserRewardInfo[] memory vUserRewardsInformation = new UserRewardInfo[](1); + + address vRewardToken = vTokenIncentiveController.REWARD_TOKEN(); + + vUserRewardsInformation[0] = UserRewardInfo( + getSymbol(vRewardToken), + address(0), + vRewardToken, + vTokenIncentiveController.getUserUnclaimedRewards(user), + vTokenIncentiveController.getUserAssetData(user, baseData.variableDebtTokenAddress), + 0, + 0, + IERC20Detailed(vRewardToken).decimals() + ); + + userReservesIncentivesData[i].aTokenIncentivesUserData = UserIncentiveData( + baseData.variableDebtTokenAddress, + address(vTokenIncentiveController), + vUserRewardsInformation + ); + } + } catch ( + bytes memory /*lowLevelData*/ + ) {} + + try IStableDebtToken(baseData.stableDebtTokenAddress).getIncentivesController() returns ( + IAaveIncentivesController sTokenIncentiveController + ) { + if (address(sTokenIncentiveController) != address(0)) { + UserRewardInfo[] memory sUserRewardsInformation = new UserRewardInfo[](1); + + address sRewardToken = sTokenIncentiveController.REWARD_TOKEN(); + + sUserRewardsInformation[0] = UserRewardInfo( + getSymbol(sRewardToken), + address(0), + sRewardToken, + sTokenIncentiveController.getUserUnclaimedRewards(user), + sTokenIncentiveController.getUserAssetData(user, baseData.stableDebtTokenAddress), + 0, + 0, + IERC20Detailed(sRewardToken).decimals() + ); + + userReservesIncentivesData[i].aTokenIncentivesUserData = UserIncentiveData( + baseData.stableDebtTokenAddress, + address(sTokenIncentiveController), + sUserRewardsInformation + ); + } + } catch ( + bytes memory /*lowLevelData*/ + ) {} + } + + return (userReservesIncentivesData); + } + + function getSymbol(address rewardToken) public view returns (string memory) { + if (address(rewardToken) == address(MKRAddress)) { + bytes32 symbol = IERC20DetailedBytes(rewardToken).symbol(); + return bytes32ToString(symbol); + } else { + return IERC20Detailed(rewardToken).symbol(); + } + } + + function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) { + uint8 i = 0; + while (i < 32 && _bytes32[i] != 0) { + i++; + } + bytes memory bytesArray = new bytes(i); + for (i = 0; i < 32 && _bytes32[i] != 0; i++) { + bytesArray[i] = _bytes32[i]; + } + return string(bytesArray); + } +} \ No newline at end of file diff --git a/contracts/misc/interfaces/IERC20DetailedBytes.sol b/contracts/misc/interfaces/IERC20DetailedBytes.sol index de91e288..3ab7e5d4 100644 --- a/contracts/misc/interfaces/IERC20DetailedBytes.sol +++ b/contracts/misc/interfaces/IERC20DetailedBytes.sol @@ -1,8 +1,12 @@ // SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; -contract IERC20DetailedBytes { - bytes32 public name; - bytes32 public symbol; - uint256 public decimals; -} +import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; + +interface IERC20DetailedBytes is IERC20 { + function name() external view returns (bytes32); + + function symbol() external view returns (bytes32); + + function decimals() external view returns (uint8); +} diff --git a/contracts/misc/interfaces/IUiIncentiveDataProviderV3.sol b/contracts/misc/interfaces/IUiIncentiveDataProviderV3.sol new file mode 100644 index 00000000..80029577 --- /dev/null +++ b/contracts/misc/interfaces/IUiIncentiveDataProviderV3.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; + +interface IUiIncentiveDataProviderV3 { + struct AggregatedReserveIncentiveData { + address underlyingAsset; + IncentiveData aIncentiveData; + IncentiveData vIncentiveData; + IncentiveData sIncentiveData; + } + + struct IncentiveData { + address tokenAddress; + address incentiveControllerAddress; + RewardInfo[] rewardsTokenInformation; + } + + struct RewardInfo { + string rewardTokenSymbol; + address rewardTokenAddress; + address rewardOracleAddress; + uint256 emissionPerSecond; + uint256 incentivesLastUpdateTimestamp; + uint256 tokenIncentivesIndex; + uint256 emissionEndTimestamp; + int256 rewardPriceFeed; + uint8 rewardTokenDecimals; + uint8 precision; + uint8 priceFeedDecimals; + } + + struct UserReserveIncentiveData { + address underlyingAsset; + UserIncentiveData aTokenIncentivesUserData; + UserIncentiveData vTokenIncentivesUserData; + UserIncentiveData sTokenIncentivesUserData; + } + + struct UserIncentiveData { + address tokenAddress; + address incentiveControllerAddress; + UserRewardInfo[] userRewardsInformation; + } + + struct UserRewardInfo { + string rewardTokenSymbol; + address rewardOracleAddress; + address rewardTokenAddress; + uint256 userUnclaimedRewards; + uint256 tokenIncentivesUserIndex; + int256 rewardPriceFeed; + uint8 priceFeedDecimals; + uint8 rewardTokenDecimals; + + } + + function getReservesIncentivesData(ILendingPoolAddressesProvider provider) + external + view + returns (AggregatedReserveIncentiveData[] memory); + + function getUserReservesIncentivesData(ILendingPoolAddressesProvider provider, address user) + external + view + returns (UserReserveIncentiveData[] memory); + + // generic method with full data + function getFullReservesIncentiveData(ILendingPoolAddressesProvider provider, address user) + external + view + returns (AggregatedReserveIncentiveData[] memory, UserReserveIncentiveData[] memory); +} diff --git a/helper-hardhat-config.ts b/helper-hardhat-config.ts index c66da367..7a9bc52c 100644 --- a/helper-hardhat-config.ts +++ b/helper-hardhat-config.ts @@ -65,8 +65,8 @@ export const NETWORKS_DEFAULT_GAS: iParamsPerNetwork = { [eEthereumNetwork.hardhat]: 65 * GWEI, [eEthereumNetwork.buidlerevm]: 65 * GWEI, [eEthereumNetwork.tenderly]: 1 * GWEI, - [ePolygonNetwork.mumbai]: 1 * GWEI, - [ePolygonNetwork.matic]: 1 * GWEI, + [ePolygonNetwork.mumbai]: 35 * GWEI, + [ePolygonNetwork.matic]: 35 * GWEI, [eXDaiNetwork.xdai]: 1 * GWEI, [eAvalancheNetwork.avalanche]: 225 * GWEI, [eAvalancheNetwork.fuji]: 85 * GWEI, diff --git a/helpers/contracts-deployments.ts b/helpers/contracts-deployments.ts index 13c64a78..3ea129e0 100644 --- a/helpers/contracts-deployments.ts +++ b/helpers/contracts-deployments.ts @@ -51,6 +51,7 @@ import { WETH9MockedFactory, WETHGatewayFactory, FlashLiquidationAdapterFactory, + UiIncentiveDataProviderV2, } from '../types'; import { withSaveAndVerify, @@ -69,6 +70,15 @@ import { LendingPoolLibraryAddresses } from '../types/LendingPoolFactory'; import { UiPoolDataProvider } from '../types'; import { eNetwork } from './types'; +export const deployUiIncentiveDataProviderV2 = async (verify?: boolean) => { + const id = eContractid.UiIncentiveDataProviderV2; + const instance = await deployContract(id, []); + if (verify) { + await verifyContract(id, instance, []); + } + return instance; +}; + export const deployUiPoolDataProvider = async ( [incentivesController, aaveOracle]: [tEthereumAddress, tEthereumAddress], verify?: boolean diff --git a/helpers/types.ts b/helpers/types.ts index babd113e..0e27ad86 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -98,6 +98,7 @@ export enum eContractid { MockParaSwapAugustus = 'MockParaSwapAugustus', MockParaSwapAugustusRegistry = 'MockParaSwapAugustusRegistry', ParaSwapLiquiditySwapAdapter = 'ParaSwapLiquiditySwapAdapter', + UiIncentiveDataProviderV2 = 'UiIncentiveDataProviderV2', } /* diff --git a/package.json b/package.json index dfe0b2d4..3f24dc8d 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,9 @@ "matic:deployUIProvider": "hardhat --network matic deploy-UiPoolDataProvider", "mumbai:deployUIProvider": "hardhat --network mumbai deploy-UiPoolDataProvider", "fuji:deployUIProvider": "hardhat --network fuji deploy-UiPoolDataProvider", + "fuji:deployUIIncentivesV2": "hardhat --network fuji deploy-UiIncentiveDataProviderV2", + "mumbai:deployUIIncentivesV2": "hardhat --network mumbai deploy-UiIncentiveDataProviderV2 --verify", + "matic:deployUIIncentivesV2": "hardhat --network matic deploy-UiIncentiveDataProviderV2 --verify", "dev:deployUniswapRepayAdapter": "hardhat --network kovan deploy-UniswapRepayAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441 --weth 0xd0a1e359811322d97991e03f863a0c30c2cf029c", "dev:UniswapLiquiditySwapAdapter": "hardhat --network kovan deploy-UniswapLiquiditySwapAdapter --provider 0x88757f2f99175387aB4C6a4b3067c77A695b0349 --router 0xfcd87315f0e4067070ade8682fcdbc3006631441 --weth 0xd0a1e359811322d97991e03f863a0c30c2cf029c", "main:deployUniswapRepayAdapter": "hardhat --network main deploy-UniswapRepayAdapter --provider 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 --router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D --weth 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", diff --git a/tasks/deployments/deploy-UiIncentiveDataProviderV2.ts b/tasks/deployments/deploy-UiIncentiveDataProviderV2.ts new file mode 100644 index 00000000..a75ad408 --- /dev/null +++ b/tasks/deployments/deploy-UiIncentiveDataProviderV2.ts @@ -0,0 +1,21 @@ +import { task } from 'hardhat/config'; +import { eContractid } from '../../helpers/types'; +import { deployUiIncentiveDataProviderV2 } from '../../helpers/contracts-deployments'; + +task( + `deploy-${eContractid.UiIncentiveDataProviderV2}`, + `Deploys the UiIncentiveDataProviderV2 contract` +) + .addFlag('verify', 'Verify UiIncentiveDataProviderV2 contract via Etherscan API.') + .setAction(async ({ verify }, localBRE) => { + await localBRE.run('set-DRE'); + if (!localBRE.network.config.chainId) { + throw new Error('INVALID_CHAIN_ID'); + } + console.log(`\n- UiIncentiveDataProviderV2 deployment`); + + const uiIncentiveDataProviderV2 = await deployUiIncentiveDataProviderV2(verify); + + console.log('UiIncentiveDataProviderV2 deployed at:', uiIncentiveDataProviderV2.address); + console.log(`\tFinished UiIncentiveDataProviderV2 deployment`); + });