github.com/cwntr/go-defi@v0.0.0-20210629134751-07f9ec2f7e66/contracts/handlers/uniswap/UniswapSwapper.sol (about)

     1  pragma solidity ^0.5.0;
     2  pragma experimental ABIEncoderV2;
     3  
     4  import "./UniswapV2Interfaces.sol";
     5  import "./IUniswapV2Callee.sol";
     6  import "../HandlerBase.sol";
     7  import "../../IProxy.sol";
     8  
     9  contract UniswapFlashSwapper is HandlerBase, IUniswapV2Callee {
    10  
    11      enum SwapType {SimpleLoan, SimpleSwap, TriangularSwap}
    12  
    13      // CONSTANTS
    14      IUniswapV2Factory constant uniswapV2Factory = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f);
    15      address constant ETH = address(0);
    16  
    17      // ACCESS CONTROL
    18      // Only the `permissionedPairAddress` may call the `uniswapV2Call` function
    19      address permissionedPairAddress = address(1);
    20  
    21      // DEFAULT TOKENS
    22      address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    23      address constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    24  
    25      constructor() public {}
    26  
    27      // Fallback must be payable
    28      function() external payable {}
    29  
    30      // @notice Flash-borrows _amount of _tokenBorrow from a Uniswap V2 pair and repays using _tokenPay
    31      // @param _tokenBorrow The address of the token you want to flash-borrow, use 0x0 for ETH
    32      // @param _amount The amount of _tokenBorrow you will borrow
    33      // @param _tokenPay The address of the token you want to use to payback the flash-borrow, use 0x0 for ETH
    34      // @param _userData Data that will be passed to the `execute` function for the user
    35      // @dev Depending on your use case, you may want to add access controls to this function
    36      function startSwap(
    37          address _tokenBorrow, uint256 _amount, address _tokenPay, bytes calldata _userData
    38      ) external payable {
    39          bool isBorrowingEth;
    40          bool isPayingEth;
    41          address tokenBorrow = _tokenBorrow;
    42          address tokenPay = _tokenPay;
    43  
    44          if (tokenBorrow == ETH) {
    45              isBorrowingEth = true;
    46              tokenBorrow = WETH; // we'll borrow WETH from UniswapV2 but then unwrap it for the user
    47          }
    48          if (tokenPay == ETH) {
    49              isPayingEth = true;
    50              tokenPay = WETH; // we'll wrap the user's ETH before sending it back to UniswapV2
    51          }
    52  
    53          if (tokenBorrow == tokenPay) {
    54              simpleFlashLoan(tokenBorrow, _amount, isBorrowingEth, isPayingEth, _userData);
    55              return;
    56          } else if (tokenBorrow == WETH || tokenPay == WETH) {
    57              simpleFlashSwap(tokenBorrow, _amount, tokenPay, isBorrowingEth, isPayingEth, _userData);
    58              return;
    59          } else {
    60              traingularFlashSwap(tokenBorrow, _amount, tokenPay, _userData);
    61              return;
    62          }
    63  
    64      }
    65  
    66  
    67      // @notice Function is called by the Uniswap V2 pair's `swap` function
    68      function uniswapV2Call(address _sender, uint _amount0, uint _amount1, bytes calldata _data) external {
    69          // access control
    70          require(_sender == address(this), "only this contract may initiate");
    71  
    72          // decode data
    73          (
    74              SwapType _swapType,
    75              address _tokenBorrow,
    76              uint _amount,
    77              address _tokenPay,
    78              bool _isBorrowingEth,
    79              bool _isPayingEth,
    80              bytes memory _triangleData,
    81              bytes memory _userData
    82          ) = abi.decode(_data, (SwapType, address, uint, address, bool, bool, bytes, bytes));
    83  
    84          if (_swapType == SwapType.SimpleLoan) {
    85              simpleFlashLoanExecute(_tokenBorrow, _amount, msg.sender, _isBorrowingEth, _isPayingEth, _userData);
    86              return;
    87          } else if (_swapType == SwapType.SimpleSwap) {
    88              simpleFlashSwapExecute(
    89                  _tokenBorrow, _amount, _tokenPay, msg.sender, _isBorrowingEth, _isPayingEth, _userData);
    90              return;
    91          } else {
    92              traingularFlashSwapExecute(_tokenBorrow, _amount, _tokenPay, _triangleData, _userData);
    93          }
    94  
    95          // NOOP to silence compiler "unused parameter" warning
    96          if (false) {
    97              _amount0;
    98              _amount1;
    99          }
   100      }
   101  
   102      // @notice This function is used when the user repays with the same token they borrowed
   103      // @dev This initiates the flash borrow. See `simpleFlashLoanExecute` for the code that executes after the borrow.
   104      function simpleFlashLoan(
   105          address _tokenBorrow, uint256 _amount, bool _isBorrowingEth, bool _isPayingEth, bytes memory _userData
   106      ) private {
   107          address tokenOther = _tokenBorrow == WETH ? DAI : WETH;
   108          permissionedPairAddress = uniswapV2Factory.getPair(_tokenBorrow, tokenOther);
   109          address pairAddress = permissionedPairAddress; // gas efficiency
   110          require(pairAddress != address(0), "Requested _token is not available.");
   111          address token0 = IUniswapV2Pair(pairAddress).token0();
   112          address token1 = IUniswapV2Pair(pairAddress).token1();
   113          uint amount0Out = _tokenBorrow == token0 ? _amount : 0;
   114          uint amount1Out = _tokenBorrow == token1 ? _amount : 0;
   115          bytes memory data = abi.encode(
   116              SwapType.SimpleLoan,
   117              _tokenBorrow,
   118              _amount,
   119              _tokenBorrow,
   120              _isBorrowingEth,
   121              _isPayingEth,
   122              bytes(""),
   123              _userData
   124          ); // note _tokenBorrow == _tokenPay
   125          IUniswapV2Pair(pairAddress).swap(amount0Out, amount1Out, address(this), data);
   126      }
   127  
   128      // @notice This is the code that is executed after `simpleFlashLoan` initiated the flash-borrow
   129      // @dev When this code executes, this contract will hold the flash-borrowed _amount of _tokenBorrow
   130      function simpleFlashLoanExecute(
   131          address _tokenBorrow,
   132          uint _amount,
   133          address _pairAddress,
   134          bool _isBorrowingEth,
   135          bool _isPayingEth,
   136          bytes memory _userData
   137      ) private {
   138          // unwrap WETH if necessary
   139          if (_isBorrowingEth) {
   140              IWETH(WETH).withdraw(_amount);
   141          }
   142  
   143          // compute amount of tokens that need to be paid back
   144          uint fee = ((_amount * 3) / 997) + 1;
   145          uint amountToRepay = _amount + fee;
   146  
   147          // do whatever the user wants
   148          execute(_userData);
   149  
   150          // payback the loan
   151          // wrap the ETH if necessary
   152          if (_isPayingEth) {
   153              IWETH(WETH).deposit.value(amountToRepay)();
   154          }
   155          IERC20(_tokenBorrow).transfer(_pairAddress, amountToRepay);
   156      }
   157  
   158      // @notice This function is used when either the _tokenBorrow or _tokenPay is WETH or ETH
   159      // @dev Since ~all tokens trade against WETH (if they trade at all), we can use a single UniswapV2 pair to
   160      //     flash-borrow and repay with the requested tokens.
   161      // @dev This initiates the flash borrow. See `simpleFlashSwapExecute` for the code that executes after the borrow.
   162      function simpleFlashSwap(
   163          address _tokenBorrow,
   164          uint _amount,
   165          address _tokenPay,
   166          bool _isBorrowingEth,
   167          bool _isPayingEth,
   168          bytes memory _userData
   169      ) private {
   170          permissionedPairAddress = uniswapV2Factory.getPair(_tokenBorrow, _tokenPay);
   171          address pairAddress = permissionedPairAddress; // gas efficiency
   172          require(pairAddress != address(0), "Requested pair is not available.");
   173          address token0 = IUniswapV2Pair(pairAddress).token0();
   174          address token1 = IUniswapV2Pair(pairAddress).token1();
   175          uint amount0Out = _tokenBorrow == token0 ? _amount : 0;
   176          uint amount1Out = _tokenBorrow == token1 ? _amount : 0;
   177          bytes memory data = abi.encode(
   178              SwapType.SimpleSwap,
   179              _tokenBorrow,
   180              _amount,
   181              _tokenPay,
   182              _isBorrowingEth,
   183              _isPayingEth,
   184              bytes(""),
   185              _userData
   186          );
   187          IUniswapV2Pair(pairAddress).swap(amount0Out, amount1Out, address(this), data);
   188      }
   189  
   190      // @notice This is the code that is executed after `simpleFlashSwap` initiated the flash-borrow
   191      // @dev When this code executes, this contract will hold the flash-borrowed _amount of _tokenBorrow
   192      function simpleFlashSwapExecute(
   193          address _tokenBorrow,
   194          uint _amount,
   195          address _tokenPay,
   196          address _pairAddress,
   197          bool _isBorrowingEth,
   198          bool _isPayingEth,
   199          bytes memory _userData
   200      ) private {
   201          // unwrap WETH if necessary
   202          if (_isBorrowingEth) {
   203              IWETH(WETH).withdraw(_amount);
   204          }
   205  
   206          // compute the amount of _tokenPay that needs to be repaid
   207          address pairAddress = permissionedPairAddress; // gas efficiency
   208          uint pairBalanceTokenBorrow = IERC20(_tokenBorrow).balanceOf(pairAddress);
   209          uint pairBalanceTokenPay = IERC20(_tokenPay).balanceOf(pairAddress);
   210          uint amountToRepay = ((1000 * pairBalanceTokenPay * _amount) / (997 * pairBalanceTokenBorrow)) + 1;
   211  
   212          // do whatever the user wants
   213          execute(_userData);
   214  
   215          // payback loan
   216          // wrap ETH if necessary
   217          if (_isPayingEth) {
   218              IWETH(WETH).deposit.value(amountToRepay)();
   219          }
   220          IERC20(_tokenPay).transfer(_pairAddress, amountToRepay);
   221      }
   222  
   223      // @notice This function is used when neither the _tokenBorrow nor the _tokenPay is WETH
   224      // @dev Since it is unlikely that the _tokenBorrow/_tokenPay pair has more liquidaity than the _tokenBorrow/WETH and
   225      //      _tokenPay/WETH pairs, we do a triangular swap here. That is, we flash borrow WETH from the _tokenPay/WETH 
   226      //      pair, Then we swap that borrowed WETH for the desired _tokenBorrow via the _tokenBorrow/WETH pair. And 
   227      //      finally, we pay back the original flash-borrow using _tokenPay.
   228      // @dev This initiates the flash borrow. See `traingularFlashSwapExecute` for the code that executes after 
   229      //      the borrow.
   230      function traingularFlashSwap(
   231          address _tokenBorrow, uint _amount, address _tokenPay, bytes memory _userData
   232      ) private {
   233          address borrowPairAddress = uniswapV2Factory.getPair(_tokenBorrow, WETH);
   234          require(borrowPairAddress != address(0), "Requested borrow token is not available.");
   235  
   236          permissionedPairAddress = uniswapV2Factory.getPair(_tokenPay, WETH);
   237          address payPairAddress = permissionedPairAddress; // gas efficiency
   238          require(payPairAddress != address(0), "Requested pay token is not available.");
   239  
   240          // STEP 1: Compute how much WETH will be needed to get _amount of _tokenBorrow out of the _tokenBorrow/WETH pool
   241          uint pairBalanceTokenBorrowBefore = IERC20(_tokenBorrow).balanceOf(borrowPairAddress);
   242          require(pairBalanceTokenBorrowBefore >= _amount, "_amount is too big");
   243          uint pairBalanceTokenBorrowAfter = pairBalanceTokenBorrowBefore - _amount;
   244          uint pairBalanceWeth = IERC20(WETH).balanceOf(borrowPairAddress);
   245          uint amountOfWeth = ((1000 * pairBalanceWeth * _amount) / (997 * pairBalanceTokenBorrowAfter)) + 1;
   246  
   247          // using a helper function here to avoid "stack too deep" :(
   248          traingularFlashSwapHelper(
   249              _tokenBorrow, _amount, _tokenPay, borrowPairAddress, payPairAddress, amountOfWeth, _userData);
   250      }
   251  
   252      // @notice Helper function for `traingularFlashSwap` to avoid `stack too deep` errors
   253      function traingularFlashSwapHelper(
   254          address _tokenBorrow,
   255          uint _amount,
   256          address _tokenPay,
   257          address _borrowPairAddress,
   258          address _payPairAddress,
   259          uint _amountOfWeth,
   260          bytes memory _userData
   261      ) private returns (uint) {
   262          // Step 2: Flash-borrow _amountOfWeth WETH from the _tokenPay/WETH pool
   263          address token0 = IUniswapV2Pair(_payPairAddress).token0();
   264          address token1 = IUniswapV2Pair(_payPairAddress).token1();
   265          uint amount0Out = WETH == token0 ? _amountOfWeth : 0;
   266          uint amount1Out = WETH == token1 ? _amountOfWeth : 0;
   267          bytes memory triangleData = abi.encode(_borrowPairAddress, _amountOfWeth);
   268          bytes memory data = abi.encode(
   269              SwapType.TriangularSwap, _tokenBorrow, _amount, _tokenPay, false, false, triangleData, _userData);
   270          // initiate the flash swap from UniswapV2
   271          IUniswapV2Pair(_payPairAddress).swap(amount0Out, amount1Out, address(this), data);
   272      }
   273  
   274      // @notice This is the code that is executed after `traingularFlashSwap` initiated the flash-borrow
   275      // @dev When this code executes, this contract will hold the amount of WETH we need in order to get _amount
   276      //     _tokenBorrow from the _tokenBorrow/WETH pair.
   277      function traingularFlashSwapExecute(
   278          address _tokenBorrow,
   279          uint _amount,
   280          address _tokenPay,
   281          bytes memory _triangleData,
   282          bytes memory _userData
   283      ) private {
   284          // decode _triangleData
   285          (address _borrowPairAddress, uint _amountOfWeth) = abi.decode(_triangleData, (address, uint));
   286  
   287          // Step 3: Using a normal swap, trade that WETH for _tokenBorrow
   288          address token0 = IUniswapV2Pair(_borrowPairAddress).token0();
   289          address token1 = IUniswapV2Pair(_borrowPairAddress).token1();
   290          uint amount0Out = _tokenBorrow == token0 ? _amount : 0;
   291          uint amount1Out = _tokenBorrow == token1 ? _amount : 0;
   292          IERC20(WETH).transfer(_borrowPairAddress, _amountOfWeth); // send our flash-borrowed WETH to the pair
   293          IUniswapV2Pair(_borrowPairAddress).swap(amount0Out, amount1Out, address(this), bytes(""));
   294  
   295          // compute the amount of _tokenPay that needs to be repaid
   296          address payPairAddress = permissionedPairAddress; // gas efficiency
   297          uint pairBalanceWETH = IERC20(WETH).balanceOf(payPairAddress);
   298          uint pairBalanceTokenPay = IERC20(_tokenPay).balanceOf(payPairAddress);
   299          uint amountToRepay = ((1000 * pairBalanceTokenPay * _amountOfWeth) / (997 * pairBalanceWETH)) + 1;
   300  
   301          // Step 4: Do whatever the user wants (arb, liqudiation, etc)
   302          execute(_userData);
   303  
   304          // Step 5: Pay back the flash-borrow to the _tokenPay/WETH pool
   305          IERC20(_tokenPay).transfer(payPairAddress, amountToRepay);
   306      }
   307  
   308      // @notice This is where the user's custom logic goes
   309      // @dev When this function executes, this contract will hold _amount of _tokenBorrow
   310      // @dev It is important that, by the end of the execution of this function, this contract holds the necessary
   311      //     amount of the original _tokenPay needed to pay back the flash-loan.
   312      // @dev Paying back the flash-loan happens automatically by the calling function 
   313      //      -- do not pay back the loan in this function
   314      // @dev If you entered `0x0` for _tokenPay when you called `flashSwap`, then make sure this contract hols 
   315      // _amount ETH before this finishes executing
   316      // @dev User will override this function on the inheriting contract
   317      function execute(bytes memory _userData) internal {
   318          (address[] memory tos, bytes[] memory datas) = abi.decode(
   319              _userData,
   320              (address[], bytes[])
   321          );
   322          IProxy(address(this)).execs(tos, datas);
   323      }
   324  
   325  }