/* Copyright 2021 DODO ZOO. SPDX-License-Identifier: Apache-2.0 */ pragma solidity 0.6.9; pragma experimental ABIEncoderV2; import {IDODOAdapter} from "../intf/IDODOAdapter.sol"; import {IUniswapV3SwapCallback} from "../intf/IUniswapV3SwapCallBack.sol"; import {IUniV3} from "../intf/IUniV3.sol"; import {IERC20} from "../../intf/IERC20.sol"; import {SafeMath} from "../../lib/SafeMath.sol"; import {UniversalERC20} from "../lib/UniversalERC20.sol"; import {SafeERC20} from "../../lib/SafeERC20.sol"; import {TickMath} from '../../external/uniswap/TickMath.sol'; import {IWETH} from "../../intf/IWETH.sol"; import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; import {PoolAddress} from '../../external/uniswap/PoolAddress.sol'; // to adapter like dodo V1 contract UniV3Adapter is IDODOAdapter, IUniswapV3SwapCallback, InitializableOwnable { using SafeMath for uint; // ============ Storage ============ address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; address public immutable _WETH_; address public immutable _V3_FACTORY_; constructor ( address payable weth, address factory ) public { _WETH_ = weth; _V3_FACTORY_ = factory; initOwner(msg.sender); } function _uniV3Swap(address to, address pool, uint160 sqrtX96, bytes memory data) internal { (address fromToken, address toToken, uint24 fee) = abi.decode(data, (address, address, uint24)); uint256 sellAmount = IERC20(fromToken).balanceOf(address(this)); bool zeroForOne = fromToken < toToken; // swap IUniV3(pool).swap( to, zeroForOne, int256(sellAmount), sqrtX96 == 0 ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) : sqrtX96, data ); } function sellBase(address to, address pool, bytes memory moreInfo) external override { (uint160 sqrtX96, bytes memory data) = abi.decode(moreInfo, (uint160, bytes)); _uniV3Swap(to, pool, sqrtX96, data); } function sellQuote(address to, address pool, bytes memory moreInfo) external override { (uint160 sqrtX96, bytes memory data) = abi.decode(moreInfo, (uint160, bytes)); _uniV3Swap(to, pool, sqrtX96, data); } // for uniV3 callback function uniswapV3SwapCallback( int256 amount0Delta, int256 amount1Delta, bytes calldata _data ) external override { require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported (address tokenIn, address tokenOut, uint24 fee) = abi.decode(_data, (address, address, uint24)); // verifyCallback address poolAddress = PoolAddress.computeAddress(_V3_FACTORY_, PoolAddress.getPoolKey(tokenIn, tokenOut, fee)); require(msg.sender == poolAddress || msg.sender == _OWNER_, "not available call address"); (bool isExactInput, uint256 amountToPay) = amount0Delta > 0 ? (tokenIn < tokenOut, uint256(amount0Delta)) : (tokenOut < tokenIn, uint256(amount1Delta)); if (isExactInput) { pay(tokenIn, address(this), msg.sender, amountToPay); } else { tokenIn = tokenOut; // swap in/out because exact output swaps are reversed pay(tokenIn, address(this), msg.sender, amountToPay); } } /// @param token The token to pay /// @param payer The entity that must pay /// @param recipient The entity that will receive payment /// @param value The amount to pay function pay( address token, address payer, address recipient, uint256 value ) internal { if (token == _WETH_ && address(this).balance >= value) { // pay with WETH9 IWETH(_WETH_).deposit{value: value}(); // wrap only what is needed to pay SafeERC20.safeTransfer(IERC20(_WETH_), recipient, value); } else if (payer == address(this)) { // pay with tokens already in the contract (for the exact input multihop case) SafeERC20.safeTransfer(IERC20(token), recipient, value); } else { // pull payment SafeERC20.safeTransferFrom(IERC20(token), payer, recipient, value); } } }