mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Merge branch 'feat/uniswap-adapter-flashloan' into '178-add-uniswap-adapters'
Add Uniswap adapter for liquidity swap and repay with collateral using flashloan See merge request aave-tech/protocol-v2!106
This commit is contained in:
commit
a32d1ce404
367
contracts/adapters/BaseUniswapAdapter.sol
Normal file
367
contracts/adapters/BaseUniswapAdapter.sol
Normal file
|
@ -0,0 +1,367 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {PercentageMath} from '../protocol/libraries/math/PercentageMath.sol';
|
||||
import {SafeMath} from '../dependencies/openzeppelin/contracts/SafeMath.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 {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
|
||||
import {ILendingPool} from '../interfaces/ILendingPool.sol';
|
||||
import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
|
||||
import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol';
|
||||
import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
|
||||
import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol';
|
||||
|
||||
/**
|
||||
* @title BaseUniswapAdapter
|
||||
* @notice Implements the logic for performing assets swaps in Uniswap V2
|
||||
* @author Aave
|
||||
**/
|
||||
contract BaseUniswapAdapter {
|
||||
using SafeMath for uint256;
|
||||
using PercentageMath for uint256;
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
struct PermitSignature {
|
||||
uint256 amount;
|
||||
uint256 deadline;
|
||||
uint8 v;
|
||||
bytes32 r;
|
||||
bytes32 s;
|
||||
}
|
||||
|
||||
struct AmountCalc {
|
||||
uint256 calculatedAmount;
|
||||
uint256 relativePrice;
|
||||
uint256 amountInUsd;
|
||||
uint256 amountOutUsd;
|
||||
}
|
||||
|
||||
// Max slippage percent allowed
|
||||
uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30%
|
||||
// FLash Loan fee set in lending pool
|
||||
uint256 public constant FLASHLOAN_PREMIUM_TOTAL = 9;
|
||||
// USD oracle asset address
|
||||
address public constant USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96;
|
||||
|
||||
ILendingPool public immutable POOL;
|
||||
IPriceOracleGetter public immutable ORACLE;
|
||||
IUniswapV2Router02 public immutable UNISWAP_ROUTER;
|
||||
|
||||
event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount);
|
||||
|
||||
constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) public {
|
||||
POOL = ILendingPool(addressesProvider.getLendingPool());
|
||||
ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle());
|
||||
UNISWAP_ROUTER = uniswapRouter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Given an input asset amount, returns the maximum output amount of the other asset and the prices
|
||||
* @param amountIn Amount of reserveIn
|
||||
* @param reserveIn Address of the asset to be swap from
|
||||
* @param reserveOut Address of the asset to be swap to
|
||||
* @return uint256 Amount out of the reserveOut
|
||||
* @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals)
|
||||
* @return uint256 In amount of reserveIn value denominated in USD (8 decimals)
|
||||
* @return uint256 Out amount of reserveOut value denominated in USD (8 decimals)
|
||||
*/
|
||||
function getAmountsOut(uint256 amountIn, address reserveIn, address reserveOut)
|
||||
external
|
||||
view
|
||||
returns (uint256, uint256, uint256, uint256)
|
||||
{
|
||||
AmountCalc memory results = _getAmountsOutData(reserveIn, reserveOut, amountIn);
|
||||
|
||||
return (
|
||||
results.calculatedAmount,
|
||||
results.relativePrice,
|
||||
results.amountInUsd,
|
||||
results.amountOutUsd
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
AmountCalc memory results = _getAmountsInData(reserveIn, reserveOut, amountOut);
|
||||
|
||||
return (
|
||||
results.calculatedAmount,
|
||||
results.relativePrice,
|
||||
results.amountInUsd,
|
||||
results.amountOutUsd
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Swaps an exact `amountToSwap` of an asset to another
|
||||
* @param assetToSwapFrom Origin asset
|
||||
* @param assetToSwapTo Destination asset
|
||||
* @param amountToSwap Exact amount of `assetToSwapFrom` to be swapped
|
||||
* @param minAmountOut the min amount of `assetToSwapTo` to be received from the swap
|
||||
* @return the amount received from the swap
|
||||
*/
|
||||
function _swapExactTokensForTokens(
|
||||
address assetToSwapFrom,
|
||||
address assetToSwapTo,
|
||||
uint256 amountToSwap,
|
||||
uint256 minAmountOut
|
||||
)
|
||||
internal
|
||||
returns (uint256)
|
||||
{
|
||||
uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom);
|
||||
uint256 toAssetDecimals = _getDecimals(assetToSwapTo);
|
||||
|
||||
uint256 fromAssetPrice = _getPrice(assetToSwapFrom);
|
||||
uint256 toAssetPrice = _getPrice(assetToSwapTo);
|
||||
|
||||
uint256 expectedMinAmountOut = amountToSwap
|
||||
.mul(fromAssetPrice.mul(10**toAssetDecimals))
|
||||
.div(toAssetPrice.mul(10**fromAssetDecimals))
|
||||
.percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(MAX_SLIPPAGE_PERCENT));
|
||||
|
||||
require(expectedMinAmountOut < minAmountOut, 'minAmountOut exceed max slippage');
|
||||
|
||||
IERC20(assetToSwapFrom).approve(address(UNISWAP_ROUTER), amountToSwap);
|
||||
|
||||
address[] memory path = new address[](2);
|
||||
path[0] = assetToSwapFrom;
|
||||
path[1] = assetToSwapTo;
|
||||
uint256[] memory amounts = UNISWAP_ROUTER.swapExactTokensForTokens(amountToSwap, minAmountOut, path, address(this), block.timestamp);
|
||||
|
||||
emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]);
|
||||
|
||||
return amounts[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Receive an exact amount `amountToReceive` of `assetToSwapTo` tokens for as few `assetToSwapFrom` tokens as
|
||||
* possible.
|
||||
* @param assetToSwapFrom Origin asset
|
||||
* @param assetToSwapTo Destination asset
|
||||
* @param maxAmountToSwap Max amount of `assetToSwapFrom` allowed to be swapped
|
||||
* @param amountToReceive Exact amount of `assetToSwapTo` to receive
|
||||
* @return the amount swapped
|
||||
*/
|
||||
function _swapTokensForExactTokens(
|
||||
address assetToSwapFrom,
|
||||
address assetToSwapTo,
|
||||
uint256 maxAmountToSwap,
|
||||
uint256 amountToReceive
|
||||
)
|
||||
internal
|
||||
returns (uint256)
|
||||
{
|
||||
uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom);
|
||||
uint256 toAssetDecimals = _getDecimals(assetToSwapTo);
|
||||
|
||||
uint256 fromAssetPrice = _getPrice(assetToSwapFrom);
|
||||
uint256 toAssetPrice = _getPrice(assetToSwapTo);
|
||||
|
||||
uint256 expectedMaxAmountToSwap = amountToReceive
|
||||
.mul(toAssetPrice.mul(10**fromAssetDecimals))
|
||||
.div(fromAssetPrice.mul(10**toAssetDecimals))
|
||||
.percentMul(PercentageMath.PERCENTAGE_FACTOR.add(MAX_SLIPPAGE_PERCENT));
|
||||
|
||||
require(maxAmountToSwap < expectedMaxAmountToSwap, 'maxAmountToSwap exceed max slippage');
|
||||
|
||||
IERC20(assetToSwapFrom).approve(address(UNISWAP_ROUTER), maxAmountToSwap);
|
||||
|
||||
address[] memory path = new address[](2);
|
||||
path[0] = assetToSwapFrom;
|
||||
path[1] = assetToSwapTo;
|
||||
uint256[] memory amounts = UNISWAP_ROUTER.swapTokensForExactTokens(amountToReceive, maxAmountToSwap, path, address(this), block.timestamp);
|
||||
|
||||
emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[1]);
|
||||
|
||||
return amounts[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get the price of the asset from the oracle denominated in eth
|
||||
* @param asset address
|
||||
* @return eth price for the asset
|
||||
*/
|
||||
function _getPrice(address asset) internal view returns (uint256) {
|
||||
return ORACLE.getAssetPrice(asset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get the decimals of an asset
|
||||
* @return number of decimals of the asset
|
||||
*/
|
||||
function _getDecimals(address asset) internal view returns (uint256) {
|
||||
return IERC20Detailed(asset).decimals();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get the aToken associated to the asset
|
||||
* @return address of the aToken
|
||||
*/
|
||||
function _getReserveData(address asset) internal view returns (DataTypes.ReserveData memory) {
|
||||
return POOL.getReserveData(asset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Pull the ATokens from the user
|
||||
* @param reserve address of the asset
|
||||
* @param reserveAToken address of the aToken of the reserve
|
||||
* @param user address
|
||||
* @param amount of tokens to be transferred to the contract
|
||||
* @param permitSignature struct containing the permit signature
|
||||
*/
|
||||
function _pullAToken(
|
||||
address reserve,
|
||||
address reserveAToken,
|
||||
address user,
|
||||
uint256 amount,
|
||||
PermitSignature memory permitSignature
|
||||
) internal {
|
||||
if (_usePermit(permitSignature)) {
|
||||
IERC20WithPermit(reserveAToken).permit(
|
||||
user,
|
||||
address(this),
|
||||
permitSignature.amount,
|
||||
permitSignature.deadline,
|
||||
permitSignature.v,
|
||||
permitSignature.r,
|
||||
permitSignature.s
|
||||
);
|
||||
}
|
||||
|
||||
// transfer from user to adapter
|
||||
IERC20(reserveAToken).safeTransferFrom(user, address(this), amount);
|
||||
|
||||
// withdraw reserve
|
||||
POOL.withdraw(reserve, amount, address(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Tells if the permit method should be called by inspecting if there is a valid signature.
|
||||
* If signature params are set to 0, then permit won't be called.
|
||||
* @param signature struct containing the permit signature
|
||||
* @return whether or not permit should be called
|
||||
*/
|
||||
function _usePermit(PermitSignature memory signature) internal pure returns (bool) {
|
||||
return !(uint256(signature.deadline) == uint256(signature.v) && uint256(signature.deadline) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Calculates the value denominated in USD
|
||||
* @param reserve Address of the reserve
|
||||
* @param amount Amount of the reserve
|
||||
* @param decimals Decimals of the reserve
|
||||
* @return whether or not permit should be called
|
||||
*/
|
||||
function _calcUsdValue(address reserve, uint256 amount, uint256 decimals) internal view returns (uint256) {
|
||||
uint256 ethUsdPrice = _getPrice(USD_ADDRESS);
|
||||
uint256 reservePrice = _getPrice(reserve);
|
||||
|
||||
return amount
|
||||
.mul(reservePrice)
|
||||
.div(10**decimals)
|
||||
.mul(ethUsdPrice)
|
||||
.div(10**18);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Given an input asset amount, returns the maximum output amount of the other asset
|
||||
* @param reserveIn Address of the asset to be swap from
|
||||
* @param reserveOut Address of the asset to be swap to
|
||||
* @param amountIn Amount of reserveIn
|
||||
* @return Struct containing the following information:
|
||||
* uint256 Amount out of the reserveOut
|
||||
* uint256 The price of out amount denominated in the reserveIn currency (18 decimals)
|
||||
* uint256 In amount of reserveIn value denominated in USD (8 decimals)
|
||||
* uint256 Out amount of reserveOut value denominated in USD (8 decimals)
|
||||
*/
|
||||
function _getAmountsOutData(address reserveIn, address reserveOut, uint256 amountIn) internal view returns (AmountCalc memory) {
|
||||
// Subtract flash loan fee
|
||||
uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000));
|
||||
|
||||
address[] memory path = new address[](2);
|
||||
path[0] = reserveIn;
|
||||
path[1] = reserveOut;
|
||||
|
||||
uint256[] memory amounts = UNISWAP_ROUTER.getAmountsOut(finalAmountIn, path);
|
||||
|
||||
uint256 reserveInDecimals = _getDecimals(reserveIn);
|
||||
uint256 reserveOutDecimals = _getDecimals(reserveOut);
|
||||
|
||||
uint256 outPerInPrice = finalAmountIn
|
||||
.mul(10**18)
|
||||
.mul(10**reserveOutDecimals)
|
||||
.div(amounts[1].mul(10**reserveInDecimals));
|
||||
|
||||
return AmountCalc(
|
||||
amounts[1],
|
||||
outPerInPrice,
|
||||
_calcUsdValue(reserveIn, amountIn, reserveInDecimals),
|
||||
_calcUsdValue(reserveOut, amounts[1], reserveOutDecimals)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the minimum input asset amount required to buy the given output asset amount
|
||||
* @param reserveIn Address of the asset to be swap from
|
||||
* @param reserveOut Address of the asset to be swap to
|
||||
* @param amountOut Amount of reserveOut
|
||||
* @return Struct containing the following information:
|
||||
* uint256 Amount in of the reserveIn
|
||||
* uint256 The price of in amount denominated in the reserveOut currency (18 decimals)
|
||||
* uint256 In amount of reserveIn value denominated in USD (8 decimals)
|
||||
* uint256 Out amount of reserveOut value denominated in USD (8 decimals)
|
||||
*/
|
||||
function _getAmountsInData(address reserveIn, address reserveOut, uint256 amountOut) internal view returns (AmountCalc memory) {
|
||||
uint256[] memory amounts = _getAmountsIn(reserveIn, reserveOut, amountOut);
|
||||
|
||||
// Add flash loan fee
|
||||
uint256 finalAmountIn = amounts[0].add(amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000));
|
||||
|
||||
uint256 reserveInDecimals = _getDecimals(reserveIn);
|
||||
uint256 reserveOutDecimals = _getDecimals(reserveOut);
|
||||
|
||||
uint256 inPerOutPrice = amountOut
|
||||
.mul(10**18)
|
||||
.mul(10**reserveInDecimals)
|
||||
.div(finalAmountIn.mul(10**reserveOutDecimals));
|
||||
|
||||
return AmountCalc(
|
||||
finalAmountIn,
|
||||
inPerOutPrice,
|
||||
_calcUsdValue(reserveIn, finalAmountIn, reserveInDecimals),
|
||||
_calcUsdValue(reserveOut, amountOut, reserveOutDecimals)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Calculates the input asset amount required to buy the given output asset amount
|
||||
* @param reserveIn Address of the asset to be swap from
|
||||
* @param reserveOut Address of the asset to be swap to
|
||||
* @param amountOut Amount of reserveOut
|
||||
* @return uint256[] amounts Array containing the amountIn and amountOut for a swap
|
||||
*/
|
||||
function _getAmountsIn(address reserveIn, address reserveOut, uint256 amountOut) internal view returns (uint256[] memory) {
|
||||
address[] memory path = new address[](2);
|
||||
path[0] = reserveIn;
|
||||
path[1] = reserveOut;
|
||||
|
||||
return UNISWAP_ROUTER.getAmountsIn(amountOut, path);
|
||||
}
|
||||
}
|
249
contracts/adapters/UniswapLiquiditySwapAdapter.sol
Normal file
249
contracts/adapters/UniswapLiquiditySwapAdapter.sol
Normal file
|
@ -0,0 +1,249 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
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 {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol';
|
||||
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
|
||||
/**
|
||||
* @title UniswapLiquiditySwapAdapter
|
||||
* @notice Uniswap V2 Adapter to swap liquidity.
|
||||
* @author Aave
|
||||
**/
|
||||
contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
|
||||
|
||||
struct PermitParams {
|
||||
uint256[] amount;
|
||||
uint256[] deadline;
|
||||
uint8[] v;
|
||||
bytes32[] r;
|
||||
bytes32[] s;
|
||||
}
|
||||
|
||||
struct SwapParams {
|
||||
address[] assetToSwapToList;
|
||||
uint256[] minAmountsToReceive;
|
||||
bool[] swapAllBalance;
|
||||
PermitParams permitParams;
|
||||
}
|
||||
|
||||
constructor(
|
||||
ILendingPoolAddressesProvider addressesProvider,
|
||||
IUniswapV2Router02 uniswapRouter
|
||||
)
|
||||
public
|
||||
BaseUniswapAdapter(addressesProvider, uniswapRouter)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @param assets Address of asset to be swapped
|
||||
* @param amounts Amount of the asset to be swapped
|
||||
* @param premiums Fee of the flash loan
|
||||
* @param initiator Address of the user
|
||||
* @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 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
|
||||
* 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,
|
||||
address initiator,
|
||||
bytes calldata params
|
||||
) external override returns (bool) {
|
||||
require(msg.sender == address(POOL), "CALLER_MUST_BE_LENDING_POOL");
|
||||
|
||||
SwapParams memory decodedParams = _decodeParams(params);
|
||||
|
||||
require(
|
||||
assets.length == decodedParams.assetToSwapToList.length
|
||||
&& assets.length == decodedParams.minAmountsToReceive.length
|
||||
&& assets.length == decodedParams.swapAllBalance.length
|
||||
&& assets.length == decodedParams.permitParams.amount.length
|
||||
&& assets.length == decodedParams.permitParams.deadline.length
|
||||
&& assets.length == decodedParams.permitParams.v.length
|
||||
&& assets.length == decodedParams.permitParams.r.length
|
||||
&& assets.length == decodedParams.permitParams.s.length,
|
||||
'INCONSISTENT_PARAMS'
|
||||
);
|
||||
|
||||
for (uint256 i = 0; i < assets.length; i++) {
|
||||
_swapLiquidity(
|
||||
assets[i],
|
||||
decodedParams.assetToSwapToList[i],
|
||||
amounts[i],
|
||||
premiums[i],
|
||||
initiator,
|
||||
decodedParams.minAmountsToReceive[i],
|
||||
decodedParams.swapAllBalance[i],
|
||||
PermitSignature(
|
||||
decodedParams.permitParams.amount[i],
|
||||
decodedParams.permitParams.deadline[i],
|
||||
decodedParams.permitParams.v[i],
|
||||
decodedParams.permitParams.r[i],
|
||||
decodedParams.permitParams.s[i]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @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
|
||||
*/
|
||||
function swapAndDeposit(
|
||||
address[] calldata assetToSwapFromList,
|
||||
address[] calldata assetToSwapToList,
|
||||
uint256[] calldata amountToSwapList,
|
||||
uint256[] calldata minAmountsToReceive,
|
||||
PermitSignature[] calldata permitParams
|
||||
) external {
|
||||
require(
|
||||
assetToSwapFromList.length == assetToSwapToList.length
|
||||
&& assetToSwapFromList.length == amountToSwapList.length
|
||||
&& assetToSwapFromList.length == minAmountsToReceive.length
|
||||
&& assetToSwapFromList.length == permitParams.length,
|
||||
'INCONSISTENT_PARAMS'
|
||||
);
|
||||
|
||||
for (uint256 i = 0; i < assetToSwapFromList.length; i++) {
|
||||
address aToken = _getReserveData(assetToSwapFromList[i]).aTokenAddress;
|
||||
|
||||
uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(msg.sender);
|
||||
uint256 amountToSwap = amountToSwapList[i] > aTokenInitiatorBalance ? aTokenInitiatorBalance : amountToSwapList[i];
|
||||
|
||||
_pullAToken(
|
||||
assetToSwapFromList[i],
|
||||
aToken,
|
||||
msg.sender,
|
||||
amountToSwap,
|
||||
permitParams[i]
|
||||
);
|
||||
|
||||
uint256 receivedAmount = _swapExactTokensForTokens(
|
||||
assetToSwapFromList[i],
|
||||
assetToSwapToList[i],
|
||||
amountToSwap,
|
||||
minAmountsToReceive[i]
|
||||
);
|
||||
|
||||
// Deposit new reserve
|
||||
IERC20(assetToSwapToList[i]).approve(address(POOL), receivedAmount);
|
||||
POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
* @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
|
||||
*/
|
||||
function _swapLiquidity(
|
||||
address assetFrom,
|
||||
address assetTo,
|
||||
uint256 amount,
|
||||
uint256 premium,
|
||||
address initiator,
|
||||
uint256 minAmountToReceive,
|
||||
bool swapAllBalance,
|
||||
PermitSignature memory permitSignature
|
||||
) internal {
|
||||
address aToken = _getReserveData(assetFrom).aTokenAddress;
|
||||
|
||||
uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(initiator);
|
||||
uint256 amountToSwap = swapAllBalance && aTokenInitiatorBalance.sub(premium) <= amount
|
||||
? aTokenInitiatorBalance.sub(premium)
|
||||
: amount;
|
||||
|
||||
uint256 receivedAmount = _swapExactTokensForTokens(
|
||||
assetFrom,
|
||||
assetTo,
|
||||
amountToSwap,
|
||||
minAmountToReceive
|
||||
);
|
||||
|
||||
// Deposit new reserve
|
||||
IERC20(assetTo).approve(address(POOL), receivedAmount);
|
||||
POOL.deposit(assetTo, receivedAmount, initiator, 0);
|
||||
|
||||
uint256 flashLoanDebt = amount.add(premium);
|
||||
uint256 amountToPull = amountToSwap.add(premium);
|
||||
|
||||
_pullAToken(assetFrom, aToken, initiator, amountToPull, permitSignature);
|
||||
|
||||
// Repay flash loan
|
||||
IERC20(assetFrom).approve(address(POOL), flashLoanDebt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Decodes the information encoded in the flash loan 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 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
|
||||
* 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
|
||||
* @return SwapParams struct containing decoded params
|
||||
*/
|
||||
function _decodeParams(bytes memory params) internal pure returns (SwapParams memory) {
|
||||
(
|
||||
address[] memory assetToSwapToList,
|
||||
uint256[] memory minAmountsToReceive,
|
||||
bool[] memory swapAllBalance,
|
||||
uint256[] memory permitAmount,
|
||||
uint256[] memory deadline,
|
||||
uint8[] memory v,
|
||||
bytes32[] memory r,
|
||||
bytes32[] memory s
|
||||
) = abi.decode(params, (address[], uint256[], bool[], uint256[], uint256[], uint8[], bytes32[], bytes32[]));
|
||||
|
||||
return SwapParams(
|
||||
assetToSwapToList,
|
||||
minAmountsToReceive,
|
||||
swapAllBalance,
|
||||
PermitParams(
|
||||
permitAmount,
|
||||
deadline,
|
||||
v,
|
||||
r,
|
||||
s
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
239
contracts/adapters/UniswapRepayAdapter.sol
Normal file
239
contracts/adapters/UniswapRepayAdapter.sol
Normal file
|
@ -0,0 +1,239 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
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 {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol';
|
||||
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
|
||||
|
||||
/**
|
||||
* @title UniswapRepayAdapter
|
||||
* @notice Uniswap V2 Adapter to perform a repay of a debt with collateral.
|
||||
* @author Aave
|
||||
**/
|
||||
contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
|
||||
|
||||
struct RepayParams {
|
||||
address collateralAsset;
|
||||
uint256 collateralAmount;
|
||||
uint256 rateMode;
|
||||
PermitSignature permitSignature;
|
||||
}
|
||||
|
||||
constructor(
|
||||
ILendingPoolAddressesProvider addressesProvider,
|
||||
IUniswapV2Router02 uniswapRouter
|
||||
)
|
||||
public
|
||||
BaseUniswapAdapter(addressesProvider, uniswapRouter)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @dev Uses the received funds from the flash loan to repay a debt on the protocol on behalf of the user. Then pulls
|
||||
* the collateral from the user and swaps it to the debt asset to repay the flash loan.
|
||||
* The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset, swap it
|
||||
* and repay the flash loan.
|
||||
* Supports only one asset on the flash loan.
|
||||
* @param assets Address of debt asset
|
||||
* @param amounts Amount of the debt to be repaid
|
||||
* @param premiums Fee of the flash loan
|
||||
* @param initiator Address of the user
|
||||
* @param params Additional variadic field to include extra params. Expected parameters:
|
||||
* address collateralAsset Address of the reserve to be swapped
|
||||
* uint256 collateralAmount Amount of reserve to be swapped
|
||||
* uint256 rateMode Rate modes of the debt to be repaid
|
||||
* uint256 permitAmount Amount for the permit signature
|
||||
* uint256 deadline Deadline for the permit signature
|
||||
* uint8 v V param for the permit signature
|
||||
* bytes32 r R param for the permit signature
|
||||
* bytes32 s S param for the permit signature
|
||||
*/
|
||||
function executeOperation(
|
||||
address[] calldata assets,
|
||||
uint256[] calldata amounts,
|
||||
uint256[] calldata premiums,
|
||||
address initiator,
|
||||
bytes calldata params
|
||||
) external override returns (bool) {
|
||||
require(msg.sender == address(POOL), "CALLER_MUST_BE_LENDING_POOL");
|
||||
|
||||
RepayParams memory decodedParams = _decodeParams(params);
|
||||
|
||||
_swapAndRepay(
|
||||
decodedParams.collateralAsset,
|
||||
assets[0],
|
||||
amounts[0],
|
||||
decodedParams.collateralAmount,
|
||||
decodedParams.rateMode,
|
||||
initiator,
|
||||
premiums[0],
|
||||
decodedParams.permitSignature
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Swaps the user collateral for the debt asset and then repay the debt on the protocol on behalf of the user
|
||||
* without using flash loans. 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
|
||||
* @param collateralAsset Address of asset to be swapped
|
||||
* @param debtAsset Address of debt asset
|
||||
* @param collateralAmount Amount of the collateral to be swapped
|
||||
* @param debtRepayAmount Amount of the debt to be repaid
|
||||
* @param debtRateMode Rate mode of the debt to be repaid
|
||||
* @param permitSignature struct containing the permit signature
|
||||
*/
|
||||
function swapAndRepay(
|
||||
address collateralAsset,
|
||||
address debtAsset,
|
||||
uint256 collateralAmount,
|
||||
uint256 debtRepayAmount,
|
||||
uint256 debtRateMode,
|
||||
PermitSignature calldata permitSignature
|
||||
) external {
|
||||
DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset);
|
||||
DataTypes.ReserveData memory debtReserveData = _getReserveData(debtAsset);
|
||||
|
||||
address debtToken = DataTypes.InterestRateMode(debtRateMode) == DataTypes.InterestRateMode.STABLE
|
||||
? debtReserveData.stableDebtTokenAddress
|
||||
: debtReserveData.variableDebtTokenAddress;
|
||||
|
||||
uint256 currentDebt = IERC20(debtToken).balanceOf(msg.sender);
|
||||
uint256 amountToRepay = debtRepayAmount <= currentDebt ? debtRepayAmount : currentDebt;
|
||||
|
||||
if (collateralAsset != debtAsset) {
|
||||
uint256 maxCollateralToSwap = collateralAmount;
|
||||
if (amountToRepay < debtRepayAmount) {
|
||||
maxCollateralToSwap = maxCollateralToSwap.mul(amountToRepay).div(debtRepayAmount);
|
||||
}
|
||||
|
||||
// Get exact collateral needed for the swap to avoid leftovers
|
||||
uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, amountToRepay);
|
||||
require(amounts[0] <= maxCollateralToSwap, 'slippage too high');
|
||||
|
||||
// Pull aTokens from user
|
||||
_pullAToken(collateralAsset, collateralReserveData.aTokenAddress, msg.sender, amounts[0], permitSignature);
|
||||
|
||||
// Swap collateral for debt asset
|
||||
_swapTokensForExactTokens(collateralAsset, debtAsset, amounts[0], amountToRepay);
|
||||
} else {
|
||||
// Pull aTokens from user
|
||||
_pullAToken(collateralAsset, collateralReserveData.aTokenAddress, msg.sender, amountToRepay, permitSignature);
|
||||
}
|
||||
|
||||
// Repay debt
|
||||
IERC20(debtAsset).approve(address(POOL), amountToRepay);
|
||||
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
|
||||
*
|
||||
* @param collateralAsset Address of token to be swapped
|
||||
* @param debtAsset Address of debt token to be received from the swap
|
||||
* @param amount Amount of the debt to be repaid
|
||||
* @param collateralAmount Amount of the reserve to be swapped
|
||||
* @param rateMode Rate mode of the debt to be repaid
|
||||
* @param initiator Address of the user
|
||||
* @param premium Fee of the flash loan
|
||||
* @param permitSignature struct containing the permit signature
|
||||
*/
|
||||
function _swapAndRepay(
|
||||
address collateralAsset,
|
||||
address debtAsset,
|
||||
uint256 amount,
|
||||
uint256 collateralAmount,
|
||||
uint256 rateMode,
|
||||
address initiator,
|
||||
uint256 premium,
|
||||
PermitSignature memory permitSignature
|
||||
) internal {
|
||||
DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset);
|
||||
|
||||
// Repay debt
|
||||
IERC20(debtAsset).approve(address(POOL), amount);
|
||||
uint256 repaidAmount = IERC20(debtAsset).balanceOf(address(this));
|
||||
POOL.repay(debtAsset, amount, rateMode, initiator);
|
||||
repaidAmount = repaidAmount.sub(IERC20(debtAsset).balanceOf(address(this)));
|
||||
|
||||
if (collateralAsset != debtAsset) {
|
||||
uint256 maxCollateralToSwap = collateralAmount;
|
||||
if (repaidAmount < amount) {
|
||||
maxCollateralToSwap = maxCollateralToSwap.mul(repaidAmount).div(amount);
|
||||
}
|
||||
|
||||
uint256 neededForFlashLoanDebt = repaidAmount.add(premium);
|
||||
uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, neededForFlashLoanDebt);
|
||||
require(amounts[0] <= maxCollateralToSwap, 'slippage too high');
|
||||
|
||||
// Pull aTokens from user
|
||||
_pullAToken(
|
||||
collateralAsset,
|
||||
collateralReserveData.aTokenAddress,
|
||||
initiator,
|
||||
amounts[0],
|
||||
permitSignature
|
||||
);
|
||||
|
||||
// Swap collateral asset to the debt asset
|
||||
_swapTokensForExactTokens(collateralAsset, debtAsset, amounts[0], neededForFlashLoanDebt);
|
||||
} else {
|
||||
// Pull aTokens from user
|
||||
_pullAToken(
|
||||
collateralAsset,
|
||||
collateralReserveData.aTokenAddress,
|
||||
initiator,
|
||||
repaidAmount.add(premium),
|
||||
permitSignature
|
||||
);
|
||||
}
|
||||
|
||||
// Repay flash loan
|
||||
IERC20(debtAsset).approve(address(POOL), amount.add(premium));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Decodes debt information encoded in the flash loan params
|
||||
* @param params Additional variadic field to include extra params. Expected parameters:
|
||||
* address collateralAsset Address of the reserve to be swapped
|
||||
* uint256 collateralAmount Amount of reserve to be swapped
|
||||
* uint256 rateMode Rate modes of the debt to be repaid
|
||||
* uint256 permitAmount Amount for the permit signature
|
||||
* uint256 deadline Deadline for the permit signature
|
||||
* uint8 v V param for the permit signature
|
||||
* bytes32 r R param for the permit signature
|
||||
* bytes32 s S param for the permit signature
|
||||
* @return RepayParams struct containing decoded params
|
||||
*/
|
||||
function _decodeParams(bytes memory params) internal pure returns (RepayParams memory) {
|
||||
(
|
||||
address collateralAsset,
|
||||
uint256 collateralAmount,
|
||||
uint256 rateMode,
|
||||
uint256 permitAmount,
|
||||
uint256 deadline,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) = abi.decode(params, (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32));
|
||||
|
||||
return RepayParams(
|
||||
collateralAsset,
|
||||
collateralAmount,
|
||||
rateMode,
|
||||
PermitSignature(
|
||||
permitAmount,
|
||||
deadline,
|
||||
v,
|
||||
r,
|
||||
s
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
16
contracts/interfaces/IERC20WithPermit.sol
Normal file
16
contracts/interfaces/IERC20WithPermit.sol
Normal file
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
|
||||
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
|
||||
interface IERC20WithPermit is IERC20 {
|
||||
function permit(
|
||||
address owner,
|
||||
address spender,
|
||||
uint256 value,
|
||||
uint256 deadline,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) external;
|
||||
}
|
24
contracts/interfaces/IUniswapV2Router02.sol
Normal file
24
contracts/interfaces/IUniswapV2Router02.sol
Normal file
|
@ -0,0 +1,24 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
|
||||
interface IUniswapV2Router02 {
|
||||
function swapExactTokensForTokens(
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256[] memory amounts);
|
||||
|
||||
function swapTokensForExactTokens(
|
||||
uint amountOut,
|
||||
uint amountInMax,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint deadline
|
||||
) external returns (uint256[] memory amounts);
|
||||
|
||||
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
|
||||
|
||||
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
|
||||
}
|
82
contracts/mocks/swap/MockUniswapV2Router02.sol
Normal file
82
contracts/mocks/swap/MockUniswapV2Router02.sol
Normal file
|
@ -0,0 +1,82 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
|
||||
import {IUniswapV2Router02} from "../../interfaces/IUniswapV2Router02.sol";
|
||||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
||||
import {MintableERC20} from '../tokens/MintableERC20.sol';
|
||||
|
||||
contract MockUniswapV2Router02 is IUniswapV2Router02 {
|
||||
mapping(address => uint256) internal _amountToReturn;
|
||||
mapping(address => uint256) internal _amountToSwap;
|
||||
mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsIn;
|
||||
mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsOut;
|
||||
uint256 internal defaultMockValue;
|
||||
|
||||
function setAmountToReturn(address reserve, uint256 amount) public {
|
||||
_amountToReturn[reserve] = amount;
|
||||
}
|
||||
|
||||
function setAmountToSwap(address reserve, uint256 amount) public {
|
||||
_amountToSwap[reserve] = amount;
|
||||
}
|
||||
|
||||
function swapExactTokensForTokens(
|
||||
uint256 amountIn,
|
||||
uint256 /* amountOutMin */,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 /* deadline */
|
||||
) external override returns (uint256[] memory amounts) {
|
||||
IERC20(path[0]).transferFrom(msg.sender, address(this), amountIn);
|
||||
|
||||
MintableERC20(path[1]).mint(_amountToReturn[path[0]]);
|
||||
IERC20(path[1]).transfer(to, _amountToReturn[path[0]]);
|
||||
|
||||
amounts = new uint[](path.length);
|
||||
amounts[0] = amountIn;
|
||||
amounts[1] = _amountToReturn[path[0]];
|
||||
}
|
||||
|
||||
function swapTokensForExactTokens(
|
||||
uint amountOut,
|
||||
uint /* amountInMax */,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint /* deadline */
|
||||
) external override returns (uint256[] memory amounts) {
|
||||
IERC20(path[0]).transferFrom(msg.sender, address(this), _amountToSwap[path[0]]);
|
||||
|
||||
MintableERC20(path[1]).mint(amountOut);
|
||||
IERC20(path[1]).transfer(to, amountOut);
|
||||
|
||||
amounts = new uint[](path.length);
|
||||
amounts[0] = _amountToSwap[path[0]];
|
||||
amounts[1] = amountOut;
|
||||
}
|
||||
|
||||
function setAmountOut(uint amountIn, address reserveIn, address reserveOut, uint amountOut) public {
|
||||
_amountsOut[reserveIn][reserveOut][amountIn] = amountOut;
|
||||
}
|
||||
|
||||
function setAmountIn(uint amountOut, address reserveIn, address reserveOut, uint amountIn) public {
|
||||
_amountsIn[reserveIn][reserveOut][amountOut] = amountIn;
|
||||
}
|
||||
|
||||
function setDefaultMockValue(uint value) public {
|
||||
defaultMockValue = value;
|
||||
}
|
||||
|
||||
function getAmountsOut(uint amountIn, address[] calldata path) external view override returns (uint[] memory) {
|
||||
uint256[] memory amounts = new uint256[](2);
|
||||
amounts[0] = amountIn;
|
||||
amounts[1] = _amountsOut[path[0]][path[1]][amountIn] > 0 ? _amountsOut[path[0]][path[1]][amountIn] : defaultMockValue;
|
||||
return amounts;
|
||||
}
|
||||
|
||||
function getAmountsIn(uint amountOut, address[] calldata path) external view override returns (uint[] memory) {
|
||||
uint256[] memory amounts = new uint256[](2);
|
||||
amounts[0] = _amountsIn[path[0]][path[1]][amountOut] > 0 ? _amountsIn[path[0]][path[1]][amountOut] : defaultMockValue;
|
||||
amounts[1] = amountOut;
|
||||
return amounts;
|
||||
}
|
||||
}
|
|
@ -38,10 +38,13 @@ import {
|
|||
MockFlashLoanReceiverFactory,
|
||||
MockStableDebtTokenFactory,
|
||||
MockVariableDebtTokenFactory,
|
||||
MockUniswapV2Router02Factory,
|
||||
PriceOracleFactory,
|
||||
ReserveLogicFactory,
|
||||
SelfdestructTransferFactory,
|
||||
StableDebtTokenFactory,
|
||||
UniswapLiquiditySwapAdapterFactory,
|
||||
UniswapRepayAdapterFactory,
|
||||
VariableDebtTokenFactory,
|
||||
WalletBalanceProviderFactory,
|
||||
WETH9MockedFactory,
|
||||
|
@ -493,3 +496,33 @@ export const deploySelfdestructTransferMock = async (verify?: boolean) =>
|
|||
[],
|
||||
verify
|
||||
);
|
||||
|
||||
export const deployMockUniswapRouter = async (verify?: boolean) =>
|
||||
withSaveAndVerify(
|
||||
await new MockUniswapV2Router02Factory(await getFirstSigner()).deploy(),
|
||||
eContractid.MockUniswapV2Router02,
|
||||
[],
|
||||
verify
|
||||
);
|
||||
|
||||
export const deployUniswapLiquiditySwapAdapter = async (
|
||||
args: [tEthereumAddress, tEthereumAddress],
|
||||
verify?: boolean
|
||||
) =>
|
||||
withSaveAndVerify(
|
||||
await new UniswapLiquiditySwapAdapterFactory(await getFirstSigner()).deploy(...args),
|
||||
eContractid.UniswapLiquiditySwapAdapter,
|
||||
args,
|
||||
verify
|
||||
);
|
||||
|
||||
export const deployUniswapRepayAdapter = async (
|
||||
args: [tEthereumAddress, tEthereumAddress],
|
||||
verify?: boolean
|
||||
) =>
|
||||
withSaveAndVerify(
|
||||
await new UniswapRepayAdapterFactory(await getFirstSigner()).deploy(...args),
|
||||
eContractid.UniswapRepayAdapter,
|
||||
args,
|
||||
verify
|
||||
);
|
||||
|
|
|
@ -17,11 +17,14 @@ import {
|
|||
MockFlashLoanReceiverFactory,
|
||||
MockStableDebtTokenFactory,
|
||||
MockVariableDebtTokenFactory,
|
||||
MockUniswapV2Router02Factory,
|
||||
PriceOracleFactory,
|
||||
ReserveLogicFactory,
|
||||
SelfdestructTransferFactory,
|
||||
StableAndVariableTokensHelperFactory,
|
||||
StableDebtTokenFactory,
|
||||
UniswapLiquiditySwapAdapterFactory,
|
||||
UniswapRepayAdapterFactory,
|
||||
VariableDebtTokenFactory,
|
||||
WalletBalanceProviderFactory,
|
||||
WETH9MockedFactory,
|
||||
|
@ -328,3 +331,26 @@ export const getAaveOracle = async (address?: tEthereumAddress) =>
|
|||
address || (await getDb().get(`${eContractid.AaveOracle}.${DRE.network.name}`).value()).address,
|
||||
await getFirstSigner()
|
||||
);
|
||||
|
||||
export const getMockUniswapRouter = async (address?: tEthereumAddress) =>
|
||||
await MockUniswapV2Router02Factory.connect(
|
||||
address ||
|
||||
(await getDb().get(`${eContractid.MockUniswapV2Router02}.${DRE.network.name}`).value())
|
||||
.address,
|
||||
await getFirstSigner()
|
||||
);
|
||||
|
||||
export const getUniswapLiquiditySwapAdapter = async (address?: tEthereumAddress) =>
|
||||
await UniswapLiquiditySwapAdapterFactory.connect(
|
||||
address ||
|
||||
(await getDb().get(`${eContractid.UniswapLiquiditySwapAdapter}.${DRE.network.name}`).value())
|
||||
.address,
|
||||
await getFirstSigner()
|
||||
);
|
||||
|
||||
export const getUniswapRepayAdapter = async (address?: tEthereumAddress) =>
|
||||
await UniswapRepayAdapterFactory.connect(
|
||||
address ||
|
||||
(await getDb().get(`${eContractid.UniswapRepayAdapter}.${DRE.network.name}`).value()).address,
|
||||
await getFirstSigner()
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Contract, Signer, utils, ethers } from 'ethers';
|
||||
import { Contract, Signer, utils, ethers , BigNumberish} from 'ethers';
|
||||
import { signTypedData_v4 } from 'eth-sig-util';
|
||||
import { fromRpcSig, ECDSASignature } from 'ethereumjs-util';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
@ -232,3 +232,53 @@ export const getSignatureFromTypedData = (
|
|||
});
|
||||
return fromRpcSig(signature);
|
||||
};
|
||||
|
||||
export const buildLiquiditySwapParams = (
|
||||
assetToSwapToList: tEthereumAddress[],
|
||||
minAmountsToReceive: BigNumberish[],
|
||||
swapAllBalances: BigNumberish[],
|
||||
permitAmounts: BigNumberish[],
|
||||
deadlines: BigNumberish[],
|
||||
v: BigNumberish[],
|
||||
r: (string | Buffer)[],
|
||||
s: (string | Buffer)[]
|
||||
) => {
|
||||
return ethers.utils.defaultAbiCoder.encode(
|
||||
[
|
||||
'address[]',
|
||||
'uint256[]',
|
||||
'bool[]',
|
||||
'uint256[]',
|
||||
'uint256[]',
|
||||
'uint8[]',
|
||||
'bytes32[]',
|
||||
'bytes32[]',
|
||||
],
|
||||
[assetToSwapToList, minAmountsToReceive, swapAllBalances, permitAmounts, deadlines, v, r, s]
|
||||
);
|
||||
};
|
||||
|
||||
export const buildRepayAdapterParams = (
|
||||
collateralAsset: tEthereumAddress,
|
||||
collateralAmount: BigNumberish,
|
||||
rateMode: BigNumberish,
|
||||
permitAmount: BigNumberish,
|
||||
deadline: BigNumberish,
|
||||
v: BigNumberish,
|
||||
r: string | Buffer,
|
||||
s: string | Buffer
|
||||
) => {
|
||||
return ethers.utils.defaultAbiCoder.encode(
|
||||
[
|
||||
'address',
|
||||
'uint256',
|
||||
'uint256',
|
||||
'uint256',
|
||||
'uint256',
|
||||
'uint8',
|
||||
'bytes32',
|
||||
'bytes32',
|
||||
],
|
||||
[collateralAsset, collateralAmount, rateMode, permitAmount, deadline, v, r, s]
|
||||
);
|
||||
};
|
||||
|
|
|
@ -67,6 +67,9 @@ export enum eContractid {
|
|||
LendingPoolImpl = 'LendingPoolImpl',
|
||||
LendingPoolConfiguratorImpl = 'LendingPoolConfiguratorImpl',
|
||||
LendingPoolCollateralManagerImpl = 'LendingPoolCollateralManagerImpl',
|
||||
MockUniswapV2Router02 = 'MockUniswapV2Router02',
|
||||
UniswapLiquiditySwapAdapter = 'UniswapLiquiditySwapAdapter',
|
||||
UniswapRepayAdapter = 'UniswapRepayAdapter',
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -22,6 +22,9 @@ import {
|
|||
deployATokensAndRatesHelper,
|
||||
deployWETHGateway,
|
||||
deployWETHMocked,
|
||||
deployMockUniswapRouter,
|
||||
deployUniswapLiquiditySwapAdapter,
|
||||
deployUniswapRepayAdapter,
|
||||
} from '../helpers/contracts-deployments';
|
||||
import { Signer } from 'ethers';
|
||||
import { TokenContractId, eContractid, tEthereumAddress, AavePools } from '../helpers/types';
|
||||
|
@ -229,6 +232,24 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => {
|
|||
const mockFlashLoanReceiver = await deployMockFlashLoanReceiver(addressesProvider.address);
|
||||
await insertContractAddressInDb(eContractid.MockFlashLoanReceiver, mockFlashLoanReceiver.address);
|
||||
|
||||
const mockUniswapRouter = await deployMockUniswapRouter();
|
||||
await insertContractAddressInDb(eContractid.MockUniswapV2Router02, mockUniswapRouter.address);
|
||||
|
||||
const UniswapLiquiditySwapAdapter = await deployUniswapLiquiditySwapAdapter([
|
||||
addressesProvider.address,
|
||||
mockUniswapRouter.address,
|
||||
]);
|
||||
await insertContractAddressInDb(
|
||||
eContractid.UniswapLiquiditySwapAdapter,
|
||||
UniswapLiquiditySwapAdapter.address
|
||||
);
|
||||
|
||||
const UniswapRepayAdapter = await deployUniswapRepayAdapter([
|
||||
addressesProvider.address,
|
||||
mockUniswapRouter.address,
|
||||
]);
|
||||
await insertContractAddressInDb(eContractid.UniswapRepayAdapter, UniswapRepayAdapter.address);
|
||||
|
||||
await deployWalletBalancerProvider();
|
||||
|
||||
await deployWETHGateway([mockTokens.WETH.address, lendingPoolAddress]);
|
||||
|
|
|
@ -11,6 +11,8 @@ import {
|
|||
getLendingPoolAddressesProviderRegistry,
|
||||
getWETHMocked,
|
||||
getWETHGateway,
|
||||
getUniswapLiquiditySwapAdapter,
|
||||
getUniswapRepayAdapter,
|
||||
} from '../../helpers/contracts-getters';
|
||||
import { tEthereumAddress } from '../../helpers/types';
|
||||
import { LendingPool } from '../../types/LendingPool';
|
||||
|
@ -27,6 +29,8 @@ import { PriceOracle } from '../../types/PriceOracle';
|
|||
import { LendingPoolAddressesProvider } from '../../types/LendingPoolAddressesProvider';
|
||||
import { LendingPoolAddressesProviderRegistry } from '../../types/LendingPoolAddressesProviderRegistry';
|
||||
import { getEthersSigners } from '../../helpers/contracts-helpers';
|
||||
import { UniswapLiquiditySwapAdapter } from '../../types/UniswapLiquiditySwapAdapter';
|
||||
import { UniswapRepayAdapter } from '../../types/UniswapRepayAdapter';
|
||||
import { WETH9Mocked } from '../../types/WETH9Mocked';
|
||||
import { WETHGateway } from '../../types/WETHGateway';
|
||||
import { solidity } from 'ethereum-waffle';
|
||||
|
@ -53,6 +57,8 @@ export interface TestEnv {
|
|||
usdc: MintableERC20;
|
||||
aave: MintableERC20;
|
||||
addressesProvider: LendingPoolAddressesProvider;
|
||||
uniswapLiquiditySwapAdapter: UniswapLiquiditySwapAdapter;
|
||||
uniswapRepayAdapter: UniswapRepayAdapter;
|
||||
registry: LendingPoolAddressesProviderRegistry;
|
||||
wethGateway: WETHGateway;
|
||||
}
|
||||
|
@ -78,6 +84,8 @@ const testEnv: TestEnv = {
|
|||
usdc: {} as MintableERC20,
|
||||
aave: {} as MintableERC20,
|
||||
addressesProvider: {} as LendingPoolAddressesProvider,
|
||||
uniswapLiquiditySwapAdapter: {} as UniswapLiquiditySwapAdapter,
|
||||
uniswapRepayAdapter: {} as UniswapRepayAdapter,
|
||||
registry: {} as LendingPoolAddressesProviderRegistry,
|
||||
wethGateway: {} as WETHGateway,
|
||||
} as TestEnv;
|
||||
|
@ -133,6 +141,9 @@ export async function initializeMakeSuite() {
|
|||
testEnv.aave = await getMintableERC20(aaveAddress);
|
||||
testEnv.weth = await getWETHMocked(wethAddress);
|
||||
testEnv.wethGateway = await getWETHGateway();
|
||||
|
||||
testEnv.uniswapLiquiditySwapAdapter = await getUniswapLiquiditySwapAdapter();
|
||||
testEnv.uniswapRepayAdapter = await getUniswapRepayAdapter();
|
||||
}
|
||||
|
||||
export function makeSuite(name: string, tests: (testEnv: TestEnv) => void) {
|
||||
|
|
3334
test/uniswapAdapters.spec.ts
Normal file
3334
test/uniswapAdapters.spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user