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-core": "1.0.0",
"@uniswap/v2-periphery": "^1.1.0-beta.0", "@uniswap/v2-periphery": "^1.1.0-beta.0",
"@uniswap/v2-sdk": "^1.0.6", "@uniswap/v2-sdk": "^1.0.6",
"@uniswap/v3-core": "^1.0.0-rc.0", "@uniswap/v3-core": "^1.0.0-rc.1",
"@uniswap/v3-periphery": "^1.0.0-beta.17", "@uniswap/v3-periphery": "^1.0.0-beta.19",
"@uniswap/v3-sdk": "^1.0.0-alpha.11", "@uniswap/v3-sdk": "^1.0.0-alpha.11",
"@web3-react/core": "^6.0.9", "@web3-react/core": "^6.0.9",
"@web3-react/fortmatic-connector": "^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.", "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.", "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.", "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}}", "inputTokenDynamic": "Input {{label}}",
"selectStartingPrice": "Select Starting Price", "selectStartingPrice": "Select Starting Price",
"newPoolPrice": "Select the market rate for the tokens being added." "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 } = { export const FACTORY_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '', [ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62', [ChainId.ROPSTEN]: '0x5BbFe6FF864718cD1cE0F126be99e96239E3caDD',
[ChainId.RINKEBY]: '0xD8A6adFB40Ba3B3CdA9F985BF1fdbDc0c1d7591e', [ChainId.RINKEBY]: '0x7ba6C6345E7a73cC0D41d762C7Db9cb3DB721396',
[ChainId.GÖRLI]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62', [ChainId.GÖRLI]: '0x5BbFe6FF864718cD1cE0F126be99e96239E3caDD',
[ChainId.KOVAN]: '', [ChainId.KOVAN]: '',
} }
export const TICK_LENS_ADDRESSES: { [chainId in ChainId]: string } = { export const TICK_LENS_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '', [ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '0x8E984b597F19E8D0FDd0b5bAfDb1d0ae4386455f', [ChainId.ROPSTEN]: '0x1C8beBE5596b60A84e6d737229aDd502E14276Eb',
[ChainId.RINKEBY]: '0xB1c59e8Ae4B72f63a5a9CB9c25A9253096A4b126', [ChainId.RINKEBY]: '0xd4013a706fa79487989b595Df35eF8AD1ffBb422',
[ChainId.GÖRLI]: '0x8E984b597F19E8D0FDd0b5bAfDb1d0ae4386455f', [ChainId.GÖRLI]: '0x1C8beBE5596b60A84e6d737229aDd502E14276Eb',
[ChainId.KOVAN]: '', [ChainId.KOVAN]: '',
} }
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: { [chainId in ChainId]: string } = { export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '', [ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '0x29e4bF3bFD649b807B4C752c01023E535094F6Bc', [ChainId.ROPSTEN]: '0x921647f0c094e2e59CDE6DEfafD77743012f52bd',
[ChainId.RINKEBY]: '0xee9e30637f84Bbf929042A9118c6E20023dab833', [ChainId.RINKEBY]: '0x30Ba713F78Ad3c175a25aD767e3f423549Ac2D65',
[ChainId.GÖRLI]: '0x29e4bF3bFD649b807B4C752c01023E535094F6Bc', [ChainId.GÖRLI]: '0x921647f0c094e2e59CDE6DEfafD77743012f52bd',
[ChainId.KOVAN]: '', [ChainId.KOVAN]: '',
} }
export const NONFUNGIBLE_TOKEN_POSITION_DESCRIPTOR_ADDRESSES: { [chainId in ChainId]: string } = { export const NONFUNGIBLE_TOKEN_POSITION_DESCRIPTOR_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '', [ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f', [ChainId.ROPSTEN]: '0xD2AAa0217a203d9FaB6e5272b211Be2Aba52f385',
[ChainId.RINKEBY]: '0x3431b9Ed12e3204bC6f7039e1c576417B70fdD67', [ChainId.RINKEBY]: '0xAc03019C975F5e79215FeDAB4a1DC30Af3E478F2',
[ChainId.GÖRLI]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f', [ChainId.GÖRLI]: '0xD2AAa0217a203d9FaB6e5272b211Be2Aba52f385',
[ChainId.KOVAN]: '', [ChainId.KOVAN]: '',
} }
export const SWAP_ROUTER_ADDRESSES: { [chainId in ChainId]: string } = { export const SWAP_ROUTER_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '', [ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '0x71bB3d0e63f2Fa2A5d04d54267211f4Caef7062e', [ChainId.ROPSTEN]: '0xDD1B8aA26ac2330e39f8B291eA1E6a82A40E65C4',
[ChainId.RINKEBY]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f', [ChainId.RINKEBY]: '0xD2AAa0217a203d9FaB6e5272b211Be2Aba52f385',
[ChainId.GÖRLI]: '0x71bB3d0e63f2Fa2A5d04d54267211f4Caef7062e', [ChainId.GÖRLI]: '0xDD1B8aA26ac2330e39f8B291eA1E6a82A40E65C4',
[ChainId.KOVAN]: '', [ChainId.KOVAN]: '',
} }
export const V2_MIGRATOR_ADDRESSES: { [chainId in ChainId]: string } = { export const V2_MIGRATOR_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '', [ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '', [ChainId.ROPSTEN]: '0x30Ba713F78Ad3c175a25aD767e3f423549Ac2D65',
[ChainId.RINKEBY]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62', [ChainId.RINKEBY]: '0x864e344eCd7f3a9A4368dEC11Be8104db5770364',
[ChainId.GÖRLI]: '0xee9e30637f84Bbf929042A9118c6E20023dab833', [ChainId.GÖRLI]: '0x30Ba713F78Ad3c175a25aD767e3f423549Ac2D65',
[ChainId.KOVAN]: '', [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 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 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 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 { 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' import ARGENT_WALLET_DETECTOR_ABI from 'abis/argent-wallet-detector.json'
@ -29,10 +30,16 @@ import {
} from 'constants/index' } from 'constants/index'
import { abi as NFTPositionManagerABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json' 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 { 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 { useMemo } from 'react'
import { TickLens, UniswapV3Factory, UniswapV3Pool } from 'types/v3' import { TickLens, UniswapV3Factory, UniswapV3Pool } from 'types/v3'
import { NonfungiblePositionManager } from 'types/v3/NonfungiblePositionManager' import { NonfungiblePositionManager } from 'types/v3/NonfungiblePositionManager'
import { V3Migrator } from 'types/v3/V3Migrator'
import { getContract } from 'utils' import { getContract } from 'utils'
import { useActiveWeb3React } from './index' import { useActiveWeb3React } from './index'
@ -60,6 +67,11 @@ export function useV1MigratorContract(): Contract | null {
return useContract(V1_MIGRATOR_ADDRESS, MIGRATOR_ABI, true) 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 { export function useV1ExchangeContract(address?: string, withSignerIfPossible?: boolean): Contract | null {
return useContract(address, V1_EXCHANGE_ABI, withSignerIfPossible) return useContract(address, V1_EXCHANGE_ABI, withSignerIfPossible)
} }

@ -1,6 +1,6 @@
import { TransactionResponse } from '@ethersproject/providers' import { TransactionResponse } from '@ethersproject/providers'
import { Currency, TokenAmount, Token, Percent, ETHER } from '@uniswap/sdk-core' import { Currency, TokenAmount, Percent, ETHER } from '@uniswap/sdk-core'
import React, { useCallback, useContext, useState, useMemo } from 'react' import React, { useCallback, useContext, useState } from 'react'
import { Link2, AlertTriangle } from 'react-feather' import { Link2, AlertTriangle } from 'react-feather'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
import { useV3NFTPositionManagerContract } from '../../hooks/useContract' 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 { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES } from 'constants/v3'
import JSBI from 'jsbi' 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({ export default function AddLiquidity({
match: { match: {
params: { currencyIdA, currencyIdB, feeAmount: feeAmountFromUrl }, params: { currencyIdA, currencyIdB, feeAmount: feeAmountFromUrl },
@ -285,19 +374,6 @@ export default function AddLiquidity({
const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks
const { [Bound.LOWER]: priceLower, [Bound.UPPER]: priceUpper } = pricesAtTicks 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(() => { const handleRateToggle = useCallback(() => {
if (currencyA && currencyB) { if (currencyA && currencyB) {
const currencyIdA = currencyId(currencyA) const currencyIdA = currencyId(currencyA)
@ -322,19 +398,6 @@ export default function AddLiquidity({
onUpperRangeInput, 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 ( return (
<ScrollablePage> <ScrollablePage>
<ScrollableContent> <ScrollableContent>
@ -415,52 +478,11 @@ export default function AddLiquidity({
</RowBetween> </RowBetween>
</AutoColumn> </AutoColumn>
<AutoColumn gap="16px"> <FeeSelector
<DynamicSection gap="md" disabled={!currencyB || !currencyA}> disabled={!currencyB || !currencyA}
<TYPE.label>{t('selectPool')}</TYPE.label> feeAmount={feeAmount}
{/* <TYPE.main fontWeight={400} fontSize="14px"> handleFeePoolSelect={handleFeePoolSelect}
{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>
{noLiquidity && ( {noLiquidity && (
<DynamicSection disabled={!currencyA || !currencyB}> <DynamicSection disabled={!currencyA || !currencyB}>
@ -470,7 +492,9 @@ export default function AddLiquidity({
</BlueCard> </BlueCard>
<RowBetween> <RowBetween>
<TYPE.label>{t('selectStartingPrice')}</TYPE.label> <TYPE.label>{t('selectStartingPrice')}</TYPE.label>
{tokenA && tokenB && <RateToggle />} {currencyA && currencyB ? (
<RateToggle currencyA={currencyA} currencyB={currencyB} handleRateToggle={handleRateToggle} />
) : null}
</RowBetween> </RowBetween>
{/* <TYPE.main fontWeight={400} fontSize="14px"> {/* <TYPE.main fontWeight={400} fontSize="14px">
{t('newPoolPrice')} {t('newPoolPrice')}
@ -499,7 +523,9 @@ export default function AddLiquidity({
<DynamicSection gap="md" disabled={!feeAmount || invalidPool || (noLiquidity && !startPriceTypedValue)}> <DynamicSection gap="md" disabled={!feeAmount || invalidPool || (noLiquidity && !startPriceTypedValue)}>
<RowBetween> <RowBetween>
<TYPE.label>{t('selectLiquidityRange')}</TYPE.label> <TYPE.label>{t('selectLiquidityRange')}</TYPE.label>
{tokenA && tokenB && !noLiquidity && <RateToggle />} {currencyA && currencyB && !noLiquidity && (
<RateToggle currencyA={currencyA} currencyB={currencyB} handleRateToggle={handleRateToggle} />
)}
</RowBetween> </RowBetween>
{/* <TYPE.main fontWeight={400} fontSize="14px"> {/* <TYPE.main fontWeight={400} fontSize="14px">
{t('rangeWarning')} {t('rangeWarning')}

@ -1,5 +1,5 @@
import React, { useMemo } from 'react' import React, { useCallback, useMemo, useState } from 'react'
import { Currency, CurrencyAmount, Price, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Fraction, Price, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core'
import { JSBI } from '@uniswap/v2-sdk' import { JSBI } from '@uniswap/v2-sdk'
import { Redirect, RouteComponentProps } from 'react-router' import { Redirect, RouteComponentProps } from 'react-router'
import { Text } from 'rebass' import { Text } from 'rebass'
@ -11,7 +11,7 @@ import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
import { useTotalSupply } from '../../data/TotalSupply' import { useTotalSupply } from '../../data/TotalSupply'
import { useActiveWeb3React } from '../../hooks' import { useActiveWeb3React } from '../../hooks'
import { useToken } from '../../hooks/Tokens' 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 { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks'
import { useTokenBalance } from '../../state/wallet/hooks' import { useTokenBalance } from '../../state/wallet/hooks'
import { BackArrow, ExternalLink, TYPE } from '../../theme' import { BackArrow, ExternalLink, TYPE } from '../../theme'
@ -19,14 +19,20 @@ import { getEtherscanLink, isAddress } from '../../utils'
import { BodyWrapper } from '../AppBody' import { BodyWrapper } from '../AppBody'
import { EmptyState } from '../MigrateV1/EmptyState' import { EmptyState } from '../MigrateV1/EmptyState'
import { V2_MIGRATOR_ADDRESSES } from 'constants/v3' 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 ZERO = JSBI.BigInt(0)
// 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))
export function V2LiquidityInfo({ export function V2LiquidityInfo({
token, token,
@ -94,7 +100,7 @@ function V2PairMigration({
token0: Token token0: Token
token1: Token token1: Token
}) { }) {
const { chainId } = useActiveWeb3React() const { chainId, account } = useActiveWeb3React()
// this is just getLiquidityValue with the fee off, but for the passed pair // this is just getLiquidityValue with the fee off, but for the passed pair
const token0Value = useMemo( const token0Value = useMemo(
@ -106,90 +112,144 @@ function V2PairMigration({
[token1, pairBalance, reserve1, totalSupply] [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 largePriceDifference = priceDifferenceFraction && !priceDifferenceFraction?.lessThan(JSBI.BigInt(4))
// const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(null)
// const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION const [invertPrice, setInvertPrice] = useState(false)
// const ethWorth: CurrencyAmount = exchangeETHBalance // TODO these obviously have to not be hardcoded eventually
// ? CurrencyAmount.ether(exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient) const lowerTick = -60000
// : CurrencyAmount.ether(ZERO) const upperTick = 60000
const percentageToMigrate = 100
// const tokenWorth: TokenAmount = exchangeTokenBalance const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false)
// ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient) const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(null)
// : new TokenAmount(token, ZERO)
// 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 = const addTransaction = useTransactionAdder()
// exchangeTokenBalance && exchangeETHBalance const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined)
// ? exchangeTokenBalance.divide(new Fraction(exchangeETHBalance.raw, WEI_DENOM))
// : null
// const priceDifferenceFraction: Fraction | undefined = const deadline = useTransactionDeadline() // custom from users settings
// v1SpotPrice && v2SpotPrice ? v1SpotPrice.divide(v2SpotPrice).multiply('100').subtract('100') : undefined const [allowedSlippage] = useUserSlippageTolerance() // custom from users
// const priceDifferenceAbs: Fraction | undefined = priceDifferenceFraction?.lessThan(ZERO) const migrator = useV2MigratorContract()
// ? priceDifferenceFraction?.multiply('-1') const migrate = useCallback(() => {
// : priceDifferenceFraction if (!migrator || !account || !deadline) return
// const minAmountETH: JSBI | undefined = // the v3 tick is either the tickCurrent, or the tick closest to the v2 spot price
// v2SpotPrice && tokenWorth const tick = pool?.tickCurrent ?? priceToClosestTick(v2SpotPrice)
// ? tokenWorth.divide(v2SpotPrice).multiply(WEI_DENOM).multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient // the price is either the current v3 price, or the price at the tick
// : ethWorth?.numerator const sqrtPrice = pool?.sqrtRatioX96 ?? TickMath.getSqrtRatioAtTick(tick)
// const minAmountToken: JSBI | undefined = const data = []
// v2SpotPrice && ethWorth
// ? ethWorth
// .multiply(v2SpotPrice)
// .multiply(JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(token.decimals)))
// .multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient
// : tokenWorth?.numerator
// const addTransaction = useTransactionAdder() // create/initialize pool if necessary
// const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined) if (noLiquidity) {
data.push(
migrator.interface.encodeFunctionData('createAndInitializePoolIfNecessary', [
token0.address,
token1.address,
feeAmount,
`0x${sqrtPrice.toString(16)}`,
])
)
}
// const migrator = useV1MigratorContract() const position = Position.fromAmounts({
// const migrate = useCallback(() => { pool: pool ?? new Pool(token0, token1, feeAmount, sqrtPrice, 0, tick, []),
// if (!minAmountToken || !minAmountETH || !migrator) return tickLower: lowerTick,
tickUpper: upperTick,
amount0: token0Value.raw,
amount1: token1Value.raw,
})
// setConfirmingMigration(true) // TODO could save gas by not doing this in multicall
// migrator data.push(
// .migrate( migrator.interface.encodeFunctionData('migrate', [
// token.address, {
// minAmountToken.toString(), pair: pairBalance.token.address,
// minAmountETH.toString(), liquidityToMigrate: `0x${pairBalance.raw.toString(16)}`,
// account, percentageToMigrate,
// Math.floor(new Date().getTime() / 1000) + DEFAULT_DEADLINE_FROM_NOW token0: token0.address,
// ) token1: token1.address,
// .then((response: TransactionResponse) => { fee: feeAmount,
// ReactGA.event({ tickLower: lowerTick,
// category: 'Migrate', tickUpper: upperTick,
// action: 'V1->V2', amount0Min: `0x${JSBI.divide(
// label: token?.symbol, 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, { setConfirmingMigration(true)
// summary: `Migrate ${token.symbol} liquidity to V2`, migrator
// }) .multicall(data)
// setPendingMigrationHash(response.hash) .then((response: TransactionResponse) => {
// }) ReactGA.event({
// .catch(() => { category: 'Migrate',
// setConfirmingMigration(false) action: 'V2->V3',
// }) label: `${token0.symbol}/${token1.symbol}`,
// }, [minAmountToken, minAmountETH, migrator, token, account, addTransaction]) })
// 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 && JSBI.equal(pairBalance.raw, ZERO)
// const isSuccessfullyMigrated = !!pendingMigrationHash && noLiquidityTokens
return ( return (
<AutoColumn gap="20px"> <AutoColumn gap="20px">
@ -204,78 +264,76 @@ function V2PairMigration({
. .
</TYPE.body> </TYPE.body>
{/* {!isFirstLiquidityProvider && largePriceDifference ? ( <FeeSelector feeAmount={feeAmount} handleFeePoolSelect={setFeeAmount} />
<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>
<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> <RowBetween>
<TYPE.body>V2 Price:</TYPE.body> <TYPE.body>V2 Price:</TYPE.body>
<TYPE.black> <TYPE.black>
{v2SpotPrice?.toSignificant(6)} {token.symbol}/ETH {invertPrice
</TYPE.black> ? `${v2SpotPrice?.invert()?.toSignificant(6)} ${token0.symbol} / ${token1.symbol}`
</RowBetween> : `${v2SpotPrice?.toSignificant(6)} ${token1.symbol} / ${token0.symbol}`}
<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}
</TYPE.black> </TYPE.black>
</RowBetween> </RowBetween>
</AutoColumn> </AutoColumn>
</PinkCard> </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> <LightCard>
<V2LiquidityInfo {/* <V2LiquidityInfo
token={token} token={token}
liquidityTokenAmount={liquidityTokenAmount} liquidityTokenAmount={liquidityTokenAmount}
tokenWorth={tokenWorth} tokenWorth={tokenWorth}
ethWorth={ethWorth} ethWorth={ethWorth}
/> /> */}
<div style={{ display: 'flex', marginTop: '1rem' }}> <div style={{ display: 'flex', marginTop: '1rem' }}>
<AutoColumn gap="12px" style={{ flex: '1', marginRight: 12 }}> <AutoColumn gap="12px" style={{ flex: '1', marginRight: 12 }}>
@ -297,22 +355,25 @@ function V2PairMigration({
<ButtonConfirmed <ButtonConfirmed
confirmed={isSuccessfullyMigrated} confirmed={isSuccessfullyMigrated}
disabled={ disabled={
isSuccessfullyMigrated ||
noLiquidityTokens ||
isMigrationPending ||
approval !== ApprovalState.APPROVED || approval !== ApprovalState.APPROVED ||
confirmingMigration confirmingMigration ||
isMigrationPending ||
isSuccessfullyMigrated
} }
onClick={migrate} onClick={migrate}
> >
{isSuccessfullyMigrated ? 'Success' : isMigrationPending ? <Dots>Migrating</Dots> : 'Migrate'} {isSuccessfullyMigrated ? 'Success!' : isMigrationPending ? <Dots>Migrating</Dots> : 'Migrate'}
</ButtonConfirmed> </ButtonConfirmed>
</AutoColumn> </AutoColumn>
</div> </div>
</LightCard> </LightCard>
<TYPE.darkGray style={{ textAlign: 'center' }}> <TYPE.darkGray style={{ textAlign: 'center' }}>
{`Your Uniswap V1 ${token.symbol}/ETH liquidity will become Uniswap V2 ${token.symbol}/ETH liquidity.`} {`Your Uniswap V2 ${invertPrice ? token0?.symbol : token1?.symbol} / ${
</TYPE.darkGray> */} 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> </AutoColumn>
) )
} }

@ -4134,7 +4134,7 @@
tiny-invariant "^1.1.0" tiny-invariant "^1.1.0"
tiny-warning "^1.0.3" 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" version "1.0.0-rc.1"
resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.0-rc.1.tgz#f2bbc483451364a951fba06eb2d978c6e8bdd58f" resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.0-rc.1.tgz#f2bbc483451364a951fba06eb2d978c6e8bdd58f"
integrity sha512-4ET2H0a8p7nVBGFWfio9SHc+RA6UIXEvlTRIJNsDwjQvfs8Jq9lfJ+eSOUTGmiB8Vp8V5dWarLDBU/rDE159pQ== integrity sha512-4ET2H0a8p7nVBGFWfio9SHc+RA6UIXEvlTRIJNsDwjQvfs8Jq9lfJ+eSOUTGmiB8Vp8V5dWarLDBU/rDE159pQ==
@ -4149,6 +4149,16 @@
"@uniswap/v2-core" "1.0.1" "@uniswap/v2-core" "1.0.1"
"@uniswap/v3-core" "1.0.0-rc.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": "@uniswap/v3-sdk@^1.0.0-alpha.11":
version "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" resolved "https://registry.yarnpkg.com/@uniswap/v3-sdk/-/v3-sdk-1.0.0-alpha.11.tgz#184ed5ee8322b27f35aa830ad5e217b5dda6bd67"