adoption to the latest

This commit is contained in:
andyk 2020-11-30 17:14:29 +04:00
parent b4a0841577
commit a21757d0fc
9 changed files with 370 additions and 252 deletions

View File

@ -8,52 +8,37 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol'; import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol';
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
import {ILendingPool} from '../interfaces/ILendingPool.sol';
import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol';
import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol'; import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol';
import {FlashLoanReceiverBase} from '../flashloan/base/FlashLoanReceiverBase.sol';
import {IBaseUniswapAdapter} from './interfaces/IBaseUniswapAdapter.sol';
/** /**
* @title BaseUniswapAdapter * @title BaseUniswapAdapter
* @notice Implements the logic for performing assets swaps in Uniswap V2 * @notice Implements the logic for performing assets swaps in Uniswap V2
* @author Aave * @author Aave
**/ **/
contract BaseUniswapAdapter { abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapter {
using SafeMath for uint256; using SafeMath for uint256;
using PercentageMath for uint256; using PercentageMath for uint256;
using SafeERC20 for IERC20; 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 // 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 // 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 // USD oracle asset address
address public constant USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; address public constant override USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96;
ILendingPool public immutable POOL; IPriceOracleGetter public immutable override ORACLE;
IPriceOracleGetter public immutable ORACLE; IUniswapV2Router02 public immutable override UNISWAP_ROUTER;
IUniswapV2Router02 public immutable UNISWAP_ROUTER;
event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount); constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter)
public
constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) public { FlashLoanReceiverBase(addressesProvider)
POOL = ILendingPool(addressesProvider.getLendingPool()); {
ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle()); ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle());
UNISWAP_ROUTER = uniswapRouter; UNISWAP_ROUTER = uniswapRouter;
} }
@ -68,10 +53,20 @@ contract BaseUniswapAdapter {
* @return uint256 In amount of reserveIn value denominated in USD (8 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) * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals)
*/ */
function getAmountsOut(uint256 amountIn, address reserveIn, address reserveOut) function getAmountsOut(
uint256 amountIn,
address reserveIn,
address reserveOut
)
external external
view view
returns (uint256, uint256, uint256, uint256) override
returns (
uint256,
uint256,
uint256,
uint256
)
{ {
AmountCalc memory results = _getAmountsOutData(reserveIn, reserveOut, amountIn); 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 In amount of reserveIn value denominated in USD (8 decimals)
* @return uint256 Out amount of reserveOut 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 external
view view
returns (uint256, uint256, uint256, uint256) override
returns (
uint256,
uint256,
uint256,
uint256
)
{ {
AmountCalc memory results = _getAmountsInData(reserveIn, reserveOut, amountOut); AmountCalc memory results = _getAmountsInData(reserveIn, reserveOut, amountOut);
@ -121,17 +126,15 @@ contract BaseUniswapAdapter {
address assetToSwapTo, address assetToSwapTo,
uint256 amountToSwap, uint256 amountToSwap,
uint256 minAmountOut uint256 minAmountOut
) ) internal returns (uint256) {
internal
returns (uint256)
{
uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom);
uint256 toAssetDecimals = _getDecimals(assetToSwapTo); uint256 toAssetDecimals = _getDecimals(assetToSwapTo);
uint256 fromAssetPrice = _getPrice(assetToSwapFrom); uint256 fromAssetPrice = _getPrice(assetToSwapFrom);
uint256 toAssetPrice = _getPrice(assetToSwapTo); uint256 toAssetPrice = _getPrice(assetToSwapTo);
uint256 expectedMinAmountOut = amountToSwap uint256 expectedMinAmountOut =
amountToSwap
.mul(fromAssetPrice.mul(10**toAssetDecimals)) .mul(fromAssetPrice.mul(10**toAssetDecimals))
.div(toAssetPrice.mul(10**fromAssetDecimals)) .div(toAssetPrice.mul(10**fromAssetDecimals))
.percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(MAX_SLIPPAGE_PERCENT)); .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(MAX_SLIPPAGE_PERCENT));
@ -143,7 +146,14 @@ contract BaseUniswapAdapter {
address[] memory path = new address[](2); address[] memory path = new address[](2);
path[0] = assetToSwapFrom; path[0] = assetToSwapFrom;
path[1] = assetToSwapTo; 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]); emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]);
@ -164,17 +174,15 @@ contract BaseUniswapAdapter {
address assetToSwapTo, address assetToSwapTo,
uint256 maxAmountToSwap, uint256 maxAmountToSwap,
uint256 amountToReceive uint256 amountToReceive
) ) internal returns (uint256) {
internal
returns (uint256)
{
uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom);
uint256 toAssetDecimals = _getDecimals(assetToSwapTo); uint256 toAssetDecimals = _getDecimals(assetToSwapTo);
uint256 fromAssetPrice = _getPrice(assetToSwapFrom); uint256 fromAssetPrice = _getPrice(assetToSwapFrom);
uint256 toAssetPrice = _getPrice(assetToSwapTo); uint256 toAssetPrice = _getPrice(assetToSwapTo);
uint256 expectedMaxAmountToSwap = amountToReceive uint256 expectedMaxAmountToSwap =
amountToReceive
.mul(toAssetPrice.mul(10**fromAssetDecimals)) .mul(toAssetPrice.mul(10**fromAssetDecimals))
.div(fromAssetPrice.mul(10**toAssetDecimals)) .div(fromAssetPrice.mul(10**toAssetDecimals))
.percentMul(PercentageMath.PERCENTAGE_FACTOR.add(MAX_SLIPPAGE_PERCENT)); .percentMul(PercentageMath.PERCENTAGE_FACTOR.add(MAX_SLIPPAGE_PERCENT));
@ -186,7 +194,14 @@ contract BaseUniswapAdapter {
address[] memory path = new address[](2); address[] memory path = new address[](2);
path[0] = assetToSwapFrom; path[0] = assetToSwapFrom;
path[1] = assetToSwapTo; 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]); emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]);
@ -215,7 +230,7 @@ contract BaseUniswapAdapter {
* @return address of the aToken * @return address of the aToken
*/ */
function _getReserveData(address asset) internal view returns (DataTypes.ReserveData memory) { 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); IERC20(reserveAToken).safeTransferFrom(user, address(this), amount);
// withdraw reserve // 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 * @return whether or not permit should be called
*/ */
function _usePermit(PermitSignature memory signature) internal pure returns (bool) { 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 * @param decimals Decimals of the reserve
* @return whether or not permit should be called * @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 ethUsdPrice = _getPrice(USD_ADDRESS);
uint256 reservePrice = _getPrice(reserve); uint256 reservePrice = _getPrice(reserve);
return amount return amount.mul(reservePrice).div(10**decimals).mul(ethUsdPrice).div(10**18);
.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 In amount of reserveIn value denominated in USD (8 decimals)
* uint256 Out amount of reserveOut 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 // Subtract flash loan fee
uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000));
@ -304,12 +324,13 @@ contract BaseUniswapAdapter {
uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveInDecimals = _getDecimals(reserveIn);
uint256 reserveOutDecimals = _getDecimals(reserveOut); uint256 reserveOutDecimals = _getDecimals(reserveOut);
uint256 outPerInPrice = finalAmountIn uint256 outPerInPrice =
.mul(10**18) finalAmountIn.mul(10**18).mul(10**reserveOutDecimals).div(
.mul(10**reserveOutDecimals) amounts[1].mul(10**reserveInDecimals)
.div(amounts[1].mul(10**reserveInDecimals)); );
return AmountCalc( return
AmountCalc(
amounts[1], amounts[1],
outPerInPrice, outPerInPrice,
_calcUsdValue(reserveIn, amountIn, reserveInDecimals), _calcUsdValue(reserveIn, amountIn, reserveInDecimals),
@ -328,7 +349,11 @@ contract BaseUniswapAdapter {
* uint256 In amount of reserveIn value denominated in USD (8 decimals) * uint256 In amount of reserveIn value denominated in USD (8 decimals)
* uint256 Out amount of reserveOut 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); uint256[] memory amounts = _getAmountsIn(reserveIn, reserveOut, amountOut);
// Add flash loan fee // Add flash loan fee
@ -337,12 +362,13 @@ contract BaseUniswapAdapter {
uint256 reserveInDecimals = _getDecimals(reserveIn); uint256 reserveInDecimals = _getDecimals(reserveIn);
uint256 reserveOutDecimals = _getDecimals(reserveOut); uint256 reserveOutDecimals = _getDecimals(reserveOut);
uint256 inPerOutPrice = amountOut uint256 inPerOutPrice =
.mul(10**18) amountOut.mul(10**18).mul(10**reserveInDecimals).div(
.mul(10**reserveInDecimals) finalAmountIn.mul(10**reserveOutDecimals)
.div(finalAmountIn.mul(10**reserveOutDecimals)); );
return AmountCalc( return
AmountCalc(
finalAmountIn, finalAmountIn,
inPerOutPrice, inPerOutPrice,
_calcUsdValue(reserveIn, finalAmountIn, reserveInDecimals), _calcUsdValue(reserveIn, finalAmountIn, reserveInDecimals),
@ -357,7 +383,11 @@ contract BaseUniswapAdapter {
* @param amountOut Amount of reserveOut * @param amountOut Amount of reserveOut
* @return uint256[] amounts Array containing the amountIn and amountOut for a swap * @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) { function _getAmountsIn(
address reserveIn,
address reserveOut,
uint256 amountOut
) internal view returns (uint256[] memory) {
address[] memory path = new address[](2); address[] memory path = new address[](2);
path[0] = reserveIn; path[0] = reserveIn;
path[1] = reserveOut; path[1] = reserveOut;

View File

@ -5,7 +5,6 @@ pragma experimental ABIEncoderV2;
import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol'; import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol';
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol';
import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol';
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.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. * @notice Uniswap V2 Adapter to swap liquidity.
* @author Aave * @author Aave
**/ **/
contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver { contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter {
struct PermitParams { struct PermitParams {
uint256[] amount; uint256[] amount;
uint256[] deadline; uint256[] deadline;
@ -30,10 +28,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
PermitParams permitParams; PermitParams permitParams;
} }
constructor( constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter)
ILendingPoolAddressesProvider addressesProvider,
IUniswapV2Router02 uniswapRouter
)
public public
BaseUniswapAdapter(addressesProvider, uniswapRouter) BaseUniswapAdapter(addressesProvider, uniswapRouter)
{} {}
@ -64,19 +59,19 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
address initiator, address initiator,
bytes calldata params bytes calldata params
) external override returns (bool) { ) 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); SwapParams memory decodedParams = _decodeParams(params);
require( require(
assets.length == decodedParams.assetToSwapToList.length assets.length == decodedParams.assetToSwapToList.length &&
&& assets.length == decodedParams.minAmountsToReceive.length assets.length == decodedParams.minAmountsToReceive.length &&
&& assets.length == decodedParams.swapAllBalance.length assets.length == decodedParams.swapAllBalance.length &&
&& assets.length == decodedParams.permitParams.amount.length assets.length == decodedParams.permitParams.amount.length &&
&& assets.length == decodedParams.permitParams.deadline.length assets.length == decodedParams.permitParams.deadline.length &&
&& assets.length == decodedParams.permitParams.v.length assets.length == decodedParams.permitParams.v.length &&
&& assets.length == decodedParams.permitParams.r.length assets.length == decodedParams.permitParams.r.length &&
&& assets.length == decodedParams.permitParams.s.length, assets.length == decodedParams.permitParams.s.length,
'INCONSISTENT_PARAMS' 'INCONSISTENT_PARAMS'
); );
@ -127,10 +122,10 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
PermitSignature[] calldata permitParams PermitSignature[] calldata permitParams
) external { ) external {
require( require(
assetToSwapFromList.length == assetToSwapToList.length assetToSwapFromList.length == assetToSwapToList.length &&
&& assetToSwapFromList.length == amountToSwapList.length assetToSwapFromList.length == amountToSwapList.length &&
&& assetToSwapFromList.length == minAmountsToReceive.length assetToSwapFromList.length == minAmountsToReceive.length &&
&& assetToSwapFromList.length == permitParams.length, assetToSwapFromList.length == permitParams.length,
'INCONSISTENT_PARAMS' 'INCONSISTENT_PARAMS'
); );
@ -138,17 +133,13 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
address aToken = _getReserveData(assetToSwapFromList[i]).aTokenAddress; address aToken = _getReserveData(assetToSwapFromList[i]).aTokenAddress;
uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(msg.sender); uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(msg.sender);
uint256 amountToSwap = amountToSwapList[i] > aTokenInitiatorBalance ? aTokenInitiatorBalance : amountToSwapList[i]; uint256 amountToSwap =
amountToSwapList[i] > aTokenInitiatorBalance ? aTokenInitiatorBalance : amountToSwapList[i];
_pullAToken( _pullAToken(assetToSwapFromList[i], aToken, msg.sender, amountToSwap, permitParams[i]);
assetToSwapFromList[i],
aToken,
msg.sender,
amountToSwap,
permitParams[i]
);
uint256 receivedAmount = _swapExactTokensForTokens( uint256 receivedAmount =
_swapExactTokensForTokens(
assetToSwapFromList[i], assetToSwapFromList[i],
assetToSwapToList[i], assetToSwapToList[i],
amountToSwap, amountToSwap,
@ -156,8 +147,8 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
); );
// Deposit new reserve // Deposit new reserve
IERC20(assetToSwapToList[i]).approve(address(POOL), receivedAmount); IERC20(assetToSwapToList[i]).approve(address(LENDING_POOL), receivedAmount);
POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0); LENDING_POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0);
} }
} }
@ -184,20 +175,17 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
address aToken = _getReserveData(assetFrom).aTokenAddress; address aToken = _getReserveData(assetFrom).aTokenAddress;
uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(initiator); uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(initiator);
uint256 amountToSwap = swapAllBalance && aTokenInitiatorBalance.sub(premium) <= amount uint256 amountToSwap =
swapAllBalance && aTokenInitiatorBalance.sub(premium) <= amount
? aTokenInitiatorBalance.sub(premium) ? aTokenInitiatorBalance.sub(premium)
: amount; : amount;
uint256 receivedAmount = _swapExactTokensForTokens( uint256 receivedAmount =
assetFrom, _swapExactTokensForTokens(assetFrom, assetTo, amountToSwap, minAmountToReceive);
assetTo,
amountToSwap,
minAmountToReceive
);
// Deposit new reserve // Deposit new reserve
IERC20(assetTo).approve(address(POOL), receivedAmount); IERC20(assetTo).approve(address(LENDING_POOL), receivedAmount);
POOL.deposit(assetTo, receivedAmount, initiator, 0); LENDING_POOL.deposit(assetTo, receivedAmount, initiator, 0);
uint256 flashLoanDebt = amount.add(premium); uint256 flashLoanDebt = amount.add(premium);
uint256 amountToPull = amountToSwap.add(premium); uint256 amountToPull = amountToSwap.add(premium);
@ -205,7 +193,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
_pullAToken(assetFrom, aToken, initiator, amountToPull, permitSignature); _pullAToken(assetFrom, aToken, initiator, amountToPull, permitSignature);
// Repay flash loan // 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, uint8[] memory v,
bytes32[] memory r, bytes32[] memory r,
bytes32[] memory s 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( return
SwapParams(
assetToSwapToList, assetToSwapToList,
minAmountsToReceive, minAmountsToReceive,
swapAllBalance, swapAllBalance,
PermitParams( PermitParams(permitAmount, deadline, v, r, s)
permitAmount,
deadline,
v,
r,
s
)
); );
} }
} }

