From e5d37e1a8ce7fb28722d1795b183aea59c247562 Mon Sep 17 00:00:00 2001 From: Gerardo Nardelli Date: Tue, 27 Oct 2020 16:32:09 -0300 Subject: [PATCH] Add view method to estimate swaps outputs --- contracts/adapters/BaseUniswapAdapter.sol | 72 ++++++++++++++++--- contracts/interfaces/IUniswapV2Router02.sol | 4 ++ .../mocks/swap/MockUniswapV2Router02.sol | 28 +++++++- 3 files changed, 94 insertions(+), 10 deletions(-) diff --git a/contracts/adapters/BaseUniswapAdapter.sol b/contracts/adapters/BaseUniswapAdapter.sol index 79b55df2..eb70d2e4 100644 --- a/contracts/adapters/BaseUniswapAdapter.sol +++ b/contracts/adapters/BaseUniswapAdapter.sol @@ -42,6 +42,48 @@ contract BaseUniswapAdapter { uniswapRouter = _uniswapRouter; } + /** + * @dev Given an input asset amount, returns the maximum output amount of the other asset + * @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 amountOut + */ + function getAmountOut(uint256 amountIn, address reserveIn, address reserveOut) + public + view + returns (uint256) + { + address[] memory path = new address[](2); + path[0] = reserveIn; + path[1] = reserveOut; + + uint256[] memory amounts = uniswapRouter.getAmountsOut(amountIn, path); + + return amounts[1]; + } + + /** + * @dev Returns the minimum input asset amount required to buy the given output asset amount + * @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 amountIn + */ + function getAmountIn(uint256 amountOut, address reserveIn, address reserveOut) + public + view + returns (uint256) + { + address[] memory path = new address[](2); + path[0] = reserveIn; + path[1] = reserveOut; + + uint256[] memory amounts = uniswapRouter.getAmountsIn(amountOut, path); + + return amounts[0]; + } + /** * @dev Swaps an `amountToSwap` of an asset to another * @param assetToSwapFrom Origin asset @@ -191,7 +233,27 @@ contract BaseUniswapAdapter { } /** - * @dev Take action with the swap left overs as configured in the parameters + * @dev Pull the ATokens from the user + * @param reserve address of the asset + * @param user address + * @param amount of tokens to be transferred to the contract + */ + function pullAToken( + address reserve, + address user, + uint256 amount + ) internal { + address reserveAToken = getAToken(reserve); + + // transfer from user to adapter + IERC20(reserveAToken).safeTransferFrom(user, address(this), amount); + + // withdraw reserve + pool.withdraw(reserve, amount); + } + + /** + * @dev Pull the ATokens from the user and use them to repay the flashloan * @param reserve address of the asset * @param user address * @param flashLoanDebt need to be repaid @@ -201,13 +263,7 @@ contract BaseUniswapAdapter { address user, uint256 flashLoanDebt ) internal { - address reserveAToken = getAToken(reserve); - - // transfer from user to adapter - IERC20(reserveAToken).safeTransferFrom(user, address(this), flashLoanDebt); - - // withdraw reserve - pool.withdraw(reserve, flashLoanDebt); + pullAToken(reserve, user, flashLoanDebt); // Repay flashloan IERC20(reserve).approve(address(pool), flashLoanDebt); diff --git a/contracts/interfaces/IUniswapV2Router02.sol b/contracts/interfaces/IUniswapV2Router02.sol index cb04c269..6453e74a 100644 --- a/contracts/interfaces/IUniswapV2Router02.sol +++ b/contracts/interfaces/IUniswapV2Router02.sol @@ -17,4 +17,8 @@ interface IUniswapV2Router02 { 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); } diff --git a/contracts/mocks/swap/MockUniswapV2Router02.sol b/contracts/mocks/swap/MockUniswapV2Router02.sol index 64cb935f..ef4c22c4 100644 --- a/contracts/mocks/swap/MockUniswapV2Router02.sol +++ b/contracts/mocks/swap/MockUniswapV2Router02.sol @@ -8,6 +8,8 @@ import {MintableERC20} from '../tokens/MintableERC20.sol'; contract MockUniswapV2Router02 is IUniswapV2Router02 { uint256 internal _amountToReturn; uint256 internal _amountToSwap; + mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsIn; + mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsOut; function setAmountToReturn(uint256 amount) public { _amountToReturn = amount; @@ -35,8 +37,8 @@ contract MockUniswapV2Router02 is IUniswapV2Router02 { } function swapTokensForExactTokens( - uint amountOut, - uint amountInMax, + uint /* amountOut */, + uint /* amountInMax */, address[] calldata path, address to, uint /* deadline */ @@ -50,4 +52,26 @@ contract MockUniswapV2Router02 is IUniswapV2Router02 { amounts[0] = _amountToSwap; amounts[1] = _amountToReturn; } + + 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 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]; + 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]; + amounts[1] = amountOut; + return amounts; + } }