// SPDX-License-Identifier: GPL-2.0-or-later pragmasolidity=0.7.6; pragma abicoder v2; import'@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3FlashCallback.sol'; import'@uniswap/v3-core/contracts/libraries/LowGasSafeMath.sol'; import'@uniswap/v3-periphery/contracts/base/PeripheryPayments.sol'; import'@uniswap/v3-periphery/contracts/base/PeripheryImmutableState.sol'; import'@uniswap/v3-periphery/contracts/libraries/PoolAddress.sol'; import'@uniswap/v3-periphery/contracts/libraries/CallbackValidation.sol'; import'@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol'; import'@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol'; /// @title Flash contract implementation /// @notice An example contract using the Uniswap V3 flash function contractPairFlashis IUniswapV3FlashCallback, PeripheryImmutableState, PeripheryPayments { usingLowGasSafeMathforuint256; usingLowGasSafeMathforint256; ISwapRouter public immutable swapRouter; constructor( ISwapRouter _swapRouter, address _factory, address _WETH9 )PeripheryImmutableState(_factory, _WETH9){ swapRouter = _swapRouter; } /// @param fee0 The fee from calling flash for token0 /// @param fee1 The fee from calling flash for token1 /// @param data The data needed in the callback passed as FlashCallbackData from `initFlash` /// @notice implements the callback called from flash /// @dev fails if the flash is not profitable, meaning the amountOut from the flash is less than the amount borrowed functionuniswapV3FlashCallback( uint256 fee0, uint256 fee1, bytescalldata data )external override { FlashCallbackData memory decoded = abi.decode(data,(FlashCallbackData)); CallbackValidation.verifyCallback(factory, decoded.poolKey); address token0 = decoded.poolKey.token0; address token1 = decoded.poolKey.token1; TransferHelper.safeApprove(token0,address(swapRouter), decoded.amount0); TransferHelper.safeApprove(token1,address(swapRouter), decoded.amount1); // profitable check // exactInputSingle will fail if this amount not met uint256 amount1Min = LowGasSafeMath.add(decoded.amount1, fee1); uint256 amount0Min = LowGasSafeMath.add(decoded.amount0, fee0); // call exactInputSingle for swapping token1 for token0 in pool w/fee2 uint256 amountOut0 = swapRouter.exactInputSingle( ISwapRouter.ExactInputSingleParams({ tokenIn: token1, tokenOut: token0, fee: decoded.poolFee2, recipient:address(this), deadline: block.timestamp, amountIn: decoded.amount1, amountOutMinimum: amount0Min, sqrtPriceLimitX96:0 }) ); // call exactInputSingle for swapping token0 for token 1 in pool w/fee3 uint256 amountOut1 = swapRouter.exactInputSingle( ISwapRouter.ExactInputSingleParams({ tokenIn: token0, tokenOut: token1, fee: decoded.poolFee3, recipient:address(this), deadline: block.timestamp, amountIn: decoded.amount0, amountOutMinimum: amount1Min, sqrtPriceLimitX96:0 }) ); // end up with amountOut0 of token0 from first swap and amountOut1 of token1 from second swap uint256 amount0Owed = LowGasSafeMath.add(decoded.amount0, fee0); uint256 amount1Owed = LowGasSafeMath.add(decoded.amount1, fee1); TransferHelper.safeApprove(token0,address(this), amount0Owed); TransferHelper.safeApprove(token1,address(this), amount1Owed); if(amount0Owed >0)pay(token0,address(this), msg.sender, amount0Owed); if(amount1Owed >0)pay(token1,address(this), msg.sender, amount1Owed); // if profitable pay profits to payer if(amountOut0 > amount0Owed){ uint256 profit0 = LowGasSafeMath.sub(amountOut0, amount0Owed); TransferHelper.safeApprove(token0,address(this), profit0); pay(token0,address(this), decoded.payer, profit0); } if(amountOut1 > amount1Owed){ uint256 profit1 = LowGasSafeMath.sub(amountOut1, amount1Owed); TransferHelper.safeApprove(token0,address(this), profit1); pay(token1,address(this), decoded.payer, profit1); } } //fee1 is the fee of the pool from the initial borrow //fee2 is the fee of the first pool to arb from //fee3 is the fee of the second pool to arb from structFlashParams{ address token0; address token1; uint24 fee1; uint256 amount0; uint256 amount1; uint24 fee2; uint24 fee3; } // fee2 and fee3 are the two other fees associated with the two other pools of token0 and token1 structFlashCallbackData{ uint256 amount0; uint256 amount1; address payer; PoolAddress.PoolKey poolKey; uint24 poolFee2; uint24 poolFee3; } /// @param params The parameters necessary for flash and the callback, passed in as FlashParams /// @notice Calls the pools flash function with data needed in `uniswapV3FlashCallback` functioninitFlash(FlashParams memory params)external{ PoolAddress.PoolKey memory poolKey = PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee1}); IUniswapV3Pool pool =IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); // recipient of borrowed amounts // amount of token0 requested to borrow // amount of token1 requested to borrow // need amount 0 and amount1 in callback to pay back pool // recipient of flash should be THIS contract pool.flash( address(this), params.amount0, params.amount1, abi.encode( FlashCallbackData({ amount0: params.amount0, amount1: params.amount1, payer: msg.sender, poolKey: poolKey, poolFee2: params.fee2, poolFee3: params.fee3 }) ) ); } }