Finish migration (#42)

* start migration (wip)

abstract some add liquidity components

bump deploy version

* add slippage params
This commit is contained in:
Noah Zinsmeister 2021-04-16 17:35:50 -04:00 committed by GitHub
parent 9f5584c37d
commit 9fc096d091
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 348 additions and 240 deletions

@ -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",

@ -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 dont 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."

@ -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]: '',
}

@ -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)
}

@ -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 (
<AutoColumn gap="16px">
<DynamicSection gap="md" disabled={disabled}>
<TYPE.label>{t('selectPool')}</TYPE.label>
<RowBetween>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.LOW}
onClick={() => handleFeePoolSelect(FeeAmount.LOW)}
>
<AutoColumn gap="sm" justify="flex-start">
<TYPE.label>0.05% {t('fee')}</TYPE.label>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
Optimized for stable assets.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.MEDIUM}
onClick={() => handleFeePoolSelect(FeeAmount.MEDIUM)}
>
<AutoColumn gap="sm" justify="flex-start">
<TYPE.label>0.3% {t('fee')}</TYPE.label>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
The classic Uniswap pool fee.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.HIGH}
onClick={() => handleFeePoolSelect(FeeAmount.HIGH)}
>
<AutoColumn gap="sm" justify="flex-start">
<TYPE.label>1% {t('fee')}</TYPE.label>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
Best for volatile assets.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
</RowBetween>
</DynamicSection>
</AutoColumn>
)
}
// 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 ? (
<ToggleWrapper width="fit-content">
<ToggleElement isActive={isSorted} fontSize="12px" onClick={handleRateToggle}>
{isSorted ? currencyA.symbol : currencyB.symbol} {t('rate')}
</ToggleElement>
<ToggleElement isActive={!isSorted} fontSize="12px" onClick={handleRateToggle}>
{isSorted ? currencyB.symbol : currencyA.symbol} {t('rate')}
</ToggleElement>
</ToggleWrapper>
) : 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 ? (
<ToggleWrapper width="fit-content">
<ToggleElement isActive={tokenA === sortedTokens[0]} fontSize="12px" onClick={handleRateToggle}>
{tokenA === sortedTokens[0] ? currencyB.symbol : currencyA?.symbol} {t('rate')}
</ToggleElement>
<ToggleElement isActive={tokenB === sortedTokens[0]} fontSize="12px" onClick={handleRateToggle}>
{tokenB === sortedTokens[0] ? currencyB.symbol : currencyA?.symbol} {t('rate')}
</ToggleElement>
</ToggleWrapper>
) : null
}
return (
<ScrollablePage>
<ScrollableContent>
@ -415,52 +478,11 @@ export default function AddLiquidity({
</RowBetween>
</AutoColumn>
<AutoColumn gap="16px">
<DynamicSection gap="md" disabled={!currencyB || !currencyA}>
<TYPE.label>{t('selectPool')}</TYPE.label>
{/* <TYPE.main fontWeight={400} fontSize="14px">
{t('poolType')}
</TYPE.main> */}
<RowBetween>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.LOW}
onClick={() => handleFeePoolSelect(FeeAmount.LOW)}
>
<AutoColumn gap="sm" justify="flex-start">
<TYPE.label>0.05% {t('fee')}</TYPE.label>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
Optimized for stable assets.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.MEDIUM}
onClick={() => handleFeePoolSelect(FeeAmount.MEDIUM)}
>
<AutoColumn gap="sm" justify="flex-start">
<TYPE.label>0.3% {t('fee')}</TYPE.label>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
The classic Uniswap pool fee.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.HIGH}
onClick={() => handleFeePoolSelect(FeeAmount.HIGH)}
>
<AutoColumn gap="sm" justify="flex-start">
<TYPE.label>1% {t('fee')}</TYPE.label>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
Best for volatile assets.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
</RowBetween>
</DynamicSection>
</AutoColumn>
<FeeSelector
disabled={!currencyB || !currencyA}
feeAmount={feeAmount}
handleFeePoolSelect={handleFeePoolSelect}
/>
{noLiquidity && (
<DynamicSection disabled={!currencyA || !currencyB}>
@ -470,7 +492,9 @@ export default function AddLiquidity({
</BlueCard>
<RowBetween>
<TYPE.label>{t('selectStartingPrice')}</TYPE.label>
{tokenA && tokenB && <RateToggle />}
{currencyA && currencyB ? (
<RateToggle currencyA={currencyA} currencyB={currencyB} handleRateToggle={handleRateToggle} />
) : null}
</RowBetween>
{/* <TYPE.main fontWeight={400} fontSize="14px">
{t('newPoolPrice')}
@ -499,7 +523,9 @@ export default function AddLiquidity({
<DynamicSection gap="md" disabled={!feeAmount || invalidPool || (noLiquidity && !startPriceTypedValue)}>
<RowBetween>
<TYPE.label>{t('selectLiquidityRange')}</TYPE.label>
{tokenA && tokenB && !noLiquidity && <RateToggle />}
{currencyA && currencyB && !noLiquidity && (
<RateToggle currencyA={currencyA} currencyB={currencyB} handleRateToggle={handleRateToggle} />
)}
</RowBetween>
{/* <TYPE.main fontWeight={400} fontSize="14px">
{t('rangeWarning')}

@ -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<boolean>(false)
// const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(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<boolean>(false)
const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(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 (
<AutoColumn gap="20px">
@ -204,78 +264,76 @@ function V2PairMigration({
.
</TYPE.body>
{/* {!isFirstLiquidityProvider && largePriceDifference ? (
<YellowCard>
<TYPE.body style={{ marginBottom: 8, fontWeight: 400 }}>
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.
</TYPE.body>
<AutoColumn gap="8px">
<RowBetween>
<TYPE.body>V1 Price:</TYPE.body>
<TYPE.black>
{v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH
</TYPE.black>
</RowBetween>
<RowBetween>
<div />
<TYPE.black>
{v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
</TYPE.black>
</RowBetween>
<FeeSelector feeAmount={feeAmount} handleFeePoolSelect={setFeeAmount} />
<div style={{ justifySelf: 'end' }}>
<RateToggle
currencyA={invertPrice ? token1 : token0}
currencyB={invertPrice ? token0 : token1}
handleRateToggle={() => setInvertPrice((invertPrice) => !invertPrice)}
/>
</div>
{noLiquidity && (
<PinkCard>
<TYPE.body style={{ marginBottom: 8, fontWeight: 400 }}>
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.
</TYPE.body>
<AutoColumn gap="8px">
<RowBetween>
<TYPE.body>V2 Price:</TYPE.body>
<TYPE.black>
{v2SpotPrice?.toSignificant(6)} {token.symbol}/ETH
</TYPE.black>
</RowBetween>
<RowBetween>
<div />
<TYPE.black>
{v2SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
</TYPE.black>
</RowBetween>
<RowBetween>
<TYPE.body color="inherit">Price Difference:</TYPE.body>
<TYPE.black color="inherit">{priceDifferenceAbs?.toSignificant(4)}%</TYPE.black>
</RowBetween>
</AutoColumn>
</YellowCard>
) : null}
{isFirstLiquidityProvider && (
<PinkCard>
<TYPE.body style={{ marginBottom: 8, fontWeight: 400 }}>
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.
</TYPE.body>
<AutoColumn gap="8px">
<RowBetween>
<TYPE.body>V1 Price:</TYPE.body>
<TYPE.black>
{v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH
</TYPE.black>
</RowBetween>
<RowBetween>
<div />
<TYPE.black>
{v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
{invertPrice
? `${v2SpotPrice?.invert()?.toSignificant(6)} ${token0.symbol} / ${token1.symbol}`
: `${v2SpotPrice?.toSignificant(6)} ${token1.symbol} / ${token0.symbol}`}
</TYPE.black>
</RowBetween>
</AutoColumn>
</PinkCard>
)}
{largePriceDifference && (
<YellowCard>
<TYPE.body style={{ marginBottom: 8, fontWeight: 400 }}>
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.
</TYPE.body>
<AutoColumn gap="8px">
<RowBetween>
<TYPE.body>V2 Price:</TYPE.body>
<TYPE.black>
{invertPrice
? `${v2SpotPrice?.invert()?.toSignificant(6)} ${token0.symbol} / ${token1.symbol}`
: `${v2SpotPrice?.toSignificant(6)} ${token1.symbol} / ${token0.symbol}`}
</TYPE.black>
</RowBetween>
<RowBetween>
<TYPE.body>V3 Price:</TYPE.body>
<TYPE.black>
{invertPrice
? `${v3SpotPrice?.invert()?.toSignificant(6)} ${token0.symbol} / ${token1.symbol}`
: `${v3SpotPrice?.toSignificant(6)} ${token1.symbol} / ${token0.symbol}`}
</TYPE.black>
</RowBetween>
<RowBetween>
<TYPE.body color="inherit">Price Difference:</TYPE.body>
<TYPE.black color="inherit">{priceDifferenceFraction?.toSignificant(4)}%</TYPE.black>
</RowBetween>
</AutoColumn>
</YellowCard>
)}
<LightCard>
<V2LiquidityInfo
{/* <V2LiquidityInfo
token={token}
liquidityTokenAmount={liquidityTokenAmount}
tokenWorth={tokenWorth}
ethWorth={ethWorth}
/>
/> */}
<div style={{ display: 'flex', marginTop: '1rem' }}>
<AutoColumn gap="12px" style={{ flex: '1', marginRight: 12 }}>
@ -297,22 +355,25 @@ function V2PairMigration({
<ButtonConfirmed
confirmed={isSuccessfullyMigrated}
disabled={
isSuccessfullyMigrated ||
noLiquidityTokens ||
isMigrationPending ||
approval !== ApprovalState.APPROVED ||
confirmingMigration
confirmingMigration ||
isMigrationPending ||
isSuccessfullyMigrated
}
onClick={migrate}
>
{isSuccessfullyMigrated ? 'Success' : isMigrationPending ? <Dots>Migrating</Dots> : 'Migrate'}
{isSuccessfullyMigrated ? 'Success!' : isMigrationPending ? <Dots>Migrating</Dots> : 'Migrate'}
</ButtonConfirmed>
</AutoColumn>
</div>
</LightCard>
<TYPE.darkGray style={{ textAlign: 'center' }}>
{`Your Uniswap V1 ${token.symbol}/ETH liquidity will become Uniswap V2 ${token.symbol}/ETH liquidity.`}
</TYPE.darkGray> */}
{`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.`}
</TYPE.darkGray>
</AutoColumn>
)
}

@ -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"