View File

@ -5,7 +5,6 @@ pragma experimental ABIEncoderV2;
import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol'; import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol';
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol';
import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol';
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
import {DataTypes} from '../protocol/libraries/types/DataTypes.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. * @notice Uniswap V2 Adapter to perform a repay of a debt with collateral.
* @author Aave * @author Aave
**/ **/
contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver { contract UniswapRepayAdapter is BaseUniswapAdapter {
struct RepayParams { struct RepayParams {
address collateralAsset; address collateralAsset;
uint256 collateralAmount; uint256 collateralAmount;
@ -23,10 +21,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
PermitSignature permitSignature; PermitSignature permitSignature;
} }
constructor( constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter)
ILendingPoolAddressesProvider addressesProvider,
IUniswapV2Router02 uniswapRouter
)
public public
BaseUniswapAdapter(addressesProvider, uniswapRouter) BaseUniswapAdapter(addressesProvider, uniswapRouter)
{} {}
@ -58,7 +53,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
address initiator, address initiator,
bytes calldata params bytes calldata params
) external override returns (bool) { ) 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); RepayParams memory decodedParams = _decodeParams(params);
@ -99,7 +94,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset);
DataTypes.ReserveData memory debtReserveData = _getReserveData(debtAsset); DataTypes.ReserveData memory debtReserveData = _getReserveData(debtAsset);
address debtToken = DataTypes.InterestRateMode(debtRateMode) == DataTypes.InterestRateMode.STABLE address debtToken =
DataTypes.InterestRateMode(debtRateMode) == DataTypes.InterestRateMode.STABLE
? debtReserveData.stableDebtTokenAddress ? debtReserveData.stableDebtTokenAddress
: debtReserveData.variableDebtTokenAddress; : debtReserveData.variableDebtTokenAddress;
@ -117,21 +113,32 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); require(amounts[0] <= maxCollateralToSwap, 'slippage too high');
// Pull aTokens from user // 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 // Swap collateral for debt asset
_swapTokensForExactTokens(collateralAsset, debtAsset, amounts[0], amountToRepay); _swapTokensForExactTokens(collateralAsset, debtAsset, amounts[0], amountToRepay);
} else { } else {
// Pull aTokens from user // Pull aTokens from user
_pullAToken(collateralAsset, collateralReserveData.aTokenAddress, msg.sender, amountToRepay, permitSignature); _pullAToken(
collateralAsset,
collateralReserveData.aTokenAddress,
msg.sender,
amountToRepay,
permitSignature
);
} }
// Repay debt // Repay debt
IERC20(debtAsset).approve(address(POOL), amountToRepay); IERC20(debtAsset).approve(address(LENDING_POOL), amountToRepay);
POOL.repay(debtAsset, amountToRepay, debtRateMode, msg.sender); 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 * @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); DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset);
// Repay debt // Repay debt
IERC20(debtAsset).approve(address(POOL), amount); IERC20(debtAsset).approve(address(LENDING_POOL), amount);
uint256 repaidAmount = IERC20(debtAsset).balanceOf(address(this)); 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))); repaidAmount = repaidAmount.sub(IERC20(debtAsset).balanceOf(address(this)));
if (collateralAsset != debtAsset) { if (collateralAsset != debtAsset) {
@ -195,7 +202,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
} }
// Repay flash loan // 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 bytes32 s
) = abi.decode(params, (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32)); ) = abi.decode(params, (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32));
return RepayParams( return
RepayParams(
collateralAsset, collateralAsset,
collateralAmount, collateralAmount,
rateMode, rateMode,
PermitSignature( PermitSignature(permitAmount, deadline, v, r, s)
permitAmount,
deadline,
v,
r,
s
)
); );
} }
} }

