Commit 9fc096d0 authored by Noah Zinsmeister's avatar Noah Zinsmeister Committed by GitHub

Finish migration (#42)

* start migration (wip)

abstract some add liquidity components

bump deploy version

* add slippage params
parent 9f5584c3
...@@ -109,7 +109,6 @@ ...@@ -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 don’t have enough tokens you can trade for them with a Swap.", "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}}", "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' ...@@ -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' ...@@ -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 { ...@@ -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 { ...@@ -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)
} }
......
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' ...@@ -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({ ...@@ -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({ ...@@ -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({ ...@@ -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({ ...@@ -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({ ...@@ -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')}
......
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' ...@@ -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' ...@@ -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'
// TODO the whole file import { FeeAmount, Pool, Position, priceToClosestTick, TickMath } from '@uniswap/v3-sdk'
import { FeeSelector, RateToggle } from 'pages/AddLiquidity'
// const WEI_DENOM = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18)) import { LightCard, PinkCard, YellowCard } from 'components/Card'
// const ZERO = JSBI.BigInt(0) import { ApprovalState, useApproveCallback } from 'hooks/useApproveCallback'
// const ONE = JSBI.BigInt(1) import { Dots } from 'components/swap/styleds'
// const ZERO_FRACTION = new Fraction(ZERO, ONE) import { ButtonConfirmed } from 'components/Button'
// const ALLOWED_OUTPUT_MIN_PERCENT = new Percent(JSBI.BigInt(10000 - INITIAL_ALLOWED_SLIPPAGE), JSBI.BigInt(10000)) 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'
const ZERO = JSBI.BigInt(0)
export function V2LiquidityInfo({ export function V2LiquidityInfo({
token, token,
...@@ -94,7 +100,7 @@ function V2PairMigration({ ...@@ -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({ ...@@ -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), [
// const isFirstLiquidityProvider: boolean = false // check for v3 pair existence token0,
token1,
// const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false) reserve0,
// const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(null) reserve1,
])
// const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION const v3SpotPrice = poolState === PoolState.EXISTS ? pool?.token0Price : undefined
// const ethWorth: CurrencyAmount = exchangeETHBalance let priceDifferenceFraction: Fraction | undefined =
// ? CurrencyAmount.ether(exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient) v2SpotPrice && v3SpotPrice ? v3SpotPrice.divide(v2SpotPrice).subtract(1).multiply(100) : undefined
// : CurrencyAmount.ether(ZERO) if (priceDifferenceFraction?.lessThan(ZERO)) {
priceDifferenceFraction = priceDifferenceFraction.multiply(-1)
}
// const tokenWorth: TokenAmount = exchangeTokenBalance const largePriceDifference = priceDifferenceFraction && !priceDifferenceFraction?.lessThan(JSBI.BigInt(4))
// ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient)
// : new TokenAmount(token, ZERO)
// const [approval, approve] = useApproveCallback(liquidityTokenAmount, V1_MIGRATOR_ADDRESS) const [invertPrice, setInvertPrice] = useState(false)
// const v1SpotPrice = // TODO these obviously have to not be hardcoded eventually
// exchangeTokenBalance && exchangeETHBalance const lowerTick = -60000
// ? exchangeTokenBalance.divide(new Fraction(exchangeETHBalance.raw, WEI_DENOM)) const upperTick = 60000
// : null const percentageToMigrate = 100
// const priceDifferenceFraction: Fraction | undefined = const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false)
// v1SpotPrice && v2SpotPrice ? v1SpotPrice.divide(v2SpotPrice).multiply('100').subtract('100') : undefined const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(null)
// const priceDifferenceAbs: Fraction | undefined = priceDifferenceFraction?.lessThan(ZERO) // TODO add permit approval
// ? priceDifferenceFraction?.multiply('-1') const [approval, approve] = useApproveCallback(pairBalance, chainId ? V2_MIGRATOR_ADDRESSES[chainId] : undefined)
// : priceDifferenceFraction
// const minAmountETH: JSBI | undefined = const addTransaction = useTransactionAdder()
// v2SpotPrice && tokenWorth const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined)
// ? tokenWorth.divide(v2SpotPrice).multiply(WEI_DENOM).multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient
// : ethWorth?.numerator
// const minAmountToken: JSBI | undefined = const deadline = useTransactionDeadline() // custom from users settings
// v2SpotPrice && ethWorth const [allowedSlippage] = useUserSlippageTolerance() // custom from users
// ? ethWorth
// .multiply(v2SpotPrice)
// .multiply(JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(token.decimals)))
// .multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient
// : tokenWorth?.numerator
// const addTransaction = useTransactionAdder() const migrator = useV2MigratorContract()
// const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined) const migrate = useCallback(() => {
if (!migrator || !account || !deadline) return
// const migrator = useV1MigratorContract() // the v3 tick is either the tickCurrent, or the tick closest to the v2 spot price
// const migrate = useCallback(() => { const tick = pool?.tickCurrent ?? priceToClosestTick(v2SpotPrice)
// if (!minAmountToken || !minAmountETH || !migrator) return // the price is either the current v3 price, or the price at the tick
const sqrtPrice = pool?.sqrtRatioX96 ?? TickMath.getSqrtRatioAtTick(tick)
// setConfirmingMigration(true) const data = []
// 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,
// })
// addTransaction(response, { // create/initialize pool if necessary
// summary: `Migrate ${token.symbol} liquidity to V2`, if (noLiquidity) {
// }) data.push(
// setPendingMigrationHash(response.hash) migrator.interface.encodeFunctionData('createAndInitializePoolIfNecessary', [
// }) token0.address,
// .catch(() => { token1.address,
// setConfirmingMigration(false) feeAmount,
// }) `0x${sqrtPrice.toString(16)}`,
// }, [minAmountToken, minAmountETH, migrator, token, account, addTransaction]) ])
)
}
// const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO) const position = Position.fromAmounts({
pool: pool ?? new Pool(token0, token1, feeAmount, sqrtPrice, 0, tick, []),
tickLower: lowerTick,
tickUpper: upperTick,
amount0: token0Value.raw,
amount1: token1Value.raw,
})
// 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?
},
])
)
// const largePriceDifference = !!priceDifferenceAbs && !priceDifferenceAbs.lessThan(JSBI.BigInt(5)) setConfirmingMigration(true)
migrator
.multicall(data)
.then((response: TransactionResponse) => {
ReactGA.event({
category: 'Migrate',
action: 'V2->V3',
label: `${token0.symbol}/${token1.symbol}`,
})
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 isSuccessfullyMigrated = !!pendingMigrationHash && noLiquidityTokens const isSuccessfullyMigrated = !!pendingMigrationHash && JSBI.equal(pairBalance.raw, ZERO)
return ( return (
<AutoColumn gap="20px"> <AutoColumn gap="20px">
...@@ -204,78 +264,76 @@ function V2PairMigration({ ...@@ -204,78 +264,76 @@ function V2PairMigration({
. .
</TYPE.body> </TYPE.body>
{/* {!isFirstLiquidityProvider && largePriceDifference ? ( <FeeSelector feeAmount={feeAmount} handleFeePoolSelect={setFeeAmount} />
<YellowCard>
<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 }}> <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 You are the first liquidity provider for this Uniswap V3 pool. Your liquidity will be migrated at the
incorrect, you can either make a swap to move the price or wait for someone else to do so. current V2 price. Your transaction cost will include the gas to create the pool.
</TYPE.body> </TYPE.body>
<AutoColumn gap="8px"> <AutoColumn gap="8px">
<RowBetween> <RowBetween>
<TYPE.body>V1 Price:</TYPE.body> <TYPE.body>V2 Price:</TYPE.body>
<TYPE.black>
{v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH
</TYPE.black>
</RowBetween>
<RowBetween>
<div />
<TYPE.black> <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> </TYPE.black>
</RowBetween> </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> <RowBetween>
<TYPE.body>V2 Price:</TYPE.body> <TYPE.body>V2 Price:</TYPE.body>
<TYPE.black> <TYPE.black>
{v2SpotPrice?.toSignificant(6)} {token.symbol}/ETH {invertPrice
? `${v2SpotPrice?.invert()?.toSignificant(6)} ${token0.symbol} / ${token1.symbol}`
: `${v2SpotPrice?.toSignificant(6)} ${token1.symbol} / ${token0.symbol}`}
</TYPE.black> </TYPE.black>
</RowBetween> </RowBetween>
<RowBetween> <RowBetween>
<div /> <TYPE.body>V3 Price:</TYPE.body>
<TYPE.black> <TYPE.black>
{v2SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol} {invertPrice
? `${v3SpotPrice?.invert()?.toSignificant(6)} ${token0.symbol} / ${token1.symbol}`
: `${v3SpotPrice?.toSignificant(6)} ${token1.symbol} / ${token0.symbol}`}
</TYPE.black> </TYPE.black>
</RowBetween> </RowBetween>
<RowBetween> <RowBetween>
<TYPE.body color="inherit">Price Difference:</TYPE.body> <TYPE.body color="inherit">Price Difference:</TYPE.body>
<TYPE.black color="inherit">{priceDifferenceAbs?.toSignificant(4)}%</TYPE.black> <TYPE.black color="inherit">{priceDifferenceFraction?.toSignificant(4)}%</TYPE.black>
</RowBetween> </RowBetween>
</AutoColumn> </AutoColumn>
</YellowCard> </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>
</RowBetween>
</AutoColumn>
</PinkCard>
)} )}
<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({ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment