aave-protocol-v2/contracts/adapters/UniswapLiquiditySwapAdapter.sol

284 lines
11 KiB
Solidity
Raw Normal View History

// SPDX-License-Identifier: agpl-3.0
2020-11-25 14:07:33 +00:00
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol';
import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol';
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
/**
* @title UniswapLiquiditySwapAdapter
2020-11-25 18:49:11 +00:00
* @notice Uniswap V2 Adapter to swap liquidity.
* @author Aave
**/
2020-11-30 13:14:29 +00:00
contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter {
2020-11-10 14:28:19 +00:00
struct PermitParams {
uint256[] amount;
uint256[] deadline;
uint8[] v;
bytes32[] r;
bytes32[] s;
}
2020-11-02 20:33:00 +00:00
struct SwapParams {
address[] assetToSwapToList;
uint256[] minAmountsToReceive;
bool[] swapAllBalance;
2020-11-02 20:33:00 +00:00
PermitParams permitParams;
bool[] useEthPath;
2020-11-02 20:33:00 +00:00
}
constructor(
ILendingPoolAddressesProvider addressesProvider,
IUniswapV2Router02 uniswapRouter,
address wethAddress
) public BaseUniswapAdapter(addressesProvider, uniswapRouter, wethAddress) {}
/**
2020-11-25 18:49:11 +00:00
* @dev Swaps the received reserve amount from the flash loan into the asset specified in the params.
* The received funds from the swap are then deposited into the protocol on behalf of the user.
* The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and
* repay the flash loan.
2020-11-25 18:49:11 +00:00
* @param assets Address of asset to be swapped
* @param amounts Amount of the asset to be swapped
* @param premiums Fee of the flash loan
2020-10-30 19:59:25 +00:00
* @param initiator Address of the user
* @param params Additional variadic field to include extra params. Expected parameters:
2020-10-30 19:59:25 +00:00
* address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited
* uint256[] minAmountsToReceive List of min amounts to be received from the swap
* bool[] swapAllBalance Flag indicating if all the user balance should be swapped
* uint256[] permitAmount List of amounts for the permit signature
2020-11-02 20:33:00 +00:00
* uint256[] deadline List of deadlines for the permit signature
* uint8[] v List of v param for the permit signature
* bytes32[] r List of r param for the permit signature
* bytes32[] s List of s param for the permit signature
*/
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
2020-10-30 19:59:25 +00:00
address initiator,
bytes calldata params
) external override returns (bool) {
2020-11-30 13:14:29 +00:00
require(msg.sender == address(LENDING_POOL), 'CALLER_MUST_BE_LENDING_POOL');
2020-10-30 19:59:25 +00:00
2020-11-02 20:33:00 +00:00
SwapParams memory decodedParams = _decodeParams(params);
require(
2020-11-30 13:14:29 +00:00
assets.length == decodedParams.assetToSwapToList.length &&
assets.length == decodedParams.minAmountsToReceive.length &&
assets.length == decodedParams.swapAllBalance.length &&
assets.length == decodedParams.permitParams.amount.length &&
assets.length == decodedParams.permitParams.deadline.length &&
assets.length == decodedParams.permitParams.v.length &&
assets.length == decodedParams.permitParams.r.length &&
assets.length == decodedParams.permitParams.s.length &&
assets.length == decodedParams.useEthPath.length,
2020-11-02 20:33:00 +00:00
'INCONSISTENT_PARAMS'
);
2020-10-30 19:59:25 +00:00
for (uint256 i = 0; i < assets.length; i++) {
_swapLiquidity(
2020-11-02 20:33:00 +00:00
assets[i],
decodedParams.assetToSwapToList[i],
amounts[i],
premiums[i],
2020-11-02 20:33:00 +00:00
initiator,
decodedParams.minAmountsToReceive[i],
decodedParams.swapAllBalance[i],
2020-11-02 20:33:00 +00:00
PermitSignature(
decodedParams.permitParams.amount[i],
2020-11-02 20:33:00 +00:00
decodedParams.permitParams.deadline[i],
decodedParams.permitParams.v[i],
decodedParams.permitParams.r[i],
decodedParams.permitParams.s[i]
),
decodedParams.useEthPath[i]
2020-11-02 20:33:00 +00:00
);
2020-10-30 19:59:25 +00:00
}
return true;
}
struct SwapAndDepositLocalVars {
uint256 i;
uint256 aTokenInitiatorBalance;
uint256 amountToSwap;
uint256 receivedAmount;
address aToken;
}
/**
2020-11-25 18:49:11 +00:00
* @dev Swaps an amount of an asset to another and deposits the new asset amount on behalf of the user without using
* a flash loan. This method can be used when the temporary transfer of the collateral asset to this contract
* does not affect the user position.
* The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and
* perform the swap.
2020-10-30 19:59:25 +00:00
* @param assetToSwapFromList List of addresses of the underlying asset to be swap from
* @param assetToSwapToList List of addresses of the underlying asset to be swap to and deposited
* @param amountToSwapList List of amounts to be swapped. If the amount exceeds the balance, the total balance is used for the swap
* @param minAmountsToReceive List of min amounts to be received from the swap
* @param permitParams List of struct containing the permit signatures
* uint256 permitAmount Amount for the permit signature
* uint256 deadline Deadline for the permit signature
* uint8 v param for the permit signature
* bytes32 r param for the permit signature
* bytes32 s param for the permit signature
* @param useEthPath true if the swap needs to occur using ETH in the routing, false otherwise
*/
function swapAndDeposit(
2020-10-30 19:59:25 +00:00
address[] calldata assetToSwapFromList,
address[] calldata assetToSwapToList,
uint256[] calldata amountToSwapList,
uint256[] calldata minAmountsToReceive,
PermitSignature[] calldata permitParams,
bool[] calldata useEthPath
) external {
2020-10-30 19:59:25 +00:00
require(
2020-11-30 13:14:29 +00:00
assetToSwapFromList.length == assetToSwapToList.length &&
assetToSwapFromList.length == amountToSwapList.length &&
assetToSwapFromList.length == minAmountsToReceive.length &&
assetToSwapFromList.length == permitParams.length,
2020-10-30 19:59:25 +00:00
'INCONSISTENT_PARAMS'
);
SwapAndDepositLocalVars memory vars;
for (vars.i = 0; vars.i < assetToSwapFromList.length; vars.i++) {
vars.aToken = _getReserveData(assetToSwapFromList[vars.i]).aTokenAddress;
vars.aTokenInitiatorBalance = IERC20(vars.aToken).balanceOf(msg.sender);
vars.amountToSwap = amountToSwapList[vars.i] > vars.aTokenInitiatorBalance
? vars.aTokenInitiatorBalance
: amountToSwapList[vars.i];
_pullAToken(
assetToSwapFromList[vars.i],
vars.aToken,
msg.sender,
vars.amountToSwap,
permitParams[vars.i]
);
vars.receivedAmount = _swapExactTokensForTokens(
assetToSwapFromList[vars.i],
assetToSwapToList[vars.i],
vars.amountToSwap,
minAmountsToReceive[vars.i],
useEthPath[vars.i]
);
2020-10-30 19:59:25 +00:00
// Deposit new reserve
2021-01-14 14:21:42 +00:00
IERC20(assetToSwapToList[vars.i]).safeApprove(address(LENDING_POOL), 0);
IERC20(assetToSwapToList[vars.i]).safeApprove(address(LENDING_POOL), vars.receivedAmount);
LENDING_POOL.deposit(assetToSwapToList[vars.i], vars.receivedAmount, msg.sender, 0);
2020-10-30 19:59:25 +00:00
}
}
2020-11-02 20:33:00 +00:00
/**
* @dev Swaps an `amountToSwap` of an asset to another and deposits the funds on behalf of the initiator.
* @param assetFrom Address of the underlying asset to be swap from
* @param assetTo Address of the underlying asset to be swap to and deposited
2020-11-25 18:49:11 +00:00
* @param amount Amount from flash loan
* @param premium Premium of the flash loan
* @param minAmountToReceive Min amount to be received from the swap
* @param swapAllBalance Flag indicating if all the user balance should be swapped
* @param permitSignature List of struct containing the permit signature
* @param useEthPath true if the swap needs to occur using ETH in the routing, false otherwise
*/
2021-02-01 14:13:53 +00:00
2021-01-14 14:21:42 +00:00
struct SwapLiquidityLocalVars {
2021-02-01 14:13:53 +00:00
address aToken;
uint256 aTokenInitiatorBalance;
uint256 amountToSwap;
uint256 receivedAmount;
uint256 flashLoanDebt;
uint256 amountToPull;
2021-01-14 14:21:42 +00:00
}
2021-02-01 14:13:53 +00:00
function _swapLiquidity(
address assetFrom,
address assetTo,
uint256 amount,
uint256 premium,
address initiator,
uint256 minAmountToReceive,
bool swapAllBalance,
PermitSignature memory permitSignature,
bool useEthPath
) internal {
2021-01-14 14:21:42 +00:00
SwapLiquidityLocalVars memory vars;
2021-02-01 14:13:53 +00:00
2021-01-14 14:21:42 +00:00
vars.aToken = _getReserveData(assetFrom).aTokenAddress;
2021-01-14 14:21:42 +00:00
vars.aTokenInitiatorBalance = IERC20(vars.aToken).balanceOf(initiator);
2021-02-01 14:13:53 +00:00
vars.amountToSwap = swapAllBalance && vars.aTokenInitiatorBalance.sub(premium) <= amount
? vars.aTokenInitiatorBalance.sub(premium)
: amount;
2020-11-30 13:14:29 +00:00
2021-02-01 14:13:53 +00:00
vars.receivedAmount = _swapExactTokensForTokens(
assetFrom,
assetTo,
vars.amountToSwap,
minAmountToReceive,
useEthPath
);
// Deposit new reserve
2021-01-14 14:21:42 +00:00
IERC20(assetTo).safeApprove(address(LENDING_POOL), 0);
IERC20(assetTo).safeApprove(address(LENDING_POOL), vars.receivedAmount);
LENDING_POOL.deposit(assetTo, vars.receivedAmount, initiator, 0);
2021-01-14 14:21:42 +00:00
vars.flashLoanDebt = amount.add(premium);
vars.amountToPull = vars.amountToSwap.add(premium);
2021-01-14 14:21:42 +00:00
_pullAToken(assetFrom, vars.aToken, initiator, vars.amountToPull, permitSignature);
2020-11-10 14:28:19 +00:00
2020-11-25 18:49:11 +00:00
// Repay flash loan
2021-01-14 14:21:42 +00:00
IERC20(assetFrom).safeApprove(address(LENDING_POOL), 0);
IERC20(assetFrom).safeApprove(address(LENDING_POOL), vars.flashLoanDebt);
}
2020-11-02 20:33:00 +00:00
/**
2020-11-25 18:49:11 +00:00
* @dev Decodes the information encoded in the flash loan params
2020-11-10 14:28:19 +00:00
* @param params Additional variadic field to include extra params. Expected parameters:
2020-11-02 20:33:00 +00:00
* address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited
* uint256[] minAmountsToReceive List of min amounts to be received from the swap
* bool[] swapAllBalance Flag indicating if all the user balance should be swapped
* uint256[] permitAmount List of amounts for the permit signature
2020-11-02 20:33:00 +00:00
* uint256[] deadline List of deadlines for the permit signature
* uint8[] v List of v param for the permit signature
* bytes32[] r List of r param for the permit signature
* bytes32[] s List of s param for the permit signature
* bool[] useEthPath true if the swap needs to occur using ETH in the routing, false otherwise
2020-11-10 14:28:19 +00:00
* @return SwapParams struct containing decoded params
*/
2020-11-03 18:37:06 +00:00
function _decodeParams(bytes memory params) internal pure returns (SwapParams memory) {
2020-11-02 20:33:00 +00:00
(
address[] memory assetToSwapToList,
uint256[] memory minAmountsToReceive,
bool[] memory swapAllBalance,
uint256[] memory permitAmount,
2020-11-02 20:33:00 +00:00
uint256[] memory deadline,
uint8[] memory v,
bytes32[] memory r,
bytes32[] memory s,
bool[] memory useEthPath
2020-11-30 13:14:29 +00:00
) =
abi.decode(
params,
(address[], uint256[], bool[], uint256[], uint256[], uint8[], bytes32[], bytes32[], bool[])
2020-11-30 13:14:29 +00:00
);
return
SwapParams(
assetToSwapToList,
minAmountsToReceive,
swapAllBalance,
PermitParams(permitAmount, deadline, v, r, s),
useEthPath
2020-11-30 13:14:29 +00:00
);
}
2021-02-01 14:13:53 +00:00
}