From a21757d0fc9017f797071c94ff57a95ed2f7dba2 Mon Sep 17 00:00:00 2001 From: andyk Date: Mon, 30 Nov 2020 17:14:29 +0400 Subject: [PATCH] adoption to the latest --- contracts/adapters/BaseUniswapAdapter.sol | 222 ++++++++++-------- .../adapters/UniswapLiquiditySwapAdapter.sol | 109 ++++----- contracts/adapters/UniswapRepayAdapter.sol | 84 +++---- .../interfaces/IBaseUniswapAdapter.sol | 83 +++++++ hardhat.config.ts | 17 +- package-lock.json | 62 ++--- package.json | 1 + .../deployments/deploy-UiPoolDataProvider.ts | 14 +- .../deployments/deploy-UniswapRepayAdapter.ts | 30 +++ 9 files changed, 370 insertions(+), 252 deletions(-) create mode 100644 contracts/adapters/interfaces/IBaseUniswapAdapter.sol create mode 100644 tasks/deployments/deploy-UniswapRepayAdapter.ts diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 718b8f24..c8d3702a 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -8,70 +8,65 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; -import {ILendingPool} from '../interfaces/ILendingPool.sol'; import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol'; +import {FlashLoanReceiverBase} from '../flashloan/base/FlashLoanReceiverBase.sol'; +import {IBaseUniswapAdapter} from './interfaces/IBaseUniswapAdapter.sol'; /** * @title BaseUniswapAdapter * @notice Implements the logic for performing assets swaps in Uniswap V2 * @author Aave **/ -contract BaseUniswapAdapter { +abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapter { using SafeMath for uint256; using PercentageMath for uint256; using SafeERC20 for IERC20; - struct PermitSignature { - uint256 amount; - uint256 deadline; - uint8 v; - bytes32 r; - bytes32 s; - } - - struct AmountCalc { - uint256 calculatedAmount; - uint256 relativePrice; - uint256 amountInUsd; - uint256 amountOutUsd; - } - // Max slippage percent allowed - uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30% + uint256 public constant override MAX_SLIPPAGE_PERCENT = 3000; // 30% // FLash Loan fee set in lending pool - uint256 public constant FLASHLOAN_PREMIUM_TOTAL = 9; + uint256 public constant override FLASHLOAN_PREMIUM_TOTAL = 9; // USD oracle asset address - address public constant USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; + address public constant override USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; - ILendingPool public immutable POOL; - IPriceOracleGetter public immutable ORACLE; - IUniswapV2Router02 public immutable UNISWAP_ROUTER; + IPriceOracleGetter public immutable override ORACLE; + IUniswapV2Router02 public immutable override UNISWAP_ROUTER; - event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount); - - constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) public { - POOL = ILendingPool(addressesProvider.getLendingPool()); + constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) + public + FlashLoanReceiverBase(addressesProvider) + { ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle()); UNISWAP_ROUTER = uniswapRouter; } /** - * @dev Given an input asset amount, returns the maximum output amount of the other asset and the prices - * @param amountIn Amount of reserveIn - * @param reserveIn Address of the asset to be swap from - * @param reserveOut Address of the asset to be swap to - * @return uint256 Amount out of the reserveOut - * @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals) - * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) - * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) - */ - function getAmountsOut(uint256 amountIn, address reserveIn, address reserveOut) + * @dev Given an input asset amount, returns the maximum output amount of the other asset and the prices + * @param amountIn Amount of reserveIn + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @return uint256 Amount out of the reserveOut + * @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals) + * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) + * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) + */ + function getAmountsOut( + uint256 amountIn, + address reserveIn, + address reserveOut + ) external view - returns (uint256, uint256, uint256, uint256) + override + returns ( + uint256, + uint256, + uint256, + uint256 + ) { AmountCalc memory results = _getAmountsOutData(reserveIn, reserveOut, amountIn); @@ -93,10 +88,20 @@ contract BaseUniswapAdapter { * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) */ - function getAmountsIn(uint256 amountOut, address reserveIn, address reserveOut) + function getAmountsIn( + uint256 amountOut, + address reserveIn, + address reserveOut + ) external view - returns (uint256, uint256, uint256, uint256) + override + returns ( + uint256, + uint256, + uint256, + uint256 + ) { AmountCalc memory results = _getAmountsInData(reserveIn, reserveOut, amountOut); @@ -121,20 +126,18 @@ contract BaseUniswapAdapter { address assetToSwapTo, uint256 amountToSwap, uint256 minAmountOut - ) - internal - returns (uint256) - { + ) internal returns (uint256) { uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); uint256 toAssetDecimals = _getDecimals(assetToSwapTo); uint256 fromAssetPrice = _getPrice(assetToSwapFrom); uint256 toAssetPrice = _getPrice(assetToSwapTo); - uint256 expectedMinAmountOut = amountToSwap - .mul(fromAssetPrice.mul(10**toAssetDecimals)) - .div(toAssetPrice.mul(10**fromAssetDecimals)) - .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(MAX_SLIPPAGE_PERCENT)); + uint256 expectedMinAmountOut = + amountToSwap + .mul(fromAssetPrice.mul(10**toAssetDecimals)) + .div(toAssetPrice.mul(10**fromAssetDecimals)) + .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(MAX_SLIPPAGE_PERCENT)); require(expectedMinAmountOut < minAmountOut, 'minAmountOut exceed max slippage'); @@ -143,7 +146,14 @@ contract BaseUniswapAdapter { address[] memory path = new address[](2); path[0] = assetToSwapFrom; path[1] = assetToSwapTo; - uint256[] memory amounts = UNISWAP_ROUTER.swapExactTokensForTokens(amountToSwap, minAmountOut, path, address(this), block.timestamp); + uint256[] memory amounts = + UNISWAP_ROUTER.swapExactTokensForTokens( + amountToSwap, + minAmountOut, + path, + address(this), + block.timestamp + ); emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]); @@ -164,20 +174,18 @@ contract BaseUniswapAdapter { address assetToSwapTo, uint256 maxAmountToSwap, uint256 amountToReceive - ) - internal - returns (uint256) - { + ) internal returns (uint256) { uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); uint256 toAssetDecimals = _getDecimals(assetToSwapTo); uint256 fromAssetPrice = _getPrice(assetToSwapFrom); uint256 toAssetPrice = _getPrice(assetToSwapTo); - uint256 expectedMaxAmountToSwap = amountToReceive - .mul(toAssetPrice.mul(10**fromAssetDecimals)) - .div(fromAssetPrice.mul(10**toAssetDecimals)) - .percentMul(PercentageMath.PERCENTAGE_FACTOR.add(MAX_SLIPPAGE_PERCENT)); + uint256 expectedMaxAmountToSwap = + amountToReceive + .mul(toAssetPrice.mul(10**fromAssetDecimals)) + .div(fromAssetPrice.mul(10**toAssetDecimals)) + .percentMul(PercentageMath.PERCENTAGE_FACTOR.add(MAX_SLIPPAGE_PERCENT)); require(maxAmountToSwap < expectedMaxAmountToSwap, 'maxAmountToSwap exceed max slippage'); @@ -186,7 +194,14 @@ contract BaseUniswapAdapter { address[] memory path = new address[](2); path[0] = assetToSwapFrom; path[1] = assetToSwapTo; - uint256[] memory amounts = UNISWAP_ROUTER.swapTokensForExactTokens(amountToReceive, maxAmountToSwap, path, address(this), block.timestamp); + uint256[] memory amounts = + UNISWAP_ROUTER.swapTokensForExactTokens( + amountToReceive, + maxAmountToSwap, + path, + address(this), + block.timestamp + ); emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]); @@ -215,7 +230,7 @@ contract BaseUniswapAdapter { * @return address of the aToken */ function _getReserveData(address asset) internal view returns (DataTypes.ReserveData memory) { - return POOL.getReserveData(asset); + return LENDING_POOL.getReserveData(asset); } /** @@ -249,7 +264,7 @@ contract BaseUniswapAdapter { IERC20(reserveAToken).safeTransferFrom(user, address(this), amount); // withdraw reserve - POOL.withdraw(reserve, amount, address(this)); + LENDING_POOL.withdraw(reserve, amount, address(this)); } /** @@ -259,7 +274,8 @@ contract BaseUniswapAdapter { * @return whether or not permit should be called */ function _usePermit(PermitSignature memory signature) internal pure returns (bool) { - return !(uint256(signature.deadline) == uint256(signature.v) && uint256(signature.deadline) == 0); + return + !(uint256(signature.deadline) == uint256(signature.v) && uint256(signature.deadline) == 0); } /** @@ -269,15 +285,15 @@ contract BaseUniswapAdapter { * @param decimals Decimals of the reserve * @return whether or not permit should be called */ - function _calcUsdValue(address reserve, uint256 amount, uint256 decimals) internal view returns (uint256) { + function _calcUsdValue( + address reserve, + uint256 amount, + uint256 decimals + ) internal view returns (uint256) { uint256 ethUsdPrice = _getPrice(USD_ADDRESS); uint256 reservePrice = _getPrice(reserve); - return amount - .mul(reservePrice) - .div(10**decimals) - .mul(ethUsdPrice) - .div(10**18); + return amount.mul(reservePrice).div(10**decimals).mul(ethUsdPrice).div(10**18); } /** @@ -291,7 +307,11 @@ contract BaseUniswapAdapter { * uint256 In amount of reserveIn value denominated in USD (8 decimals) * uint256 Out amount of reserveOut value denominated in USD (8 decimals) */ - function _getAmountsOutData(address reserveIn, address reserveOut, uint256 amountIn) internal view returns (AmountCalc memory) { + function _getAmountsOutData( + address reserveIn, + address reserveOut, + uint256 amountIn + ) internal view returns (AmountCalc memory) { // Subtract flash loan fee uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); @@ -304,17 +324,18 @@ contract BaseUniswapAdapter { uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveOutDecimals = _getDecimals(reserveOut); - uint256 outPerInPrice = finalAmountIn - .mul(10**18) - .mul(10**reserveOutDecimals) - .div(amounts[1].mul(10**reserveInDecimals)); + uint256 outPerInPrice = + finalAmountIn.mul(10**18).mul(10**reserveOutDecimals).div( + amounts[1].mul(10**reserveInDecimals) + ); - return AmountCalc( - amounts[1], - outPerInPrice, - _calcUsdValue(reserveIn, amountIn, reserveInDecimals), - _calcUsdValue(reserveOut, amounts[1], reserveOutDecimals) - ); + return + AmountCalc( + amounts[1], + outPerInPrice, + _calcUsdValue(reserveIn, amountIn, reserveInDecimals), + _calcUsdValue(reserveOut, amounts[1], reserveOutDecimals) + ); } /** @@ -328,7 +349,11 @@ contract BaseUniswapAdapter { * uint256 In amount of reserveIn value denominated in USD (8 decimals) * uint256 Out amount of reserveOut value denominated in USD (8 decimals) */ - function _getAmountsInData(address reserveIn, address reserveOut, uint256 amountOut) internal view returns (AmountCalc memory) { + function _getAmountsInData( + address reserveIn, + address reserveOut, + uint256 amountOut + ) internal view returns (AmountCalc memory) { uint256[] memory amounts = _getAmountsIn(reserveIn, reserveOut, amountOut); // Add flash loan fee @@ -337,27 +362,32 @@ contract BaseUniswapAdapter { uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveOutDecimals = _getDecimals(reserveOut); - uint256 inPerOutPrice = amountOut - .mul(10**18) - .mul(10**reserveInDecimals) - .div(finalAmountIn.mul(10**reserveOutDecimals)); + uint256 inPerOutPrice = + amountOut.mul(10**18).mul(10**reserveInDecimals).div( + finalAmountIn.mul(10**reserveOutDecimals) + ); - return AmountCalc( - finalAmountIn, - inPerOutPrice, - _calcUsdValue(reserveIn, finalAmountIn, reserveInDecimals), - _calcUsdValue(reserveOut, amountOut, reserveOutDecimals) - ); + return + AmountCalc( + finalAmountIn, + inPerOutPrice, + _calcUsdValue(reserveIn, finalAmountIn, reserveInDecimals), + _calcUsdValue(reserveOut, amountOut, reserveOutDecimals) + ); } /** - * @dev Calculates the input asset amount required to buy the given output asset amount - * @param reserveIn Address of the asset to be swap from - * @param reserveOut Address of the asset to be swap to - * @param amountOut Amount of reserveOut - * @return uint256[] amounts Array containing the amountIn and amountOut for a swap - */ - function _getAmountsIn(address reserveIn, address reserveOut, uint256 amountOut) internal view returns (uint256[] memory) { + * @dev Calculates the input asset amount required to buy the given output asset amount + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @param amountOut Amount of reserveOut + * @return uint256[] amounts Array containing the amountIn and amountOut for a swap + */ + function _getAmountsIn( + address reserveIn, + address reserveOut, + uint256 amountOut + ) internal view returns (uint256[] memory) { address[] memory path = new address[](2); path[0] = reserveIn; path[1] = reserveOut; diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 8b325499..0adad441 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -5,7 +5,6 @@ pragma experimental ABIEncoderV2; import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; -import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol'; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; /** @@ -13,8 +12,7 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; * @notice Uniswap V2 Adapter to swap liquidity. * @author Aave **/ -contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { - +contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { struct PermitParams { uint256[] amount; uint256[] deadline; @@ -30,10 +28,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { PermitParams permitParams; } - constructor( - ILendingPoolAddressesProvider addressesProvider, - IUniswapV2Router02 uniswapRouter - ) + constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) public BaseUniswapAdapter(addressesProvider, uniswapRouter) {} @@ -64,19 +59,19 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address initiator, bytes calldata params ) external override returns (bool) { - require(msg.sender == address(POOL), "CALLER_MUST_BE_LENDING_POOL"); + require(msg.sender == address(LENDING_POOL), 'CALLER_MUST_BE_LENDING_POOL'); SwapParams memory decodedParams = _decodeParams(params); require( - assets.length == decodedParams.assetToSwapToList.length - && assets.length == decodedParams.minAmountsToReceive.length - && assets.length == decodedParams.swapAllBalance.length - && assets.length == decodedParams.permitParams.amount.length - && assets.length == decodedParams.permitParams.deadline.length - && assets.length == decodedParams.permitParams.v.length - && assets.length == decodedParams.permitParams.r.length - && assets.length == decodedParams.permitParams.s.length, + assets.length == decodedParams.assetToSwapToList.length && + assets.length == decodedParams.minAmountsToReceive.length && + assets.length == decodedParams.swapAllBalance.length && + assets.length == decodedParams.permitParams.amount.length && + assets.length == decodedParams.permitParams.deadline.length && + assets.length == decodedParams.permitParams.v.length && + assets.length == decodedParams.permitParams.r.length && + assets.length == decodedParams.permitParams.s.length, 'INCONSISTENT_PARAMS' ); @@ -127,10 +122,10 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { PermitSignature[] calldata permitParams ) external { require( - assetToSwapFromList.length == assetToSwapToList.length - && assetToSwapFromList.length == amountToSwapList.length - && assetToSwapFromList.length == minAmountsToReceive.length - && assetToSwapFromList.length == permitParams.length, + assetToSwapFromList.length == assetToSwapToList.length && + assetToSwapFromList.length == amountToSwapList.length && + assetToSwapFromList.length == minAmountsToReceive.length && + assetToSwapFromList.length == permitParams.length, 'INCONSISTENT_PARAMS' ); @@ -138,26 +133,22 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address aToken = _getReserveData(assetToSwapFromList[i]).aTokenAddress; uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(msg.sender); - uint256 amountToSwap = amountToSwapList[i] > aTokenInitiatorBalance ? aTokenInitiatorBalance : amountToSwapList[i]; + uint256 amountToSwap = + amountToSwapList[i] > aTokenInitiatorBalance ? aTokenInitiatorBalance : amountToSwapList[i]; - _pullAToken( - assetToSwapFromList[i], - aToken, - msg.sender, - amountToSwap, - permitParams[i] - ); + _pullAToken(assetToSwapFromList[i], aToken, msg.sender, amountToSwap, permitParams[i]); - uint256 receivedAmount = _swapExactTokensForTokens( - assetToSwapFromList[i], - assetToSwapToList[i], - amountToSwap, - minAmountsToReceive[i] - ); + uint256 receivedAmount = + _swapExactTokensForTokens( + assetToSwapFromList[i], + assetToSwapToList[i], + amountToSwap, + minAmountsToReceive[i] + ); // Deposit new reserve - IERC20(assetToSwapToList[i]).approve(address(POOL), receivedAmount); - POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0); + IERC20(assetToSwapToList[i]).approve(address(LENDING_POOL), receivedAmount); + LENDING_POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0); } } @@ -184,20 +175,17 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address aToken = _getReserveData(assetFrom).aTokenAddress; uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(initiator); - uint256 amountToSwap = swapAllBalance && aTokenInitiatorBalance.sub(premium) <= amount - ? aTokenInitiatorBalance.sub(premium) - : amount; + uint256 amountToSwap = + swapAllBalance && aTokenInitiatorBalance.sub(premium) <= amount + ? aTokenInitiatorBalance.sub(premium) + : amount; - uint256 receivedAmount = _swapExactTokensForTokens( - assetFrom, - assetTo, - amountToSwap, - minAmountToReceive - ); + uint256 receivedAmount = + _swapExactTokensForTokens(assetFrom, assetTo, amountToSwap, minAmountToReceive); // Deposit new reserve - IERC20(assetTo).approve(address(POOL), receivedAmount); - POOL.deposit(assetTo, receivedAmount, initiator, 0); + IERC20(assetTo).approve(address(LENDING_POOL), receivedAmount); + LENDING_POOL.deposit(assetTo, receivedAmount, initiator, 0); uint256 flashLoanDebt = amount.add(premium); uint256 amountToPull = amountToSwap.add(premium); @@ -205,7 +193,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { _pullAToken(assetFrom, aToken, initiator, amountToPull, permitSignature); // Repay flash loan - IERC20(assetFrom).approve(address(POOL), flashLoanDebt); + IERC20(assetFrom).approve(address(LENDING_POOL), flashLoanDebt); } /** @@ -231,19 +219,18 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { uint8[] memory v, bytes32[] memory r, bytes32[] memory s - ) = abi.decode(params, (address[], uint256[], bool[], uint256[], uint256[], uint8[], bytes32[], bytes32[])); + ) = + abi.decode( + params, + (address[], uint256[], bool[], uint256[], uint256[], uint8[], bytes32[], bytes32[]) + ); - return SwapParams( - assetToSwapToList, - minAmountsToReceive, - swapAllBalance, - PermitParams( - permitAmount, - deadline, - v, - r, - s - ) - ); + return + SwapParams( + assetToSwapToList, + minAmountsToReceive, + swapAllBalance, + PermitParams(permitAmount, deadline, v, r, s) + ); } } diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 4945edf0..45d998ed 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -5,7 +5,6 @@ pragma experimental ABIEncoderV2; import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; -import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol'; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; @@ -14,8 +13,7 @@ import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; * @notice Uniswap V2 Adapter to perform a repay of a debt with collateral. * @author Aave **/ -contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { - +contract UniswapRepayAdapter is BaseUniswapAdapter { struct RepayParams { address collateralAsset; uint256 collateralAmount; @@ -23,10 +21,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { PermitSignature permitSignature; } - constructor( - ILendingPoolAddressesProvider addressesProvider, - IUniswapV2Router02 uniswapRouter - ) + constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) public BaseUniswapAdapter(addressesProvider, uniswapRouter) {} @@ -58,20 +53,20 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { address initiator, bytes calldata params ) external override returns (bool) { - require(msg.sender == address(POOL), "CALLER_MUST_BE_LENDING_POOL"); + require(msg.sender == address(LENDING_POOL), 'CALLER_MUST_BE_LENDING_POOL'); RepayParams memory decodedParams = _decodeParams(params); - _swapAndRepay( - decodedParams.collateralAsset, - assets[0], - amounts[0], - decodedParams.collateralAmount, - decodedParams.rateMode, - initiator, - premiums[0], - decodedParams.permitSignature - ); + _swapAndRepay( + decodedParams.collateralAsset, + assets[0], + amounts[0], + decodedParams.collateralAmount, + decodedParams.rateMode, + initiator, + premiums[0], + decodedParams.permitSignature + ); return true; } @@ -99,9 +94,10 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); DataTypes.ReserveData memory debtReserveData = _getReserveData(debtAsset); - address debtToken = DataTypes.InterestRateMode(debtRateMode) == DataTypes.InterestRateMode.STABLE - ? debtReserveData.stableDebtTokenAddress - : debtReserveData.variableDebtTokenAddress; + address debtToken = + DataTypes.InterestRateMode(debtRateMode) == DataTypes.InterestRateMode.STABLE + ? debtReserveData.stableDebtTokenAddress + : debtReserveData.variableDebtTokenAddress; uint256 currentDebt = IERC20(debtToken).balanceOf(msg.sender); uint256 amountToRepay = debtRepayAmount <= currentDebt ? debtRepayAmount : currentDebt; @@ -117,21 +113,32 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); // Pull aTokens from user - _pullAToken(collateralAsset, collateralReserveData.aTokenAddress, msg.sender, amounts[0], permitSignature); + _pullAToken( + collateralAsset, + collateralReserveData.aTokenAddress, + msg.sender, + amounts[0], + permitSignature + ); // Swap collateral for debt asset _swapTokensForExactTokens(collateralAsset, debtAsset, amounts[0], amountToRepay); } else { // Pull aTokens from user - _pullAToken(collateralAsset, collateralReserveData.aTokenAddress, msg.sender, amountToRepay, permitSignature); + _pullAToken( + collateralAsset, + collateralReserveData.aTokenAddress, + msg.sender, + amountToRepay, + permitSignature + ); } // Repay debt - IERC20(debtAsset).approve(address(POOL), amountToRepay); - POOL.repay(debtAsset, amountToRepay, debtRateMode, msg.sender); + IERC20(debtAsset).approve(address(LENDING_POOL), amountToRepay); + LENDING_POOL.repay(debtAsset, amountToRepay, debtRateMode, msg.sender); } - /** * @dev Perform the repay of the debt, pulls the initiator collateral and swaps to repay the flash loan * @@ -157,9 +164,9 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); // Repay debt - IERC20(debtAsset).approve(address(POOL), amount); + IERC20(debtAsset).approve(address(LENDING_POOL), amount); uint256 repaidAmount = IERC20(debtAsset).balanceOf(address(this)); - POOL.repay(debtAsset, amount, rateMode, initiator); + LENDING_POOL.repay(debtAsset, amount, rateMode, initiator); repaidAmount = repaidAmount.sub(IERC20(debtAsset).balanceOf(address(this))); if (collateralAsset != debtAsset) { @@ -195,7 +202,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { } // Repay flash loan - IERC20(debtAsset).approve(address(POOL), amount.add(premium)); + IERC20(debtAsset).approve(address(LENDING_POOL), amount.add(premium)); } /** @@ -223,17 +230,12 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { bytes32 s ) = abi.decode(params, (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32)); - return RepayParams( - collateralAsset, - collateralAmount, - rateMode, - PermitSignature( - permitAmount, - deadline, - v, - r, - s - ) - ); + return + RepayParams( + collateralAsset, + collateralAmount, + rateMode, + PermitSignature(permitAmount, deadline, v, r, s) + ); } } diff --git a/contracts/adapters/interfaces/IBaseUniswapAdapter.sol b/contracts/adapters/interfaces/IBaseUniswapAdapter.sol new file mode 100644 index 00000000..2dd6449b --- /dev/null +++ b/contracts/adapters/interfaces/IBaseUniswapAdapter.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol'; +import {IUniswapV2Router02} from '../../interfaces/IUniswapV2Router02.sol'; + +interface IBaseUniswapAdapter { + event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount); + + struct PermitSignature { + uint256 amount; + uint256 deadline; + uint8 v; + bytes32 r; + bytes32 s; + } + + struct AmountCalc { + uint256 calculatedAmount; + uint256 relativePrice; + uint256 amountInUsd; + uint256 amountOutUsd; + } + + function MAX_SLIPPAGE_PERCENT() external returns (uint256); + + function FLASHLOAN_PREMIUM_TOTAL() external returns (uint256); + + function USD_ADDRESS() external returns (address); + + function ORACLE() external returns (IPriceOracleGetter); + + function UNISWAP_ROUTER() external returns (IUniswapV2Router02); + + /** + * @dev Given an input asset amount, returns the maximum output amount of the other asset and the prices + * @param amountIn Amount of reserveIn + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @return uint256 Amount out of the reserveOut + * @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals) + * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) + * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) + */ + function getAmountsOut( + uint256 amountIn, + address reserveIn, + address reserveOut + ) + external + view + returns ( + uint256, + uint256, + uint256, + uint256 + ); + + /** + * @dev Returns the minimum input asset amount required to buy the given output asset amount and the prices + * @param amountOut Amount of reserveOut + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @return uint256 Amount in of the reserveIn + * @return uint256 The price of in amount denominated in the reserveOut currency (18 decimals) + * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) + * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) + */ + function getAmountsIn( + uint256 amountOut, + address reserveIn, + address reserveOut + ) + external + view + returns ( + uint256, + uint256, + uint256, + uint256 + ); +} diff --git a/hardhat.config.ts b/hardhat.config.ts index fafa0eff..1d1a0e1d 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,10 +1,13 @@ import path from 'path'; import fs from 'fs'; -import {HardhatUserConfig} from 'hardhat/types'; +import { HardhatUserConfig } from 'hardhat/types'; + +require('dotenv').config(); + // @ts-ignore -import {accounts} from './test-wallets.js'; -import {eEthereumNetwork} from './helpers/types'; -import {BUIDLEREVM_CHAINID, COVERAGE_CHAINID} from './helpers/buidler-constants'; +import { accounts } from './test-wallets.js'; +import { eEthereumNetwork } from './helpers/types'; +import { BUIDLEREVM_CHAINID, COVERAGE_CHAINID } from './helpers/buidler-constants'; import '@nomiclabs/hardhat-ethers'; import '@nomiclabs/hardhat-waffle'; @@ -27,7 +30,7 @@ const MAINNET_FORK = process.env.MAINNET_FORK === 'true'; // Prevent to load scripts before compilation and typechain if (!SKIP_LOAD) { - ['misc', 'migrations', 'dev', 'full', 'verifications'].forEach((folder) => { + ['misc', 'migrations', 'dev', 'full', 'verifications', 'deployments'].forEach((folder) => { const tasksPath = path.join(__dirname, 'tasks', folder); fs.readdirSync(tasksPath) .filter((pth) => pth.includes('.ts')) @@ -73,7 +76,7 @@ const buidlerConfig: HardhatUserConfig = { solidity: { version: '0.6.12', settings: { - optimizer: {enabled: true, runs: 200}, + optimizer: { enabled: true, runs: 200 }, evmVersion: 'istanbul', }, }, @@ -109,7 +112,7 @@ const buidlerConfig: HardhatUserConfig = { chainId: BUIDLEREVM_CHAINID, throwOnTransactionFailures: true, throwOnCallFailures: true, - accounts: accounts.map(({secretKey, balance}: {secretKey: string; balance: string}) => ({ + accounts: accounts.map(({ secretKey, balance }: { secretKey: string; balance: string }) => ({ privateKey: secretKey, balance, })), diff --git a/package-lock.json b/package-lock.json index 32738e30..3ea8f296 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9504,14 +9504,6 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - }, - "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - } } }, "merkle-patricia-tree": { @@ -9901,14 +9893,6 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - }, - "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - } } }, "merkle-patricia-tree": { @@ -10200,14 +10184,6 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - }, - "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - } } }, "merkle-patricia-tree": { @@ -10459,14 +10435,6 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - }, - "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - } } }, "merkle-patricia-tree": { @@ -12428,6 +12396,17 @@ "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "safe-buffer": { @@ -12436,6 +12415,11 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -14250,6 +14234,12 @@ "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==", "dev": true }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, "semver-greatest-satisfied-range": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", @@ -16306,14 +16296,6 @@ "prr": "~1.0.1", "semver": "~5.4.1", "xtend": "~4.0.0" - }, - "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - } } }, "merkle-patricia-tree": { diff --git a/package.json b/package.json index 14dd43c3..f22269f4 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "print-contracts:main": "npm run hardhat:main -- print-contracts", "print-contracts:ropsten": "npm run hardhat:main -- print-contracts", "dev:deployUIProvider": "npm run hardhat:kovan deploy-UiPoolDataProvider", + "dev:deployUniswapRepayAdapter": "npm run hardhat:kovan deploy-UniswapRepayAdapter", "kovan:verify": "npm run hardhat:kovan verify:general -- --all --pool Aave", "ropsten:verify": "npm run hardhat:ropsten verify:general -- --all --pool Aave", "mainnet:verify": "npm run hardhat:main verify:general -- --all --pool Aave", diff --git a/tasks/deployments/deploy-UiPoolDataProvider.ts b/tasks/deployments/deploy-UiPoolDataProvider.ts index b03ca70d..2d696702 100644 --- a/tasks/deployments/deploy-UiPoolDataProvider.ts +++ b/tasks/deployments/deploy-UiPoolDataProvider.ts @@ -1,13 +1,13 @@ -import {task} from '@nomiclabs/buidler/config'; +import { task } from 'hardhat/config'; -import {UiPoolDataProviderFactory} from '../../types'; -import {verifyContract} from '../../helpers/etherscan-verification'; -import {eContractid} from '../../helpers/types'; +import { UiPoolDataProviderFactory } from '../../types'; +import { verifyContract } from '../../helpers/etherscan-verification'; +import { eContractid } from '../../helpers/types'; task(`deploy-${eContractid.UiPoolDataProvider}`, `Deploys the UiPoolDataProvider contract`) .addFlag('verify', 'Verify UiPoolDataProvider contract via Etherscan API.') - .setAction(async ({verify}, localBRE) => { - await localBRE.run('set-bre'); + .setAction(async ({ verify }, localBRE) => { + await localBRE.run('set-DRE'); if (!localBRE.network.config.chainId) { throw new Error('INVALID_CHAIN_ID'); @@ -21,7 +21,7 @@ task(`deploy-${eContractid.UiPoolDataProvider}`, `Deploys the UiPoolDataProvider ).deploy(); await uiPoolDataProvider.deployTransaction.wait(); console.log('uiPoolDataProvider.address', uiPoolDataProvider.address); - await verifyContract(eContractid.UiPoolDataProvider, uiPoolDataProvider.address, []); + await verifyContract(uiPoolDataProvider.address, []); console.log(`\tFinished UiPoolDataProvider proxy and implementation deployment`); }); diff --git a/tasks/deployments/deploy-UniswapRepayAdapter.ts b/tasks/deployments/deploy-UniswapRepayAdapter.ts new file mode 100644 index 00000000..19c77b5d --- /dev/null +++ b/tasks/deployments/deploy-UniswapRepayAdapter.ts @@ -0,0 +1,30 @@ +import { task } from 'hardhat/config'; + +import { UniswapRepayAdapterFactory } from '../../types'; +import { verifyContract } from '../../helpers/etherscan-verification'; + +const CONTRACT_NAME = 'UniswapRepayAdapter'; + +task(`deploy-${CONTRACT_NAME}`, `Deploys the UniswapRepayAdapter contract`) + .addFlag('verify', 'Verify UniswapRepayAdapter 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- UniswapRepayAdapter deployment`); + const args = [ + '0x9fe532197ad76c5a68961439604c037eb79681f0', + '0xfcd87315f0e4067070ade8682fcdbc3006631441', + ]; + const uniswapRepayAdapter = await new UniswapRepayAdapterFactory( + await localBRE.ethers.provider.getSigner() + ).deploy(args[0], args[1]); + await uniswapRepayAdapter.deployTransaction.wait(); + console.log('uniswapRepayAdapter.address', uniswapRepayAdapter.address); + await verifyContract(uniswapRepayAdapter.address, args); + + console.log(`\tFinished UiPoolDataProvider proxy and implementation deployment`); + });