diff --git a/contracts/mainnet/connectors/uniswap/v3_auto_router/helpers.sol b/contracts/mainnet/connectors/uniswap/v3_auto_router/helpers.sol index 8edb4bb7..6a65c6d6 100644 --- a/contracts/mainnet/connectors/uniswap/v3_auto_router/helpers.sol +++ b/contracts/mainnet/connectors/uniswap/v3_auto_router/helpers.sol @@ -4,13 +4,16 @@ pragma abicoder v2; import {TokenInterface} from "../../../common/interfaces.sol"; import {DSMath} from "../../../common/math.sol"; import {Basic} from "../../../common/basic.sol"; +import {SqrtPriceMath} from "../../../../arbitrum/connectors/uniswap-sell-beta/libraries/SqrtPriceMath.sol"; import "./interface.sol"; import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; import "@uniswap/v3-core/contracts/libraries/TickMath.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol"; +import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol"; abstract contract Helpers is DSMath, Basic { + using SafeMath for uint; /** * @dev uniswap v3 NFT Position Manager & Swap Router */ @@ -18,15 +21,23 @@ abstract contract Helpers is DSMath, Basic { INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88); ISwapRouter internal constant swapRouter = ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); + IUniswapV3Factory internal constant factory = + IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984); bytes32 internal constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54; - address constant COMMON_ADDRESSES = [ + address[] COMMON_ADDRESSES = [ 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, // WETH - 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, // USDC + 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, // USDC 0xdAC17F958D2ee523a2206206994597C13D831ec7, // USDT - 0x6b175474e89094c44da98b954eedeac495271d0f, // DAI - ] + 0x6B175474E89094C44Da98b954EedeAC495271d0F // DAI + ]; + + struct PoolKey { + address token0; + address token1; + uint24 fee; + } struct MintParams { address tokenA; @@ -53,11 +64,11 @@ abstract contract Helpers is DSMath, Basic { } function getPrice(address tokenIn, address tokenOut, uint24 fee) - external + internal view returns (uint256 price) { - IUniswapV3Pool pool = IUniswapV3Pool(factory.getPool(tokenIn, tokenOut, fee); + IUniswapV3Pool pool = IUniswapV3Pool(factory.getPool(tokenIn, tokenOut, fee)); (uint160 sqrtPriceX96,,,,,,) = pool.slot0(); return uint(sqrtPriceX96).mul(uint(sqrtPriceX96)).mul(1e18) >> (96 * 2); } @@ -98,6 +109,28 @@ abstract contract Helpers is DSMath, Basic { ) ); } + + function getPriceLimit( + uint256 amountIn, + bool zeroForOne, + address tokenA, + address tokenB, + uint24 fee + ) internal view returns (uint160) { + UniswapV3Pool state = UniswapV3Pool( + getPoolAddress(tokenA, tokenB, fee) + ); + + return ( + SqrtPriceMath.getNextSqrtPriceFromInput( + state.slot0().sqrtPriceX96, + state.liquidity(), + amountIn, + zeroForOne + ) + ); + } + function getParams( address tokenIn, address tokenOut, diff --git a/contracts/mainnet/connectors/uniswap/v3_auto_router/interface.sol b/contracts/mainnet/connectors/uniswap/v3_auto_router/interface.sol index f8bc6e84..6ca9756b 100644 --- a/contracts/mainnet/connectors/uniswap/v3_auto_router/interface.sol +++ b/contracts/mainnet/connectors/uniswap/v3_auto_router/interface.sol @@ -366,3 +366,94 @@ interface INonfungiblePositionManager is /// @param tokenId The ID of the token that is being burned function burn(uint256 tokenId) external payable; } + +interface IUniswapV3Factory { + /// @notice Emitted when the owner of the factory is changed + /// @param oldOwner The owner before the owner was changed + /// @param newOwner The owner after the owner was changed + event OwnerChanged(address indexed oldOwner, address indexed newOwner); + + /// @notice Emitted when a pool is created + /// @param token0 The first token of the pool by address sort order + /// @param token1 The second token of the pool by address sort order + /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// @param tickSpacing The minimum number of ticks between initialized ticks + /// @param pool The address of the created pool + event PoolCreated( + address indexed token0, + address indexed token1, + uint24 indexed fee, + int24 tickSpacing, + address pool + ); + + /// @notice Emitted when a new fee amount is enabled for pool creation via the factory + /// @param fee The enabled fee, denominated in hundredths of a bip + /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee + event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing); + + /// @notice Returns the current owner of the factory + /// @dev Can be changed by the current owner via setOwner + /// @return The address of the factory owner + function owner() external view returns (address); + + /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled + /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context + /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee + /// @return The tick spacing + function feeAmountTickSpacing(uint24 fee) external view returns (int24); + + /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist + /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order + /// @param tokenA The contract address of either token0 or token1 + /// @param tokenB The contract address of the other token + /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// @return pool The pool address + function getPool( + address tokenA, + address tokenB, + uint24 fee + ) external view returns (address pool); + + /// @notice Creates a pool for the given two tokens and fee + /// @param tokenA One of the two tokens in the desired pool + /// @param tokenB The other of the two tokens in the desired pool + /// @param fee The desired fee for the pool + /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved + /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments + /// are invalid. + /// @return pool The address of the newly created pool + function createPool( + address tokenA, + address tokenB, + uint24 fee + ) external returns (address pool); + + /// @notice Updates the owner of the factory + /// @dev Must be called by the current owner + /// @param _owner The new owner of the factory + function setOwner(address _owner) external; + + /// @notice Enables a fee amount with the given tickSpacing + /// @dev Fee amounts may never be removed once enabled + /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6) + /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount + function enableFeeAmount(uint24 fee, int24 tickSpacing) external; +} + + +interface UniswapV3Pool { + struct Slot0 { + uint160 sqrtPriceX96; + int24 tick; + uint16 observationIndex; + uint16 observationCardinality; + uint16 observationCardinalityNext; + uint8 feeProtocol; + bool unlocked; + } + + function liquidity() external view returns (uint128); + + function slot0() external view returns (Slot0 memory); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/uniswap/v3_auto_router/main.sol b/contracts/mainnet/connectors/uniswap/v3_auto_router/main.sol index 320cc497..f42eccda 100644 --- a/contracts/mainnet/connectors/uniswap/v3_auto_router/main.sol +++ b/contracts/mainnet/connectors/uniswap/v3_auto_router/main.sol @@ -9,8 +9,12 @@ pragma abicoder v2; import {TokenInterface} from "../../../common/interfaces.sol"; import {Helpers} from "./helpers.sol"; import {Events} from "./events.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; abstract contract UniswapResolver is Helpers, Events { + using SafeERC20 for IERC20; + function swapExactTokensForTokens( address tokenIn, address tokenOut, @@ -61,7 +65,7 @@ abstract contract UniswapResolver is Helpers, Events { uint256 amountOut = swapSingleInput( getParams( COMMON_ADDRESSES[pathIndex], - tokenOut + tokenOut, recipient, fee, amountOut1,