diff --git a/contracts/interfaces/IChainlinkAggregator.sol b/contracts/interfaces/IChainlinkAggregator.sol
index 4b75788d..75686371 100644
--- a/contracts/interfaces/IChainlinkAggregator.sol
+++ b/contracts/interfaces/IChainlinkAggregator.sol
@@ -2,6 +2,8 @@
 pragma solidity 0.6.12;
 
 interface IChainlinkAggregator {
+  function decimals() external view returns (uint8);
+  
   function latestAnswer() external view returns (int256);
 
   function latestTimestamp() external view returns (uint256);
@@ -14,4 +16,4 @@ interface IChainlinkAggregator {
 
   event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp);
   event NewRound(uint256 indexed roundId, address indexed startedBy);
-}
+}
\ No newline at end of file
diff --git a/contracts/misc/UiIncentiveDataProviderV2.sol b/contracts/misc/UiIncentiveDataProviderV2.sol
new file mode 100644
index 00000000..56b3c0b8
--- /dev/null
+++ b/contracts/misc/UiIncentiveDataProviderV2.sol
@@ -0,0 +1,288 @@
+// 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 {IUiIncentiveDataProviderV2} from './interfaces/IUiIncentiveDataProviderV2.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';
+
+contract UiIncentiveDataProviderV2 is IUiIncentiveDataProviderV2 {
+  using UserConfiguration for DataTypes.UserConfigurationMap;
+
+  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 IStableDebtToken(baseData.aTokenAddress).getIncentivesController() returns (IAaveIncentivesController aTokenIncentiveController) {
+        if (address(aTokenIncentiveController) != address(0)) {
+          address aRewardToken = aTokenIncentiveController.REWARD_TOKEN();
+
+          try aTokenIncentiveController.getAssetData(baseData.aTokenAddress) returns (
+            uint256 aTokenIncentivesIndex,
+            uint256 aEmissionPerSecond,
+            uint256 aIncentivesLastUpdateTimestamp
+          ) {
+            reserveIncentiveData.aIncentiveData = IncentiveData(
+              aEmissionPerSecond,
+              aIncentivesLastUpdateTimestamp,
+              aTokenIncentivesIndex,
+              aTokenIncentiveController.DISTRIBUTION_END(),
+              baseData.aTokenAddress,
+              aRewardToken,
+              address(aTokenIncentiveController),
+              IERC20Detailed(aRewardToken).decimals(),
+              aTokenIncentiveController.PRECISION()
+            );
+          } catch (bytes memory /*lowLevelData*/) {
+            (
+              uint256 aEmissionPerSecond,
+              uint256 aIncentivesLastUpdateTimestamp,
+              uint256 aTokenIncentivesIndex
+            ) = aTokenIncentiveController.assets(baseData.aTokenAddress);
+
+            reserveIncentiveData.aIncentiveData = IncentiveData(
+              aEmissionPerSecond,
+              aIncentivesLastUpdateTimestamp,
+              aTokenIncentivesIndex,
+              aTokenIncentiveController.DISTRIBUTION_END(),
+              baseData.aTokenAddress,
+              aRewardToken,
+              address(aTokenIncentiveController),
+              IERC20Detailed(aRewardToken).decimals(),
+              aTokenIncentiveController.PRECISION()
+            );
+          } 
+        }
+      } catch(bytes memory /*lowLevelData*/) {
+        // Will not get here
+      } 
+
+      try IStableDebtToken(baseData.stableDebtTokenAddress).getIncentivesController() returns (IAaveIncentivesController sTokenIncentiveController) {
+        if (address(sTokenIncentiveController) != address(0)) {
+
+          address sRewardToken = sTokenIncentiveController.REWARD_TOKEN();
+          try sTokenIncentiveController.getAssetData(baseData.stableDebtTokenAddress) returns (
+            uint256 sTokenIncentivesIndex,
+            uint256 sEmissionPerSecond,
+            uint256 sIncentivesLastUpdateTimestamp
+          ) {
+            reserveIncentiveData.sIncentiveData = IncentiveData(
+              sEmissionPerSecond,
+              sIncentivesLastUpdateTimestamp,
+              sTokenIncentivesIndex,
+              sTokenIncentiveController.DISTRIBUTION_END(),
+              baseData.stableDebtTokenAddress,
+              sRewardToken,
+              address(sTokenIncentiveController),
+              IERC20Detailed(sRewardToken).decimals(),
+              sTokenIncentiveController.PRECISION()
+            );
+          } catch (bytes memory /*lowLevelData*/) {
+            (
+              uint256 sEmissionPerSecond,
+              uint256 sIncentivesLastUpdateTimestamp,
+              uint256 sTokenIncentivesIndex
+            ) = sTokenIncentiveController.assets(baseData.stableDebtTokenAddress);
+
+            reserveIncentiveData.sIncentiveData = IncentiveData(
+              sEmissionPerSecond,
+              sIncentivesLastUpdateTimestamp,
+              sTokenIncentivesIndex,
+              sTokenIncentiveController.DISTRIBUTION_END(),
+              baseData.stableDebtTokenAddress,
+              sRewardToken,
+              address(sTokenIncentiveController),
+              IERC20Detailed(sRewardToken).decimals(),
+              sTokenIncentiveController.PRECISION()
+            );
+          } 
+        }
+      } catch(bytes memory /*lowLevelData*/) {
+        // Will not get here
+      }
+
+      try IStableDebtToken(baseData.variableDebtTokenAddress).getIncentivesController() returns (IAaveIncentivesController vTokenIncentiveController) {
+        if (address(vTokenIncentiveController) != address(0)) {
+          address vRewardToken = vTokenIncentiveController.REWARD_TOKEN();
+
+          try vTokenIncentiveController.getAssetData(baseData.variableDebtTokenAddress) returns (
+            uint256 vTokenIncentivesIndex,
+            uint256 vEmissionPerSecond,
+            uint256 vIncentivesLastUpdateTimestamp
+          ) {
+            reserveIncentiveData.vIncentiveData = IncentiveData(
+              vEmissionPerSecond,
+              vIncentivesLastUpdateTimestamp,
+              vTokenIncentivesIndex,
+              vTokenIncentiveController.DISTRIBUTION_END(),
+              baseData.variableDebtTokenAddress,
+              vRewardToken,
+              address(vTokenIncentiveController),
+              IERC20Detailed(vRewardToken).decimals(),
+              vTokenIncentiveController.PRECISION()
+            );
+          } catch (bytes memory /*lowLevelData*/) {
+            (
+              uint256 vEmissionPerSecond,
+              uint256 vIncentivesLastUpdateTimestamp,
+              uint256 vTokenIncentivesIndex
+            ) = vTokenIncentiveController.assets(baseData.variableDebtTokenAddress);
+
+            reserveIncentiveData.vIncentiveData = IncentiveData(
+              vEmissionPerSecond,
+              vIncentivesLastUpdateTimestamp,
+              vTokenIncentivesIndex,
+              vTokenIncentiveController.DISTRIBUTION_END(),
+              baseData.variableDebtTokenAddress,
+              vRewardToken,
+              address(vTokenIncentiveController),
+              IERC20Detailed(vRewardToken).decimals(),
+              vTokenIncentiveController.PRECISION()
+            );
+          }
+        }
+      } 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];
+
+      IUiIncentiveDataProviderV2.UserIncentiveData memory aUserIncentiveData;
+
+      try IAToken(baseData.aTokenAddress).getIncentivesController() returns (IAaveIncentivesController aTokenIncentiveController) {
+        if (address(aTokenIncentiveController) != address(0)) {
+          address aRewardToken = aTokenIncentiveController.REWARD_TOKEN();
+          aUserIncentiveData.tokenincentivesUserIndex = aTokenIncentiveController.getUserAssetData(
+            user,
+            baseData.aTokenAddress
+          );
+          aUserIncentiveData.userUnclaimedRewards = aTokenIncentiveController.getUserUnclaimedRewards(
+            user
+          );
+          aUserIncentiveData.tokenAddress = baseData.aTokenAddress;
+          aUserIncentiveData.rewardTokenAddress = aRewardToken;
+          aUserIncentiveData.incentiveControllerAddress = address(aTokenIncentiveController);
+          aUserIncentiveData.rewardTokenDecimals = IERC20Detailed(aRewardToken).decimals();
+        }
+      } catch (bytes memory /*lowLevelData*/) {
+
+      }
+
+      userReservesIncentivesData[i].aTokenIncentivesUserData = aUserIncentiveData;
+
+      UserIncentiveData memory vUserIncentiveData;
+
+      try IVariableDebtToken(baseData.variableDebtTokenAddress).getIncentivesController() returns(IAaveIncentivesController vTokenIncentiveController) {
+        if (address(vTokenIncentiveController) != address(0)) {
+          address vRewardToken = vTokenIncentiveController.REWARD_TOKEN();
+          vUserIncentiveData.tokenincentivesUserIndex = vTokenIncentiveController.getUserAssetData(
+            user,
+            baseData.variableDebtTokenAddress
+          );
+          vUserIncentiveData.userUnclaimedRewards = vTokenIncentiveController.getUserUnclaimedRewards(
+            user
+          );
+          vUserIncentiveData.tokenAddress = baseData.variableDebtTokenAddress;
+          vUserIncentiveData.rewardTokenAddress = vRewardToken;
+          vUserIncentiveData.incentiveControllerAddress = address(vTokenIncentiveController);
+          vUserIncentiveData.rewardTokenDecimals = IERC20Detailed(vRewardToken).decimals();
+        }
+      } catch (bytes memory /*lowLevelData*/) {
+
+      }
+
+      userReservesIncentivesData[i].vTokenIncentivesUserData = vUserIncentiveData;
+
+      UserIncentiveData memory sUserIncentiveData;
+
+      try IStableDebtToken(baseData.stableDebtTokenAddress).getIncentivesController() returns (IAaveIncentivesController sTokenIncentiveController) {
+        if (address(sTokenIncentiveController) != address(0)) {
+          address sRewardToken = sTokenIncentiveController.REWARD_TOKEN();
+          sUserIncentiveData.tokenincentivesUserIndex = sTokenIncentiveController.getUserAssetData(
+            user,
+            baseData.stableDebtTokenAddress
+          );
+          sUserIncentiveData.userUnclaimedRewards = sTokenIncentiveController.getUserUnclaimedRewards(
+            user
+          );
+          sUserIncentiveData.tokenAddress = baseData.stableDebtTokenAddress;
+          sUserIncentiveData.rewardTokenAddress = sRewardToken;
+          sUserIncentiveData.incentiveControllerAddress = address(sTokenIncentiveController);
+          sUserIncentiveData.rewardTokenDecimals = IERC20Detailed(sRewardToken).decimals();
+        }
+      } catch (bytes memory /*lowLevelData*/) {
+
+      }
+
+      userReservesIncentivesData[i].sTokenIncentivesUserData = sUserIncentiveData;
+    }
+
+    return (userReservesIncentivesData);
+  }
+}
\ No newline at end of file
diff --git a/contracts/misc/UiIncentiveDataProviderV2V3.sol b/contracts/misc/UiIncentiveDataProviderV2V3.sol
new file mode 100644
index 00000000..5c2dff1a
--- /dev/null
+++ b/contracts/misc/UiIncentiveDataProviderV2V3.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 UiIncentiveDataProviderV2V3 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].vTokenIncentivesUserData = 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].sTokenIncentivesUserData = 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/UiPoolDataProviderV2.sol b/contracts/misc/UiPoolDataProviderV2.sol
new file mode 100644
index 00000000..bb561683
--- /dev/null
+++ b/contracts/misc/UiPoolDataProviderV2.sol
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: agpl-3.0
+pragma solidity 0.6.12;
+pragma experimental ABIEncoderV2;
+
+import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
+import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
+import {IUiPoolDataProviderV2} from './interfaces/IUiPoolDataProviderV2.sol';
+import {ILendingPool} from '../interfaces/ILendingPool.sol';
+import {IAaveOracle} from './interfaces/IAaveOracle.sol';
+import {IAToken} from '../interfaces/IAToken.sol';
+import {IVariableDebtToken} from '../interfaces/IVariableDebtToken.sol';
+import {IStableDebtToken} from '../interfaces/IStableDebtToken.sol';
+import {WadRayMath} from '../protocol/libraries/math/WadRayMath.sol';
+import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol';
+import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol';
+import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
+import {IChainlinkAggregator} from '../interfaces/IChainlinkAggregator.sol';
+import {DefaultReserveInterestRateStrategy} from '../protocol/lendingpool/DefaultReserveInterestRateStrategy.sol';
+import {IERC20DetailedBytes} from './interfaces/IERC20DetailedBytes.sol';
+
+contract UiPoolDataProviderV2 is IUiPoolDataProviderV2 {
+  using WadRayMath for uint256;
+  using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
+  using UserConfiguration for DataTypes.UserConfigurationMap;
+
+  IChainlinkAggregator public immutable networkBaseTokenPriceInUsdProxyAggregator;
+  IChainlinkAggregator public immutable marketReferenceCurrencyPriceInUsdProxyAggregator;
+  uint256 public constant ETH_CURRENCY_UNIT = 1 ether;
+  address public constant MKRAddress = 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2;
+
+  constructor(
+    IChainlinkAggregator _networkBaseTokenPriceInUsdProxyAggregator,
+    IChainlinkAggregator _marketReferenceCurrencyPriceInUsdProxyAggregator
+  ) public {
+    networkBaseTokenPriceInUsdProxyAggregator = _networkBaseTokenPriceInUsdProxyAggregator;
+    marketReferenceCurrencyPriceInUsdProxyAggregator = _marketReferenceCurrencyPriceInUsdProxyAggregator;
+  }
+
+  function getInterestRateStrategySlopes(DefaultReserveInterestRateStrategy interestRateStrategy)
+    internal
+    view
+    returns (
+      uint256,
+      uint256,
+      uint256,
+      uint256
+    )
+  {
+    return (
+      interestRateStrategy.variableRateSlope1(),
+      interestRateStrategy.variableRateSlope2(),
+      interestRateStrategy.stableRateSlope1(),
+      interestRateStrategy.stableRateSlope2()
+    );
+  }
+
+  function getReservesList(ILendingPoolAddressesProvider provider)
+    public
+    view
+    override
+    returns (address[] memory)
+  {
+    ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
+    return lendingPool.getReservesList();
+  }
+
+  function getReservesData(ILendingPoolAddressesProvider provider)
+    public
+    view
+    override
+    returns (AggregatedReserveData[] memory, BaseCurrencyInfo memory)
+  {
+    IAaveOracle oracle = IAaveOracle(provider.getPriceOracle());
+    ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
+    address[] memory reserves = lendingPool.getReservesList();
+    AggregatedReserveData[] memory reservesData = new AggregatedReserveData[](reserves.length);
+
+    for (uint256 i = 0; i < reserves.length; i++) {
+      AggregatedReserveData memory reserveData = reservesData[i];
+      reserveData.underlyingAsset = reserves[i];
+
+      // reserve current state
+      DataTypes.ReserveData memory baseData = lendingPool.getReserveData(
+        reserveData.underlyingAsset
+      );
+      reserveData.liquidityIndex = baseData.liquidityIndex;
+      reserveData.variableBorrowIndex = baseData.variableBorrowIndex;
+      reserveData.liquidityRate = baseData.currentLiquidityRate;
+      reserveData.variableBorrowRate = baseData.currentVariableBorrowRate;
+      reserveData.stableBorrowRate = baseData.currentStableBorrowRate;
+      reserveData.lastUpdateTimestamp = baseData.lastUpdateTimestamp;
+      reserveData.aTokenAddress = baseData.aTokenAddress;
+      reserveData.stableDebtTokenAddress = baseData.stableDebtTokenAddress;
+      reserveData.variableDebtTokenAddress = baseData.variableDebtTokenAddress;
+      reserveData.interestRateStrategyAddress = baseData.interestRateStrategyAddress;
+      reserveData.priceInMarketReferenceCurrency = oracle.getAssetPrice(
+        reserveData.underlyingAsset
+      );
+
+      reserveData.availableLiquidity = IERC20Detailed(reserveData.underlyingAsset).balanceOf(
+        reserveData.aTokenAddress
+      );
+      (
+        reserveData.totalPrincipalStableDebt,
+        ,
+        reserveData.averageStableRate,
+        reserveData.stableDebtLastUpdateTimestamp
+      ) = IStableDebtToken(reserveData.stableDebtTokenAddress).getSupplyData();
+      reserveData.totalScaledVariableDebt = IVariableDebtToken(reserveData.variableDebtTokenAddress)
+        .scaledTotalSupply();
+
+      if (address(reserveData.underlyingAsset) == address(MKRAddress)) {
+        bytes32 symbol = IERC20DetailedBytes(reserveData.underlyingAsset).symbol();
+        reserveData.symbol = bytes32ToString(symbol);
+      } else {
+        reserveData.symbol = IERC20Detailed(reserveData.underlyingAsset).symbol();
+      }
+
+      (
+        reserveData.baseLTVasCollateral,
+        reserveData.reserveLiquidationThreshold,
+        reserveData.reserveLiquidationBonus,
+        reserveData.decimals,
+        reserveData.reserveFactor
+      ) = baseData.configuration.getParamsMemory();
+      (
+        reserveData.isActive,
+        reserveData.isFrozen,
+        reserveData.borrowingEnabled,
+        reserveData.stableBorrowRateEnabled
+      ) = baseData.configuration.getFlagsMemory();
+      reserveData.usageAsCollateralEnabled = reserveData.baseLTVasCollateral != 0;
+      (
+        reserveData.variableRateSlope1,
+        reserveData.variableRateSlope2,
+        reserveData.stableRateSlope1,
+        reserveData.stableRateSlope2
+      ) = getInterestRateStrategySlopes(
+        DefaultReserveInterestRateStrategy(reserveData.interestRateStrategyAddress)
+      );
+    }
+
+    BaseCurrencyInfo memory baseCurrencyInfo;
+    baseCurrencyInfo.networkBaseTokenPriceInUsd = networkBaseTokenPriceInUsdProxyAggregator
+      .latestAnswer();
+    baseCurrencyInfo.networkBaseTokenPriceDecimals = networkBaseTokenPriceInUsdProxyAggregator
+      .decimals();
+
+    try oracle.BASE_CURRENCY_UNIT() returns (uint256 baseCurrencyUnit) {
+      if (ETH_CURRENCY_UNIT == baseCurrencyUnit) {
+        baseCurrencyInfo.marketReferenceCurrencyUnit = ETH_CURRENCY_UNIT;
+        baseCurrencyInfo
+        .marketReferenceCurrencyPriceInUsd = marketReferenceCurrencyPriceInUsdProxyAggregator
+        .latestAnswer();
+      } else {
+        baseCurrencyInfo.marketReferenceCurrencyUnit = baseCurrencyUnit;
+        baseCurrencyInfo.marketReferenceCurrencyPriceInUsd = int256(baseCurrencyUnit);
+      }
+    } catch (
+      bytes memory /*lowLevelData*/
+    ) {
+      baseCurrencyInfo.marketReferenceCurrencyUnit = ETH_CURRENCY_UNIT;
+      baseCurrencyInfo
+        .marketReferenceCurrencyPriceInUsd = marketReferenceCurrencyPriceInUsdProxyAggregator
+        .latestAnswer();
+    }
+
+    return (reservesData, baseCurrencyInfo);
+  }
+
+  function getUserReservesData(ILendingPoolAddressesProvider provider, address user)
+    external
+    view
+    override
+    returns (UserReserveData[] memory)
+  {
+    ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
+    address[] memory reserves = lendingPool.getReservesList();
+    DataTypes.UserConfigurationMap memory userConfig = lendingPool.getUserConfiguration(user);
+
+    UserReserveData[] memory userReservesData = new UserReserveData[](
+      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
+      userReservesData[i].underlyingAsset = reserves[i];
+      userReservesData[i].scaledATokenBalance = IAToken(baseData.aTokenAddress).scaledBalanceOf(
+        user
+      );
+      userReservesData[i].usageAsCollateralEnabledOnUser = userConfig.isUsingAsCollateral(i);
+
+      if (userConfig.isBorrowing(i)) {
+        userReservesData[i].scaledVariableDebt = IVariableDebtToken(
+          baseData.variableDebtTokenAddress
+        ).scaledBalanceOf(user);
+        userReservesData[i].principalStableDebt = IStableDebtToken(baseData.stableDebtTokenAddress)
+          .principalBalanceOf(user);
+        if (userReservesData[i].principalStableDebt != 0) {
+          userReservesData[i].stableBorrowRate = IStableDebtToken(baseData.stableDebtTokenAddress)
+            .getUserStableRate(user);
+          userReservesData[i].stableBorrowLastUpdateTimestamp = IStableDebtToken(
+            baseData.stableDebtTokenAddress
+          ).getUserLastUpdated(user);
+        }
+      }
+    }
+
+    return (userReservesData);
+  }
+
+  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/UiPoolDataProviderV2V3.sol b/contracts/misc/UiPoolDataProviderV2V3.sol
new file mode 100644
index 00000000..a11c9572
--- /dev/null
+++ b/contracts/misc/UiPoolDataProviderV2V3.sol
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: agpl-3.0
+pragma solidity 0.6.12;
+pragma experimental ABIEncoderV2;
+
+import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
+import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
+import {IUiPoolDataProviderV3} from './interfaces/IUiPoolDataProviderV3.sol';
+import {ILendingPool} from '../interfaces/ILendingPool.sol';
+import {IAaveOracle} from './interfaces/IAaveOracle.sol';
+import {IAToken} from '../interfaces/IAToken.sol';
+import {IVariableDebtToken} from '../interfaces/IVariableDebtToken.sol';
+import {IStableDebtToken} from '../interfaces/IStableDebtToken.sol';
+import {WadRayMath} from '../protocol/libraries/math/WadRayMath.sol';
+import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol';
+import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol';
+import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
+import {IChainlinkAggregator} from '../interfaces/IChainlinkAggregator.sol';
+import {DefaultReserveInterestRateStrategy} from '../protocol/lendingpool/DefaultReserveInterestRateStrategy.sol';
+import {IERC20DetailedBytes} from './interfaces/IERC20DetailedBytes.sol';
+
+contract UiPoolDataProviderV2V3 is IUiPoolDataProviderV3 {
+  using WadRayMath for uint256;
+  using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
+  using UserConfiguration for DataTypes.UserConfigurationMap;
+
+  IChainlinkAggregator public immutable networkBaseTokenPriceInUsdProxyAggregator;
+  IChainlinkAggregator public immutable marketReferenceCurrencyPriceInUsdProxyAggregator;
+  uint256 public constant ETH_CURRENCY_UNIT = 1 ether;
+  address public constant MKRAddress = 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2;
+
+  constructor(
+    IChainlinkAggregator _networkBaseTokenPriceInUsdProxyAggregator,
+    IChainlinkAggregator _marketReferenceCurrencyPriceInUsdProxyAggregator
+  ) public {
+    networkBaseTokenPriceInUsdProxyAggregator = _networkBaseTokenPriceInUsdProxyAggregator;
+    marketReferenceCurrencyPriceInUsdProxyAggregator = _marketReferenceCurrencyPriceInUsdProxyAggregator;
+  }
+
+  function getInterestRateStrategySlopes(DefaultReserveInterestRateStrategy interestRateStrategy)
+    internal
+    view
+    returns (
+      uint256,
+      uint256,
+      uint256,
+      uint256
+    )
+  {
+    return (
+      interestRateStrategy.variableRateSlope1(),
+      interestRateStrategy.variableRateSlope2(),
+      interestRateStrategy.stableRateSlope1(),
+      interestRateStrategy.stableRateSlope2()
+    );
+  }
+
+  function getReservesList(ILendingPoolAddressesProvider provider)
+    public
+    view
+    override
+    returns (address[] memory)
+  {
+    ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
+    return lendingPool.getReservesList();
+  }
+
+  function getReservesData(ILendingPoolAddressesProvider provider)
+    public
+    view
+    override
+    returns (AggregatedReserveData[] memory, BaseCurrencyInfo memory)
+  {
+    IAaveOracle oracle = IAaveOracle(provider.getPriceOracle());
+    ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
+    address[] memory reserves = lendingPool.getReservesList();
+    AggregatedReserveData[] memory reservesData = new AggregatedReserveData[](reserves.length);
+
+    for (uint256 i = 0; i < reserves.length; i++) {
+      AggregatedReserveData memory reserveData = reservesData[i];
+      reserveData.underlyingAsset = reserves[i];
+
+      // reserve current state
+      DataTypes.ReserveData memory baseData = lendingPool.getReserveData(
+        reserveData.underlyingAsset
+      );
+      reserveData.liquidityIndex = baseData.liquidityIndex;
+      reserveData.variableBorrowIndex = baseData.variableBorrowIndex;
+      reserveData.liquidityRate = baseData.currentLiquidityRate;
+      reserveData.variableBorrowRate = baseData.currentVariableBorrowRate;
+      reserveData.stableBorrowRate = baseData.currentStableBorrowRate;
+      reserveData.lastUpdateTimestamp = baseData.lastUpdateTimestamp;
+      reserveData.aTokenAddress = baseData.aTokenAddress;
+      reserveData.stableDebtTokenAddress = baseData.stableDebtTokenAddress;
+      reserveData.variableDebtTokenAddress = baseData.variableDebtTokenAddress;
+      reserveData.interestRateStrategyAddress = baseData.interestRateStrategyAddress;
+      reserveData.priceInMarketReferenceCurrency = oracle.getAssetPrice(
+        reserveData.underlyingAsset
+      );
+
+      reserveData.availableLiquidity = IERC20Detailed(reserveData.underlyingAsset).balanceOf(
+        reserveData.aTokenAddress
+      );
+      (
+        reserveData.totalPrincipalStableDebt,
+        ,
+        reserveData.averageStableRate,
+        reserveData.stableDebtLastUpdateTimestamp
+      ) = IStableDebtToken(reserveData.stableDebtTokenAddress).getSupplyData();
+      reserveData.totalScaledVariableDebt = IVariableDebtToken(reserveData.variableDebtTokenAddress)
+        .scaledTotalSupply();
+
+      if (address(reserveData.underlyingAsset) == address(MKRAddress)) {
+        bytes32 symbol = IERC20DetailedBytes(reserveData.underlyingAsset).symbol();
+        reserveData.symbol = bytes32ToString(symbol);
+      } else {
+        reserveData.symbol = IERC20Detailed(reserveData.underlyingAsset).symbol();
+      }
+
+      (
+        reserveData.baseLTVasCollateral,
+        reserveData.reserveLiquidationThreshold,
+        reserveData.reserveLiquidationBonus,
+        reserveData.decimals,
+        reserveData.reserveFactor
+      ) = baseData.configuration.getParamsMemory();
+      (
+        reserveData.isActive,
+        reserveData.isFrozen,
+        reserveData.borrowingEnabled,
+        reserveData.stableBorrowRateEnabled
+      ) = baseData.configuration.getFlagsMemory();
+      reserveData.usageAsCollateralEnabled = reserveData.baseLTVasCollateral != 0;
+      (
+        reserveData.variableRateSlope1,
+        reserveData.variableRateSlope2,
+        reserveData.stableRateSlope1,
+        reserveData.stableRateSlope2
+      ) = getInterestRateStrategySlopes(
+        DefaultReserveInterestRateStrategy(reserveData.interestRateStrategyAddress)
+      );
+    }
+
+    BaseCurrencyInfo memory baseCurrencyInfo;
+    baseCurrencyInfo.networkBaseTokenPriceInUsd = networkBaseTokenPriceInUsdProxyAggregator
+      .latestAnswer();
+    baseCurrencyInfo.networkBaseTokenPriceDecimals = networkBaseTokenPriceInUsdProxyAggregator
+      .decimals();
+
+    try oracle.BASE_CURRENCY_UNIT() returns (uint256 baseCurrencyUnit) {
+      if (ETH_CURRENCY_UNIT == baseCurrencyUnit) {
+        baseCurrencyInfo.marketReferenceCurrencyUnit = ETH_CURRENCY_UNIT;
+        baseCurrencyInfo
+        .marketReferenceCurrencyPriceInUsd = marketReferenceCurrencyPriceInUsdProxyAggregator
+        .latestAnswer();
+      } else {
+        baseCurrencyInfo.marketReferenceCurrencyUnit = baseCurrencyUnit;
+        baseCurrencyInfo.marketReferenceCurrencyPriceInUsd = int256(baseCurrencyUnit);
+      }
+    } catch (
+      bytes memory /*lowLevelData*/
+    ) {
+      baseCurrencyInfo.marketReferenceCurrencyUnit = ETH_CURRENCY_UNIT;
+      baseCurrencyInfo
+        .marketReferenceCurrencyPriceInUsd = marketReferenceCurrencyPriceInUsdProxyAggregator
+        .latestAnswer();
+    }
+
+    return (reservesData, baseCurrencyInfo);
+  }
+
+  function getUserReservesData(ILendingPoolAddressesProvider provider, address user)
+    external
+    view
+    override
+    returns (UserReserveData[] memory, uint8)
+  {
+    ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
+    address[] memory reserves = lendingPool.getReservesList();
+    DataTypes.UserConfigurationMap memory userConfig = lendingPool.getUserConfiguration(user);
+
+    UserReserveData[] memory userReservesData = new UserReserveData[](
+      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
+      userReservesData[i].underlyingAsset = reserves[i];
+      userReservesData[i].scaledATokenBalance = IAToken(baseData.aTokenAddress).scaledBalanceOf(
+        user
+      );
+      userReservesData[i].usageAsCollateralEnabledOnUser = userConfig.isUsingAsCollateral(i);
+
+      if (userConfig.isBorrowing(i)) {
+        userReservesData[i].scaledVariableDebt = IVariableDebtToken(
+          baseData.variableDebtTokenAddress
+        ).scaledBalanceOf(user);
+        userReservesData[i].principalStableDebt = IStableDebtToken(baseData.stableDebtTokenAddress)
+          .principalBalanceOf(user);
+        if (userReservesData[i].principalStableDebt != 0) {
+          userReservesData[i].stableBorrowRate = IStableDebtToken(baseData.stableDebtTokenAddress)
+            .getUserStableRate(user);
+          userReservesData[i].stableBorrowLastUpdateTimestamp = IStableDebtToken(
+            baseData.stableDebtTokenAddress
+          ).getUserLastUpdated(user);
+        }
+      }
+    }
+
+    // Return 0 to be compatible with v3 userEmodeCategoryId return
+    return (userReservesData, 0);
+  }
+
+  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/IAaveOracle.sol b/contracts/misc/interfaces/IAaveOracle.sol
new file mode 100644
index 00000000..3ecc82b6
--- /dev/null
+++ b/contracts/misc/interfaces/IAaveOracle.sol
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: agpl-3.0
+pragma solidity 0.6.12;
+
+/**
+ * @title IAaveOracle interface
+ * @notice Interface for the Aave oracle.
+ **/
+
+interface IAaveOracle {
+  function BASE_CURRENCY() external view returns (address); // if usd returns 0x0, if eth returns weth address
+  function BASE_CURRENCY_UNIT() external view returns (uint256);
+
+  /***********
+    @dev returns the asset price in ETH
+     */
+  function getAssetPrice(address asset) external view returns (uint256);
+}
\ No newline at end of file
diff --git a/contracts/misc/interfaces/IERC20DetailedBytes.sol b/contracts/misc/interfaces/IERC20DetailedBytes.sol
index de91e288..8c47df16 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);
+}
\ No newline at end of file
diff --git a/contracts/misc/interfaces/IUiIncentiveDataProviderV2.sol b/contracts/misc/interfaces/IUiIncentiveDataProviderV2.sol
new file mode 100644
index 00000000..f397a30c
--- /dev/null
+++ b/contracts/misc/interfaces/IUiIncentiveDataProviderV2.sol
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: agpl-3.0
+pragma solidity 0.6.12;
+pragma experimental ABIEncoderV2;
+
+import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
+
+interface IUiIncentiveDataProviderV2 {
+  struct AggregatedReserveIncentiveData {
+    address underlyingAsset;
+    IncentiveData aIncentiveData;
+    IncentiveData vIncentiveData;
+    IncentiveData sIncentiveData;
+  }
+
+  struct IncentiveData {
+    uint256 emissionPerSecond;
+    uint256 incentivesLastUpdateTimestamp;
+    uint256 tokenIncentivesIndex;
+    uint256 emissionEndTimestamp;
+    address tokenAddress;
+    address rewardTokenAddress;
+    address incentiveControllerAddress;
+    uint8 rewardTokenDecimals;
+    uint8 precision;
+  }
+
+  struct UserReserveIncentiveData {
+    address underlyingAsset;
+    UserIncentiveData aTokenIncentivesUserData;
+    UserIncentiveData vTokenIncentivesUserData;
+    UserIncentiveData sTokenIncentivesUserData;
+  }
+
+  struct UserIncentiveData {
+    uint256 tokenincentivesUserIndex;
+    uint256 userUnclaimedRewards;
+    address tokenAddress;
+    address rewardTokenAddress;
+    address incentiveControllerAddress;
+    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);
+}
\ No newline at end of file
diff --git a/contracts/misc/interfaces/IUiIncentiveDataProviderV3.sol b/contracts/misc/interfaces/IUiIncentiveDataProviderV3.sol
new file mode 100644
index 00000000..85754658
--- /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);
+} 
\ No newline at end of file
diff --git a/contracts/misc/interfaces/IUiPoolDataProviderV2.sol b/contracts/misc/interfaces/IUiPoolDataProviderV2.sol
new file mode 100644
index 00000000..a286f9cc
--- /dev/null
+++ b/contracts/misc/interfaces/IUiPoolDataProviderV2.sol
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: agpl-3.0
+pragma solidity 0.6.12;
+pragma experimental ABIEncoderV2;
+
+import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
+
+interface IUiPoolDataProviderV2 {
+  struct AggregatedReserveData {
+    address underlyingAsset;
+    string name;
+    string symbol;
+    uint256 decimals;
+    uint256 baseLTVasCollateral;
+    uint256 reserveLiquidationThreshold;
+    uint256 reserveLiquidationBonus;
+    uint256 reserveFactor;
+    bool usageAsCollateralEnabled;
+    bool borrowingEnabled;
+    bool stableBorrowRateEnabled;
+    bool isActive;
+    bool isFrozen;
+    // base data
+    uint128 liquidityIndex;
+    uint128 variableBorrowIndex;
+    uint128 liquidityRate;
+    uint128 variableBorrowRate;
+    uint128 stableBorrowRate;
+    uint40 lastUpdateTimestamp;
+    address aTokenAddress;
+    address stableDebtTokenAddress;
+    address variableDebtTokenAddress;
+    address interestRateStrategyAddress;
+    //
+    uint256 availableLiquidity;
+    uint256 totalPrincipalStableDebt;
+    uint256 averageStableRate;
+    uint256 stableDebtLastUpdateTimestamp;
+    uint256 totalScaledVariableDebt;
+    uint256 priceInMarketReferenceCurrency;
+    uint256 variableRateSlope1;
+    uint256 variableRateSlope2;
+    uint256 stableRateSlope1;
+    uint256 stableRateSlope2;
+  }
+
+  struct UserReserveData {
+    address underlyingAsset;
+    uint256 scaledATokenBalance;
+    bool usageAsCollateralEnabledOnUser;
+    uint256 stableBorrowRate;
+    uint256 scaledVariableDebt;
+    uint256 principalStableDebt;
+    uint256 stableBorrowLastUpdateTimestamp;
+  }
+
+  struct BaseCurrencyInfo {
+    uint256 marketReferenceCurrencyUnit;
+    int256 marketReferenceCurrencyPriceInUsd;
+    int256 networkBaseTokenPriceInUsd;
+    uint8 networkBaseTokenPriceDecimals;
+  }
+
+  function getReservesList(ILendingPoolAddressesProvider provider)
+    external
+    view
+    returns (address[] memory);
+
+  function getReservesData(ILendingPoolAddressesProvider provider)
+    external
+    view
+    returns (
+      AggregatedReserveData[] memory,
+      BaseCurrencyInfo memory
+    );
+
+  function getUserReservesData(ILendingPoolAddressesProvider provider, address user)
+    external
+    view
+    returns (
+      UserReserveData[] memory
+    );
+}
\ No newline at end of file
diff --git a/contracts/misc/interfaces/IUiPoolDataProviderV3.sol b/contracts/misc/interfaces/IUiPoolDataProviderV3.sol
new file mode 100644
index 00000000..6153a526
--- /dev/null
+++ b/contracts/misc/interfaces/IUiPoolDataProviderV3.sol
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: agpl-3.0
+pragma solidity 0.6.12;
+pragma experimental ABIEncoderV2;
+
+import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
+
+interface IUiPoolDataProviderV3 {
+  struct AggregatedReserveData {
+    address underlyingAsset;
+    string name;
+    string symbol;
+    uint256 decimals;
+    uint256 baseLTVasCollateral;
+    uint256 reserveLiquidationThreshold;
+    uint256 reserveLiquidationBonus;
+    uint256 reserveFactor;
+    bool usageAsCollateralEnabled;
+    bool borrowingEnabled;
+    bool stableBorrowRateEnabled;
+    bool isActive;
+    bool isFrozen;
+    // base data
+    uint128 liquidityIndex;
+    uint128 variableBorrowIndex;
+    uint128 liquidityRate;
+    uint128 variableBorrowRate;
+    uint128 stableBorrowRate;
+    uint40 lastUpdateTimestamp;
+    address aTokenAddress;
+    address stableDebtTokenAddress;
+    address variableDebtTokenAddress;
+    address interestRateStrategyAddress;
+    //
+    uint256 availableLiquidity;
+    uint256 totalPrincipalStableDebt;
+    uint256 averageStableRate;
+    uint256 stableDebtLastUpdateTimestamp;
+    uint256 totalScaledVariableDebt;
+    uint256 priceInMarketReferenceCurrency;
+    uint256 variableRateSlope1;
+    uint256 variableRateSlope2;
+    uint256 stableRateSlope1;
+    uint256 stableRateSlope2;
+    // v3
+    bool isPaused;
+    uint128 accruedToTreasury;
+    uint128 unbacked;
+    uint128 isolationModeTotalDebt;
+    //
+    uint256 debtCeiling;
+    uint256 debtCeilingDecimals;
+    uint8 eModeCategoryId;
+    uint256 borrowCap;
+    uint256 supplyCap; 
+    // eMode
+    uint16 eModeLtv;
+    uint16 eModeLiquidationThreshold;
+    uint16 eModeLiquidationBonus;
+    address eModePriceSource;
+    string eModeLabel;
+    bool borrowableInIsolation;
+  }
+
+  struct UserReserveData {
+    address underlyingAsset;
+    uint256 scaledATokenBalance;
+    bool usageAsCollateralEnabledOnUser;
+    uint256 stableBorrowRate;
+    uint256 scaledVariableDebt;
+    uint256 principalStableDebt;
+    uint256 stableBorrowLastUpdateTimestamp;
+  }
+
+  struct BaseCurrencyInfo {
+    uint256 marketReferenceCurrencyUnit;
+    int256 marketReferenceCurrencyPriceInUsd;
+    int256 networkBaseTokenPriceInUsd;
+    uint8 networkBaseTokenPriceDecimals;
+  }
+
+  function getReservesList(ILendingPoolAddressesProvider provider)
+    external
+    view
+    returns (address[] memory);
+
+  function getReservesData(ILendingPoolAddressesProvider provider)
+    external
+    view
+    returns (
+      AggregatedReserveData[] memory,
+      BaseCurrencyInfo memory
+    );
+
+  function getUserReservesData(ILendingPoolAddressesProvider provider, address user)
+    external
+    view
+    returns (
+      UserReserveData[] memory, uint8
+    );
+}
\ No newline at end of file
diff --git a/helper-hardhat-config.ts b/helper-hardhat-config.ts
index c66da367..88633d85 100644
--- a/helper-hardhat-config.ts
+++ b/helper-hardhat-config.ts
@@ -58,15 +58,15 @@ export const NETWORKS_RPC_URL: iParamsPerNetwork<string> = {
 };
 
 export const NETWORKS_DEFAULT_GAS: iParamsPerNetwork<number> = {
-  [eEthereumNetwork.kovan]: 1 * GWEI,
+  [eEthereumNetwork.kovan]: 3 * GWEI,
   [eEthereumNetwork.ropsten]: 65 * GWEI,
   [eEthereumNetwork.main]: 65 * GWEI,
   [eEthereumNetwork.coverage]: 65 * GWEI,
   [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/constants.ts b/helpers/constants.ts
index fe743fa2..26e2c611 100644
--- a/helpers/constants.ts
+++ b/helpers/constants.ts
@@ -75,3 +75,29 @@ export const MOCK_CHAINLINK_AGGREGATORS_PRICES = {
   WAVAX: oneEther.multipliedBy('0.006051936629').toFixed(),
   USD: '5848466240000000',
 };
+
+export const chainlinkAggregatorProxy = {
+  main: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419',
+  kovan: '0x9326BFA02ADD2366b30bacB125260Af641031331',
+  matic: '0xAB594600376Ec9fD91F8e885dADF0CE036862dE0',
+  mumbai: '0xd0D5e3DB44DE05E9F294BB0a3bEEaF030DE24Ada',
+  avalanche: '0x0A77230d17318075983913bC2145DB16C7366156',
+  fuji: '0x5498BB86BC934c8D34FDA08E81D444153d0D06aD',
+  tenderly: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419',
+  'arbitrum-rinkeby': '0x5f0423B1a6935dc5596e7A24d98532b67A0AeFd8',
+  arbitrum: '0x639Fe6ab55C921f74e7fac1ee960C0B6293ba612',
+  rinkeby: '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e',
+};
+
+export const chainlinkEthUsdAggregatorProxy = {
+  main: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419',
+  kovan: '0x9326BFA02ADD2366b30bacB125260Af641031331',
+  matic: '0xF9680D99D6C9589e2a93a78A04A279e509205945',
+  mumbai: '0x0715A7794a1dc8e42615F059dD6e406A6594651A',
+  avalanche: '0x976B3D034E162d8bD72D6b9C989d545b839003b0',
+  fuji: '0x86d67c3D38D2bCeE722E601025C25a575021c6EA',
+  tenderly: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419',
+  'arbitrum-rinkeby': '0x5f0423B1a6935dc5596e7A24d98532b67A0AeFd8',
+  arbitrum: '0x639Fe6ab55C921f74e7fac1ee960C0B6293ba612',
+  rinkeby: '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e',
+};
diff --git a/helpers/contracts-deployments.ts b/helpers/contracts-deployments.ts
index 13c64a78..7292a217 100644
--- a/helpers/contracts-deployments.ts
+++ b/helpers/contracts-deployments.ts
@@ -51,6 +51,10 @@ import {
   WETH9MockedFactory,
   WETHGatewayFactory,
   FlashLiquidationAdapterFactory,
+  UiPoolDataProviderV2Factory,
+  UiPoolDataProviderV2V3Factory,
+  UiIncentiveDataProviderV2V3,
+  UiIncentiveDataProviderV2Factory,
 } from '../types';
 import {
   withSaveAndVerify,
@@ -69,6 +73,53 @@ import { LendingPoolLibraryAddresses } from '../types/LendingPoolFactory';
 import { UiPoolDataProvider } from '../types';
 import { eNetwork } from './types';
 
+export const deployUiIncentiveDataProviderV2 = async (verify?: boolean) =>
+  withSaveAndVerify(
+    await new UiIncentiveDataProviderV2Factory(await getFirstSigner()).deploy(),
+    eContractid.UiIncentiveDataProviderV2,
+    [],
+    verify
+  );
+
+export const deployUiIncentiveDataProviderV2V3 = async (verify?: boolean) => {
+  const id = eContractid.UiIncentiveDataProviderV2V3;
+  const instance = await deployContract<UiIncentiveDataProviderV2V3>(id, []);
+  if (verify) {
+    await verifyContract(id, instance, []);
+  }
+  return instance;
+};
+
+export const deployUiPoolDataProviderV2 = async (
+  chainlinkAggregatorProxy: string,
+  chainlinkEthUsdAggregatorProxy: string,
+  verify?: boolean
+) =>
+  withSaveAndVerify(
+    await new UiPoolDataProviderV2Factory(await getFirstSigner()).deploy(
+      chainlinkAggregatorProxy,
+      chainlinkEthUsdAggregatorProxy
+    ),
+    eContractid.UiPoolDataProvider,
+    [chainlinkAggregatorProxy, chainlinkEthUsdAggregatorProxy],
+    verify
+  );
+
+export const deployUiPoolDataProviderV2V3 = async (
+  chainlinkAggregatorProxy: string,
+  chainlinkEthUsdAggregatorProxy: string,
+  verify?: boolean
+) =>
+  withSaveAndVerify(
+    await new UiPoolDataProviderV2V3Factory(await getFirstSigner()).deploy(
+      chainlinkAggregatorProxy,
+      chainlinkEthUsdAggregatorProxy
+    ),
+    eContractid.UiPoolDataProvider,
+    [chainlinkAggregatorProxy, chainlinkEthUsdAggregatorProxy],
+    verify
+  );
+
 export const deployUiPoolDataProvider = async (
   [incentivesController, aaveOracle]: [tEthereumAddress, tEthereumAddress],
   verify?: boolean
diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts
index dc786128..87d39f03 100644
--- a/helpers/contracts-helpers.ts
+++ b/helpers/contracts-helpers.ts
@@ -192,7 +192,10 @@ export const getOptionalParamAddressPerNetwork = (
   return getParamPerNetwork(param, network);
 };
 
-export const getParamPerPool = <T>({ proto, amm, matic, avalanche }: iParamsPerPool<T>, pool: AavePools) => {
+export const getParamPerPool = <T>(
+  { proto, amm, matic, avalanche }: iParamsPerPool<T>,
+  pool: AavePools
+) => {
   switch (pool) {
     case AavePools.proto:
       return proto;
@@ -378,14 +381,10 @@ export const verifyContract = async (
   instance: Contract,
   args: (string | string[])[]
 ) => {
-  if (usingPolygon()) {
-    await verifyAtPolygon(id, instance, args);
-  } else {
-    if (usingTenderly()) {
-      await verifyAtTenderly(id, instance);
-    }
-    await verifyEtherscanContract(instance.address, args);
+  if (usingTenderly()) {
+    await verifyAtTenderly(id, instance);
   }
+  await verifyEtherscanContract(instance.address, args);
   return instance;
 };
 
diff --git a/helpers/etherscan-verification.ts b/helpers/etherscan-verification.ts
index 3bf097d3..e8f1972e 100644
--- a/helpers/etherscan-verification.ts
+++ b/helpers/etherscan-verification.ts
@@ -14,7 +14,7 @@ const okErrors = [`Contract source code already verified`];
 
 const unableVerifyError = 'Fail - Unable to verify';
 
-export const SUPPORTED_ETHERSCAN_NETWORKS = ['main', 'ropsten', 'kovan'];
+export const SUPPORTED_ETHERSCAN_NETWORKS = ['main', 'ropsten', 'kovan', 'matic', 'mumbai'];
 
 function delay(ms: number) {
   return new Promise((resolve) => setTimeout(resolve, ms));
diff --git a/helpers/types.ts b/helpers/types.ts
index babd113e..71597f9c 100644
--- a/helpers/types.ts
+++ b/helpers/types.ts
@@ -84,6 +84,8 @@ export enum eContractid {
   StableAndVariableTokensHelper = 'StableAndVariableTokensHelper',
   ATokensAndRatesHelper = 'ATokensAndRatesHelper',
   UiPoolDataProvider = 'UiPoolDataProvider',
+  UiPoolDataProviderV2 = 'UiPoolDataProviderV2',
+  UiPoolDataProviderV2V3 = 'UiPoolDataProviderV2V3',
   WETHGateway = 'WETHGateway',
   WETH = 'WETH',
   WETHMocked = 'WETHMocked',
@@ -98,6 +100,8 @@ export enum eContractid {
   MockParaSwapAugustus = 'MockParaSwapAugustus',
   MockParaSwapAugustusRegistry = 'MockParaSwapAugustusRegistry',
   ParaSwapLiquiditySwapAdapter = 'ParaSwapLiquiditySwapAdapter',
+  UiIncentiveDataProviderV2V3 = 'UiIncentiveDataProviderV2V3',
+  UiIncentiveDataProviderV2 = 'UiIncentiveDataProviderV2',
 }
 
 /*
diff --git a/package.json b/package.json
index dfe0b2d4..1fb3ed60 100644
--- a/package.json
+++ b/package.json
@@ -76,6 +76,15 @@
     "matic:deployUIProvider": "hardhat --network matic deploy-UiPoolDataProvider",
     "mumbai:deployUIProvider": "hardhat --network mumbai deploy-UiPoolDataProvider",
     "fuji:deployUIProvider": "hardhat --network fuji deploy-UiPoolDataProvider",
+    "dev:deployUIProviderV2": "hardhat --network kovan deploy-UiPoolDataProviderV2 --verify",
+    "dev:deployUIProviderV2V3": "hardhat --network kovan deploy-UiPoolDataProviderV2V3 --verify",
+    "fuji:deployUIIncentivesV2V3": "hardhat --network fuji deploy-UiIncentiveDataProviderV2V3",
+    "mumbai:deployUIIncentivesV2V3": "hardhat --network mumbai deploy-UiIncentiveDataProviderV2V3 --verify",
+    "matic:deployUIIncentivesV2V3": "hardhat --network matic deploy-UiIncentiveDataProviderV2V3 --verify",
+    "dev:deployUIIncentivesProviderV2": "hardhat --network kovan deploy-UiIncentiveDataProviderV2 --verify",
+    "main:deployUIIncentivesProviderV2": "hardhat --network main deploy-UiIncentiveDataProviderV2 --verify",
+    "matic:deployUIIncentivesProviderV2": "hardhat --network matic deploy-UiIncentiveDataProviderV2 --verify",
+    "mumbai:deployUIIncentivesProviderV2": "hardhat --network mumbai 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..ca3efe45
--- /dev/null
+++ b/tasks/deployments/deploy-UiIncentiveDataProviderV2.ts
@@ -0,0 +1,23 @@
+import { task } from 'hardhat/config';
+import { eContractid, eEthereumNetwork, eNetwork, ePolygonNetwork } from '../../helpers/types';
+import { deployUiIncentiveDataProviderV2 } from '../../helpers/contracts-deployments';
+import { exit } from 'process';
+
+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`);
+  });
diff --git a/tasks/deployments/deploy-UiIncentiveDataProviderV2V3.ts b/tasks/deployments/deploy-UiIncentiveDataProviderV2V3.ts
new file mode 100644
index 00000000..8c798db8
--- /dev/null
+++ b/tasks/deployments/deploy-UiIncentiveDataProviderV2V3.ts
@@ -0,0 +1,21 @@
+import { task } from 'hardhat/config';
+import { eContractid } from '../../helpers/types';
+import { deployUiIncentiveDataProviderV2V3 } from '../../helpers/contracts-deployments';
+
+task(
+  `deploy-${eContractid.UiIncentiveDataProviderV2V3}`,
+  `Deploys the UiIncentiveDataProviderV2V3 contract`
+)
+  .addFlag('verify', 'Verify UiIncentiveDataProviderV2V3 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- UiIncentiveDataProviderV2V3 deployment`);
+
+    const uiIncentiveDataProviderV2V3 = await deployUiIncentiveDataProviderV2V3(verify);
+
+    console.log('UiIncentiveDataProviderV2V3 deployed at:', uiIncentiveDataProviderV2V3.address);
+    console.log(`\tFinished UiIncentiveDataProviderV2V3 deployment`);
+  });
diff --git a/tasks/deployments/deploy-UiPoolDataProviderV2.ts b/tasks/deployments/deploy-UiPoolDataProviderV2.ts
new file mode 100644
index 00000000..6c0e0262
--- /dev/null
+++ b/tasks/deployments/deploy-UiPoolDataProviderV2.ts
@@ -0,0 +1,34 @@
+import { task } from 'hardhat/config';
+import { eContractid } from '../../helpers/types';
+import { deployUiPoolDataProviderV2 } from '../../helpers/contracts-deployments';
+import { chainlinkAggregatorProxy, chainlinkEthUsdAggregatorProxy } from '../../helpers/constants';
+
+task(`deploy-${eContractid.UiPoolDataProviderV2}`, `Deploys the UiPoolDataProviderV2 contract`)
+  .addFlag('verify', 'Verify UiPoolDataProviderV2 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- UiPoolDataProviderV2 price aggregator: ${
+        chainlinkAggregatorProxy[localBRE.network.name]
+      }`
+    );
+    console.log(
+      `\n- UiPoolDataProviderV2 eth/usd price aggregator: ${
+        chainlinkAggregatorProxy[localBRE.network.name]
+      }`
+    );
+    console.log(`\n- UiPoolDataProviderV2 deployment`);
+
+    const UiPoolDataProviderV2 = await deployUiPoolDataProviderV2(
+      chainlinkAggregatorProxy[localBRE.network.name],
+      chainlinkEthUsdAggregatorProxy[localBRE.network.name],
+      verify
+    );
+
+    console.log('UiPoolDataProviderV2 deployed at:', UiPoolDataProviderV2.address);
+    console.log(`\tFinished UiPoolDataProvider deployment`);
+  });
diff --git a/tasks/deployments/deploy-UiPoolDataProviderV2V3.ts b/tasks/deployments/deploy-UiPoolDataProviderV2V3.ts
new file mode 100644
index 00000000..3fee992e
--- /dev/null
+++ b/tasks/deployments/deploy-UiPoolDataProviderV2V3.ts
@@ -0,0 +1,34 @@
+import { task } from 'hardhat/config';
+import { eContractid } from '../../helpers/types';
+import { deployUiPoolDataProviderV2V3 } from '../../helpers/contracts-deployments';
+import { chainlinkAggregatorProxy, chainlinkEthUsdAggregatorProxy } from '../../helpers/constants';
+
+task(`deploy-${eContractid.UiPoolDataProviderV2V3}`, `Deploys the UiPoolDataProviderV2V3 contract`)
+  .addFlag('verify', 'Verify UiPoolDataProviderV2V3 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- UiPoolDataProviderV2V3 price aggregator: ${
+        chainlinkAggregatorProxy[localBRE.network.name]
+      }`
+    );
+    console.log(
+      `\n- UiPoolDataProviderV2V3 eth/usd price aggregator: ${
+        chainlinkAggregatorProxy[localBRE.network.name]
+      }`
+    );
+    console.log(`\n- UiPoolDataProviderV2V3 deployment`);
+
+    const UiPoolDataProviderV2V3 = await deployUiPoolDataProviderV2V3(
+      chainlinkAggregatorProxy[localBRE.network.name],
+      chainlinkEthUsdAggregatorProxy[localBRE.network.name],
+      verify
+    );
+
+    console.log('UiPoolDataProviderV2 deployed at:', UiPoolDataProviderV2V3.address);
+    console.log(`\tFinished UiPoolDataProvider deployment`);
+  });
diff --git a/tasks/full/6-initialize.ts b/tasks/full/6-initialize.ts
index fc93600d..fa7a20cf 100644
--- a/tasks/full/6-initialize.ts
+++ b/tasks/full/6-initialize.ts
@@ -4,7 +4,7 @@ import {
   deployLendingPoolCollateralManager,
   deployWalletBalancerProvider,
   authorizeWETHGateway,
-  deployUiPoolDataProvider,
+  deployUiPoolDataProviderV2,
 } from '../../helpers/contracts-deployments';
 import { loadPoolConfig, ConfigNames, getTreasuryAddress } from '../../helpers/configuration';
 import { getWETHGateway } from '../../helpers/contracts-getters';
@@ -16,7 +16,7 @@ import {
   getAaveProtocolDataProvider,
   getLendingPoolAddressesProvider,
 } from '../../helpers/contracts-getters';
-import { ZERO_ADDRESS } from '../../helpers/constants';
+import { chainlinkAggregatorProxy, chainlinkEthUsdAggregatorProxy } from '../../helpers/constants';
 
 task('full:initialize-lending-pool', 'Initialize lending pool configuration.')
   .addFlag('verify', 'Verify contracts at Etherscan')
@@ -100,8 +100,9 @@ task('full:initialize-lending-pool', 'Initialize lending pool configuration.')
 
       await deployWalletBalancerProvider(verify);
 
-      const uiPoolDataProvider = await deployUiPoolDataProvider(
-        [incentivesController, oracle],
+      const uiPoolDataProvider = await deployUiPoolDataProviderV2(
+        chainlinkAggregatorProxy[localBRE.network.name],
+        chainlinkEthUsdAggregatorProxy[localBRE.network.name],
         verify
       );
       console.log('UiPoolDataProvider deployed at:', uiPoolDataProvider.address);