View File

@ -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
);
}

View File

@ -1,6 +1,9 @@
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import { HardhatUserConfig } from 'hardhat/types'; import { HardhatUserConfig } from 'hardhat/types';
require('dotenv').config();
// @ts-ignore // @ts-ignore
import { accounts } from './test-wallets.js'; import { accounts } from './test-wallets.js';
import { eEthereumNetwork } from './helpers/types'; import { eEthereumNetwork } from './helpers/types';
@ -27,7 +30,7 @@ const MAINNET_FORK = process.env.MAINNET_FORK === 'true';
// Prevent to load scripts before compilation and typechain // Prevent to load scripts before compilation and typechain
if (!SKIP_LOAD) { 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); const tasksPath = path.join(__dirname, 'tasks', folder);
fs.readdirSync(tasksPath) fs.readdirSync(tasksPath)
.filter((pth) => pth.includes('.ts')) .filter((pth) => pth.includes('.ts'))

62
package-lock.json generated
View File

@ -9504,14 +9504,6 @@
"prr": "~1.0.1", "prr": "~1.0.1",
"semver": "~5.4.1", "semver": "~5.4.1",
"xtend": "~4.0.0" "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": { "merkle-patricia-tree": {
@ -9901,14 +9893,6 @@
"prr": "~1.0.1", "prr": "~1.0.1",
"semver": "~5.4.1", "semver": "~5.4.1",
"xtend": "~4.0.0" "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": { "merkle-patricia-tree": {
@ -10200,14 +10184,6 @@
"prr": "~1.0.1", "prr": "~1.0.1",
"semver": "~5.4.1", "semver": "~5.4.1",
"xtend": "~4.0.0" "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": { "merkle-patricia-tree": {
@ -10459,14 +10435,6 @@
"prr": "~1.0.1", "prr": "~1.0.1",
"semver": "~5.4.1", "semver": "~5.4.1",
"xtend": "~4.0.0" "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": { "merkle-patricia-tree": {
@ -12428,6 +12396,17 @@
"safe-buffer": "~5.1.1", "safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1", "string_decoder": "~1.1.1",
"util-deprecate": "~1.0.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": { "safe-buffer": {
@ -12436,6 +12415,11 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true "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": { "xtend": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@ -14250,6 +14234,12 @@
"integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==", "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==",
"dev": true "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": { "semver-greatest-satisfied-range": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", "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", "prr": "~1.0.1",
"semver": "~5.4.1", "semver": "~5.4.1",
"xtend": "~4.0.0" "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": { "merkle-patricia-tree": {

View File

@ -43,6 +43,7 @@
"print-contracts:main": "npm run hardhat:main -- print-contracts", "print-contracts:main": "npm run hardhat:main -- print-contracts",
"print-contracts:ropsten": "npm run hardhat:main -- print-contracts", "print-contracts:ropsten": "npm run hardhat:main -- print-contracts",
"dev:deployUIProvider": "npm run hardhat:kovan deploy-UiPoolDataProvider", "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", "kovan:verify": "npm run hardhat:kovan verify:general -- --all --pool Aave",
"ropsten:verify": "npm run hardhat:ropsten 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", "mainnet:verify": "npm run hardhat:main verify:general -- --all --pool Aave",

View File

@ -1,4 +1,4 @@
import {task} from '@nomiclabs/buidler/config'; import { task } from 'hardhat/config';
import { UiPoolDataProviderFactory } from '../../types'; import { UiPoolDataProviderFactory } from '../../types';
import { verifyContract } from '../../helpers/etherscan-verification'; import { verifyContract } from '../../helpers/etherscan-verification';
@ -7,7 +7,7 @@ import {eContractid} from '../../helpers/types';
task(`deploy-${eContractid.UiPoolDataProvider}`, `Deploys the UiPoolDataProvider contract`) task(`deploy-${eContractid.UiPoolDataProvider}`, `Deploys the UiPoolDataProvider contract`)
.addFlag('verify', 'Verify UiPoolDataProvider contract via Etherscan API.') .addFlag('verify', 'Verify UiPoolDataProvider contract via Etherscan API.')
.setAction(async ({ verify }, localBRE) => { .setAction(async ({ verify }, localBRE) => {
await localBRE.run('set-bre'); await localBRE.run('set-DRE');
if (!localBRE.network.config.chainId) { if (!localBRE.network.config.chainId) {
throw new Error('INVALID_CHAIN_ID'); throw new Error('INVALID_CHAIN_ID');
@ -21,7 +21,7 @@ task(`deploy-${eContractid.UiPoolDataProvider}`, `Deploys the UiPoolDataProvider
).deploy(); ).deploy();
await uiPoolDataProvider.deployTransaction.wait(); await uiPoolDataProvider.deployTransaction.wait();
console.log('uiPoolDataProvider.address', uiPoolDataProvider.address); console.log('uiPoolDataProvider.address', uiPoolDataProvider.address);
await verifyContract(eContractid.UiPoolDataProvider, uiPoolDataProvider.address, []); await verifyContract(uiPoolDataProvider.address, []);
console.log(`\tFinished UiPoolDataProvider proxy and implementation deployment`); console.log(`\tFinished UiPoolDataProvider proxy and implementation deployment`);
}); });

View File

@ -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`);
});