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 }