From fab8febc367c49b611dc8a40ccb8604ef1167a98 Mon Sep 17 00:00:00 2001 From: owen05 Date: Fri, 10 Sep 2021 19:08:25 +0800 Subject: [PATCH] add uniV3RouteAdapter --- .../GeneralizedFragment/impl/Fragment.sol | 3 +- contracts/SmartRoute/adapter/UniV3Adapter.sol | 108 ++++++++++ contracts/SmartRoute/intf/IUniV3.sol | 12 ++ .../intf/IUniswapV3SwapCallBack.sol | 20 ++ contracts/external/uniswap/TickMath.sol | 204 ++++++++++++++++++ 5 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 contracts/SmartRoute/adapter/UniV3Adapter.sol create mode 100644 contracts/SmartRoute/intf/IUniV3.sol create mode 100644 contracts/SmartRoute/intf/IUniswapV3SwapCallBack.sol create mode 100644 contracts/external/uniswap/TickMath.sol diff --git a/contracts/GeneralizedFragment/impl/Fragment.sol b/contracts/GeneralizedFragment/impl/Fragment.sol index 79e62f5..61fe41a 100644 --- a/contracts/GeneralizedFragment/impl/Fragment.sol +++ b/contracts/GeneralizedFragment/impl/Fragment.sol @@ -76,7 +76,8 @@ contract Fragment is InitializableFragERC20 { // init FRAG meta data name = string(abi.encodePacked("DODO_FRAG_", _symbol)); - symbol = string(abi.encodePacked("d_", _symbol)); + // symbol = string(abi.encodePacked("d_", _symbol)); + symbol = _symbol; super.init(address(this), _totalSupply, name, symbol); // init FRAG distribution diff --git a/contracts/SmartRoute/adapter/UniV3Adapter.sol b/contracts/SmartRoute/adapter/UniV3Adapter.sol new file mode 100644 index 0000000..9434c14 --- /dev/null +++ b/contracts/SmartRoute/adapter/UniV3Adapter.sol @@ -0,0 +1,108 @@ +/* + 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"; + +// to adapter like dodo V1 +contract UniV3Adapter is IDODOAdapter, IUniswapV3SwapCallback { + using SafeMath for uint; + + // ============ Storage ============ + + address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public immutable _WETH_; + + constructor ( + address payable weth + ) public { + _WETH_ = weth; + } + + 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; + + // transfer + //IERC20(fromToken).transfer(pool, sellAmount); + // 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)); + + (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 + IWETH(_WETH_).transfer(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); + } + } +} diff --git a/contracts/SmartRoute/intf/IUniV3.sol b/contracts/SmartRoute/intf/IUniV3.sol new file mode 100644 index 0000000..3588c62 --- /dev/null +++ b/contracts/SmartRoute/intf/IUniV3.sol @@ -0,0 +1,12 @@ +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +interface IUniV3 { + function swap( + address recipient, + bool zeroForOne, + int256 amountSpecified, + uint160 sqrtPriceLimitX96, + bytes calldata data + ) external returns (int256 amount0, int256 amount1); +} \ No newline at end of file diff --git a/contracts/SmartRoute/intf/IUniswapV3SwapCallBack.sol b/contracts/SmartRoute/intf/IUniswapV3SwapCallBack.sol new file mode 100644 index 0000000..a76ebda --- /dev/null +++ b/contracts/SmartRoute/intf/IUniswapV3SwapCallBack.sol @@ -0,0 +1,20 @@ +pragma solidity 0.6.9; + +/// @title Callback for IUniswapV3PoolActions#swap +/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface +interface IUniswapV3SwapCallback { + /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. + /// @dev In the implementation you must pay the pool tokens owed for the swap. + /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. + /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by + /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. + /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by + /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. + /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external; +} \ No newline at end of file diff --git a/contracts/external/uniswap/TickMath.sol b/contracts/external/uniswap/TickMath.sol new file mode 100644 index 0000000..e0e1189 --- /dev/null +++ b/contracts/external/uniswap/TickMath.sol @@ -0,0 +1,204 @@ +pragma solidity 0.6.9; + +/// @title Math library for computing sqrt prices from ticks and vice versa +/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports +/// prices between 2**-128 and 2**128 +library TickMath { + /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128 + int24 internal constant MIN_TICK = -887272; + /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128 + int24 internal constant MAX_TICK = -MIN_TICK; + + /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK) + uint160 internal constant MIN_SQRT_RATIO = 4295128739; + /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) + uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; + + /// @notice Calculates sqrt(1.0001^tick) * 2^96 + /// @dev Throws if |tick| > max tick + /// @param tick The input tick for the above formula + /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0) + /// at the given tick + function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) { + uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick)); + require(absTick <= uint256(MAX_TICK), 'T'); + + uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000; + if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128; + if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; + if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; + if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128; + if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128; + if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128; + if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; + if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; + if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128; + if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; + if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; + if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; + if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; + if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; + if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128; + if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; + if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128; + if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128; + if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128; + + if (tick > 0) ratio = type(uint256).max / ratio; + + // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96. + // we then downcast because we know the result always fits within 160 bits due to our tick input constraint + // we round up in the division so getTickAtSqrtRatio of the output price is always consistent + sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)); + } + + /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio + /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may + /// ever return. + /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96 + /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio + function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) { + // second inequality must be < because the price can never reach the price at the max tick + require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R'); + uint256 ratio = uint256(sqrtPriceX96) << 32; + + uint256 r = ratio; + uint256 msb = 0; + + assembly { + let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(5, gt(r, 0xFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(4, gt(r, 0xFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(3, gt(r, 0xFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(2, gt(r, 0xF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(1, gt(r, 0x3)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := gt(r, 0x1) + msb := or(msb, f) + } + + if (msb >= 128) r = ratio >> (msb - 127); + else r = ratio << (127 - msb); + + int256 log_2 = (int256(msb) - 128) << 64; + + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(63, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(62, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(61, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(60, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(59, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(58, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(57, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(56, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(55, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(54, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(53, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(52, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(51, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(50, f)) + } + + int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number + + int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128); + int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128); + + tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow; + } +} \ No newline at end of file