From 9fc096d0910aee3117cf878657f9abc14e1f91ef Mon Sep 17 00:00:00 2001 From: Noah Zinsmeister Date: Fri, 16 Apr 2021 17:35:50 -0400 Subject: [PATCH] Finish migration (#42) * start migration (wip) abstract some add liquidity components bump deploy version * add slippage params --- package.json | 4 +- public/locales/en.json | 1 - src/constants/v3/index.ts | 36 +-- src/hooks/useContract.ts | 14 +- src/pages/AddLiquidity/index.tsx | 178 +++++++------ src/pages/MigrateV2/MigrateV2Pair.tsx | 343 +++++++++++++++----------- yarn.lock | 12 +- 7 files changed, 348 insertions(+), 240 deletions(-) diff --git a/package.json b/package.json index 6ba9eedf70..c2b7ad8ff1 100644 --- a/package.json +++ b/package.json @@ -48,8 +48,8 @@ "@uniswap/v2-core": "1.0.0", "@uniswap/v2-periphery": "^1.1.0-beta.0", "@uniswap/v2-sdk": "^1.0.6", - "@uniswap/v3-core": "^1.0.0-rc.0", - "@uniswap/v3-periphery": "^1.0.0-beta.17", + "@uniswap/v3-core": "^1.0.0-rc.1", + "@uniswap/v3-periphery": "^1.0.0-beta.19", "@uniswap/v3-sdk": "^1.0.0-alpha.11", "@web3-react/core": "^6.0.9", "@web3-react/fortmatic-connector": "^6.0.9", diff --git a/public/locales/en.json b/public/locales/en.json index 0c706785df..331cbc9fec 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -109,7 +109,6 @@ "poolType": "Select a fee tier based on your preferred liquidity provider fee.", "rangeWarning": "Your liquidity will only be active and earning fees when the rate of the pool is within this price range.", "chooseLiquidityAmount": "Choose an amount of tokens to open this liquidity position. If you don’t have enough tokens you can trade for them with a Swap.", - "selectRange": "Select Liquidity Range", "inputTokenDynamic": "Input {{label}}", "selectStartingPrice": "Select Starting Price", "newPoolPrice": "Select the market rate for the tokens being added." diff --git a/src/constants/v3/index.ts b/src/constants/v3/index.ts index ac11f33584..58576efb7d 100644 --- a/src/constants/v3/index.ts +++ b/src/constants/v3/index.ts @@ -2,48 +2,48 @@ import { ChainId } from '@uniswap/sdk-core' export const FACTORY_ADDRESSES: { [chainId in ChainId]: string } = { [ChainId.MAINNET]: '', - [ChainId.ROPSTEN]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62', - [ChainId.RINKEBY]: '0xD8A6adFB40Ba3B3CdA9F985BF1fdbDc0c1d7591e', - [ChainId.GÖRLI]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62', + [ChainId.ROPSTEN]: '0x5BbFe6FF864718cD1cE0F126be99e96239E3caDD', + [ChainId.RINKEBY]: '0x7ba6C6345E7a73cC0D41d762C7Db9cb3DB721396', + [ChainId.GÖRLI]: '0x5BbFe6FF864718cD1cE0F126be99e96239E3caDD', [ChainId.KOVAN]: '', } export const TICK_LENS_ADDRESSES: { [chainId in ChainId]: string } = { [ChainId.MAINNET]: '', - [ChainId.ROPSTEN]: '0x8E984b597F19E8D0FDd0b5bAfDb1d0ae4386455f', - [ChainId.RINKEBY]: '0xB1c59e8Ae4B72f63a5a9CB9c25A9253096A4b126', - [ChainId.GÖRLI]: '0x8E984b597F19E8D0FDd0b5bAfDb1d0ae4386455f', + [ChainId.ROPSTEN]: '0x1C8beBE5596b60A84e6d737229aDd502E14276Eb', + [ChainId.RINKEBY]: '0xd4013a706fa79487989b595Df35eF8AD1ffBb422', + [ChainId.GÖRLI]: '0x1C8beBE5596b60A84e6d737229aDd502E14276Eb', [ChainId.KOVAN]: '', } export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: { [chainId in ChainId]: string } = { [ChainId.MAINNET]: '', - [ChainId.ROPSTEN]: '0x29e4bF3bFD649b807B4C752c01023E535094F6Bc', - [ChainId.RINKEBY]: '0xee9e30637f84Bbf929042A9118c6E20023dab833', - [ChainId.GÖRLI]: '0x29e4bF3bFD649b807B4C752c01023E535094F6Bc', + [ChainId.ROPSTEN]: '0x921647f0c094e2e59CDE6DEfafD77743012f52bd', + [ChainId.RINKEBY]: '0x30Ba713F78Ad3c175a25aD767e3f423549Ac2D65', + [ChainId.GÖRLI]: '0x921647f0c094e2e59CDE6DEfafD77743012f52bd', [ChainId.KOVAN]: '', } export const NONFUNGIBLE_TOKEN_POSITION_DESCRIPTOR_ADDRESSES: { [chainId in ChainId]: string } = { [ChainId.MAINNET]: '', - [ChainId.ROPSTEN]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f', - [ChainId.RINKEBY]: '0x3431b9Ed12e3204bC6f7039e1c576417B70fdD67', - [ChainId.GÖRLI]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f', + [ChainId.ROPSTEN]: '0xD2AAa0217a203d9FaB6e5272b211Be2Aba52f385', + [ChainId.RINKEBY]: '0xAc03019C975F5e79215FeDAB4a1DC30Af3E478F2', + [ChainId.GÖRLI]: '0xD2AAa0217a203d9FaB6e5272b211Be2Aba52f385', [ChainId.KOVAN]: '', } export const SWAP_ROUTER_ADDRESSES: { [chainId in ChainId]: string } = { [ChainId.MAINNET]: '', - [ChainId.ROPSTEN]: '0x71bB3d0e63f2Fa2A5d04d54267211f4Caef7062e', - [ChainId.RINKEBY]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f', - [ChainId.GÖRLI]: '0x71bB3d0e63f2Fa2A5d04d54267211f4Caef7062e', + [ChainId.ROPSTEN]: '0xDD1B8aA26ac2330e39f8B291eA1E6a82A40E65C4', + [ChainId.RINKEBY]: '0xD2AAa0217a203d9FaB6e5272b211Be2Aba52f385', + [ChainId.GÖRLI]: '0xDD1B8aA26ac2330e39f8B291eA1E6a82A40E65C4', [ChainId.KOVAN]: '', } export const V2_MIGRATOR_ADDRESSES: { [chainId in ChainId]: string } = { [ChainId.MAINNET]: '', - [ChainId.ROPSTEN]: '', - [ChainId.RINKEBY]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62', - [ChainId.GÖRLI]: '0xee9e30637f84Bbf929042A9118c6E20023dab833', + [ChainId.ROPSTEN]: '0x30Ba713F78Ad3c175a25aD767e3f423549Ac2D65', + [ChainId.RINKEBY]: '0x864e344eCd7f3a9A4368dEC11Be8104db5770364', + [ChainId.GÖRLI]: '0x30Ba713F78Ad3c175a25aD767e3f423549Ac2D65', [ChainId.KOVAN]: '', } diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts index 1aec379118..ccf758b1eb 100644 --- a/src/hooks/useContract.ts +++ b/src/hooks/useContract.ts @@ -7,6 +7,7 @@ import { ChainId, WETH9 } from '@uniswap/sdk-core' import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json' import { abi as V3FactoryABI } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json' import { abi as V3PoolABI } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json' +import { abi as V2MigratorABI } from '@uniswap/v3-periphery/artifacts/contracts/V3Migrator.sol/V3Migrator.json' import { abi as TickLensABI } from '@uniswap/v3-periphery/artifacts/contracts/lens/TickLens.sol/TickLens.json' import ARGENT_WALLET_DETECTOR_ABI from 'abis/argent-wallet-detector.json' @@ -29,10 +30,16 @@ import { } from 'constants/index' import { abi as NFTPositionManagerABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json' import { V1_EXCHANGE_ABI, V1_FACTORY_ABI, V1_FACTORY_ADDRESSES } from 'constants/v1' -import { FACTORY_ADDRESSES, NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, TICK_LENS_ADDRESSES } from 'constants/v3' +import { + NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, + FACTORY_ADDRESSES, + TICK_LENS_ADDRESSES, + V2_MIGRATOR_ADDRESSES, +} from 'constants/v3' import { useMemo } from 'react' import { TickLens, UniswapV3Factory, UniswapV3Pool } from 'types/v3' import { NonfungiblePositionManager } from 'types/v3/NonfungiblePositionManager' +import { V3Migrator } from 'types/v3/V3Migrator' import { getContract } from 'utils' import { useActiveWeb3React } from './index' @@ -60,6 +67,11 @@ export function useV1MigratorContract(): Contract | null { return useContract(V1_MIGRATOR_ADDRESS, MIGRATOR_ABI, true) } +export function useV2MigratorContract(): V3Migrator | null { + const { chainId } = useActiveWeb3React() + return useContract(chainId && V2_MIGRATOR_ADDRESSES[chainId], V2MigratorABI, true) as V3Migrator | null +} + export function useV1ExchangeContract(address?: string, withSignerIfPossible?: boolean): Contract | null { return useContract(address, V1_EXCHANGE_ABI, withSignerIfPossible) } diff --git a/src/pages/AddLiquidity/index.tsx b/src/pages/AddLiquidity/index.tsx index 95e5db080c..d68a652db0 100644 --- a/src/pages/AddLiquidity/index.tsx +++ b/src/pages/AddLiquidity/index.tsx @@ -1,6 +1,6 @@ import { TransactionResponse } from '@ethersproject/providers' -import { Currency, TokenAmount, Token, Percent, ETHER } from '@uniswap/sdk-core' -import React, { useCallback, useContext, useState, useMemo } from 'react' +import { Currency, TokenAmount, Percent, ETHER } from '@uniswap/sdk-core' +import React, { useCallback, useContext, useState } from 'react' import { Link2, AlertTriangle } from 'react-feather' import ReactGA from 'react-ga' import { useV3NFTPositionManagerContract } from '../../hooks/useContract' @@ -47,6 +47,95 @@ import { FeeAmount, NonfungiblePositionManager } from '@uniswap/v3-sdk' import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES } from 'constants/v3' import JSBI from 'jsbi' +export function FeeSelector({ + disabled = false, + feeAmount, + handleFeePoolSelect, +}: { + disabled?: boolean + feeAmount?: FeeAmount + handleFeePoolSelect: (feeAmount: FeeAmount) => void +}) { + const { t } = useTranslation() + + return ( + + + {t('selectPool')} + + handleFeePoolSelect(FeeAmount.LOW)} + > + + 0.05% {t('fee')} + + Optimized for stable assets. + + + + handleFeePoolSelect(FeeAmount.MEDIUM)} + > + + 0.3% {t('fee')} + + The classic Uniswap pool fee. + + + + handleFeePoolSelect(FeeAmount.HIGH)} + > + + 1% {t('fee')} + + Best for volatile assets. + + + + + + + ) +} + +// the order of displayed base currencies from left to right is always in sort order +// currencyA is treated as the preferred base currency +export function RateToggle({ + currencyA, + currencyB, + handleRateToggle, +}: { + currencyA: Currency + currencyB: Currency + handleRateToggle: () => void +}) { + const { t } = useTranslation() + const { chainId } = useActiveWeb3React() + + const tokenA = wrappedCurrency(currencyA, chainId) + const tokenB = wrappedCurrency(currencyB, chainId) + + const isSorted = tokenA && tokenB && tokenA.sortsBefore(tokenB) + + return tokenA && tokenB ? ( + + + {isSorted ? currencyA.symbol : currencyB.symbol} {t('rate')} + + + {isSorted ? currencyB.symbol : currencyA.symbol} {t('rate')} + + + ) : null +} + export default function AddLiquidity({ match: { params: { currencyIdA, currencyIdB, feeAmount: feeAmountFromUrl }, @@ -285,19 +374,6 @@ export default function AddLiquidity({ const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks const { [Bound.LOWER]: priceLower, [Bound.UPPER]: priceUpper } = pricesAtTicks - // used sort sorted toggle - const tokenA = wrappedCurrency(currencyA ?? undefined, chainId) - const tokenB = wrappedCurrency(currencyB ?? undefined, chainId) - const sortedTokens: Token[] | undefined = useMemo( - () => - tokenA && tokenB && !tokenA.equals(tokenB) - ? tokenA.sortsBefore(tokenB) - ? [tokenA, tokenB] - : [tokenB, tokenA] - : undefined, - [tokenA, tokenB] - ) - const handleRateToggle = useCallback(() => { if (currencyA && currencyB) { const currencyIdA = currencyId(currencyA) @@ -322,19 +398,6 @@ export default function AddLiquidity({ onUpperRangeInput, ]) - const RateToggle = () => { - return sortedTokens && currencyB && currencyA ? ( - - - {tokenA === sortedTokens[0] ? currencyB.symbol : currencyA?.symbol} {t('rate')} - - - {tokenB === sortedTokens[0] ? currencyB.symbol : currencyA?.symbol} {t('rate')} - - - ) : null - } - return ( @@ -415,52 +478,11 @@ export default function AddLiquidity({ - - - {t('selectPool')} - {/* - {t('poolType')} - */} - - handleFeePoolSelect(FeeAmount.LOW)} - > - - 0.05% {t('fee')} - - Optimized for stable assets. - - - - handleFeePoolSelect(FeeAmount.MEDIUM)} - > - - 0.3% {t('fee')} - - The classic Uniswap pool fee. - - - - handleFeePoolSelect(FeeAmount.HIGH)} - > - - 1% {t('fee')} - - Best for volatile assets. - - - - - - + {noLiquidity && ( @@ -470,7 +492,9 @@ export default function AddLiquidity({ {t('selectStartingPrice')} - {tokenA && tokenB && } + {currencyA && currencyB ? ( + + ) : null} {/* {t('newPoolPrice')} @@ -499,7 +523,9 @@ export default function AddLiquidity({ {t('selectLiquidityRange')} - {tokenA && tokenB && !noLiquidity && } + {currencyA && currencyB && !noLiquidity && ( + + )} {/* {t('rangeWarning')} diff --git a/src/pages/MigrateV2/MigrateV2Pair.tsx b/src/pages/MigrateV2/MigrateV2Pair.tsx index 4396294b5f..60a48a1112 100644 --- a/src/pages/MigrateV2/MigrateV2Pair.tsx +++ b/src/pages/MigrateV2/MigrateV2Pair.tsx @@ -1,5 +1,5 @@ -import React, { useMemo } from 'react' -import { Currency, CurrencyAmount, Price, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core' +import React, { useCallback, useMemo, useState } from 'react' +import { Currency, CurrencyAmount, Fraction, Price, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core' import { JSBI } from '@uniswap/v2-sdk' import { Redirect, RouteComponentProps } from 'react-router' import { Text } from 'rebass' @@ -11,7 +11,7 @@ import { AutoRow, RowBetween, RowFixed } from '../../components/Row' import { useTotalSupply } from '../../data/TotalSupply' import { useActiveWeb3React } from '../../hooks' import { useToken } from '../../hooks/Tokens' -import { usePairContract } from '../../hooks/useContract' +import { usePairContract, useV2MigratorContract } from '../../hooks/useContract' import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks' import { useTokenBalance } from '../../state/wallet/hooks' import { BackArrow, ExternalLink, TYPE } from '../../theme' @@ -19,14 +19,20 @@ import { getEtherscanLink, isAddress } from '../../utils' import { BodyWrapper } from '../AppBody' import { EmptyState } from '../MigrateV1/EmptyState' import { V2_MIGRATOR_ADDRESSES } from 'constants/v3' +import { PoolState, usePool } from 'data/Pools' +import { FeeAmount, Pool, Position, priceToClosestTick, TickMath } from '@uniswap/v3-sdk' +import { FeeSelector, RateToggle } from 'pages/AddLiquidity' +import { LightCard, PinkCard, YellowCard } from 'components/Card' +import { ApprovalState, useApproveCallback } from 'hooks/useApproveCallback' +import { Dots } from 'components/swap/styleds' +import { ButtonConfirmed } from 'components/Button' +import useTransactionDeadline from 'hooks/useTransactionDeadline' +import { useUserSlippageTolerance } from 'state/user/hooks' +import ReactGA from 'react-ga' +import { TransactionResponse } from '@ethersproject/providers' +import { useIsTransactionPending, useTransactionAdder } from 'state/transactions/hooks' -// TODO the whole file - -// const WEI_DENOM = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18)) -// const ZERO = JSBI.BigInt(0) -// const ONE = JSBI.BigInt(1) -// const ZERO_FRACTION = new Fraction(ZERO, ONE) -// const ALLOWED_OUTPUT_MIN_PERCENT = new Percent(JSBI.BigInt(10000 - INITIAL_ALLOWED_SLIPPAGE), JSBI.BigInt(10000)) +const ZERO = JSBI.BigInt(0) export function V2LiquidityInfo({ token, @@ -94,7 +100,7 @@ function V2PairMigration({ token0: Token token1: Token }) { - const { chainId } = useActiveWeb3React() + const { chainId, account } = useActiveWeb3React() // this is just getLiquidityValue with the fee off, but for the passed pair const token0Value = useMemo( @@ -106,90 +112,144 @@ function V2PairMigration({ [token1, pairBalance, reserve1, totalSupply] ) - const v2SpotPrice = new Price(token0, token1, reserve0.raw, reserve1.raw) + // set up v3 pool + const [feeAmount, setFeeAmount] = useState(FeeAmount.MEDIUM) + const [poolState, pool] = usePool(token0, token1, feeAmount) + const noLiquidity = poolState === PoolState.NOT_EXISTS - console.log(token0Value, token1Value, v2SpotPrice) + // get spot prices + price difference + const v2SpotPrice = useMemo(() => new Price(token0, token1, reserve0.raw, reserve1.raw), [ + token0, + token1, + reserve0, + reserve1, + ]) + const v3SpotPrice = poolState === PoolState.EXISTS ? pool?.token0Price : undefined - // const isFirstLiquidityProvider: boolean = false // check for v3 pair existence + let priceDifferenceFraction: Fraction | undefined = + v2SpotPrice && v3SpotPrice ? v3SpotPrice.divide(v2SpotPrice).subtract(1).multiply(100) : undefined + if (priceDifferenceFraction?.lessThan(ZERO)) { + priceDifferenceFraction = priceDifferenceFraction.multiply(-1) + } - // const [confirmingMigration, setConfirmingMigration] = useState(false) - // const [pendingMigrationHash, setPendingMigrationHash] = useState(null) + const largePriceDifference = priceDifferenceFraction && !priceDifferenceFraction?.lessThan(JSBI.BigInt(4)) - // const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION + const [invertPrice, setInvertPrice] = useState(false) - // const ethWorth: CurrencyAmount = exchangeETHBalance - // ? CurrencyAmount.ether(exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient) - // : CurrencyAmount.ether(ZERO) + // TODO these obviously have to not be hardcoded eventually + const lowerTick = -60000 + const upperTick = 60000 + const percentageToMigrate = 100 - // const tokenWorth: TokenAmount = exchangeTokenBalance - // ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient) - // : new TokenAmount(token, ZERO) + const [confirmingMigration, setConfirmingMigration] = useState(false) + const [pendingMigrationHash, setPendingMigrationHash] = useState(null) - // const [approval, approve] = useApproveCallback(liquidityTokenAmount, V1_MIGRATOR_ADDRESS) + // TODO add permit approval + const [approval, approve] = useApproveCallback(pairBalance, chainId ? V2_MIGRATOR_ADDRESSES[chainId] : undefined) - // const v1SpotPrice = - // exchangeTokenBalance && exchangeETHBalance - // ? exchangeTokenBalance.divide(new Fraction(exchangeETHBalance.raw, WEI_DENOM)) - // : null + const addTransaction = useTransactionAdder() + const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined) - // const priceDifferenceFraction: Fraction | undefined = - // v1SpotPrice && v2SpotPrice ? v1SpotPrice.divide(v2SpotPrice).multiply('100').subtract('100') : undefined + const deadline = useTransactionDeadline() // custom from users settings + const [allowedSlippage] = useUserSlippageTolerance() // custom from users - // const priceDifferenceAbs: Fraction | undefined = priceDifferenceFraction?.lessThan(ZERO) - // ? priceDifferenceFraction?.multiply('-1') - // : priceDifferenceFraction + const migrator = useV2MigratorContract() + const migrate = useCallback(() => { + if (!migrator || !account || !deadline) return - // const minAmountETH: JSBI | undefined = - // v2SpotPrice && tokenWorth - // ? tokenWorth.divide(v2SpotPrice).multiply(WEI_DENOM).multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient - // : ethWorth?.numerator + // the v3 tick is either the tickCurrent, or the tick closest to the v2 spot price + const tick = pool?.tickCurrent ?? priceToClosestTick(v2SpotPrice) + // the price is either the current v3 price, or the price at the tick + const sqrtPrice = pool?.sqrtRatioX96 ?? TickMath.getSqrtRatioAtTick(tick) - // const minAmountToken: JSBI | undefined = - // v2SpotPrice && ethWorth - // ? ethWorth - // .multiply(v2SpotPrice) - // .multiply(JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(token.decimals))) - // .multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient - // : tokenWorth?.numerator + const data = [] - // const addTransaction = useTransactionAdder() - // const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined) + // create/initialize pool if necessary + if (noLiquidity) { + data.push( + migrator.interface.encodeFunctionData('createAndInitializePoolIfNecessary', [ + token0.address, + token1.address, + feeAmount, + `0x${sqrtPrice.toString(16)}`, + ]) + ) + } - // const migrator = useV1MigratorContract() - // const migrate = useCallback(() => { - // if (!minAmountToken || !minAmountETH || !migrator) return + const position = Position.fromAmounts({ + pool: pool ?? new Pool(token0, token1, feeAmount, sqrtPrice, 0, tick, []), + tickLower: lowerTick, + tickUpper: upperTick, + amount0: token0Value.raw, + amount1: token1Value.raw, + }) - // setConfirmingMigration(true) - // migrator - // .migrate( - // token.address, - // minAmountToken.toString(), - // minAmountETH.toString(), - // account, - // Math.floor(new Date().getTime() / 1000) + DEFAULT_DEADLINE_FROM_NOW - // ) - // .then((response: TransactionResponse) => { - // ReactGA.event({ - // category: 'Migrate', - // action: 'V1->V2', - // label: token?.symbol, - // }) + // TODO could save gas by not doing this in multicall + data.push( + migrator.interface.encodeFunctionData('migrate', [ + { + pair: pairBalance.token.address, + liquidityToMigrate: `0x${pairBalance.raw.toString(16)}`, + percentageToMigrate, + token0: token0.address, + token1: token1.address, + fee: feeAmount, + tickLower: lowerTick, + tickUpper: upperTick, + amount0Min: `0x${JSBI.divide( + JSBI.multiply(position.amount0.raw, JSBI.BigInt(allowedSlippage)), + JSBI.BigInt(10000) + ).toString(16)}`, + amount1Min: `0x${JSBI.divide( + JSBI.multiply(position.amount1.raw, JSBI.BigInt(allowedSlippage)), + JSBI.BigInt(10000) + ).toString(16)}`, + recipient: account, + deadline, + refundAsETH: true, // TODO might want to change this? + }, + ]) + ) - // addTransaction(response, { - // summary: `Migrate ${token.symbol} liquidity to V2`, - // }) - // setPendingMigrationHash(response.hash) - // }) - // .catch(() => { - // setConfirmingMigration(false) - // }) - // }, [minAmountToken, minAmountETH, migrator, token, account, addTransaction]) + setConfirmingMigration(true) + migrator + .multicall(data) + .then((response: TransactionResponse) => { + ReactGA.event({ + category: 'Migrate', + action: 'V2->V3', + label: `${token0.symbol}/${token1.symbol}`, + }) - // const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO) + addTransaction(response, { + summary: `Migrate ${token0.symbol}/${token1.symbol} liquidity to V3`, + }) + setPendingMigrationHash(response.hash) + }) + .catch(() => { + setConfirmingMigration(false) + }) + }, [ + migrator, + noLiquidity, + token0, + token1, + feeAmount, + v2SpotPrice, + pairBalance, + token0Value, + token1Value, + percentageToMigrate, + lowerTick, + allowedSlippage, + pool, + upperTick, + account, + deadline, + addTransaction, + ]) - // const largePriceDifference = !!priceDifferenceAbs && !priceDifferenceAbs.lessThan(JSBI.BigInt(5)) - - // const isSuccessfullyMigrated = !!pendingMigrationHash && noLiquidityTokens + const isSuccessfullyMigrated = !!pendingMigrationHash && JSBI.equal(pairBalance.raw, ZERO) return ( @@ -204,78 +264,76 @@ function V2PairMigration({ . - {/* {!isFirstLiquidityProvider && largePriceDifference ? ( - - - It{"'"}s best to deposit liquidity into Uniswap V2 at a price you believe is correct. If the V2 price seems - incorrect, you can either make a swap to move the price or wait for someone else to do so. - - - - V1 Price: - - {v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH - - - -
- - {v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol} - - + +
+ setInvertPrice((invertPrice) => !invertPrice)} + /> +
+ + {noLiquidity && ( + + + You are the first liquidity provider for this Uniswap V3 pool. Your liquidity will be migrated at the + current V2 price. Your transaction cost will include the gas to create the pool. + + + V2 Price: - {v2SpotPrice?.toSignificant(6)} {token.symbol}/ETH - - - -
- - {v2SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol} - - - - - Price Difference: - {priceDifferenceAbs?.toSignificant(4)}% - - - - ) : null} - - {isFirstLiquidityProvider && ( - - - You are the first liquidity provider for this pair on Uniswap V2. Your liquidity will be migrated at the - current V1 price. Your transaction cost also includes the gas to create the pool. - - - - - V1 Price: - - {v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH - - - -
- - {v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol} + {invertPrice + ? `${v2SpotPrice?.invert()?.toSignificant(6)} ${token0.symbol} / ${token1.symbol}` + : `${v2SpotPrice?.toSignificant(6)} ${token1.symbol} / ${token0.symbol}`} )} + {largePriceDifference && ( + + + You should only deposit liquidity into Uniswap V3 at a price you believe is correct. If the price seems + incorrect, you can either make a swap to move the price or wait for someone else to do so. + + + + V2 Price: + + {invertPrice + ? `${v2SpotPrice?.invert()?.toSignificant(6)} ${token0.symbol} / ${token1.symbol}` + : `${v2SpotPrice?.toSignificant(6)} ${token1.symbol} / ${token0.symbol}`} + + + + + V3 Price: + + {invertPrice + ? `${v3SpotPrice?.invert()?.toSignificant(6)} ${token0.symbol} / ${token1.symbol}` + : `${v3SpotPrice?.toSignificant(6)} ${token1.symbol} / ${token0.symbol}`} + + + + + Price Difference: + {priceDifferenceFraction?.toSignificant(4)}% + + + + )} + - + /> */}
@@ -297,22 +355,25 @@ function V2PairMigration({ - {isSuccessfullyMigrated ? 'Success' : isMigrationPending ? Migrating : 'Migrate'} + {isSuccessfullyMigrated ? 'Success!' : isMigrationPending ? Migrating : 'Migrate'}
- {`Your Uniswap V1 ${token.symbol}/ETH liquidity will become Uniswap V2 ${token.symbol}/ETH liquidity.`} - */} + {`Your Uniswap V2 ${invertPrice ? token0?.symbol : token1?.symbol} / ${ + invertPrice ? token1?.symbol : token0?.symbol + } liquidity tokens will become a Uniswap V3 ${invertPrice ? token0?.symbol : token1?.symbol} / ${ + invertPrice ? token1?.symbol : token0?.symbol + } NFT.`} + ) } diff --git a/yarn.lock b/yarn.lock index b7ba2a9f2b..80fff66ea3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4134,7 +4134,7 @@ tiny-invariant "^1.1.0" tiny-warning "^1.0.3" -"@uniswap/v3-core@1.0.0-rc.1", "@uniswap/v3-core@^1.0.0-rc.0": +"@uniswap/v3-core@1.0.0-rc.1", "@uniswap/v3-core@^1.0.0-rc.1": version "1.0.0-rc.1" resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.0-rc.1.tgz#f2bbc483451364a951fba06eb2d978c6e8bdd58f" integrity sha512-4ET2H0a8p7nVBGFWfio9SHc+RA6UIXEvlTRIJNsDwjQvfs8Jq9lfJ+eSOUTGmiB8Vp8V5dWarLDBU/rDE159pQ== @@ -4149,6 +4149,16 @@ "@uniswap/v2-core" "1.0.1" "@uniswap/v3-core" "1.0.0-rc.1" +"@uniswap/v3-periphery@^1.0.0-beta.19": + version "1.0.0-beta.19" + resolved "https://registry.yarnpkg.com/@uniswap/v3-periphery/-/v3-periphery-1.0.0-beta.19.tgz#d9af90b12657049674cd2f26ae1c61b6cc393261" + integrity sha512-ZQX5KN/89OB7UjrmGOSB7QZIEbgW+R0uaVM5NdlK63Ji0rZjmddeoYS8oNk7i5BU3WR+xJY5DgfiDSmn1W6Sww== + dependencies: + "@openzeppelin/contracts" "3.4.1-solc-0.7-2" + "@uniswap/lib" "^4.0.1-alpha" + "@uniswap/v2-core" "1.0.1" + "@uniswap/v3-core" "1.0.0-rc.1" + "@uniswap/v3-sdk@^1.0.0-alpha.11": version "1.0.0-alpha.11" resolved "https://registry.yarnpkg.com/@uniswap/v3-sdk/-/v3-sdk-1.0.0-alpha.11.tgz#184ed5ee8322b27f35aa830ad5e217b5dda6bd67"