Commit c3f65e3a authored by Noah Zinsmeister's avatar Noah Zinsmeister Committed by GitHub

migration v2 (#38)

* wip

* add some v2 paths

* start to refactor migrate page

update deploy addresses

* fix tests

* stub out v3 remove

* silence some lint warnings
parent 306aaa3a
describe('Remove Liquidity', () => {
it('redirects', () => {
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.visit('/remove/v2/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.url().should(
'contain',
'/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
'/remove/v2/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
)
})
it('eth remove', () => {
cy.visit('/remove/ETH/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.visit('/remove/v2/ETH/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'ETH')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
})
it('eth remove swap order', () => {
cy.visit('/remove/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/ETH')
cy.visit('/remove/v2/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/ETH')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'MKR')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'ETH')
})
it('loads the two correct tokens', () => {
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.visit('/remove/v2/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
})
it('does not crash if ETH is duplicated', () => {
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.visit('/remove/v2/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'WETH')
})
it('token not in storage is loaded', () => {
cy.visit('/remove/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.visit('/remove/v2/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'SKL')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
})
......
......@@ -70,11 +70,11 @@ export function SwapPoolTabs({ active }: { active: 'swap' | 'pool' }) {
)
}
export function FindPoolTabs() {
export function FindPoolTabs({ origin }: { origin: string }) {
return (
<Tabs>
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }}>
<HistoryLink to="/pool">
<HistoryLink to={origin}>
<StyledArrowLeft />
</HistoryLink>
<ActiveText>Import Pool</ActiveText>
......
import React, { useState } from 'react'
import { Percent, TokenAmount } from '@uniswap/sdk-core'
import { JSBI, Pair } from '@uniswap/v2-sdk'
import { ChevronDown, ChevronUp } from 'react-feather'
import { Link } from 'react-router-dom'
import { Text } from 'rebass'
import styled from 'styled-components'
import { useTotalSupply } from '../../data/TotalSupply'
import { useActiveWeb3React } from '../../hooks'
import { useTokenBalance } from '../../state/wallet/hooks'
import { currencyId } from '../../utils/currencyId'
import { unwrappedToken } from '../../utils/wrappedCurrency'
import { ButtonPrimary, ButtonSecondary, ButtonEmpty } from '../Button'
import { transparentize } from 'polished'
import { CardNoise } from '../earn/styled'
import { useColor } from '../../hooks/useColor'
import { LightCard } from '../Card'
import { AutoColumn } from '../Column'
import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
import { RowBetween, RowFixed, AutoRow } from '../Row'
import { Dots } from '../swap/styleds'
import { BIG_INT_ZERO } from '../../constants'
import { FixedHeightRow } from '.'
const StyledPositionCard = styled(LightCard)<{ bgColor: any }>`
border: none;
background: ${({ theme, bgColor }) =>
`radial-gradient(91.85% 100% at 1.84% 0%, ${transparentize(0.8, bgColor)} 0%, ${theme.bg3} 100%) `};
position: relative;
overflow: hidden;
`
interface PositionCardProps {
pair: Pair
showUnwrapped?: boolean
border?: string
stakedBalance?: TokenAmount // optional balance to indicate that liquidity is deposited in mining pool
}
export default function V2PositionCard({ pair, border, stakedBalance }: PositionCardProps) {
const { account } = useActiveWeb3React()
const currency0 = unwrappedToken(pair.token0)
const currency1 = unwrappedToken(pair.token1)
const [showMore, setShowMore] = useState(false)
const userDefaultPoolBalance = useTokenBalance(account ?? undefined, pair.liquidityToken)
const totalPoolTokens = useTotalSupply(pair.liquidityToken)
// if staked balance balance provided, add to standard liquidity amount
const userPoolBalance = stakedBalance ? userDefaultPoolBalance?.add(stakedBalance) : userDefaultPoolBalance
const poolTokenPercentage =
!!userPoolBalance && !!totalPoolTokens && JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
? new Percent(userPoolBalance.raw, totalPoolTokens.raw)
: undefined
const [token0Deposited, token1Deposited] =
!!pair &&
!!totalPoolTokens &&
!!userPoolBalance &&
// this condition is a short-circuit in the case where useTokenBalance updates sooner than useTotalSupply
JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
? [
pair.getLiquidityValue(pair.token0, totalPoolTokens, userPoolBalance, false),
pair.getLiquidityValue(pair.token1, totalPoolTokens, userPoolBalance, false),
]
: [undefined, undefined]
const backgroundColor = useColor(pair?.token0)
return (
<StyledPositionCard border={border} bgColor={backgroundColor}>
<CardNoise />
<AutoColumn gap="12px">
<FixedHeightRow>
<AutoRow gap="8px">
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={20} />
<Text fontWeight={500} fontSize={20}>
{!currency0 || !currency1 ? <Dots>Loading</Dots> : `${currency0.symbol}/${currency1.symbol}`}
</Text>
</AutoRow>
<RowFixed gap="8px">
<ButtonEmpty
padding="6px 8px"
borderRadius="12px"
width="fit-content"
onClick={() => setShowMore(!showMore)}
>
{showMore ? (
<>
Manage
<ChevronUp size="20" style={{ marginLeft: '10px' }} />
</>
) : (
<>
Manage
<ChevronDown size="20" style={{ marginLeft: '10px' }} />
</>
)}
</ButtonEmpty>
</RowFixed>
</FixedHeightRow>
{showMore && (
<AutoColumn gap="8px">
<FixedHeightRow>
<Text fontSize={16} fontWeight={500}>
Your total pool tokens:
</Text>
<Text fontSize={16} fontWeight={500}>
{userPoolBalance ? userPoolBalance.toSignificant(4) : '-'}
</Text>
</FixedHeightRow>
{stakedBalance && (
<FixedHeightRow>
<Text fontSize={16} fontWeight={500}>
Pool tokens in rewards pool:
</Text>
<Text fontSize={16} fontWeight={500}>
{stakedBalance.toSignificant(4)}
</Text>
</FixedHeightRow>
)}
<FixedHeightRow>
<RowFixed>
<Text fontSize={16} fontWeight={500}>
Pooled {currency0.symbol}:
</Text>
</RowFixed>
{token0Deposited ? (
<RowFixed>
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
{token0Deposited?.toSignificant(6)}
</Text>
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency0} />
</RowFixed>
) : (
'-'
)}
</FixedHeightRow>
<FixedHeightRow>
<RowFixed>
<Text fontSize={16} fontWeight={500}>
Pooled {currency1.symbol}:
</Text>
</RowFixed>
{token1Deposited ? (
<RowFixed>
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
{token1Deposited?.toSignificant(6)}
</Text>
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency1} />
</RowFixed>
) : (
'-'
)}
</FixedHeightRow>
<FixedHeightRow>
<Text fontSize={16} fontWeight={500}>
Your pool share:
</Text>
<Text fontSize={16} fontWeight={500}>
{poolTokenPercentage
? (poolTokenPercentage.toFixed(2) === '0.00' ? '<0.01' : poolTokenPercentage.toFixed(2)) + '%'
: '-'}
</Text>
</FixedHeightRow>
{userDefaultPoolBalance && JSBI.greaterThan(userDefaultPoolBalance.raw, BIG_INT_ZERO) && (
<RowBetween marginTop="10px">
<ButtonPrimary
padding="8px"
borderRadius="8px"
as={Link}
to={`/migrate/v2/${pair.liquidityToken.address}`}
width="64%"
>
Migrate
</ButtonPrimary>
<ButtonSecondary
padding="8px"
borderRadius="8px"
as={Link}
width="32%"
to={`/remove/v2/${currencyId(currency0)}/${currencyId(currency1)}`}
>
Remove
</ButtonSecondary>
</RowBetween>
)}
</AutoColumn>
)}
</AutoColumn>
</StyledPositionCard>
)
}
......@@ -317,7 +317,7 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
borderRadius="8px"
as={Link}
width="48%"
to={`/remove/${currencyId(currency0)}/${currencyId(currency1)}`}
to={`/remove/v2/${currencyId(currency0)}/${currencyId(currency1)}`}
>
Remove
</ButtonPrimary>
......
......@@ -47,7 +47,6 @@ export const COMMON_CONTRACT_NAMES: { [address: string]: string } = {
[TIMELOCK_ADDRESS]: 'Timelock',
}
// TODO: specify merkle distributor for mainnet
export const MERKLE_DISTRIBUTOR_ADDRESS: { [chainId in ChainId]?: string } = {
[ChainId.MAINNET]: '0x090D4613473dEE047c3f2706764f49E0821D256e',
}
......@@ -217,4 +216,4 @@ export const BLOCKED_ADDRESSES: string[] = [
export const ARGENT_WALLET_DETECTOR_MAINNET_ADDRESS = '0xeca4B0bDBf7c55E9b7925919d03CbF8Dc82537E8'
export const MIGRATOR_ADDRESS = '0x16D4F26C15f3658ec65B1126ff27DD3dF2a2996b'
export const V1_MIGRATOR_ADDRESS = '0x16D4F26C15f3658ec65B1126ff27DD3dF2a2996b'
......@@ -4,7 +4,7 @@ export const FACTORY_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '',
[ChainId.RINKEBY]: '',
[ChainId.GÖRLI]: '0x2e52A6ed50aeD4396BdC5889ACB9D04F6804D150',
[ChainId.GÖRLI]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62',
[ChainId.KOVAN]: '',
}
......@@ -12,7 +12,7 @@ export const TICK_LENS_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '',
[ChainId.RINKEBY]: '',
[ChainId.GÖRLI]: '0x637df729C49f229B328445AB64573B726F6814dC',
[ChainId.GÖRLI]: '0x8E984b597F19E8D0FDd0b5bAfDb1d0ae4386455f',
[ChainId.KOVAN]: '',
}
......@@ -20,7 +20,7 @@ export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: { [chainId in ChainId]: str
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '',
[ChainId.RINKEBY]: '',
[ChainId.GÖRLI]: '0x514A3562Bbf313B262A4d2377B559CDe75FAC3C9',
[ChainId.GÖRLI]: '0x29e4bF3bFD649b807B4C752c01023E535094F6Bc',
[ChainId.KOVAN]: '',
}
......@@ -28,7 +28,7 @@ export const NONFUNGIBLE_TOKEN_POSITION_DESCRIPTOR_ADDRESSES: { [chainId in Chai
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '',
[ChainId.RINKEBY]: '',
[ChainId.GÖRLI]: '0x2a3DB14c12674986f6F52deaD1d5ee0919715E8A',
[ChainId.GÖRLI]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f',
[ChainId.KOVAN]: '',
}
......@@ -36,6 +36,14 @@ export const SWAP_ROUTER_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '',
[ChainId.RINKEBY]: '',
[ChainId.GÖRLI]: '0x86e14633F8ab81FD49aEF388172A43A7C8731664',
[ChainId.GÖRLI]: '0x71bB3d0e63f2Fa2A5d04d54267211f4Caef7062e',
[ChainId.KOVAN]: '',
}
export const V2_MIGRATOR_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '',
[ChainId.RINKEBY]: '',
[ChainId.GÖRLI]: '0xee9e30637f84Bbf929042A9118c6E20023dab833',
[ChainId.KOVAN]: '',
}
......@@ -15,7 +15,7 @@ import { useUserSingleHopOnly } from 'state/user/hooks'
function useAllCommonPairs(currencyA?: Currency, currencyB?: Currency): Pair[] {
const { chainId } = useActiveWeb3React()
const bases: Token[] = chainId ? BASES_TO_CHECK_TRADES_AGAINST[chainId] : []
const bases: Token[] = useMemo(() => (chainId ? BASES_TO_CHECK_TRADES_AGAINST[chainId] : []), [chainId])
const [tokenA, tokenB] = chainId
? [wrappedCurrency(currencyA, chainId), wrappedCurrency(currencyB, chainId)]
......
......@@ -22,7 +22,7 @@ import {
ARGENT_WALLET_DETECTOR_MAINNET_ADDRESS,
GOVERNANCE_ADDRESS,
MERKLE_DISTRIBUTOR_ADDRESS,
MIGRATOR_ADDRESS,
V1_MIGRATOR_ADDRESS,
UNI,
} from 'constants/index'
import { MULTICALL_ABI, MULTICALL_NETWORKS } from 'constants/multicall'
......@@ -54,8 +54,8 @@ export function useV1FactoryContract(): Contract | null {
return useContract(chainId && V1_FACTORY_ADDRESSES[chainId], V1_FACTORY_ABI, false)
}
export function useV2MigratorContract(): Contract | null {
return useContract(MIGRATOR_ADDRESS, MIGRATOR_ABI, true)
export function useV1MigratorContract(): Contract | null {
return useContract(V1_MIGRATOR_ADDRESS, MIGRATOR_ABI, true)
}
export function useV1ExchangeContract(address?: string, withSignerIfPossible?: boolean): Contract | null {
......
......@@ -23,10 +23,12 @@ import MigrateV1 from './MigrateV1'
import MigrateV1Exchange from './MigrateV1/MigrateV1Exchange'
import RemoveV1Exchange from './MigrateV1/RemoveV1Exchange'
import MigrateV2 from './MigrateV2'
import MigrateV2Pair from './MigrateV2/MigrateV2Pair'
import Pool from './Pool'
import PoolV2 from './Pool/v2'
import PoolFinder from './PoolFinder'
import RemoveLiquidity from './RemoveLiquidity'
import RemoveLiquidityV3 from './RemoveLiquidity/V3'
import { RedirectOldRemoveLiquidityPathStructure } from './RemoveLiquidity/redirects'
import Swap from './Swap'
import { OpenClaimAddressModalAndRedirectToSwap, RedirectPathToSwapOnly, RedirectToSwap } from './Swap/redirects'
......@@ -93,15 +95,19 @@ export default function App() {
<TopLevelModals />
<Web3ReactManager>
<Switch>
<Route exact strict path="/swap" component={Swap} />
<Route exact strict path="/vote" component={Vote} />
<Route exact strict path="/vote/:id" component={VotePage} />
<Route exact strict path="/claim" component={OpenClaimAddressModalAndRedirectToSwap} />
<Route exact strict path="/swap/:outputCurrency" component={RedirectToSwap} />
<Route exact strict path="/uni" component={Earn} />
<Route exact strict path="/uni/:currencyIdA/:currencyIdB" component={Manage} />
<Route exact strict path="/send" component={RedirectPathToSwapOnly} />
<Route exact strict path="/swap/:outputCurrency" component={RedirectToSwap} />
<Route exact strict path="/swap" component={Swap} />
<Route exact strict path="/find" component={PoolFinder} />
<Route exact strict path="/pool/v2" component={PoolV2} />
<Route exact strict path="/pool" component={Pool} />
<Route exact strict path="/v2/pool" component={PoolV2} />
<Route exact strict path="/uni" component={Earn} />
<Route exact strict path="/vote" component={Vote} />
<Route exact strict path="/create" component={RedirectToAddLiquidity} />
<Route exact path="/add" component={AddLiquidity} />
<Route exact path="/add/:currencyIdA" component={RedirectOldAddLiquidityPathStructure} />
......@@ -110,13 +116,15 @@ export default function App() {
<Route exact path="/create/:currencyIdA" component={RedirectOldAddLiquidityPathStructure} />
<Route exact path="/create/:currencyIdA/:currencyIdB" component={RedirectDuplicateTokenIds} />
<Route exact strict path="/remove/v1/:address" component={RemoveV1Exchange} />
<Route exact strict path="/remove/:tokens" component={RedirectOldRemoveLiquidityPathStructure} />
<Route exact strict path="/remove/:currencyIdA/:currencyIdB" component={RemoveLiquidity} />
<Route exact strict path="/remove/v2/:tokens" component={RedirectOldRemoveLiquidityPathStructure} />
<Route exact strict path="/remove/v2/:currencyIdA/:currencyIdB" component={RemoveLiquidity} />
<Route exact strict path="/remove/:currencyIdA/:currencyIdB/:fee" component={RemoveLiquidityV3} />
<Route exact strict path="/migrate/v1" component={MigrateV1} />
<Route exact strict path="/migrate/v1/:address" component={MigrateV1Exchange} />
<Route exact strict path="/migrate/v2" component={MigrateV2} />
<Route exact strict path="/uni/:currencyIdA/:currencyIdB" component={Manage} />
<Route exact strict path="/vote/:id" component={VotePage} />
<Route exact strict path="/migrate/v2/:address" component={MigrateV2Pair} />
<Route component={RedirectPathToSwapOnly} />
</Switch>
</Web3ReactManager>
......
......@@ -14,13 +14,13 @@ import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount'
import QuestionHelper from '../../components/QuestionHelper'
import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
import { Dots } from '../../components/swap/styleds'
import { DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE, MIGRATOR_ADDRESS } from 'constants/index'
import { DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE, V1_MIGRATOR_ADDRESS } from 'constants/index'
import { PairState, usePair } from '../../data/Reserves'
import { useTotalSupply } from '../../data/TotalSupply'
import { useActiveWeb3React } from '../../hooks'
import { useToken } from '../../hooks/Tokens'
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
import { useV1ExchangeContract, useV2MigratorContract } from '../../hooks/useContract'
import { useV1ExchangeContract, useV1MigratorContract } from '../../hooks/useContract'
import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks'
import { useIsTransactionPending, useTransactionAdder } from '../../state/transactions/hooks'
import { useETHBalances, useTokenBalance } from '../../state/wallet/hooks'
......@@ -110,7 +110,7 @@ function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount
? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient)
: new TokenAmount(token, ZERO)
const [approval, approve] = useApproveCallback(liquidityTokenAmount, MIGRATOR_ADDRESS)
const [approval, approve] = useApproveCallback(liquidityTokenAmount, V1_MIGRATOR_ADDRESS)
const v1SpotPrice =
exchangeTokenBalance && exchangeETHBalance
......@@ -140,7 +140,7 @@ function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount
const addTransaction = useTransactionAdder()
const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined)
const migrator = useV2MigratorContract()
const migrator = useV1MigratorContract()
const migrate = useCallback(() => {
if (!minAmountToken || !minAmountETH || !migrator) return
......@@ -182,7 +182,7 @@ function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount
This tool will safely migrate your V1 liquidity to V2 with minimal price risk. The process is completely
trustless thanks to the{' '}
{chainId && (
<ExternalLink href={getEtherscanLink(chainId, MIGRATOR_ADDRESS, 'address')}>
<ExternalLink href={getEtherscanLink(chainId, V1_MIGRATOR_ADDRESS, 'address')}>
<TYPE.blue display="inline">Uniswap migration contract↗</TYPE.blue>
</ExternalLink>
)}
......
import { TransactionResponse } from '@ethersproject/abstract-provider'
import { AddressZero } from '@ethersproject/constants'
import { Currency, CurrencyAmount, Fraction, Percent, Price, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core'
import { JSBI } from '@uniswap/v2-sdk'
import React, { useCallback, useMemo, useState } from 'react'
import ReactGA from 'react-ga'
import { Redirect, RouteComponentProps } from 'react-router'
import { Text } from 'rebass'
import { ButtonConfirmed } from '../../components/Button'
import { LightCard, PinkCard, YellowCard } from '../../components/Card'
import { AutoColumn } from '../../components/Column'
import CurrencyLogo from '../../components/CurrencyLogo'
import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount'
import QuestionHelper from '../../components/QuestionHelper'
import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
import { Dots } from '../../components/swap/styleds'
import { DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE, V1_MIGRATOR_ADDRESS } from 'constants/index'
import { PairState, usePair } from '../../data/Reserves'
import { useTotalSupply } from '../../data/TotalSupply'
import { useActiveWeb3React } from '../../hooks'
import { useToken } from '../../hooks/Tokens'
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
import { usePairContract, useV1MigratorContract } from '../../hooks/useContract'
import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks'
import { useIsTransactionPending, useTransactionAdder } from '../../state/transactions/hooks'
import { useETHBalances, useTokenBalance } from '../../state/wallet/hooks'
import { BackArrow, ExternalLink, TYPE } from '../../theme'
import { getEtherscanLink, isAddress } from '../../utils'
import { BodyWrapper } from '../AppBody'
import { EmptyState } from '../MigrateV1/EmptyState'
import { toV2LiquidityToken } from 'state/user/hooks'
import { V2_MIGRATOR_ADDRESSES } from 'constants/v3'
// 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))
export function V2LiquidityInfo({
token,
liquidityTokenAmount,
tokenWorth,
ethWorth,
}: {
token: Token
liquidityTokenAmount: TokenAmount
tokenWorth: TokenAmount
ethWorth: CurrencyAmount
}) {
const { chainId } = useActiveWeb3React()
return (
<>
<AutoRow style={{ justifyContent: 'flex-start', width: 'fit-content' }}>
<CurrencyLogo size="24px" currency={token} />
<div style={{ marginLeft: '.75rem' }}>
<TYPE.mediumHeader>
{<FormattedCurrencyAmount currencyAmount={liquidityTokenAmount} />}{' '}
{chainId && token.equals(WETH9[chainId]) ? 'WETH' : token.symbol}/ETH
</TYPE.mediumHeader>
</div>
</AutoRow>
<RowBetween my="1rem">
<Text fontSize={16} fontWeight={500}>
Pooled {chainId && token.equals(WETH9[chainId]) ? 'WETH' : token.symbol}:
</Text>
<RowFixed>
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
{tokenWorth.toSignificant(4)}
</Text>
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={token} />
</RowFixed>
</RowBetween>
<RowBetween mb="1rem">
<Text fontSize={16} fontWeight={500}>
Pooled ETH:
</Text>
<RowFixed>
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
<FormattedCurrencyAmount currencyAmount={ethWorth} />
</Text>
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={Currency.ETHER} />
</RowFixed>
</RowBetween>
</>
)
}
function V2PairMigration({
pairBalance,
totalSupply,
reserve0,
reserve1,
token0,
token1,
}: {
pairBalance: TokenAmount
totalSupply: TokenAmount
reserve0: TokenAmount
reserve1: TokenAmount
token0: Token
token1: Token
}) {
const { account, chainId } = useActiveWeb3React()
// this is just getLiquidityValue with the fee off, but for the passed pair
const token0Value = useMemo(
() => new TokenAmount(token0, JSBI.divide(JSBI.multiply(pairBalance.raw, reserve0.raw), totalSupply.raw)),
[token0, pairBalance, reserve0, totalSupply]
)
const token1Value = useMemo(
() => new TokenAmount(token1, JSBI.divide(JSBI.multiply(pairBalance.raw, reserve1.raw), totalSupply.raw)),
[token1, pairBalance, reserve1, totalSupply]
)
const v2SpotPrice = new Price(token0, token1, reserve0.raw, reserve1.raw)
// const isFirstLiquidityProvider: boolean = false // check for v3 pair existence
// const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false)
// const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(null)
// const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION
// const ethWorth: CurrencyAmount = exchangeETHBalance
// ? CurrencyAmount.ether(exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient)
// : CurrencyAmount.ether(ZERO)
// const tokenWorth: TokenAmount = exchangeTokenBalance
// ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient)
// : new TokenAmount(token, ZERO)
// const [approval, approve] = useApproveCallback(liquidityTokenAmount, V1_MIGRATOR_ADDRESS)
// const v1SpotPrice =
// exchangeTokenBalance && exchangeETHBalance
// ? exchangeTokenBalance.divide(new Fraction(exchangeETHBalance.raw, WEI_DENOM))
// : null
// const priceDifferenceFraction: Fraction | undefined =
// v1SpotPrice && v2SpotPrice ? v1SpotPrice.divide(v2SpotPrice).multiply('100').subtract('100') : undefined
// const priceDifferenceAbs: Fraction | undefined = priceDifferenceFraction?.lessThan(ZERO)
// ? priceDifferenceFraction?.multiply('-1')
// : priceDifferenceFraction
// const minAmountETH: JSBI | undefined =
// v2SpotPrice && tokenWorth
// ? tokenWorth.divide(v2SpotPrice).multiply(WEI_DENOM).multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient
// : ethWorth?.numerator
// 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 addTransaction = useTransactionAdder()
// const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined)
// const migrator = useV1MigratorContract()
// const migrate = useCallback(() => {
// if (!minAmountToken || !minAmountETH || !migrator) return
// 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,
// })
// addTransaction(response, {
// summary: `Migrate ${token.symbol} liquidity to V2`,
// })
// setPendingMigrationHash(response.hash)
// })
// .catch(() => {
// setConfirmingMigration(false)
// })
// }, [minAmountToken, minAmountETH, migrator, token, account, addTransaction])
// const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO)
// const largePriceDifference = !!priceDifferenceAbs && !priceDifferenceAbs.lessThan(JSBI.BigInt(5))
// const isSuccessfullyMigrated = !!pendingMigrationHash && noLiquidityTokens
return (
<AutoColumn gap="20px">
<TYPE.body my={9} style={{ fontWeight: 400 }}>
This tool will safely migrate your V2 liquidity to V3 with minimal price risk. The process is completely
trustless thanks to the{' '}
{chainId && (
<ExternalLink href={getEtherscanLink(chainId, V2_MIGRATOR_ADDRESSES[chainId], 'address')}>
<TYPE.blue display="inline">Uniswap migration contract↗</TYPE.blue>
</ExternalLink>
)}
.
</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>
<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}
</TYPE.black>
</RowBetween>
</AutoColumn>
</PinkCard>
)}
<LightCard>
<V2LiquidityInfo
token={token}
liquidityTokenAmount={liquidityTokenAmount}
tokenWorth={tokenWorth}
ethWorth={ethWorth}
/>
<div style={{ display: 'flex', marginTop: '1rem' }}>
<AutoColumn gap="12px" style={{ flex: '1', marginRight: 12 }}>
<ButtonConfirmed
confirmed={approval === ApprovalState.APPROVED}
disabled={approval !== ApprovalState.NOT_APPROVED}
onClick={approve}
>
{approval === ApprovalState.PENDING ? (
<Dots>Approving</Dots>
) : approval === ApprovalState.APPROVED ? (
'Approved'
) : (
'Approve'
)}
</ButtonConfirmed>
</AutoColumn>
<AutoColumn gap="12px" style={{ flex: '1' }}>
<ButtonConfirmed
confirmed={isSuccessfullyMigrated}
disabled={
isSuccessfullyMigrated ||
noLiquidityTokens ||
isMigrationPending ||
approval !== ApprovalState.APPROVED ||
confirmingMigration
}
onClick={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> */}
</AutoColumn>
)
}
export default function MigrateV2Pair({
match: {
params: { address },
},
}: RouteComponentProps<{ address: string }>) {
const { chainId, account } = useActiveWeb3React()
// get pair contract
const validatedAddress = isAddress(address)
const pair = usePairContract(validatedAddress ? validatedAddress : undefined)
// get token addresses from pair contract
const token0AddressCallState = useSingleCallResult(pair, 'token0', undefined, NEVER_RELOAD)
const token0Address = token0AddressCallState?.result?.[0]
const token1Address = useSingleCallResult(pair, 'token1', undefined, NEVER_RELOAD)?.result?.[0]
// get tokens
const token0 = useToken(token0Address)
const token1 = useToken(token1Address)
// get liquidity token balance
const liquidityToken: Token | undefined = useMemo(
() => (chainId && validatedAddress ? new Token(chainId, validatedAddress, 18, 'UNI-V2', 'Uniswap V2') : undefined),
[chainId, validatedAddress]
)
// get data required for V2 pair migration
const pairBalance = useTokenBalance(account ?? undefined, liquidityToken)
const totalSupply = useTotalSupply(liquidityToken)
const [reserve0Raw, reserve1Raw] = useSingleCallResult(pair, 'getReserves')?.result ?? []
const reserve0 = useMemo(() => (token0 && reserve0Raw ? new TokenAmount(token0, reserve0Raw) : undefined), [
token0,
reserve0Raw,
])
const reserve1 = useMemo(() => (token1 && reserve1Raw ? new TokenAmount(token1, reserve1Raw) : undefined), [
token1,
reserve1Raw,
])
// redirect for invalid url params
if (
!validatedAddress ||
(pair &&
token0AddressCallState?.valid &&
!token0AddressCallState?.loading &&
!token0AddressCallState?.error &&
!token0Address)
) {
console.error('Invalid pair address')
return <Redirect to="/migrate/v2" />
}
return (
<BodyWrapper style={{ padding: 24 }}>
<AutoColumn gap="16px">
<AutoRow style={{ alignItems: 'center', justifyContent: 'space-between' }} gap="8px">
<BackArrow to="/migrate/v2" />
<TYPE.mediumHeader>Migrate V2 Liquidity</TYPE.mediumHeader>
<div>
<QuestionHelper text="Migrate your liquidity tokens from Uniswap V2 to Uniswap V3." />
</div>
</AutoRow>
{!account ? (
<TYPE.largeHeader>You must connect an account.</TYPE.largeHeader>
) : pairBalance && totalSupply && reserve0 && reserve1 && token0 && token1 ? (
<V2PairMigration
pairBalance={pairBalance}
totalSupply={totalSupply}
reserve0={reserve0}
reserve1={reserve1}
token0={token0}
token1={token1}
/>
) : (
<EmptyState message="Loading..." />
)}
</AutoColumn>
</BodyWrapper>
)
}
import React from 'react'
import React, { useContext, useMemo } from 'react'
import { Pair } from '@uniswap/v2-sdk'
import { ThemeContext } from 'styled-components'
import { AutoColumn } from '../../components/Column'
import { AutoRow } from '../../components/Row'
import { Text } from 'rebass'
import { useActiveWeb3React } from '../../hooks'
import { useTokenBalancesWithLoadingIndicator } from '../../state/wallet/hooks'
import { BackArrow, StyledInternalLink, TYPE } from '../../theme'
import { LightCard } from '../../components/Card'
import { BodyWrapper } from '../AppBody'
import { EmptyState } from '../MigrateV1/EmptyState'
import QuestionHelper from '../../components/QuestionHelper'
import { Dots } from '../../components/swap/styleds'
import { toV2LiquidityToken, useTrackedTokenPairs } from '../../state/user/hooks'
import { usePairs } from 'data/Reserves'
import MigrateV2PositionCard from 'components/PositionCard/V2'
export default function MigrateV1() {
return <BodyWrapper style={{ padding: 24 }}>migrate v2 liquidity to v3</BodyWrapper>
// TODO there's a bug in loading where "No V2 Liquidity found" flashes
// TODO add support for more pairs
export default function MigrateV2() {
const theme = useContext(ThemeContext)
const { account } = useActiveWeb3React()
// fetch the user's balances of all tracked V2 LP tokens
const trackedTokenPairs = useTrackedTokenPairs()
const tokenPairsWithLiquidityTokens = useMemo(
() => trackedTokenPairs.map((tokens) => ({ liquidityToken: toV2LiquidityToken(tokens), tokens })),
[trackedTokenPairs]
)
const liquidityTokens = useMemo(() => tokenPairsWithLiquidityTokens.map((tpwlt) => tpwlt.liquidityToken), [
tokenPairsWithLiquidityTokens,
])
const [v2PairsBalances, fetchingV2PairBalances] = useTokenBalancesWithLoadingIndicator(
account ?? undefined,
liquidityTokens
)
// fetch the reserves for all V2 pools in which the user has a balance
const liquidityTokensWithBalances = useMemo(
() =>
tokenPairsWithLiquidityTokens.filter(({ liquidityToken }) =>
v2PairsBalances[liquidityToken.address]?.greaterThan('0')
),
[tokenPairsWithLiquidityTokens, v2PairsBalances]
)
const v2Pairs = usePairs(liquidityTokensWithBalances.map(({ tokens }) => tokens))
const v2IsLoading =
fetchingV2PairBalances || v2Pairs?.length < liquidityTokensWithBalances.length || v2Pairs?.some((V2Pair) => !V2Pair)
const allV2PairsWithLiquidity = v2Pairs.map(([, pair]) => pair).filter((v2Pair): v2Pair is Pair => Boolean(v2Pair))
return (
<BodyWrapper style={{ padding: 24 }}>
<AutoColumn gap="16px">
<AutoRow style={{ alignItems: 'center', justifyContent: 'space-between' }} gap="8px">
<BackArrow to="/pool" />
<TYPE.mediumHeader>Migrate V2 Liquidity</TYPE.mediumHeader>
<div>
<QuestionHelper text="Migrate your liquidity tokens from Uniswap V2 to Uniswap V3." />
</div>
</AutoRow>
<TYPE.body style={{ marginBottom: 8, fontWeight: 400 }}>
For each pool shown below, click migrate to remove your liquidity from Uniswap V2 and deposit it into Uniswap
V3.
</TYPE.body>
{!account ? (
<LightCard padding="40px">
<TYPE.body color={theme.text3} textAlign="center">
Connect to a wallet to view your V2 liquidity.
</TYPE.body>
</LightCard>
) : v2IsLoading ? (
<LightCard padding="40px">
<TYPE.body color={theme.text3} textAlign="center">
<Dots>Loading</Dots>
</TYPE.body>
</LightCard>
) : allV2PairsWithLiquidity?.length > 0 ? (
<>
{allV2PairsWithLiquidity.map((pair) => (
<MigrateV2PositionCard key={pair.liquidityToken.address} pair={pair} />
))}
</>
) : (
<EmptyState message="No V2 Liquidity found." />
)}
<AutoColumn justify={'center'} gap="md">
<Text textAlign="center" fontSize={14} style={{ padding: '.5rem 0 .5rem 0' }}>
{"Don't see a pool you joined?"}{' '}
<StyledInternalLink id="import-pool-link" to={'/find?origin=/migrate/v2'}>
{'Import it.'}
</StyledInternalLink>
</Text>
</AutoColumn>
</AutoColumn>
</BodyWrapper>
)
}
......@@ -21,13 +21,20 @@ import AppBody from '../AppBody'
import { Dots } from '../Pool/styleds'
import { BlueCard } from '../../components/Card'
import { TYPE } from '../../theme'
import { useLocation } from 'react-router'
enum Fields {
TOKEN0 = 0,
TOKEN1 = 1,
}
function useQuery() {
return new URLSearchParams(useLocation().search)
}
export default function PoolFinder() {
const query = useQuery()
const { account } = useActiveWeb3React()
const [showSearch, setShowSearch] = useState<boolean>(false)
......@@ -81,7 +88,7 @@ export default function PoolFinder() {
return (
<AppBody>
<FindPoolTabs />
<FindPoolTabs origin={query.get('origin') ?? '/pool'} />
<AutoColumn style={{ padding: '1rem' }} gap="md">
<BlueCard>
<AutoColumn gap="10px">
......
import React from 'react'
import { RouteComponentProps } from 'react-router'
import AppBody from '../AppBody'
// TODO
export default function RemoveLiquidityV3({
match: {
params: { currencyIdA, currencyIdB, fee },
},
}: RouteComponentProps<{ currencyIdA: string; currencyIdB: string; fee: string }>) {
return <AppBody>TODO</AppBody>
}
......@@ -439,9 +439,9 @@ export default function RemoveLiquidity({
const handleSelectCurrencyA = useCallback(
(currency: Currency) => {
if (currencyIdB && currencyId(currency) === currencyIdB) {
history.push(`/remove/${currencyId(currency)}/${currencyIdA}`)
history.push(`/remove/v2/${currencyId(currency)}/${currencyIdA}`)
} else {
history.push(`/remove/${currencyId(currency)}/${currencyIdB}`)
history.push(`/remove/v2/${currencyId(currency)}/${currencyIdB}`)
}
},
[currencyIdA, currencyIdB, history]
......@@ -449,9 +449,9 @@ export default function RemoveLiquidity({
const handleSelectCurrencyB = useCallback(
(currency: Currency) => {
if (currencyIdA && currencyId(currency) === currencyIdA) {
history.push(`/remove/${currencyIdB}/${currencyId(currency)}`)
history.push(`/remove/v2/${currencyIdB}/${currencyId(currency)}`)
} else {
history.push(`/remove/${currencyIdA}/${currencyId(currency)}`)
history.push(`/remove/v2/${currencyIdA}/${currencyId(currency)}`)
}
},
[currencyIdA, currencyIdB, history]
......@@ -573,7 +573,7 @@ export default function RemoveLiquidity({
<RowBetween style={{ justifyContent: 'flex-end' }}>
{oneCurrencyIsETH ? (
<StyledInternalLink
to={`/remove/${currencyA === ETHER ? WETH9[chainId].address : currencyIdA}/${
to={`/remove/v2/${currencyA === ETHER ? WETH9[chainId].address : currencyIdA}/${
currencyB === ETHER ? WETH9[chainId].address : currencyIdB
}`}
>
......@@ -581,7 +581,7 @@ export default function RemoveLiquidity({
</StyledInternalLink>
) : oneCurrencyIsWETH ? (
<StyledInternalLink
to={`/remove/${
to={`/remove/v2/${
currencyA && currencyEquals(currencyA, WETH9[chainId]) ? 'ETH' : currencyIdA
}/${currencyB && currencyEquals(currencyB, WETH9[chainId]) ? 'ETH' : currencyIdB}`}
>
......
......@@ -9,9 +9,9 @@ export function RedirectOldRemoveLiquidityPathStructure({
},
}: RouteComponentProps<{ tokens: string }>) {
if (!OLD_PATH_STRUCTURE.test(tokens)) {
return <Redirect to="/pool" />
return <Redirect to="/pool/v2" />
}
const [currency0, currency1] = tokens.split('-')
return <Redirect to={`/remove/${currency0}/${currency1}`} />
return <Redirect to={`/remove/v2/${currency0}/${currency1}`} />
}
......@@ -8,7 +8,7 @@ import { ethers, utils } from 'ethers'
import { calculateGasMargin } from '../../utils'
import { TransactionResponse } from '@ethersproject/providers'
import { useTransactionAdder } from '../transactions/hooks'
import { useState, useEffect, useCallback } from 'react'
import { useState, useEffect, useCallback, useMemo } from 'react'
import { abi as GOV_ABI } from '@uniswap/governance/build/GovernorAlpha.json'
interface ProposalDetail {
......@@ -49,14 +49,16 @@ export function useProposalCount(): number | undefined {
* Need proposal events to get description data emitted from
* new proposal event.
*/
const eventParser = new ethers.utils.Interface(GOV_ABI)
export function useDataFromEventLogs() {
const { library } = useActiveWeb3React()
const [formattedEvents, setFormattedEvents] = useState<any>()
const govContract = useGovernanceContract()
// create filter for these specific events
const filter = { ...govContract?.filters?.['ProposalCreated'](), fromBlock: 0, toBlock: 'latest' }
const eventParser = new ethers.utils.Interface(GOV_ABI)
const filter = useMemo(() => ({ ...govContract?.filters?.['ProposalCreated'](), fromBlock: 0, toBlock: 'latest' }), [
govContract,
])
useEffect(() => {
async function fetchData() {
......@@ -88,7 +90,7 @@ export function useDataFromEventLogs() {
if (!formattedEvents) {
fetchData()
}
}, [eventParser, filter, library, formattedEvents])
}, [filter, library, formattedEvents])
return formattedEvents
}
......
......@@ -13,7 +13,6 @@ export const STAKING_GENESIS = 1600387200
export const REWARDS_DURATION_DAYS = 60
// TODO add staking rewards addresses here
export const STAKING_REWARDS_INFO: {
[chainId in ChainId]?: {
tokens: [Token, Token]
......
import { useEffect } from 'react'
import { useEffect, useMemo } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useActiveWeb3React } from '../../hooks'
import { useAddPopup, useBlockNumber } from '../application/hooks'
......@@ -37,7 +37,7 @@ export default function Updater(): null {
const dispatch = useDispatch<AppDispatch>()
const state = useSelector<AppState, AppState['transactions']>((state) => state.transactions)
const transactions = chainId ? state[chainId] ?? {} : {}
const transactions = useMemo(() => (chainId ? state[chainId] ?? {} : {}), [chainId, state])
// show popup on confirm
const addPopup = useAddPopup()
......
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