From d611b1e8dc01459dfc2c30640179a8836fbef900 Mon Sep 17 00:00:00 2001 From: The3D Date: Wed, 2 Dec 2020 15:30:57 +0100 Subject: [PATCH] Updated to receive theindication on which path to use from outside --- contracts/adapters/BaseUniswapAdapter.sol | 52 ++++++++++++--- .../adapters/UniswapLiquiditySwapAdapter.sol | 63 ++++++++++++------- contracts/adapters/UniswapRepayAdapter.sol | 24 ++++--- 3 files changed, 103 insertions(+), 36 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index b0a71942..5f0851a7 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -131,7 +131,8 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt address assetToSwapFrom, address assetToSwapTo, uint256 amountToSwap, - uint256 minAmountOut + uint256 minAmountOut, + bool useEthPath ) internal returns (uint256) { uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); uint256 toAssetDecimals = _getDecimals(assetToSwapTo); @@ -149,9 +150,17 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt IERC20(assetToSwapFrom).approve(address(UNISWAP_ROUTER), amountToSwap); - address[] memory path = new address[](2); - path[0] = assetToSwapFrom; - path[1] = assetToSwapTo; + address[] memory path; + if (useEthPath) { + path = new address[](3); + path[0] = assetToSwapFrom; + path[1] = WETH_ADDRESS; + path[2] = assetToSwapTo; + } else { + path = new address[](2); + path[0] = assetToSwapFrom; + path[1] = assetToSwapTo; + } uint256[] memory amounts = UNISWAP_ROUTER.swapExactTokensForTokens( amountToSwap, @@ -374,7 +383,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt uint256 amountOut ) internal view returns (AmountCalc memory) { (uint256[] memory amounts, address[] memory path) = - _getAmountsIn(reserveIn, reserveOut, amountOut); + _getAmountsInAndPath(reserveIn, reserveOut, amountOut); // Add flash loan fee uint256 finalAmountIn = amounts[0].add(amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); @@ -404,7 +413,7 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt * @param amountOut Amount of reserveOut * @return uint256[] amounts Array containing the amountIn and amountOut for a swap */ - function _getAmountsIn( + function _getAmountsInAndPath( address reserveIn, address reserveOut, uint256 amountOut @@ -419,14 +428,43 @@ abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapt uint256[] memory resultAmounts ) { amounts = resultAmounts; + return (amounts, simplePath); } catch { pathWithWeth[0] = reserveIn; pathWithWeth[1] = WETH_ADDRESS; pathWithWeth[2] = reserveOut; amounts = UNISWAP_ROUTER.getAmountsIn(amountOut, pathWithWeth); + return (amounts, pathWithWeth); + } + } + + /** + * @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, + bool useEthPath + ) internal view returns (uint256[] memory) { + address[] memory path; + + if (useEthPath) { + path = new address[](3); + path[0] = reserveIn; + path[1] = WETH_ADDRESS; + path[2] = reserveOut; + } else { + path = new address[](2); + path[0] = reserveIn; + path[1] = reserveOut; } - return (amounts, (pathWithWeth[0] != address(0) ? pathWithWeth : simplePath)); + return UNISWAP_ROUTER.getAmountsIn(amountOut, path); } } diff --git a/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/contracts/adapters/UniswapLiquiditySwapAdapter.sol index 0adad441..b4d01550 100644 --- a/contracts/adapters/UniswapLiquiditySwapAdapter.sol +++ b/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -26,6 +26,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { uint256[] minAmountsToReceive; bool[] swapAllBalance; PermitParams permitParams; + bool[] useEthPath; } constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) @@ -71,7 +72,8 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { 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.permitParams.s.length && + assets.length == decodedParams.useEthPath.length, 'INCONSISTENT_PARAMS' ); @@ -90,13 +92,22 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { decodedParams.permitParams.v[i], decodedParams.permitParams.r[i], decodedParams.permitParams.s[i] - ) + ), + decodedParams.useEthPath[i] ); } return true; } + struct SwapAndDepositLocalVars { + uint256 i; + uint256 aTokenInitiatorBalance; + uint256 amountToSwap; + uint256 receivedAmount; + address aToken; + } + /** * @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 @@ -113,13 +124,15 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { * 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( address[] calldata assetToSwapFromList, address[] calldata assetToSwapToList, uint256[] calldata amountToSwapList, uint256[] calldata minAmountsToReceive, - PermitSignature[] calldata permitParams + PermitSignature[] calldata permitParams, + bool[] calldata useEthPath ) external { require( assetToSwapFromList.length == assetToSwapToList.length && @@ -129,26 +142,29 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { 'INCONSISTENT_PARAMS' ); - for (uint256 i = 0; i < assetToSwapFromList.length; i++) { - address aToken = _getReserveData(assetToSwapFromList[i]).aTokenAddress; + SwapAndDepositLocalVars memory vars; - uint256 aTokenInitiatorBalance = IERC20(aToken).balanceOf(msg.sender); - uint256 amountToSwap = - amountToSwapList[i] > aTokenInitiatorBalance ? aTokenInitiatorBalance : amountToSwapList[i]; + for (vars.i = 0; vars.i < assetToSwapFromList.length; vars.i++) { + vars.aToken = _getReserveData(assetToSwapFromList[vars.i]).aTokenAddress; - _pullAToken(assetToSwapFromList[i], aToken, msg.sender, amountToSwap, permitParams[i]); + vars.aTokenInitiatorBalance = IERC20(vars.aToken).balanceOf(msg.sender); + vars.amountToSwap = + amountToSwapList[vars.i] > vars.aTokenInitiatorBalance ? vars.aTokenInitiatorBalance : amountToSwapList[vars.i]; - uint256 receivedAmount = + _pullAToken(assetToSwapFromList[vars.i], vars.aToken, msg.sender, vars.amountToSwap, permitParams[vars.i]); + + vars.receivedAmount = _swapExactTokensForTokens( - assetToSwapFromList[i], - assetToSwapToList[i], - amountToSwap, - minAmountsToReceive[i] + assetToSwapFromList[vars.i], + assetToSwapToList[vars.i], + vars.amountToSwap, + minAmountsToReceive[vars.i], + useEthPath[vars.i] ); // Deposit new reserve - IERC20(assetToSwapToList[i]).approve(address(LENDING_POOL), receivedAmount); - LENDING_POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0); + IERC20(assetToSwapToList[vars.i]).approve(address(LENDING_POOL), vars.receivedAmount); + LENDING_POOL.deposit(assetToSwapToList[vars.i], vars.receivedAmount, msg.sender, 0); } } @@ -161,6 +177,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { * @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 */ function _swapLiquidity( address assetFrom, @@ -170,7 +187,8 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { address initiator, uint256 minAmountToReceive, bool swapAllBalance, - PermitSignature memory permitSignature + PermitSignature memory permitSignature, + bool useEthPath ) internal { address aToken = _getReserveData(assetFrom).aTokenAddress; @@ -181,7 +199,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { : amount; uint256 receivedAmount = - _swapExactTokensForTokens(assetFrom, assetTo, amountToSwap, minAmountToReceive); + _swapExactTokensForTokens(assetFrom, assetTo, amountToSwap, minAmountToReceive, useEthPath); // Deposit new reserve IERC20(assetTo).approve(address(LENDING_POOL), receivedAmount); @@ -207,6 +225,7 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { * 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 * @return SwapParams struct containing decoded params */ function _decodeParams(bytes memory params) internal pure returns (SwapParams memory) { @@ -218,11 +237,12 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { uint256[] memory deadline, uint8[] memory v, bytes32[] memory r, - bytes32[] memory s + bytes32[] memory s, + bool[] memory useEthPath ) = abi.decode( params, - (address[], uint256[], bool[], uint256[], uint256[], uint8[], bytes32[], bytes32[]) + (address[], uint256[], bool[], uint256[], uint256[], uint8[], bytes32[], bytes32[], bool[]) ); return @@ -230,7 +250,8 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { assetToSwapToList, minAmountsToReceive, swapAllBalance, - PermitParams(permitAmount, deadline, v, r, s) + PermitParams(permitAmount, deadline, v, r, s), + useEthPath ); } } diff --git a/contracts/adapters/UniswapRepayAdapter.sol b/contracts/adapters/UniswapRepayAdapter.sol index 2fa23d6d..9e725653 100644 --- a/contracts/adapters/UniswapRepayAdapter.sol +++ b/contracts/adapters/UniswapRepayAdapter.sol @@ -19,6 +19,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { uint256 collateralAmount; uint256 rateMode; PermitSignature permitSignature; + bool useEthPath; } constructor(ILendingPoolAddressesProvider addressesProvider, IUniswapV2Router02 uniswapRouter) @@ -65,7 +66,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { decodedParams.rateMode, initiator, premiums[0], - decodedParams.permitSignature + decodedParams.permitSignature, + decodedParams.useEthPath ); return true; @@ -82,6 +84,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { * @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 + * @param useEthPath struct containing the permit signature + */ function swapAndRepay( address collateralAsset, @@ -89,7 +93,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { uint256 collateralAmount, uint256 debtRepayAmount, uint256 debtRateMode, - PermitSignature calldata permitSignature + PermitSignature calldata permitSignature, + bool useEthPath ) external { DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); DataTypes.ReserveData memory debtReserveData = _getReserveData(debtAsset); @@ -109,7 +114,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { } // Get exact collateral needed for the swap to avoid leftovers - (uint256[] memory amounts,) = _getAmountsIn(collateralAsset, debtAsset, amountToRepay); + uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, amountToRepay, useEthPath); require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); // Pull aTokens from user @@ -159,7 +164,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { uint256 rateMode, address initiator, uint256 premium, - PermitSignature memory permitSignature + PermitSignature memory permitSignature, + bool useEthPath ) internal { DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); @@ -176,7 +182,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { } uint256 neededForFlashLoanDebt = repaidAmount.add(premium); - (uint256[] memory amounts,) = _getAmountsIn(collateralAsset, debtAsset, neededForFlashLoanDebt); + uint256[] memory amounts = _getAmountsIn(collateralAsset, debtAsset, neededForFlashLoanDebt, useEthPath); require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); // Pull aTokens from user @@ -227,15 +233,17 @@ contract UniswapRepayAdapter is BaseUniswapAdapter { uint256 deadline, uint8 v, bytes32 r, - bytes32 s - ) = abi.decode(params, (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32)); + bytes32 s, + bool useEthPath + ) = abi.decode(params, (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32, bool)); return RepayParams( collateralAsset, collateralAmount, rateMode, - PermitSignature(permitAmount, deadline, v, r, s) + PermitSignature(permitAmount, deadline, v, r, s), + useEthPath ); } }