mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Apply feedback fixes
This commit is contained in:
parent
8278d2e6d8
commit
0eddff4933
|
@ -5,15 +5,14 @@ pragma experimental ABIEncoderV2;
|
||||||
import {PercentageMath} from '../libraries/math/PercentageMath.sol';
|
import {PercentageMath} from '../libraries/math/PercentageMath.sol';
|
||||||
import {SafeMath} from '../dependencies/openzeppelin/contracts/SafeMath.sol';
|
import {SafeMath} from '../dependencies/openzeppelin/contracts/SafeMath.sol';
|
||||||
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
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 {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 {ILendingPool} from '../interfaces/ILendingPool.sol';
|
||||||
import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
|
import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
|
||||||
import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.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';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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
|
||||||
|
@ -23,23 +22,24 @@ contract BaseUniswapAdapter {
|
||||||
using SafeMath for uint256;
|
using SafeMath for uint256;
|
||||||
using PercentageMath for uint256;
|
using PercentageMath for uint256;
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
using ReserveConfiguration for ReserveConfiguration.Map;
|
|
||||||
|
enum LeftoverAction {DEPOSIT, TRANSFER}
|
||||||
|
|
||||||
// Max slippage percent allow by param
|
// Max slippage percent allow by param
|
||||||
uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30%
|
uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30%
|
||||||
// Min slippage percent allow by param
|
// Min slippage percent allow by param
|
||||||
uint256 public constant MIN_SLIPPAGE_PERCENT = 10; // 0,1%
|
uint256 public constant MIN_SLIPPAGE_PERCENT = 10; // 0,1%
|
||||||
|
|
||||||
ILendingPoolAddressesProvider public immutable addressesProvider;
|
ILendingPool public immutable POOL;
|
||||||
IUniswapV2Router02 public immutable uniswapRouter;
|
IPriceOracleGetter public immutable ORACLE;
|
||||||
ILendingPool public immutable pool;
|
IUniswapV2Router02 public immutable UNISWAP_ROUTER;
|
||||||
|
|
||||||
event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount);
|
event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount);
|
||||||
|
|
||||||
constructor(ILendingPoolAddressesProvider _addressesProvider, IUniswapV2Router02 _uniswapRouter) public {
|
constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) public {
|
||||||
addressesProvider = _addressesProvider;
|
POOL = ILendingPool(addressesProvider.getLendingPool());
|
||||||
pool = ILendingPool(_addressesProvider.getLendingPool());
|
ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle());
|
||||||
uniswapRouter = _uniswapRouter;
|
UNISWAP_ROUTER = uniswapRouter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,7 +58,7 @@ contract BaseUniswapAdapter {
|
||||||
path[0] = reserveIn;
|
path[0] = reserveIn;
|
||||||
path[1] = reserveOut;
|
path[1] = reserveOut;
|
||||||
|
|
||||||
uint256[] memory amounts = uniswapRouter.getAmountsOut(amountIn, path);
|
uint256[] memory amounts = UNISWAP_ROUTER.getAmountsOut(amountIn, path);
|
||||||
|
|
||||||
return amounts[1];
|
return amounts[1];
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ contract BaseUniswapAdapter {
|
||||||
path[0] = reserveIn;
|
path[0] = reserveIn;
|
||||||
path[1] = reserveOut;
|
path[1] = reserveOut;
|
||||||
|
|
||||||
uint256[] memory amounts = uniswapRouter.getAmountsIn(amountOut, path);
|
uint256[] memory amounts = UNISWAP_ROUTER.getAmountsIn(amountOut, path);
|
||||||
|
|
||||||
return amounts[0];
|
return amounts[0];
|
||||||
}
|
}
|
||||||
|
@ -101,24 +101,23 @@ contract BaseUniswapAdapter {
|
||||||
internal
|
internal
|
||||||
returns (uint256)
|
returns (uint256)
|
||||||
{
|
{
|
||||||
uint256 fromAssetDecimals = getDecimals(assetToSwapFrom);
|
uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom);
|
||||||
uint256 toAssetDecimals = getDecimals(assetToSwapTo);
|
uint256 toAssetDecimals = _getDecimals(assetToSwapTo);
|
||||||
|
|
||||||
(uint256 fromAssetPrice, uint256 toAssetPrice) = getPrices(assetToSwapFrom, assetToSwapTo);
|
uint256 fromAssetPrice = _getPrice(assetToSwapFrom);
|
||||||
|
uint256 toAssetPrice = _getPrice(assetToSwapTo);
|
||||||
|
|
||||||
uint256 amountOutMin = amountToSwap
|
uint256 amountOutMin = 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(slippage));
|
.percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(slippage));
|
||||||
|
|
||||||
IERC20(assetToSwapFrom).approve(address(uniswapRouter), amountToSwap);
|
IERC20(assetToSwapFrom).approve(address(UNISWAP_ROUTER), amountToSwap);
|
||||||
|
|
||||||
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 = uniswapRouter.swapExactTokensForTokens(amountToSwap, amountOutMin, path, address(this), block.timestamp);
|
uint256[] memory amounts = UNISWAP_ROUTER.swapExactTokensForTokens(amountToSwap, amountOutMin, path, address(this), block.timestamp);
|
||||||
|
|
||||||
require(amounts[1] >= amountOutMin, 'INSUFFICIENT_OUTPUT_AMOUNT');
|
|
||||||
|
|
||||||
emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]);
|
emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]);
|
||||||
|
|
||||||
|
@ -143,10 +142,11 @@ contract BaseUniswapAdapter {
|
||||||
internal
|
internal
|
||||||
returns (uint256)
|
returns (uint256)
|
||||||
{
|
{
|
||||||
uint256 fromAssetDecimals = getDecimals(assetToSwapFrom);
|
uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom);
|
||||||
uint256 toAssetDecimals = getDecimals(assetToSwapTo);
|
uint256 toAssetDecimals = _getDecimals(assetToSwapTo);
|
||||||
|
|
||||||
(uint256 fromAssetPrice, uint256 toAssetPrice) = getPrices(assetToSwapFrom, assetToSwapTo);
|
uint256 fromAssetPrice = _getPrice(assetToSwapFrom);
|
||||||
|
uint256 toAssetPrice = _getPrice(assetToSwapTo);
|
||||||
|
|
||||||
uint256 expectedMaxAmountToSwap = amountToReceive
|
uint256 expectedMaxAmountToSwap = amountToReceive
|
||||||
.mul(toAssetPrice.mul(10**fromAssetDecimals))
|
.mul(toAssetPrice.mul(10**fromAssetDecimals))
|
||||||
|
@ -155,14 +155,12 @@ contract BaseUniswapAdapter {
|
||||||
|
|
||||||
require(maxAmountToSwap < expectedMaxAmountToSwap, 'maxAmountToSwap exceed max slippage');
|
require(maxAmountToSwap < expectedMaxAmountToSwap, 'maxAmountToSwap exceed max slippage');
|
||||||
|
|
||||||
IERC20(assetToSwapFrom).approve(address(uniswapRouter), maxAmountToSwap);
|
IERC20(assetToSwapFrom).approve(address(UNISWAP_ROUTER), maxAmountToSwap);
|
||||||
|
|
||||||
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 = uniswapRouter.swapTokensForExactTokens(amountToReceive, maxAmountToSwap, path, address(this), block.timestamp);
|
uint256[] memory amounts = UNISWAP_ROUTER.swapTokensForExactTokens(amountToReceive, maxAmountToSwap, path, address(this), block.timestamp);
|
||||||
|
|
||||||
require(amounts[1] >= amountToReceive, 'INSUFFICIENT_OUTPUT_AMOUNT');
|
|
||||||
|
|
||||||
emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]);
|
emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]);
|
||||||
|
|
||||||
|
@ -170,34 +168,20 @@ contract BaseUniswapAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Get assets prices from the oracle denominated in eth
|
* @dev Get the price of the asset from the oracle denominated in eth
|
||||||
* @param assetToSwapFrom first asset
|
* @param asset address
|
||||||
* @param assetToSwapTo second asset
|
* @return eth price for the asset
|
||||||
* @return fromAssetPrice eth price for the first asset
|
|
||||||
* @return toAssetPrice eth price for the second asset
|
|
||||||
*/
|
*/
|
||||||
function getPrices(
|
function _getPrice(address asset) internal view returns (uint256) {
|
||||||
address assetToSwapFrom,
|
return ORACLE.getAssetPrice(asset);
|
||||||
address assetToSwapTo
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (uint256 fromAssetPrice, uint256 toAssetPrice)
|
|
||||||
{
|
|
||||||
IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle());
|
|
||||||
fromAssetPrice = oracle.getAssetPrice(assetToSwapFrom);
|
|
||||||
toAssetPrice = oracle.getAssetPrice(assetToSwapTo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Get the decimals of an asset
|
* @dev Get the decimals of an asset
|
||||||
* @return number of decimals of the asset
|
* @return number of decimals of the asset
|
||||||
*/
|
*/
|
||||||
function getDecimals(address asset) internal view returns (uint256) {
|
function _getDecimals(address asset) internal view returns (uint256) {
|
||||||
ReserveConfiguration.Map memory configuration = pool.getConfiguration(asset);
|
return IERC20Detailed(asset).decimals();
|
||||||
(, , , uint256 decimals, ) = configuration.getParamsMemory();
|
|
||||||
|
|
||||||
return decimals;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -205,7 +189,7 @@ contract BaseUniswapAdapter {
|
||||||
* @return address of the aToken
|
* @return address of the aToken
|
||||||
*/
|
*/
|
||||||
function getAToken(address asset) internal view returns (address) {
|
function getAToken(address asset) internal view returns (address) {
|
||||||
ReserveLogic.ReserveData memory reserve = pool.getReserveData(asset);
|
ReserveLogic.ReserveData memory reserve = POOL.getReserveData(asset);
|
||||||
return reserve.aTokenAddress;
|
return reserve.aTokenAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,19 +197,19 @@ contract BaseUniswapAdapter {
|
||||||
* @dev Take action with the swap left overs as configured in the parameters
|
* @dev Take action with the swap left overs as configured in the parameters
|
||||||
* @param asset address of the asset
|
* @param asset address of the asset
|
||||||
* @param reservedAmount Amount reserved to be used by the contract to repay the flash loan
|
* @param reservedAmount Amount reserved to be used by the contract to repay the flash loan
|
||||||
* @param leftOverAction Flag indicating what to do with the left over balance from the swap:
|
* @param leftOverAction enum indicating what to do with the left over balance from the swap:
|
||||||
* (0) Deposit back
|
* (0) Deposit back
|
||||||
* (1) Direct transfer to user
|
* (1) Direct transfer to user
|
||||||
* @param user address
|
* @param user address
|
||||||
*/
|
*/
|
||||||
function sendLeftOver(address asset, uint256 reservedAmount, uint256 leftOverAction, address user) internal {
|
function sendLeftovers(address asset, uint256 reservedAmount, LeftoverAction leftOverAction, address user) internal {
|
||||||
uint256 balance = IERC20(asset).balanceOf(address(this));
|
uint256 balance = IERC20(asset).balanceOf(address(this));
|
||||||
uint256 assetLeftOver = balance.sub(reservedAmount);
|
uint256 assetLeftOver = balance.sub(reservedAmount);
|
||||||
|
|
||||||
if (assetLeftOver > 0) {
|
if (assetLeftOver > 0) {
|
||||||
if (leftOverAction == 0) {
|
if (leftOverAction == LeftoverAction.DEPOSIT) {
|
||||||
IERC20(asset).approve(address(pool), balance);
|
IERC20(asset).approve(address(POOL), balance);
|
||||||
pool.deposit(asset, assetLeftOver, user, 0);
|
POOL.deposit(asset, assetLeftOver, user, 0);
|
||||||
} else {
|
} else {
|
||||||
IERC20(asset).transfer(user, assetLeftOver);
|
IERC20(asset).transfer(user, assetLeftOver);
|
||||||
}
|
}
|
||||||
|
@ -249,7 +233,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);
|
POOL.withdraw(reserve, amount, address(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -266,6 +250,6 @@ contract BaseUniswapAdapter {
|
||||||
pullAToken(reserve, user, flashLoanDebt);
|
pullAToken(reserve, user, flashLoanDebt);
|
||||||
|
|
||||||
// Repay flashloan
|
// Repay flashloan
|
||||||
IERC20(reserve).approve(address(pool), flashLoanDebt);
|
IERC20(reserve).approve(address(POOL), flashLoanDebt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,11 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||||
contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
|
contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
ILendingPoolAddressesProvider _addressesProvider,
|
ILendingPoolAddressesProvider addressesProvider,
|
||||||
IUniswapV2Router02 _uniswapRouter
|
IUniswapV2Router02 uniswapRouter
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
BaseUniswapAdapter(_addressesProvider, _uniswapRouter)
|
BaseUniswapAdapter(addressesProvider, uniswapRouter)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,32 +31,34 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
|
||||||
* @param assets Address to be swapped
|
* @param assets Address to be swapped
|
||||||
* @param amounts Amount of the reserve to be swapped
|
* @param amounts Amount of the reserve to be swapped
|
||||||
* @param premiums Fee of the flash loan
|
* @param premiums Fee of the flash loan
|
||||||
|
* @param initiator Address of the user
|
||||||
* @param params Additional variadic field to include extra params. Expected parameters:
|
* @param params Additional variadic field to include extra params. Expected parameters:
|
||||||
* address assetToSwapTo Address of the reserve to be swapped to and deposited
|
* address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited
|
||||||
* address user The address of the user
|
|
||||||
* uint256 slippage The max slippage percentage allowed for the swap
|
* uint256 slippage The max slippage percentage allowed for the swap
|
||||||
*/
|
*/
|
||||||
function executeOperation(
|
function executeOperation(
|
||||||
address[] calldata assets,
|
address[] calldata assets,
|
||||||
uint256[] calldata amounts,
|
uint256[] calldata amounts,
|
||||||
uint256[] calldata premiums,
|
uint256[] calldata premiums,
|
||||||
|
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");
|
||||||
address assetToSwapTo,
|
|
||||||
address user,
|
(address[] memory assetToSwapToList, uint256 slippage) = abi.decode(params, (address[], uint256));
|
||||||
uint256 slippage
|
|
||||||
) = abi.decode(params, (address, address, uint256));
|
|
||||||
require(slippage < MAX_SLIPPAGE_PERCENT && slippage >= MIN_SLIPPAGE_PERCENT, 'SLIPPAGE_OUT_OF_RANGE');
|
require(slippage < MAX_SLIPPAGE_PERCENT && slippage >= MIN_SLIPPAGE_PERCENT, 'SLIPPAGE_OUT_OF_RANGE');
|
||||||
|
require(assetToSwapToList.length == assets.length, 'INCONSISTENT_PARAMS');
|
||||||
|
|
||||||
uint256 receivedAmount = swapExactTokensForTokens(assets[0], assetToSwapTo, amounts[0], slippage);
|
for (uint256 i = 0; i < assets.length; i++) {
|
||||||
|
uint256 receivedAmount = swapExactTokensForTokens(assets[i], assetToSwapToList[i], amounts[i], slippage);
|
||||||
|
|
||||||
// Deposit new reserve
|
// Deposit new reserve
|
||||||
IERC20(assetToSwapTo).approve(address(pool), receivedAmount);
|
IERC20(assetToSwapToList[i]).approve(address(POOL), receivedAmount);
|
||||||
pool.deposit(assetToSwapTo, receivedAmount, user, 0);
|
POOL.deposit(assetToSwapToList[i], receivedAmount, initiator, 0);
|
||||||
|
|
||||||
uint256 flashLoanDebt = amounts[0].add(premiums[0]);
|
uint256 flashLoanDebt = amounts[i].add(premiums[i]);
|
||||||
pullATokenAndRepayFlashLoan(assets[0], user, flashLoanDebt);
|
pullATokenAndRepayFlashLoan(assets[i], initiator, flashLoanDebt);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -66,25 +68,35 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
|
||||||
* This method can be used when the user has no debts.
|
* This method can be used when the user has no debts.
|
||||||
* The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and
|
* The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and
|
||||||
* perform the swap.
|
* perform the swap.
|
||||||
* @param assetToSwapFrom Address of the underlying asset to be swap from
|
* @param assetToSwapFromList List of addresses of the underlying asset to be swap from
|
||||||
* @param assetToSwapTo Address of the underlying asset to be swap to and deposited
|
* @param assetToSwapToList List of addresses of the underlying asset to be swap to and deposited
|
||||||
* @param amountToSwap How much `assetToSwapFrom` needs to be swapped
|
* @param amountToSwapList List of amounts to be swapped
|
||||||
* @param user Address that will be pulling the swapped funds
|
|
||||||
* @param slippage The max slippage percentage allowed for the swap
|
* @param slippage The max slippage percentage allowed for the swap
|
||||||
*/
|
*/
|
||||||
function swapAndDeposit(
|
function swapAndDeposit(
|
||||||
address assetToSwapFrom,
|
address[] calldata assetToSwapFromList,
|
||||||
address assetToSwapTo,
|
address[] calldata assetToSwapToList,
|
||||||
uint256 amountToSwap,
|
uint256[] calldata amountToSwapList,
|
||||||
address user,
|
|
||||||
uint256 slippage
|
uint256 slippage
|
||||||
) external {
|
) external {
|
||||||
pullAToken(assetToSwapFrom, user, amountToSwap);
|
require(
|
||||||
|
assetToSwapFromList.length == assetToSwapToList.length && assetToSwapFromList.length == amountToSwapList.length,
|
||||||
|
'INCONSISTENT_PARAMS'
|
||||||
|
);
|
||||||
|
|
||||||
uint256 receivedAmount = swapExactTokensForTokens(assetToSwapFrom, assetToSwapTo, amountToSwap, slippage);
|
for (uint256 i = 0; i < assetToSwapFromList.length; i++) {
|
||||||
|
pullAToken(assetToSwapFromList[i], msg.sender, amountToSwapList[i]);
|
||||||
|
|
||||||
// Deposit new reserve
|
uint256 receivedAmount = swapExactTokensForTokens(
|
||||||
IERC20(assetToSwapTo).approve(address(pool), receivedAmount);
|
assetToSwapFromList[i],
|
||||||
pool.deposit(assetToSwapTo, receivedAmount, user, 0);
|
assetToSwapToList[i],
|
||||||
|
amountToSwapList[i],
|
||||||
|
slippage
|
||||||
|
);
|
||||||
|
|
||||||
|
// Deposit new reserve
|
||||||
|
IERC20(assetToSwapToList[i]).approve(address(POOL), receivedAmount);
|
||||||
|
POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,19 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||||
**/
|
**/
|
||||||
contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
|
contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
|
||||||
|
|
||||||
|
struct RepayParams {
|
||||||
|
address[] assetToSwapToList;
|
||||||
|
LeftoverAction leftOverAction;
|
||||||
|
uint256[] repayAmounts;
|
||||||
|
uint256[] rateModes;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
ILendingPoolAddressesProvider _addressesProvider,
|
ILendingPoolAddressesProvider addressesProvider,
|
||||||
IUniswapV2Router02 _uniswapRouter
|
IUniswapV2Router02 uniswapRouter
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
BaseUniswapAdapter(_addressesProvider, _uniswapRouter)
|
BaseUniswapAdapter(addressesProvider, uniswapRouter)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,41 +38,102 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
|
||||||
* @param assets Address to be swapped
|
* @param assets Address to be swapped
|
||||||
* @param amounts Amount of the reserve to be swapped
|
* @param amounts Amount of the reserve to be swapped
|
||||||
* @param premiums Fee of the flash loan
|
* @param premiums Fee of the flash loan
|
||||||
|
* @param initiator Address of the user
|
||||||
* @param params Additional variadic field to include extra params. Expected parameters:
|
* @param params Additional variadic field to include extra params. Expected parameters:
|
||||||
* address assetToSwapTo Address of the reserve to be swapped to and deposited
|
* address[] assetToSwapToList List of the addresses of the reserve to be swapped to and repay
|
||||||
* address user The address of the user
|
|
||||||
* uint256 leftOverAction Flag indicating what to do with the left over balance from the swap:
|
* uint256 leftOverAction Flag indicating what to do with the left over balance from the swap:
|
||||||
* (0) Deposit back
|
* (0) Deposit back
|
||||||
* (1) Direct transfer to user
|
* (1) Direct transfer to user
|
||||||
* uint256 repayAmount Amount of debt to be repaid
|
* uint256[] repayAmounts List of amounts of debt to be repaid
|
||||||
* uint256 rateMode The rate modes of the debt to be repaid
|
* uint256[] rateModes List of the rate modes of the debt to be repaid
|
||||||
*/
|
*/
|
||||||
function executeOperation(
|
function executeOperation(
|
||||||
address[] calldata assets,
|
address[] calldata assets,
|
||||||
uint256[] calldata amounts,
|
uint256[] calldata amounts,
|
||||||
uint256[] calldata premiums,
|
uint256[] calldata premiums,
|
||||||
|
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");
|
||||||
address assetToSwapTo,
|
|
||||||
address user,
|
|
||||||
uint256 leftOverAction,
|
|
||||||
uint256 repayAmount,
|
|
||||||
uint256 rateMode
|
|
||||||
) = abi.decode(params, (address, address, uint256, uint256, uint256));
|
|
||||||
|
|
||||||
swapTokensForExactTokens(assets[0], assetToSwapTo, amounts[0], repayAmount);
|
RepayParams memory decodedParams = _decodeParams(params);
|
||||||
|
|
||||||
// Repay debt
|
require(
|
||||||
IERC20(assetToSwapTo).approve(address(pool), repayAmount);
|
assets.length == decodedParams.assetToSwapToList.length
|
||||||
pool.repay(assetToSwapTo, repayAmount, rateMode, user);
|
&& assets.length == decodedParams.repayAmounts.length
|
||||||
|
&& assets.length == decodedParams.rateModes.length,
|
||||||
|
'INCONSISTENT_PARAMS');
|
||||||
|
|
||||||
uint256 flashLoanDebt = amounts[0].add(premiums[0]);
|
for (uint256 i = 0; i < assets.length; i++) {
|
||||||
pullATokenAndRepayFlashLoan(assets[0], user, flashLoanDebt);
|
_swapAndRepay(
|
||||||
|
assets[i],
|
||||||
// Take care of reserve leftover from the swap
|
decodedParams.assetToSwapToList[i],
|
||||||
sendLeftOver(assets[0], flashLoanDebt, leftOverAction, user);
|
amounts[i],
|
||||||
|
decodedParams.repayAmounts[i],
|
||||||
|
decodedParams.rateModes[i],
|
||||||
|
initiator,
|
||||||
|
decodedParams.leftOverAction,
|
||||||
|
premiums[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Perform the swap, the repay of the debt and send back the left overs
|
||||||
|
*
|
||||||
|
* @param assetFrom Address of token to be swapped
|
||||||
|
* @param assetTo Address of token to be received
|
||||||
|
* @param amount Amount of the reserve to be swapped
|
||||||
|
* @param repayAmount Amount of the debt to be repaid
|
||||||
|
* @param rateMode Rate mode of the debt to be repaid
|
||||||
|
* @param initiator Address of the user
|
||||||
|
* @param leftOverAction enum indicating what to do with the left over balance from the swap
|
||||||
|
* @param premium Fee of the flash loan
|
||||||
|
*/
|
||||||
|
function _swapAndRepay(
|
||||||
|
address assetFrom,
|
||||||
|
address assetTo,
|
||||||
|
uint256 amount,
|
||||||
|
uint256 repayAmount,
|
||||||
|
uint256 rateMode,
|
||||||
|
address initiator,
|
||||||
|
LeftoverAction leftOverAction,
|
||||||
|
uint256 premium
|
||||||
|
) internal {
|
||||||
|
swapTokensForExactTokens(assetFrom, assetTo, amount, repayAmount);
|
||||||
|
|
||||||
|
// Repay debt
|
||||||
|
IERC20(assetTo).approve(address(POOL), repayAmount);
|
||||||
|
POOL.repay(assetTo, repayAmount, rateMode, initiator);
|
||||||
|
|
||||||
|
uint256 flashLoanDebt = amount.add(premium);
|
||||||
|
pullATokenAndRepayFlashLoan(assetFrom, initiator, flashLoanDebt);
|
||||||
|
|
||||||
|
// Take care of reserve leftover from the swap
|
||||||
|
sendLeftovers(assetFrom, flashLoanDebt, leftOverAction, initiator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Decodes debt information encoded in flashloan params
|
||||||
|
* @param params Additional variadic field to include extra params. Expected parameters:
|
||||||
|
* address[] assetToSwapToList List of the addresses of the reserve to be swapped to and repay
|
||||||
|
* uint256 leftOverAction Flag indicating what to do with the left over balance from the swap:
|
||||||
|
* (0) Deposit back
|
||||||
|
* (1) Direct transfer to user
|
||||||
|
* uint256[] repayAmounts List of amounts of debt to be repaid
|
||||||
|
* uint256[] rateModes List of the rate modes of the debt to be repaid
|
||||||
|
* @return RepayParams struct containing decoded params
|
||||||
|
*/
|
||||||
|
function _decodeParams(bytes memory params) internal returns (RepayParams memory) {
|
||||||
|
(
|
||||||
|
address[] memory assetToSwapToList,
|
||||||
|
LeftoverAction leftOverAction,
|
||||||
|
uint256[] memory repayAmounts,
|
||||||
|
uint256[] memory rateModes
|
||||||
|
) = abi.decode(params, (address[], LeftoverAction, uint256[], uint256[]));
|
||||||
|
|
||||||
|
return RepayParams(assetToSwapToList, leftOverAction, repayAmounts, rateModes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,8 +121,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
// 0,5% slippage
|
// 0,5% slippage
|
||||||
const params = ethers.utils.defaultAbiCoder.encode(
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
['address', 'address', 'uint256'],
|
['address[]', 'uint256'],
|
||||||
[dai.address, userAddress, 50]
|
[[dai.address], 50]
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -132,7 +132,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
uniswapLiquiditySwapAdapter.address,
|
uniswapLiquiditySwapAdapter.address,
|
||||||
[weth.address],
|
[weth.address],
|
||||||
[flashloanAmount.toString()],
|
[flashloanAmount.toString()],
|
||||||
0,
|
[0],
|
||||||
userAddress,
|
userAddress,
|
||||||
params,
|
params,
|
||||||
0
|
0
|
||||||
|
@ -158,6 +158,90 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap));
|
expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should revert if inconsistent params', async () => {
|
||||||
|
const {users, weth, oracle, dai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv;
|
||||||
|
const user = users[0].signer;
|
||||||
|
const userAddress = users[0].address;
|
||||||
|
|
||||||
|
const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10');
|
||||||
|
|
||||||
|
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||||
|
const expectedDaiAmount = await convertToCurrencyDecimals(
|
||||||
|
dai.address,
|
||||||
|
new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
await mockUniswapRouter.setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
|
// User will swap liquidity 10 aEth to aDai
|
||||||
|
const liquidityToSwap = parseEther('10');
|
||||||
|
await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap);
|
||||||
|
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
// 0,5% slippage
|
||||||
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address[]', 'uint256'],
|
||||||
|
[[dai.address, weth.address], 50]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapLiquiditySwapAdapter.address,
|
||||||
|
[weth.address],
|
||||||
|
[flashloanAmount.toString()],
|
||||||
|
[0],
|
||||||
|
userAddress,
|
||||||
|
params,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
).to.be.revertedWith('INCONSISTENT_PARAMS');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should revert if caller not lending pool', async () => {
|
||||||
|
const {users, weth, oracle, dai, aWETH, uniswapLiquiditySwapAdapter} = testEnv;
|
||||||
|
const user = users[0].signer;
|
||||||
|
const userAddress = users[0].address;
|
||||||
|
|
||||||
|
const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10');
|
||||||
|
|
||||||
|
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||||
|
const expectedDaiAmount = await convertToCurrencyDecimals(
|
||||||
|
dai.address,
|
||||||
|
new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
await mockUniswapRouter.setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
|
// User will swap liquidity 10 aEth to aDai
|
||||||
|
const liquidityToSwap = parseEther('10');
|
||||||
|
await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap);
|
||||||
|
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
// 0,5% slippage
|
||||||
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address[]', 'uint256'],
|
||||||
|
[[dai.address, weth.address], 50]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
uniswapLiquiditySwapAdapter
|
||||||
|
.connect(user)
|
||||||
|
.executeOperation(
|
||||||
|
[weth.address],
|
||||||
|
[flashloanAmount.toString()],
|
||||||
|
[0],
|
||||||
|
userAddress,
|
||||||
|
params
|
||||||
|
)
|
||||||
|
).to.be.revertedWith('CALLER_MUST_BE_LENDING_POOL');
|
||||||
|
});
|
||||||
|
|
||||||
it('should work correctly with tokens of different decimals', async () => {
|
it('should work correctly with tokens of different decimals', async () => {
|
||||||
const {
|
const {
|
||||||
users,
|
users,
|
||||||
|
@ -215,8 +299,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
// 0,5% slippage
|
// 0,5% slippage
|
||||||
const params = ethers.utils.defaultAbiCoder.encode(
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
['address', 'address', 'uint256'],
|
['address[]', 'uint256'],
|
||||||
[dai.address, userAddress, 50]
|
[[dai.address], 50]
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -226,7 +310,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
uniswapLiquiditySwapAdapter.address,
|
uniswapLiquiditySwapAdapter.address,
|
||||||
[usdc.address],
|
[usdc.address],
|
||||||
[flashloanAmount.toString()],
|
[flashloanAmount.toString()],
|
||||||
0,
|
[0],
|
||||||
userAddress,
|
userAddress,
|
||||||
params,
|
params,
|
||||||
0
|
0
|
||||||
|
@ -275,14 +359,14 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
// 30% slippage
|
// 30% slippage
|
||||||
const params1 = ethers.utils.defaultAbiCoder.encode(
|
const params1 = ethers.utils.defaultAbiCoder.encode(
|
||||||
['address', 'address', 'uint256'],
|
['address[]', 'uint256'],
|
||||||
[dai.address, userAddress, 3000]
|
[[dai.address], 3000]
|
||||||
);
|
);
|
||||||
|
|
||||||
// 0,05% slippage
|
// 0,05% slippage
|
||||||
const params2 = ethers.utils.defaultAbiCoder.encode(
|
const params2 = ethers.utils.defaultAbiCoder.encode(
|
||||||
['address', 'address', 'uint256'],
|
['address[]', 'uint256'],
|
||||||
[dai.address, userAddress, 5]
|
[[dai.address], 5]
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -292,7 +376,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
uniswapLiquiditySwapAdapter.address,
|
uniswapLiquiditySwapAdapter.address,
|
||||||
[weth.address],
|
[weth.address],
|
||||||
[flashloanAmount.toString()],
|
[flashloanAmount.toString()],
|
||||||
0,
|
[0],
|
||||||
userAddress,
|
userAddress,
|
||||||
params1,
|
params1,
|
||||||
0
|
0
|
||||||
|
@ -305,62 +389,13 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
uniswapLiquiditySwapAdapter.address,
|
uniswapLiquiditySwapAdapter.address,
|
||||||
[weth.address],
|
[weth.address],
|
||||||
[flashloanAmount.toString()],
|
[flashloanAmount.toString()],
|
||||||
0,
|
[0],
|
||||||
userAddress,
|
userAddress,
|
||||||
params2,
|
params2,
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
).to.be.revertedWith('SLIPPAGE_OUT_OF_RANGE');
|
).to.be.revertedWith('SLIPPAGE_OUT_OF_RANGE');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should revert when swap exceed slippage', async () => {
|
|
||||||
const {users, weth, oracle, dai, aWETH, pool, uniswapLiquiditySwapAdapter} = testEnv;
|
|
||||||
const user = users[0].signer;
|
|
||||||
const userAddress = users[0].address;
|
|
||||||
|
|
||||||
const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10');
|
|
||||||
|
|
||||||
await weth.connect(user).mint(amountWETHtoSwap);
|
|
||||||
await weth.connect(user).transfer(uniswapLiquiditySwapAdapter.address, amountWETHtoSwap);
|
|
||||||
|
|
||||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
|
||||||
const expectedDaiAmount = await convertToCurrencyDecimals(
|
|
||||||
dai.address,
|
|
||||||
new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
// 1,5% slippage
|
|
||||||
const returnedDaiAmountWithBigSlippage = new BigNumber(expectedDaiAmount.toString())
|
|
||||||
.multipliedBy(0.985)
|
|
||||||
.toFixed(0);
|
|
||||||
await mockUniswapRouter.connect(user).setAmountToReturn(returnedDaiAmountWithBigSlippage);
|
|
||||||
|
|
||||||
// User will swap liquidity 10 aEth to aDai
|
|
||||||
const liquidityToSwap = parseEther('10');
|
|
||||||
await aWETH.connect(user).approve(uniswapLiquiditySwapAdapter.address, liquidityToSwap);
|
|
||||||
// Subtract the FL fee from the amount to be swapped 0,09%
|
|
||||||
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
|
||||||
|
|
||||||
// 0,5% slippage
|
|
||||||
const params = ethers.utils.defaultAbiCoder.encode(
|
|
||||||
['address', 'address', 'uint256'],
|
|
||||||
[dai.address, userAddress, 50]
|
|
||||||
);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool
|
|
||||||
.connect(user)
|
|
||||||
.flashLoan(
|
|
||||||
uniswapLiquiditySwapAdapter.address,
|
|
||||||
[weth.address],
|
|
||||||
[flashloanAmount.toString()],
|
|
||||||
0,
|
|
||||||
userAddress,
|
|
||||||
params,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
).to.be.revertedWith('INSUFFICIENT_OUTPUT_AMOUNT');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('swapAndDeposit', () => {
|
describe('swapAndDeposit', () => {
|
||||||
|
@ -400,13 +435,9 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
const userAEthBalanceBefore = await aWETH.balanceOf(userAddress);
|
const userAEthBalanceBefore = await aWETH.balanceOf(userAddress);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
uniswapLiquiditySwapAdapter.swapAndDeposit(
|
uniswapLiquiditySwapAdapter
|
||||||
weth.address,
|
.connect(user)
|
||||||
dai.address,
|
.swapAndDeposit([weth.address], [dai.address], [amountWETHtoSwap], 50)
|
||||||
amountWETHtoSwap,
|
|
||||||
userAddress,
|
|
||||||
50
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.to.emit(uniswapLiquiditySwapAdapter, 'Swapped')
|
.to.emit(uniswapLiquiditySwapAdapter, 'Swapped')
|
||||||
.withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount);
|
.withArgs(weth.address, dai.address, amountWETHtoSwap.toString(), expectedDaiAmount);
|
||||||
|
@ -427,6 +458,30 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
expect(userAEthBalance).to.be.lt(userAEthBalanceBefore);
|
expect(userAEthBalance).to.be.lt(userAEthBalanceBefore);
|
||||||
expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap));
|
expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap));
|
||||||
});
|
});
|
||||||
|
it('should revert if inconsistent params', async () => {
|
||||||
|
const {users, weth, dai, uniswapLiquiditySwapAdapter} = testEnv;
|
||||||
|
const user = users[0].signer;
|
||||||
|
|
||||||
|
const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10');
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
uniswapLiquiditySwapAdapter
|
||||||
|
.connect(user)
|
||||||
|
.swapAndDeposit([weth.address, dai.address], [dai.address], [amountWETHtoSwap], 50)
|
||||||
|
).to.be.revertedWith('INCONSISTENT_PARAMS');
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
uniswapLiquiditySwapAdapter
|
||||||
|
.connect(user)
|
||||||
|
.swapAndDeposit([weth.address], [dai.address, weth.address], [amountWETHtoSwap], 50)
|
||||||
|
).to.be.revertedWith('INCONSISTENT_PARAMS');
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
uniswapLiquiditySwapAdapter
|
||||||
|
.connect(user)
|
||||||
|
.swapAndDeposit([weth.address], [dai.address], [amountWETHtoSwap, amountWETHtoSwap], 50)
|
||||||
|
).to.be.revertedWith('INCONSISTENT_PARAMS');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -506,8 +561,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
const params = ethers.utils.defaultAbiCoder.encode(
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
['address[]', 'uint256', 'uint256[]', 'uint256[]'],
|
||||||
[dai.address, userAddress, 0, expectedDaiAmount, 1]
|
[[dai.address], 0, [expectedDaiAmount], [1]]
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -517,7 +572,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
uniswapRepayAdapter.address,
|
uniswapRepayAdapter.address,
|
||||||
[weth.address],
|
[weth.address],
|
||||||
[flashloanAmount.toString()],
|
[flashloanAmount.toString()],
|
||||||
0,
|
[0],
|
||||||
userAddress,
|
userAddress,
|
||||||
params,
|
params,
|
||||||
0
|
0
|
||||||
|
@ -539,6 +594,132 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap));
|
expect(userAEthBalance).to.be.gte(userAEthBalanceBefore.sub(liquidityToSwap));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should revert if inconsistent params', async () => {
|
||||||
|
const {users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter} = testEnv;
|
||||||
|
const user = users[0].signer;
|
||||||
|
const userAddress = users[0].address;
|
||||||
|
|
||||||
|
const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10');
|
||||||
|
|
||||||
|
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||||
|
const expectedDaiAmount = await convertToCurrencyDecimals(
|
||||||
|
dai.address,
|
||||||
|
new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open user Debt
|
||||||
|
await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress);
|
||||||
|
|
||||||
|
const liquidityToSwap = amountWETHtoSwap;
|
||||||
|
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
|
||||||
|
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount);
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
|
const params1 = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address[]', 'uint256', 'uint256[]', 'uint256[]'],
|
||||||
|
[[dai.address, weth.address], 0, [expectedDaiAmount], [1]]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapRepayAdapter.address,
|
||||||
|
[weth.address],
|
||||||
|
[flashloanAmount.toString()],
|
||||||
|
[0],
|
||||||
|
userAddress,
|
||||||
|
params1,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
).to.be.revertedWith('INCONSISTENT_PARAMS');
|
||||||
|
|
||||||
|
const params2 = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address[]', 'uint256', 'uint256[]', 'uint256[]'],
|
||||||
|
[[dai.address], 0, [expectedDaiAmount, expectedDaiAmount], [1]]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapRepayAdapter.address,
|
||||||
|
[weth.address],
|
||||||
|
[flashloanAmount.toString()],
|
||||||
|
[0],
|
||||||
|
userAddress,
|
||||||
|
params2,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
).to.be.revertedWith('INCONSISTENT_PARAMS');
|
||||||
|
|
||||||
|
const params3 = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address[]', 'uint256', 'uint256[]', 'uint256[]'],
|
||||||
|
[[dai.address], 0, [expectedDaiAmount], [1, 1]]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
pool
|
||||||
|
.connect(user)
|
||||||
|
.flashLoan(
|
||||||
|
uniswapRepayAdapter.address,
|
||||||
|
[weth.address],
|
||||||
|
[flashloanAmount.toString()],
|
||||||
|
[0],
|
||||||
|
userAddress,
|
||||||
|
params3,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
).to.be.revertedWith('INCONSISTENT_PARAMS');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should revert if caller not lending pool', async () => {
|
||||||
|
const {users, pool, weth, aWETH, oracle, dai, uniswapRepayAdapter} = testEnv;
|
||||||
|
const user = users[0].signer;
|
||||||
|
const userAddress = users[0].address;
|
||||||
|
|
||||||
|
const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10');
|
||||||
|
|
||||||
|
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||||
|
const expectedDaiAmount = await convertToCurrencyDecimals(
|
||||||
|
dai.address,
|
||||||
|
new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open user Debt
|
||||||
|
await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress);
|
||||||
|
|
||||||
|
const liquidityToSwap = amountWETHtoSwap;
|
||||||
|
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
|
||||||
|
|
||||||
|
// Subtract the FL fee from the amount to be swapped 0,09%
|
||||||
|
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
||||||
|
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount);
|
||||||
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
|
['address[]', 'uint256', 'uint256[]', 'uint256[]'],
|
||||||
|
[[dai.address], 0, [expectedDaiAmount], [1]]
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
uniswapRepayAdapter
|
||||||
|
.connect(user)
|
||||||
|
.executeOperation(
|
||||||
|
[weth.address],
|
||||||
|
[flashloanAmount.toString()],
|
||||||
|
[0],
|
||||||
|
userAddress,
|
||||||
|
params
|
||||||
|
)
|
||||||
|
).to.be.revertedWith('CALLER_MUST_BE_LENDING_POOL');
|
||||||
|
});
|
||||||
|
|
||||||
it('should revert if there is not debt to repay with the specified rate mode', async () => {
|
it('should revert if there is not debt to repay with the specified rate mode', async () => {
|
||||||
const {users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH} = testEnv;
|
const {users, pool, weth, oracle, dai, uniswapRepayAdapter, aWETH} = testEnv;
|
||||||
const user = users[0].signer;
|
const user = users[0].signer;
|
||||||
|
@ -568,8 +749,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
const params = ethers.utils.defaultAbiCoder.encode(
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
['address[]', 'uint256', 'uint256[]', 'uint256[]'],
|
||||||
[dai.address, userAddress, 0, expectedDaiAmount, 1]
|
[[dai.address], 0, [expectedDaiAmount], [1]]
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -579,7 +760,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
uniswapRepayAdapter.address,
|
uniswapRepayAdapter.address,
|
||||||
[weth.address],
|
[weth.address],
|
||||||
[flashloanAmount.toString()],
|
[flashloanAmount.toString()],
|
||||||
0,
|
[0],
|
||||||
userAddress,
|
userAddress,
|
||||||
params,
|
params,
|
||||||
0
|
0
|
||||||
|
@ -613,8 +794,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
const params = ethers.utils.defaultAbiCoder.encode(
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
['address[]', 'uint256', 'uint256[]', 'uint256[]'],
|
||||||
[dai.address, userAddress, 0, expectedDaiAmount, 1]
|
[[dai.address], 0, [expectedDaiAmount], [1]]
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -624,7 +805,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
uniswapRepayAdapter.address,
|
uniswapRepayAdapter.address,
|
||||||
[weth.address],
|
[weth.address],
|
||||||
[flashloanAmount.toString()],
|
[flashloanAmount.toString()],
|
||||||
0,
|
[0],
|
||||||
userAddress,
|
userAddress,
|
||||||
params,
|
params,
|
||||||
0
|
0
|
||||||
|
@ -632,55 +813,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
).to.be.reverted;
|
).to.be.reverted;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should revert when the received amount is less than expected', async () => {
|
|
||||||
const {users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter} = testEnv;
|
|
||||||
const user = users[0].signer;
|
|
||||||
const userAddress = users[0].address;
|
|
||||||
|
|
||||||
const amountWETHtoSwap = await convertToCurrencyDecimals(weth.address, '10');
|
|
||||||
|
|
||||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
|
||||||
const expectedDaiAmount = await convertToCurrencyDecimals(
|
|
||||||
dai.address,
|
|
||||||
new BigNumber(amountWETHtoSwap.toString()).div(daiPrice.toString()).toFixed(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Open user Debt
|
|
||||||
await pool.connect(user).borrow(dai.address, expectedDaiAmount, 1, 0, userAddress);
|
|
||||||
|
|
||||||
const liquidityToSwap = amountWETHtoSwap;
|
|
||||||
await aWETH.connect(user).approve(uniswapRepayAdapter.address, liquidityToSwap);
|
|
||||||
|
|
||||||
// Subtract the FL fee from the amount to be swapped 0,09%
|
|
||||||
const flashloanAmount = new BigNumber(liquidityToSwap.toString()).div(1.0009).toFixed(0);
|
|
||||||
|
|
||||||
const insufficientOutput = new BigNumber(expectedDaiAmount.toString())
|
|
||||||
.multipliedBy(0.985)
|
|
||||||
.toFixed(0);
|
|
||||||
|
|
||||||
await mockUniswapRouter.connect(user).setAmountToSwap(flashloanAmount);
|
|
||||||
await mockUniswapRouter.connect(user).setAmountToReturn(insufficientOutput);
|
|
||||||
|
|
||||||
const params = ethers.utils.defaultAbiCoder.encode(
|
|
||||||
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
|
||||||
[dai.address, userAddress, 0, expectedDaiAmount, 1]
|
|
||||||
);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
pool
|
|
||||||
.connect(user)
|
|
||||||
.flashLoan(
|
|
||||||
uniswapRepayAdapter.address,
|
|
||||||
[weth.address],
|
|
||||||
[flashloanAmount.toString()],
|
|
||||||
0,
|
|
||||||
userAddress,
|
|
||||||
params,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
).to.be.revertedWith('INSUFFICIENT_OUTPUT_AMOUNT');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should revert when max amount allowed to swap is bigger than max slippage', async () => {
|
it('should revert when max amount allowed to swap is bigger than max slippage', async () => {
|
||||||
const {users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter} = testEnv;
|
const {users, pool, weth, oracle, dai, aWETH, uniswapRepayAdapter} = testEnv;
|
||||||
const user = users[0].signer;
|
const user = users[0].signer;
|
||||||
|
@ -707,8 +839,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
const params = ethers.utils.defaultAbiCoder.encode(
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
['address[]', 'uint256', 'uint256[]', 'uint256[]'],
|
||||||
[dai.address, userAddress, 0, expectedDaiAmount, 1]
|
[[dai.address], 0, [expectedDaiAmount], [1]]
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -718,7 +850,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
uniswapRepayAdapter.address,
|
uniswapRepayAdapter.address,
|
||||||
[weth.address],
|
[weth.address],
|
||||||
[flashloanAmount.toString()],
|
[flashloanAmount.toString()],
|
||||||
0,
|
[0],
|
||||||
userAddress,
|
userAddress,
|
||||||
params,
|
params,
|
||||||
0
|
0
|
||||||
|
@ -779,8 +911,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
await mockUniswapRouter.connect(user).setAmountToReturn(expectedDaiAmount);
|
||||||
|
|
||||||
const params = ethers.utils.defaultAbiCoder.encode(
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
['address[]', 'uint256', 'uint256[]', 'uint256[]'],
|
||||||
[dai.address, userAddress, 0, expectedDaiAmount, 1]
|
[[dai.address], 0, [expectedDaiAmount], [1]]
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -790,7 +922,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
uniswapRepayAdapter.address,
|
uniswapRepayAdapter.address,
|
||||||
[weth.address],
|
[weth.address],
|
||||||
[flashloanAmount.toString()],
|
[flashloanAmount.toString()],
|
||||||
0,
|
[0],
|
||||||
userAddress,
|
userAddress,
|
||||||
params,
|
params,
|
||||||
0
|
0
|
||||||
|
@ -870,8 +1002,8 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
const wethBalanceBefore = await weth.balanceOf(userAddress);
|
const wethBalanceBefore = await weth.balanceOf(userAddress);
|
||||||
|
|
||||||
const params = ethers.utils.defaultAbiCoder.encode(
|
const params = ethers.utils.defaultAbiCoder.encode(
|
||||||
['address', 'address', 'uint256', 'uint256', 'uint256'],
|
['address[]', 'uint256', 'uint256[]', 'uint256[]'],
|
||||||
[dai.address, userAddress, 1, expectedDaiAmount, 1]
|
[[dai.address], 1, [expectedDaiAmount], [1]]
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -881,7 +1013,7 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => {
|
||||||
uniswapRepayAdapter.address,
|
uniswapRepayAdapter.address,
|
||||||
[weth.address],
|
[weth.address],
|
||||||
[flashloanAmount.toString()],
|
[flashloanAmount.toString()],
|
||||||
0,
|
[0],
|
||||||
userAddress,
|
userAddress,
|
||||||
params,
|
params,
|
||||||
0
|
0
|
||||||
|
|
Loading…
Reference in New Issue
Block a user