Merge remote-tracking branch 'refs/remotes/origin/main'

This commit is contained in:
Noah Zinsmeister 2021-04-22 18:36:20 -04:00
commit 6b99309fab
No known key found for this signature in database
GPG Key ID: 83022DD49188C9F2
31 changed files with 169 additions and 2397 deletions

@ -1,8 +0,0 @@
describe('Migrate V1 Liquidity', () => {
describe('Remove V1 liquidity', () => {
it('renders the correct page', () => {
cy.visit('/remove/v1/0x93bB63aFe1E0180d0eF100D774B473034fd60C36')
cy.get('#remove-v1-exchange').should('contain', 'MKR/ETH')
})
})
})

@ -12,19 +12,19 @@ describe('Remove Liquidity', () => {
})
it('loads the two correct tokens', () => {
cy.visit('/remove/v2/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/v2/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/v2/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')
})

@ -50,7 +50,7 @@
"@uniswap/v2-sdk": "^1.0.7",
"@uniswap/v3-core": "^1.0.0-rc.2",
"@uniswap/v3-periphery": "^1.0.0-beta.20",
"@uniswap/v3-sdk": "^1.0.0-alpha.13",
"@uniswap/v3-sdk": "^1.0.0-alpha.17",
"@web3-react/core": "^6.0.9",
"@web3-react/fortmatic-connector": "^6.0.9",
"@web3-react/injected-connector": "^6.0.7",

@ -116,23 +116,23 @@ export default function Menu() {
{open && (
<MenuFlyout>
<MenuItem id="link" href="https://uniswap.org/">
<MenuItem href="https://uniswap.org/">
<Info size={14} />
About
</MenuItem>
<MenuItem id="link" href="https://uniswap.org/docs/v2">
<MenuItem href="https://uniswap.org/docs/v2">
<BookOpen size={14} />
Docs
</MenuItem>
<MenuItem id="link" href={CODE_LINK}>
<MenuItem href={CODE_LINK}>
<Code size={14} />
Code
</MenuItem>
<MenuItem id="link" href="https://discord.gg/FCfyBSbCU5">
<MenuItem href="https://discord.gg/FCfyBSbCU5">
<MessageCircle size={14} />
Discord
</MenuItem>
<MenuItem id="link" href="https://uniswap.info/">
<MenuItem href="https://uniswap.info/">
<PieChart size={14} />
Analytics
</MenuItem>
@ -175,7 +175,7 @@ export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, men
{open && (
<NewMenuFlyout flyoutAlignment={flyoutAlignment}>
{menuItems.map(({ content, link }, i) => (
<NewMenuItem id="link" href={link} key={link + i}>
<NewMenuItem href={link} key={i}>
{content}
</NewMenuItem>
))}

@ -1,68 +0,0 @@
import React, { useContext } from 'react'
import { Link, RouteComponentProps, withRouter } from 'react-router-dom'
import { Token, TokenAmount, WETH9 } from '@uniswap/sdk-core'
import { Text } from 'rebass'
import { ThemeContext } from 'styled-components'
import { useActiveWeb3React } from '../../hooks'
import { ButtonSecondary } from '../Button'
import { AutoColumn } from '../Column'
import DoubleCurrencyLogo from '../DoubleLogo'
import { RowBetween, RowFixed } from '../Row'
import { FixedHeightRow, HoverCard } from './index'
interface PositionCardProps extends RouteComponentProps<Record<string, any>> {
token: Token
V1LiquidityBalance: TokenAmount
}
function V1PositionCard({ token, V1LiquidityBalance }: PositionCardProps) {
const theme = useContext(ThemeContext)
const { chainId } = useActiveWeb3React()
return (
<HoverCard>
<AutoColumn gap="12px">
<FixedHeightRow>
<RowFixed>
<DoubleCurrencyLogo currency0={token} margin={true} size={20} />
<Text fontWeight={500} fontSize={20} style={{ marginLeft: '' }}>
{`${chainId && token.equals(WETH9[chainId]) ? 'WETH' : token.symbol}/ETH`}
</Text>
<Text
fontSize={12}
fontWeight={500}
ml="0.5rem"
px="0.75rem"
py="0.25rem"
style={{ borderRadius: '1rem' }}
backgroundColor={theme.yellow1}
color={'black'}
>
V1
</Text>
</RowFixed>
</FixedHeightRow>
<AutoColumn gap="8px">
<RowBetween marginTop="10px">
<ButtonSecondary width="68%" as={Link} to={`/migrate/v1/${V1LiquidityBalance.token.address}`}>
Migrate
</ButtonSecondary>
<ButtonSecondary
style={{ backgroundColor: 'transparent' }}
width="28%"
as={Link}
to={`/remove/v1/${V1LiquidityBalance.token.address}`}
>
Remove
</ButtonSecondary>
</RowBetween>
</AutoColumn>
</AutoColumn>
</HoverCard>
)
}
export default withRouter(V1PositionCard)

@ -1,17 +0,0 @@
import { Interface } from '@ethersproject/abi'
import { ChainId } from '@uniswap/sdk-core'
import V1_EXCHANGE_ABI from './v1_exchange.json'
import V1_FACTORY_ABI from './v1_factory.json'
const V1_FACTORY_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95',
[ChainId.ROPSTEN]: '0x9c83dCE8CA20E9aAF9D3efc003b2ea62aBC08351',
[ChainId.RINKEBY]: '0xf5D915570BC477f9B8D6C0E980aA81757A3AaC36',
[ChainId.GÖRLI]: '0x6Ce570d02D73d4c384b46135E87f8C592A8c86dA',
[ChainId.KOVAN]: '0xD3E51Ef092B2845f10401a0159B2B96e8B6c3D30',
}
const V1_FACTORY_INTERFACE = new Interface(V1_FACTORY_ABI)
const V1_EXCHANGE_INTERFACE = new Interface(V1_EXCHANGE_ABI)
export { V1_FACTORY_ADDRESSES, V1_FACTORY_INTERFACE, V1_FACTORY_ABI, V1_EXCHANGE_INTERFACE, V1_EXCHANGE_ABI }

@ -1,971 +0,0 @@
[
{
"name": "TokenPurchase",
"inputs": [
{
"type": "address",
"name": "buyer",
"indexed": true
},
{
"type": "uint256",
"name": "eth_sold",
"indexed": true
},
{
"type": "uint256",
"name": "tokens_bought",
"indexed": true
}
],
"anonymous": false,
"type": "event"
},
{
"name": "EthPurchase",
"inputs": [
{
"type": "address",
"name": "buyer",
"indexed": true
},
{
"type": "uint256",
"name": "tokens_sold",
"indexed": true
},
{
"type": "uint256",
"name": "eth_bought",
"indexed": true
}
],
"anonymous": false,
"type": "event"
},
{
"name": "AddLiquidity",
"inputs": [
{
"type": "address",
"name": "provider",
"indexed": true
},
{
"type": "uint256",
"name": "eth_amount",
"indexed": true
},
{
"type": "uint256",
"name": "token_amount",
"indexed": true
}
],
"anonymous": false,
"type": "event"
},
{
"name": "RemoveLiquidity",
"inputs": [
{
"type": "address",
"name": "provider",
"indexed": true
},
{
"type": "uint256",
"name": "eth_amount",
"indexed": true
},
{
"type": "uint256",
"name": "token_amount",
"indexed": true
}
],
"anonymous": false,
"type": "event"
},
{
"name": "Transfer",
"inputs": [
{
"type": "address",
"name": "_from",
"indexed": true
},
{
"type": "address",
"name": "_to",
"indexed": true
},
{
"type": "uint256",
"name": "_value",
"indexed": false
}
],
"anonymous": false,
"type": "event"
},
{
"name": "Approval",
"inputs": [
{
"type": "address",
"name": "_owner",
"indexed": true
},
{
"type": "address",
"name": "_spender",
"indexed": true
},
{
"type": "uint256",
"name": "_value",
"indexed": false
}
],
"anonymous": false,
"type": "event"
},
{
"name": "setup",
"outputs": [],
"inputs": [
{
"type": "address",
"name": "token_addr"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "addLiquidity",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "min_liquidity"
},
{
"type": "uint256",
"name": "max_tokens"
},
{
"type": "uint256",
"name": "deadline"
}
],
"constant": false,
"payable": true,
"type": "function"
},
{
"name": "removeLiquidity",
"outputs": [
{
"type": "uint256",
"name": "outA"
},
{
"type": "uint256",
"name": "outB"
}
],
"inputs": [
{
"type": "uint256",
"name": "amount"
},
{
"type": "uint256",
"name": "min_eth"
},
{
"type": "uint256",
"name": "min_tokens"
},
{
"type": "uint256",
"name": "deadline"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "__default__",
"outputs": [],
"inputs": [],
"constant": false,
"payable": true,
"type": "function"
},
{
"name": "ethToTokenSwapInput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "min_tokens"
},
{
"type": "uint256",
"name": "deadline"
}
],
"constant": false,
"payable": true,
"type": "function"
},
{
"name": "ethToTokenTransferInput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "min_tokens"
},
{
"type": "uint256",
"name": "deadline"
},
{
"type": "address",
"name": "recipient"
}
],
"constant": false,
"payable": true,
"type": "function"
},
{
"name": "ethToTokenSwapOutput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_bought"
},
{
"type": "uint256",
"name": "deadline"
}
],
"constant": false,
"payable": true,
"type": "function"
},
{
"name": "ethToTokenTransferOutput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_bought"
},
{
"type": "uint256",
"name": "deadline"
},
{
"type": "address",
"name": "recipient"
}
],
"constant": false,
"payable": true,
"type": "function"
},
{
"name": "tokenToEthSwapInput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_sold"
},
{
"type": "uint256",
"name": "min_eth"
},
{
"type": "uint256",
"name": "deadline"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "tokenToEthTransferInput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_sold"
},
{
"type": "uint256",
"name": "min_eth"
},
{
"type": "uint256",
"name": "deadline"
},
{
"type": "address",
"name": "recipient"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "tokenToEthSwapOutput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "eth_bought"
},
{
"type": "uint256",
"name": "max_tokens"
},
{
"type": "uint256",
"name": "deadline"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "tokenToEthTransferOutput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "eth_bought"
},
{
"type": "uint256",
"name": "max_tokens"
},
{
"type": "uint256",
"name": "deadline"
},
{
"type": "address",
"name": "recipient"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "tokenToTokenSwapInput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_sold"
},
{
"type": "uint256",
"name": "min_tokens_bought"
},
{
"type": "uint256",
"name": "min_eth_bought"
},
{
"type": "uint256",
"name": "deadline"
},
{
"type": "address",
"name": "token_addr"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "tokenToTokenTransferInput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_sold"
},
{
"type": "uint256",
"name": "min_tokens_bought"
},
{
"type": "uint256",
"name": "min_eth_bought"
},
{
"type": "uint256",
"name": "deadline"
},
{
"type": "address",
"name": "recipient"
},
{
"type": "address",
"name": "token_addr"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "tokenToTokenSwapOutput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_bought"
},
{
"type": "uint256",
"name": "max_tokens_sold"
},
{
"type": "uint256",
"name": "max_eth_sold"
},
{
"type": "uint256",
"name": "deadline"
},
{
"type": "address",
"name": "token_addr"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "tokenToTokenTransferOutput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_bought"
},
{
"type": "uint256",
"name": "max_tokens_sold"
},
{
"type": "uint256",
"name": "max_eth_sold"
},
{
"type": "uint256",
"name": "deadline"
},
{
"type": "address",
"name": "recipient"
},
{
"type": "address",
"name": "token_addr"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "tokenToExchangeSwapInput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_sold"
},
{
"type": "uint256",
"name": "min_tokens_bought"
},
{
"type": "uint256",
"name": "min_eth_bought"
},
{
"type": "uint256",
"name": "deadline"
},
{
"type": "address",
"name": "exchange_addr"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "tokenToExchangeTransferInput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_sold"
},
{
"type": "uint256",
"name": "min_tokens_bought"
},
{
"type": "uint256",
"name": "min_eth_bought"
},
{
"type": "uint256",
"name": "deadline"
},
{
"type": "address",
"name": "recipient"
},
{
"type": "address",
"name": "exchange_addr"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "tokenToExchangeSwapOutput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_bought"
},
{
"type": "uint256",
"name": "max_tokens_sold"
},
{
"type": "uint256",
"name": "max_eth_sold"
},
{
"type": "uint256",
"name": "deadline"
},
{
"type": "address",
"name": "exchange_addr"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "tokenToExchangeTransferOutput",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_bought"
},
{
"type": "uint256",
"name": "max_tokens_sold"
},
{
"type": "uint256",
"name": "max_eth_sold"
},
{
"type": "uint256",
"name": "deadline"
},
{
"type": "address",
"name": "recipient"
},
{
"type": "address",
"name": "exchange_addr"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "getEthToTokenInputPrice",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "eth_sold"
}
],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "getEthToTokenOutputPrice",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_bought"
}
],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "getTokenToEthInputPrice",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "tokens_sold"
}
],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "getTokenToEthOutputPrice",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "eth_bought"
}
],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "tokenAddress",
"outputs": [
{
"type": "address",
"name": "out"
}
],
"inputs": [],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "factoryAddress",
"outputs": [
{
"type": "address",
"name": "out"
}
],
"inputs": [],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "balanceOf",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "address",
"name": "_owner"
}
],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "transfer",
"outputs": [
{
"type": "bool",
"name": "out"
}
],
"inputs": [
{
"type": "address",
"name": "_to"
},
{
"type": "uint256",
"name": "_value"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "transferFrom",
"outputs": [
{
"type": "bool",
"name": "out"
}
],
"inputs": [
{
"type": "address",
"name": "_from"
},
{
"type": "address",
"name": "_to"
},
{
"type": "uint256",
"name": "_value"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "approve",
"outputs": [
{
"type": "bool",
"name": "out"
}
],
"inputs": [
{
"type": "address",
"name": "_spender"
},
{
"type": "uint256",
"name": "_value"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "allowance",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "address",
"name": "_owner"
},
{
"type": "address",
"name": "_spender"
}
],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "name",
"outputs": [
{
"type": "bytes32",
"name": "out"
}
],
"inputs": [],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "symbol",
"outputs": [
{
"type": "bytes32",
"name": "out"
}
],
"inputs": [],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "decimals",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "totalSupply",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [],
"constant": true,
"payable": false,
"type": "function"
}
]

@ -1,67 +0,0 @@
[
{
"name": "NewExchange",
"inputs": [
{ "type": "address", "name": "token", "indexed": true },
{ "type": "address", "name": "exchange", "indexed": true }
],
"anonymous": false,
"type": "event"
},
{
"name": "initializeFactory",
"outputs": [],
"inputs": [{ "type": "address", "name": "template" }],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "createExchange",
"outputs": [{ "type": "address", "name": "out" }],
"inputs": [{ "type": "address", "name": "token" }],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "getExchange",
"outputs": [{ "type": "address", "name": "out" }],
"inputs": [{ "type": "address", "name": "token" }],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "getToken",
"outputs": [{ "type": "address", "name": "out" }],
"inputs": [{ "type": "address", "name": "exchange" }],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "getTokenWithId",
"outputs": [{ "type": "address", "name": "out" }],
"inputs": [{ "type": "uint256", "name": "token_id" }],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "exchangeTemplate",
"outputs": [{ "type": "address", "name": "out" }],
"inputs": [],
"constant": true,
"payable": false,
"type": "function"
},
{
"name": "tokenCount",
"outputs": [{ "type": "uint256", "name": "out" }],
"inputs": [],
"constant": true,
"payable": false,
"type": "function"
}
]

@ -1,49 +1,31 @@
import { ChainId } from '@uniswap/sdk-core'
export const FACTORY_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '0x864e344eCd7f3a9A4368dEC11Be8104db5770364',
[ChainId.RINKEBY]: '0xAE28628c0fdFb5e54d60FEDC6C9085199aec14dF',
[ChainId.GÖRLI]: '0x864e344eCd7f3a9A4368dEC11Be8104db5770364',
[ChainId.KOVAN]: '0xd4013a706fa79487989b595Df35eF8AD1ffBb422',
export const V3_CORE_FACTORY_ADDRESSES: { [chainId in ChainId]?: string } = {
[ChainId.RINKEBY]: '0xFeabCc62240297F1e4b238937D68e7516f0918D7',
[ChainId.GÖRLI]: '0xA31B47971cdC5376E41CfA2D4378912156ab1F10',
[ChainId.KOVAN]: '0x58f6b77148BE49BF7898472268ae8f26377d0AA6',
}
export const TICK_LENS_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '0xc97c7D6C2F1EE518bE4D4B8566bcEb917dED4F39',
[ChainId.RINKEBY]: '0x2d31366B7D446d629ac36933F12bdbca96860f84',
[ChainId.GÖRLI]: '0xc97c7D6C2F1EE518bE4D4B8566bcEb917dED4F39',
[ChainId.KOVAN]: '0xD2AAa0217a203d9FaB6e5272b211Be2Aba52f385',
export const TICK_LENS_ADDRESSES: { [chainId in ChainId]?: string } = {
[ChainId.RINKEBY]: '0x3d137e860008BaF6d1c063158e5ec0baBbcFefF8',
[ChainId.GÖRLI]: '0x80AacDBEe92DC1c2Fbaa261Fb369696AF1AD9f98',
[ChainId.KOVAN]: '0xB79bDE60fc227217f4EE2102dC93fa1264E33DaB',
}
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '0xc97c7D6C2F1EE518bE4D4B8566bcEb917dED4F39',
[ChainId.RINKEBY]: '0xbBca0fFBFE60F60071630A8c80bb6253dC9D6023',
[ChainId.GÖRLI]: '0x539BF58f052dE91ae369dAd59f1ac6887dF39Bc5',
[ChainId.KOVAN]: '0xAE28628c0fdFb5e54d60FEDC6C9085199aec14dF',
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: { [chainId in ChainId]?: string } = {
[ChainId.RINKEBY]: '0x2F9e608FD881861B8916257B76613Cb22EE0652c',
[ChainId.GÖRLI]: '0xd6852c52B9c97cBfb7e79B6ab4407AA20Ba31439',
[ChainId.KOVAN]: '0xA31B47971cdC5376E41CfA2D4378912156ab1F10',
}
export const NONFUNGIBLE_TOKEN_POSITION_DESCRIPTOR_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '0x8dF824f7885611c587AA45924BF23153EC832b89',
[ChainId.RINKEBY]: '0x3b1aC1c352F3A18A58471908982b8b870c836EC0',
[ChainId.GÖRLI]: '0x8dF824f7885611c587AA45924BF23153EC832b89',
[ChainId.KOVAN]: '0x30Ba713F78Ad3c175a25aD767e3f423549Ac2D65',
export const SWAP_ROUTER_ADDRESSES: { [chainId in ChainId]?: string } = {
[ChainId.RINKEBY]: '0x273Edaa13C845F605b5886Dd66C89AB497A6B17b',
[ChainId.GÖRLI]: '0x91a64CCaead471caFF912314E466D9CF7C55E0E8',
[ChainId.KOVAN]: '0x1988F2e49A72C4D73961C7f4Bb896819d3d2F6a3',
}
export const SWAP_ROUTER_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '0x7046f9311663DB8B7cf218BC7B6F3f17B0Ea1047',
[ChainId.RINKEBY]: '0x8dF824f7885611c587AA45924BF23153EC832b89',
[ChainId.GÖRLI]: '0x7046f9311663DB8B7cf218BC7B6F3f17B0Ea1047',
[ChainId.KOVAN]: '0x921647f0c094e2e59CDE6DEfafD77743012f52bd',
}
export const V2_MIGRATOR_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '0xbBca0fFBFE60F60071630A8c80bb6253dC9D6023',
[ChainId.RINKEBY]: '0xc4b81504F9a2bd6a6f2617091FB01Efb38D119c8',
[ChainId.GÖRLI]: '0xbBca0fFBFE60F60071630A8c80bb6253dC9D6023',
[ChainId.KOVAN]: '0xc97c7D6C2F1EE518bE4D4B8566bcEb917dED4F39',
export const V2_MIGRATOR_ADDRESSES: { [chainId in ChainId]?: string } = {
[ChainId.RINKEBY]: '0x03782388516e94FcD4c18666303601A12Aa729Ea',
[ChainId.GÖRLI]: '0x2F9e608FD881861B8916257B76613Cb22EE0652c',
[ChainId.KOVAN]: '0xFeabCc62240297F1e4b238937D68e7516f0918D7',
}

@ -7,7 +7,7 @@ import { useSingleCallResult } from '../state/multicall/hooks'
import { wrappedCurrency } from '../utils/wrappedCurrency'
import { Pool, FeeAmount, computePoolAddress } from '@uniswap/v3-sdk'
import { useV3Factory, useV3Pool } from 'hooks/useContract'
import { FACTORY_ADDRESSES } from 'constants/v3'
import { V3_CORE_FACTORY_ADDRESSES } from 'constants/v3'
import { useAllV3Ticks } from 'hooks/useAllV3Ticks'
export enum PoolState {
@ -38,9 +38,10 @@ export function usePool(currencyA?: Currency, currencyB?: Currency, feeAmount?:
// fetch all generated addresses for pools
const poolAddress = useMemo(() => {
try {
return chainId && tokenA && tokenB && feeAmount && !tokenA.equals(tokenB)
const addr = chainId && V3_CORE_FACTORY_ADDRESSES[chainId]
return addr && tokenA && tokenB && feeAmount && !tokenA.equals(tokenB)
? computePoolAddress({
factoryAddress: FACTORY_ADDRESSES[chainId],
factoryAddress: addr,
tokenA,
tokenB,
fee: feeAmount,
@ -71,10 +72,12 @@ export function usePool(currencyA?: Currency, currencyB?: Currency, feeAmount?:
const poolAddressFromFactory = addressesResult?.[0]
// fetch tick data for pool
const { tickData, loading: tickLoading } = useAllV3Ticks(token0, token1, feeAmount)
const { tickData, loading: tickLoading, syncing: tickSyncing } = useAllV3Ticks(token0, token1, feeAmount)
return useMemo(() => {
// still loading data
if (slot0Loading || addressesLoading || liquidityLoading || tickLoading) return [PoolState.LOADING, null]
if (slot0Loading || addressesLoading || liquidityLoading || tickLoading || tickSyncing)
return [PoolState.LOADING, null]
// invalid pool setup
if (!tokenA || !tokenB || !feeAmount || tokenA.equals(tokenB)) return [PoolState.INVALID, null]
@ -95,4 +98,18 @@ export function usePool(currencyA?: Currency, currencyB?: Currency, feeAmount?:
.sort((tickA, tickB) => (tickA.index > tickB.index ? 1 : -1))
return [PoolState.EXISTS, new Pool(tokenA, tokenB, feeAmount, slot0.sqrtPriceX96, liquidity, slot0.tick, tickList)]
}, [
addressesLoading,
feeAmount,
liquidity,
liquidityLoading,
poolAddressFromFactory,
slot0,
slot0Loading,
tickData,
tickLoading,
tickSyncing,
tokenA,
tokenB,
])
}

@ -1,157 +0,0 @@
import { AddressZero } from '@ethersproject/constants'
import {
BigintIsh,
Currency,
CurrencyAmount,
ETHER,
Token,
TokenAmount,
TradeType,
WETH9,
ChainId,
} from '@uniswap/sdk-core'
import JSBI from 'jsbi'
import { Pair as V2Pair, Route as V2Route, Trade as V2Trade } from '@uniswap/v2-sdk'
import { useMemo } from 'react'
import { useActiveWeb3React } from '../hooks'
import { useAllTokens } from '../hooks/Tokens'
import { useV1FactoryContract } from '../hooks/useContract'
import { Version } from '../hooks/useToggledVersion'
import { NEVER_RELOAD, useSingleCallResult, useSingleContractMultipleData } from '../state/multicall/hooks'
import { useETHBalances, useTokenBalance, useTokenBalances } from '../state/wallet/hooks'
import { supportedChainId } from 'utils'
export function useV1ExchangeAddress(tokenAddress?: string): string | undefined {
const contract = useV1FactoryContract()
const inputs = useMemo(() => [tokenAddress], [tokenAddress])
return useSingleCallResult(contract, 'getExchange', inputs)?.result?.[0]
}
export class MockV1Pair extends V2Pair {
constructor(etherAmount: BigintIsh, tokenAmount: TokenAmount) {
super(tokenAmount, new TokenAmount(WETH9[tokenAmount.token.chainId as ChainId], etherAmount))
}
}
function useMockV1Pair(inputCurrency?: Currency): MockV1Pair | undefined {
const token = inputCurrency instanceof Token ? inputCurrency : undefined
const chainId: ChainId | undefined = token && supportedChainId(token.chainId)
const isWETH = Boolean(token && chainId && token.equals(WETH9[chainId]))
const v1PairAddress = useV1ExchangeAddress(isWETH ? undefined : token?.address)
const tokenBalance = useTokenBalance(v1PairAddress, token)
const ETHBalance = useETHBalances([v1PairAddress])[v1PairAddress ?? '']
return useMemo(
() =>
token && tokenBalance && ETHBalance && inputCurrency ? new MockV1Pair(ETHBalance.raw, tokenBalance) : undefined,
[ETHBalance, inputCurrency, token, tokenBalance]
)
}
// returns all v1 exchange addresses in the user's token list
export function useAllTokenV1Exchanges(): { [exchangeAddress: string]: Token } {
const allTokens = useAllTokens()
const factory = useV1FactoryContract()
const args = useMemo(() => Object.keys(allTokens).map((tokenAddress) => [tokenAddress]), [allTokens])
const data = useSingleContractMultipleData(factory, 'getExchange', args, NEVER_RELOAD)
return useMemo(
() =>
data?.reduce<{ [exchangeAddress: string]: Token }>((memo, { result }, ix) => {
if (result?.[0] && result[0] !== AddressZero) {
memo[result[0]] = allTokens[args[ix][0]]
}
return memo
}, {}) ?? {},
[allTokens, args, data]
)
}
// returns whether any of the tokens in the user's token list have liquidity on v1
export function useUserHasLiquidityInAllTokens(): boolean | undefined {
const { account, chainId } = useActiveWeb3React()
const exchanges = useAllTokenV1Exchanges()
const v1ExchangeLiquidityTokens = useMemo(
() =>
chainId ? Object.keys(exchanges).map((address) => new Token(chainId, address, 18, 'UNI-V1', 'Uniswap V1')) : [],
[chainId, exchanges]
)
const balances = useTokenBalances(account ?? undefined, v1ExchangeLiquidityTokens)
return useMemo(
() =>
Object.keys(balances).some((tokenAddress) => {
const b = balances[tokenAddress]?.raw
return b && JSBI.greaterThan(b, JSBI.BigInt(0))
}),
[balances]
)
}
/**
* Returns the trade to execute on V1 to go between input and output token
*/
export function useV1Trade(
isExactIn?: boolean,
inputCurrency?: Currency,
outputCurrency?: Currency,
exactAmount?: CurrencyAmount
): V2Trade | undefined {
// get the mock v1 pairs
const inputPair = useMockV1Pair(inputCurrency)
const outputPair = useMockV1Pair(outputCurrency)
const inputIsETH = inputCurrency === ETHER
const outputIsETH = outputCurrency === ETHER
// construct a direct or through ETH v1 route
let pairs: V2Pair[] = []
if (inputIsETH && outputPair) {
pairs = [outputPair]
} else if (outputIsETH && inputPair) {
pairs = [inputPair]
}
// if neither are ETH, it's token-to-token (if they both exist)
else if (inputPair && outputPair) {
pairs = [inputPair, outputPair]
}
const route = inputCurrency && pairs && pairs.length > 0 && new V2Route(pairs, inputCurrency, outputCurrency)
let v1Trade: V2Trade | undefined
try {
v1Trade =
route && exactAmount
? new V2Trade(route, exactAmount, isExactIn ? TradeType.EXACT_INPUT : TradeType.EXACT_OUTPUT)
: undefined
} catch (error) {
console.debug('Failed to create V1 trade', error)
}
return v1Trade
}
export function getTradeVersion(trade?: V2Trade): Version | undefined {
const isV1 = trade?.route?.pairs?.some((pair) => pair instanceof MockV1Pair)
if (isV1) return Version.v1
if (isV1 === false) return Version.v2
return undefined
}
// returns the v1 exchange against which a trade should be executed
export function useV1TradeExchangeAddress(trade: V2Trade | undefined): string | undefined {
const tokenAddress: string | undefined = useMemo(() => {
if (!trade) return undefined
const isV1 = getTradeVersion(trade) === Version.v1
if (!isV1) return undefined
return trade.inputAmount instanceof TokenAmount
? trade.inputAmount.token.address
: trade.outputAmount instanceof TokenAmount
? trade.outputAmount.token.address
: undefined
}, [trade])
return useV1ExchangeAddress(tokenAddress)
}

@ -0,0 +1,7 @@
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Version } from '../hooks/useToggledVersion'
export function getTradeVersion(trade?: V2Trade): Version | undefined {
if (!trade) return undefined
return Version.v2
}

@ -1,22 +1,13 @@
import { Token } from '@uniswap/sdk-core'
import { FeeAmount, TICK_SPACINGS } from '@uniswap/v3-sdk'
import { nearestUsableTick, TickMath } from '@uniswap/v3-sdk/dist/'
import { ZERO_ADDRESS } from '../constants'
import { useMemo } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { Result, useSingleCallResult, useSingleContractMultipleData } from 'state/multicall/hooks'
import { useTickLens, useV3Factory } from './useContract'
// the following should probably all be from the sdk, just mocking it for now
function MIN_TICK(tickSpacing: number) {
return Math.ceil(-887272 / tickSpacing) * tickSpacing
}
function MAX_TICK(tickSpacing: number) {
return Math.floor(887272 / tickSpacing) * tickSpacing
}
function bitmapIndex(tick: number, tickSpacing: number) {
const compressed = tick / tickSpacing
return compressed >> 8
return Math.floor(tick / tickSpacing / 256)
}
const REFRESH_FREQUENCY = { blocksPerFetch: 2 }
@ -38,19 +29,22 @@ export function useAllV3Ticks(
valid: boolean
tickData: TickData[]
} {
const tickSpacing = useMemo(() => (feeAmount ? TICK_SPACINGS[feeAmount] : undefined), [feeAmount])
const tickSpacing = feeAmount && TICK_SPACINGS[feeAmount]
const minIndex = useMemo(() => (tickSpacing ? bitmapIndex(MIN_TICK(tickSpacing), tickSpacing) : undefined), [
tickSpacing,
])
const maxIndex = useMemo(() => (tickSpacing ? bitmapIndex(MAX_TICK(tickSpacing), tickSpacing) : undefined), [
tickSpacing,
])
const minIndex = useMemo(
() => (tickSpacing ? bitmapIndex(nearestUsableTick(TickMath.MIN_TICK, tickSpacing), tickSpacing) : undefined),
[tickSpacing]
)
const maxIndex = useMemo(
() => (tickSpacing ? bitmapIndex(nearestUsableTick(TickMath.MAX_TICK, tickSpacing), tickSpacing) : undefined),
[tickSpacing]
)
const [tickDataLatestSynced, setTickDataLatestSynced] = useState<TickData[]>([])
// fetch the pool address
const factoryContract = useV3Factory()
const addressParams = token0 && token1 && feeAmount ? [token0.address, token1.address, feeAmount] : undefined
const poolAddress = useSingleCallResult(addressParams ? factoryContract : undefined, 'getPool', addressParams)
const poolAddress = useSingleCallResult(factoryContract, 'getPool', [token0?.address, token1?.address, feeAmount])
.result?.[0]
const tickLensArgs: [string, number][] = useMemo(
@ -98,14 +92,21 @@ export function useAllV3Ticks(
[callStates]
)
// return the latest synced tickdata even if we are still loading the newest data
useEffect(() => {
if (!syncing && !loading && !error && valid) {
setTickDataLatestSynced(tickData)
}
}, [error, loading, syncing, tickData, valid])
return useMemo(
() => ({
loading,
syncing,
error,
valid,
tickData,
tickData: tickDataLatestSynced,
}),
[loading, syncing, error, valid, tickData]
[loading, syncing, error, valid, tickDataLatestSynced]
)
}

@ -5,14 +5,12 @@ import { Trade } from '@uniswap/v2-sdk'
import { useCallback, useMemo } from 'react'
import { ROUTER_ADDRESS } from '../constants'
import { useTokenAllowance } from '../data/Allowances'
import { getTradeVersion, useV1TradeExchangeAddress } from '../data/V1'
import { Field } from '../state/swap/actions'
import { useTransactionAdder, useHasPendingApproval } from '../state/transactions/hooks'
import { computeSlippageAdjustedAmounts } from '../utils/prices'
import { calculateGasMargin } from '../utils'
import { useTokenContract } from './useContract'
import { useActiveWeb3React } from './index'
import { Version } from './useToggledVersion'
export enum ApprovalState {
UNKNOWN,
@ -106,7 +104,5 @@ export function useApproveCallbackFromTrade(trade?: Trade, allowedSlippage = 0)
() => (trade ? computeSlippageAdjustedAmounts(trade, allowedSlippage)[Field.INPUT] : undefined),
[trade, allowedSlippage]
)
const tradeIsV1 = getTradeVersion(trade) === Version.v1
const v1ExchangeAddress = useV1TradeExchangeAddress(trade)
return useApproveCallback(amountToApprove, tradeIsV1 ? v1ExchangeAddress : ROUTER_ADDRESS)
return useApproveCallback(amountToApprove, ROUTER_ADDRESS)
}

@ -29,10 +29,9 @@ import {
MULTICALL_ADDRESSES,
} from 'constants/index'
import { abi as NFTPositionManagerABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
import { V1_EXCHANGE_ABI, V1_FACTORY_ABI, V1_FACTORY_ADDRESSES } from 'constants/v1'
import {
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
FACTORY_ADDRESSES,
V3_CORE_FACTORY_ADDRESSES,
TICK_LENS_ADDRESSES,
V2_MIGRATOR_ADDRESSES,
} from 'constants/v3'
@ -58,11 +57,6 @@ export function useContract(address: string | undefined, ABI: any, withSignerIfP
}, [address, ABI, library, withSignerIfPossible, account])
}
export function useV1FactoryContract(): Contract | null {
const { chainId } = useActiveWeb3React()
return useContract(chainId && V1_FACTORY_ADDRESSES[chainId], V1_FACTORY_ABI, false)
}
export function useV1MigratorContract(): Contract | null {
return useContract(V1_MIGRATOR_ADDRESS, MIGRATOR_ABI, true)
}
@ -72,13 +66,10 @@ export function useV2MigratorContract(): V3Migrator | null {
return useContract(chainId && V2_MIGRATOR_ADDRESSES[chainId], V2MigratorABI, true) as V3Migrator | null
}
export function useV1ExchangeContract(address?: string, withSignerIfPossible?: boolean): Contract | null {
return useContract(address, V1_EXCHANGE_ABI, withSignerIfPossible)
}
export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): Contract | null {
return useContract(tokenAddress, ERC20_ABI, withSignerIfPossible)
}
export function useWETHContract(withSignerIfPossible?: boolean): Contract | null {
const { chainId } = useActiveWeb3React()
const address = chainId && chainId in WETH9 ? WETH9[chainId].address : undefined
@ -162,7 +153,7 @@ export function useV3NFTPositionManagerContract(): NonfungiblePositionManager |
export function useV3Factory(): UniswapV3Factory | null {
const { chainId } = useActiveWeb3React()
const address = chainId ? FACTORY_ADDRESSES[chainId] : undefined
const address = chainId ? V3_CORE_FACTORY_ADDRESSES[chainId] : undefined
return useContract(address, V3FactoryABI) as UniswapV3Factory | null
}

@ -4,13 +4,11 @@ import { JSBI, Router, SwapParameters, Trade } from '@uniswap/v2-sdk'
import { Percent, TradeType } from '@uniswap/sdk-core'
import { useMemo } from 'react'
import { BIPS_BASE, INITIAL_ALLOWED_SLIPPAGE } from '../constants'
import { getTradeVersion, useV1TradeExchangeAddress } from '../data/V1'
import { getTradeVersion } from '../data/getTradeVersion'
import { useTransactionAdder } from '../state/transactions/hooks'
import { calculateGasMargin, getRouterContract, isAddress, shortenAddress } from '../utils'
import isZero from '../utils/isZero'
import v1SwapArguments from '../utils/v1SwapArguments'
import { useActiveWeb3React } from './index'
import { useV1ExchangeContract } from './useContract'
import useTransactionDeadline from './useTransactionDeadline'
import useENS from './useENS'
import { Version } from './useToggledVersion'
@ -55,22 +53,16 @@ function useSwapCallArguments(
const recipient = recipientAddressOrName === null ? account : recipientAddress
const deadline = useTransactionDeadline()
const v1Exchange = useV1ExchangeContract(useV1TradeExchangeAddress(trade), true)
return useMemo(() => {
const tradeVersion = getTradeVersion(trade)
if (!trade || !recipient || !library || !account || !tradeVersion || !chainId || !deadline) return []
if (!trade || !recipient || !library || !account || !chainId || !deadline) return []
const contract: Contract | null =
tradeVersion === Version.v2 ? getRouterContract(chainId, library, account) : v1Exchange
const contract: Contract | null = getRouterContract(chainId, library, account)
if (!contract) {
return []
}
const swapMethods = []
switch (tradeVersion) {
case Version.v2:
swapMethods.push(
Router.swapCallParameters(trade, {
feeOnTransfer: false,
@ -90,19 +82,8 @@ function useSwapCallArguments(
})
)
}
break
case Version.v1:
swapMethods.push(
v1SwapArguments(trade, {
allowedSlippage: new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE),
recipient,
deadline: deadline.toNumber(),
})
)
break
}
return swapMethods.map((parameters) => ({ parameters, contract }))
}, [account, allowedSlippage, chainId, deadline, library, recipient, trade, v1Exchange])
}, [account, allowedSlippage, chainId, deadline, library, recipient, trade])
}
// returns a function that will execute a swap, if the parameters are all valid

@ -1,7 +1,6 @@
import useParsedQueryString from './useParsedQueryString'
export enum Version {
v1 = 'v1',
v2 = 'v2',
}
@ -9,7 +8,8 @@ export const DEFAULT_VERSION: Version = Version.v2
export default function useToggledVersion(): Version {
const { use } = useParsedQueryString()
if (!use || typeof use !== 'string') return Version.v2
if (use.toLowerCase() === 'v1') return Version.v1
if (typeof use !== 'string') {
return DEFAULT_VERSION
}
return DEFAULT_VERSION
}

@ -9,8 +9,9 @@ export function RedirectDuplicateTokenIdsV2(props: RouteComponentProps<{ currenc
},
} = props
if (currencyIdA.toLowerCase() === currencyIdB.toLowerCase()) {
if (currencyIdA && currencyIdB && currencyIdA.toLowerCase() === currencyIdB.toLowerCase()) {
return <Redirect to={`/add/v2/${currencyIdA}`} />
}
return <AddLiquidityV2 {...props} />
}

@ -14,9 +14,6 @@ import DarkModeQueryParamReader from '../theme/DarkModeQueryParamReader'
import { RedirectDuplicateTokenIds } from './AddLiquidity/redirects'
import Earn from './Earn'
import Manage from './Earn/Manage'
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'
@ -114,12 +111,9 @@ export default function App() {
component={RedirectDuplicateTokenIds}
/>
<Route exact strict path="/remove/v1/:address" component={RemoveV1Exchange} />
<Route exact strict path="/remove/v2/:currencyIdA/:currencyIdB" component={RemoveLiquidity} />
<Route exact strict path="/remove/:tokenId" 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="/migrate/v2/:address" component={MigrateV2Pair} />

@ -1,11 +0,0 @@
import React from 'react'
import { AutoColumn } from '../../components/Column'
import { TYPE } from '../../theme'
export function EmptyState({ message }: { message: string }) {
return (
<AutoColumn style={{ minHeight: 200, justifyContent: 'center', alignItems: 'center' }}>
<TYPE.body>{message}</TYPE.body>
</AutoColumn>
)
}

@ -1,370 +0,0 @@
import { TransactionResponse } from '@ethersproject/abstract-provider'
import { AddressZero } from '@ethersproject/constants'
import { Currency, CurrencyAmount, Fraction, Percent, 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'
import { PairState, usePair } from '../../data/V2'
import { useTotalSupply } from '../../data/TotalSupply'
import { useActiveWeb3React } from '../../hooks'
import { useToken } from '../../hooks/Tokens'
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
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 './EmptyState'
import { useV1ExchangeContract, useV1MigratorContract } from 'hooks/useContract'
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 V1LiquidityInfo({
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 V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount: TokenAmount; token: Token }) {
const { account, chainId } = useActiveWeb3React()
const totalSupply = useTotalSupply(liquidityTokenAmount.token)
const exchangeETHBalance = useETHBalances([liquidityTokenAmount.token.address])?.[liquidityTokenAmount.token.address]
const exchangeTokenBalance = useTokenBalance(liquidityTokenAmount.token.address, token)
const [v2PairState, v2Pair] = usePair(chainId ? WETH9[chainId] : undefined, token)
const isFirstLiquidityProvider: boolean = v2PairState === PairState.NOT_EXISTS
const v2SpotPrice = chainId && v2Pair ? v2Pair.reserveOf(token).divide(v2Pair.reserveOf(WETH9[chainId])) : undefined
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 V1 liquidity to V2 with minimal price risk. The process is completely
trustless thanks to the{' '}
{chainId && (
<ExternalLink href={getEtherscanLink(chainId, V1_MIGRATOR_ADDRESS, '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>
<V1LiquidityInfo
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 MigrateV1Exchange({
history,
match: {
params: { address },
},
}: RouteComponentProps<{ address: string }>) {
const validatedAddress = isAddress(address)
const { chainId, account } = useActiveWeb3React()
const exchangeContract = useV1ExchangeContract(validatedAddress ? validatedAddress : undefined)
const tokenAddress = useSingleCallResult(exchangeContract, 'tokenAddress', undefined, NEVER_RELOAD)?.result?.[0]
const token = useToken(tokenAddress)
const liquidityToken: Token | undefined = useMemo(
() =>
validatedAddress && chainId && token
? new Token(chainId, validatedAddress, 18, `UNI-V1-${token.symbol}`, 'Uniswap V1')
: undefined,
[chainId, validatedAddress, token]
)
const userLiquidityBalance = useTokenBalance(account ?? undefined, liquidityToken)
// redirect for invalid url params
if (!validatedAddress || tokenAddress === AddressZero) {
console.error('Invalid address in path', address)
return <Redirect to="/migrate/v1" />
}
return (
<BodyWrapper style={{ padding: 24 }}>
<AutoColumn gap="16px">
<AutoRow style={{ alignItems: 'center', justifyContent: 'space-between' }} gap="8px">
<BackArrow to="/migrate/v1" />
<TYPE.mediumHeader>Migrate V1 Liquidity</TYPE.mediumHeader>
<div>
<QuestionHelper text="Migrate your liquidity tokens from Uniswap V1 to Uniswap V2." />
</div>
</AutoRow>
{!account ? (
<TYPE.largeHeader>You must connect an account.</TYPE.largeHeader>
) : validatedAddress && chainId && token?.equals(WETH9[chainId]) ? (
<>
<TYPE.body my={9} style={{ fontWeight: 400 }}>
Because Uniswap V2 uses WETH under the hood, your Uniswap V1 WETH/ETH liquidity cannot be migrated. You
may want to remove your liquidity instead.
</TYPE.body>
<ButtonConfirmed
onClick={() => {
history.push(`/remove/v1/${validatedAddress}`)
}}
>
Remove
</ButtonConfirmed>
</>
) : userLiquidityBalance && token ? (
<V1PairMigration liquidityTokenAmount={userLiquidityBalance} token={token} />
) : (
<EmptyState message="Loading..." />
)}
</AutoColumn>
</BodyWrapper>
)
}

@ -1,182 +0,0 @@
import { TransactionResponse } from '@ethersproject/abstract-provider'
import { Token, TokenAmount, WETH9, Fraction, Percent, CurrencyAmount } from '@uniswap/sdk-core'
import JSBI from 'jsbi'
import React, { useCallback, useMemo, useState } from 'react'
import ReactGA from 'react-ga'
import { Redirect, RouteComponentProps } from 'react-router'
import { ButtonConfirmed } from '../../components/Button'
import { LightCard } from '../../components/Card'
import { AutoColumn } from '../../components/Column'
import QuestionHelper from '../../components/QuestionHelper'
import { AutoRow } from '../../components/Row'
import { DEFAULT_DEADLINE_FROM_NOW } from '../../constants'
import { useActiveWeb3React } from '../../hooks'
import { useToken } from '../../hooks/Tokens'
import { useV1ExchangeContract } from '../../hooks/useContract'
import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks'
import { useIsTransactionPending, useTransactionAdder } from '../../state/transactions/hooks'
import { useTokenBalance, useETHBalances } from '../../state/wallet/hooks'
import { BackArrow, TYPE } from '../../theme'
import { isAddress } from '../../utils'
import { BodyWrapper } from '../AppBody'
import { EmptyState } from './EmptyState'
import { V1LiquidityInfo } from './MigrateV1Exchange'
import { AddressZero } from '@ethersproject/constants'
import { Dots } from '../../components/swap/styleds'
import { Contract } from '@ethersproject/contracts'
import { useTotalSupply } from '../../data/TotalSupply'
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)
function V1PairRemoval({
exchangeContract,
liquidityTokenAmount,
token,
}: {
exchangeContract: Contract
liquidityTokenAmount: TokenAmount
token: Token
}) {
const { chainId } = useActiveWeb3React()
const totalSupply = useTotalSupply(liquidityTokenAmount.token)
const exchangeETHBalance = useETHBalances([liquidityTokenAmount.token.address])?.[liquidityTokenAmount.token.address]
const exchangeTokenBalance = useTokenBalance(liquidityTokenAmount.token.address, token)
const [confirmingRemoval, setConfirmingRemoval] = useState<boolean>(false)
const [pendingRemovalHash, setPendingRemovalHash] = 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 addTransaction = useTransactionAdder()
const isRemovalPending = useIsTransactionPending(pendingRemovalHash ?? undefined)
const remove = useCallback(() => {
if (!liquidityTokenAmount) return
setConfirmingRemoval(true)
exchangeContract
.removeLiquidity(
liquidityTokenAmount.raw.toString(),
1, // min_eth, this is safe because we're removing liquidity
1, // min_tokens, this is safe because we're removing liquidity
Math.floor(new Date().getTime() / 1000) + DEFAULT_DEADLINE_FROM_NOW
)
.then((response: TransactionResponse) => {
ReactGA.event({
category: 'Remove',
action: 'V1',
label: token?.symbol,
})
addTransaction(response, {
summary: `Remove ${chainId && token.equals(WETH9[chainId]) ? 'WETH' : token.symbol}/ETH V1 liquidity`,
})
setPendingRemovalHash(response.hash)
})
.catch((error: Error) => {
console.error(error)
setConfirmingRemoval(false)
})
}, [exchangeContract, liquidityTokenAmount, token, chainId, addTransaction])
const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO)
const isSuccessfullyRemoved = !!pendingRemovalHash && noLiquidityTokens
return (
<AutoColumn gap="20px">
<TYPE.body my={9} style={{ fontWeight: 400 }}>
This tool will remove your V1 liquidity and send the underlying assets to your wallet.
</TYPE.body>
<LightCard>
<V1LiquidityInfo
token={token}
liquidityTokenAmount={liquidityTokenAmount}
tokenWorth={tokenWorth}
ethWorth={ethWorth}
/>
<div style={{ display: 'flex', marginTop: '1rem' }}>
<ButtonConfirmed
confirmed={isSuccessfullyRemoved}
disabled={isSuccessfullyRemoved || noLiquidityTokens || isRemovalPending || confirmingRemoval}
onClick={remove}
>
{isSuccessfullyRemoved ? 'Success' : isRemovalPending ? <Dots>Removing</Dots> : 'Remove'}
</ButtonConfirmed>
</div>
</LightCard>
<TYPE.darkGray style={{ textAlign: 'center' }}>
{`Your Uniswap V1 ${
chainId && token.equals(WETH9[chainId]) ? 'WETH' : token.symbol
}/ETH liquidity will be redeemed for underlying assets.`}
</TYPE.darkGray>
</AutoColumn>
)
}
export default function RemoveV1Exchange({
match: {
params: { address },
},
}: RouteComponentProps<{ address: string }>) {
const validatedAddress = isAddress(address)
const { chainId, account } = useActiveWeb3React()
const exchangeContract = useV1ExchangeContract(validatedAddress ? validatedAddress : undefined, true)
const tokenAddress = useSingleCallResult(exchangeContract, 'tokenAddress', undefined, NEVER_RELOAD)?.result?.[0]
const token = useToken(tokenAddress)
const liquidityToken: Token | undefined = useMemo(
() =>
validatedAddress && chainId && token
? new Token(chainId, validatedAddress, 18, `UNI-V1-${token.symbol}`, 'Uniswap V1')
: undefined,
[chainId, validatedAddress, token]
)
const userLiquidityBalance = useTokenBalance(account ?? undefined, liquidityToken)
// redirect for invalid url params
if (!validatedAddress || tokenAddress === AddressZero) {
console.error('Invalid address in path', address)
return <Redirect to="/migrate/v1" />
}
return (
<BodyWrapper style={{ padding: 24 }} id="remove-v1-exchange">
<AutoColumn gap="16px">
<AutoRow style={{ alignItems: 'center', justifyContent: 'space-between' }} gap="8px">
<BackArrow to="/migrate/v1" />
<TYPE.mediumHeader>Remove V1 Liquidity</TYPE.mediumHeader>
<div>
<QuestionHelper text="Remove your Uniswap V1 liquidity tokens." />
</div>
</AutoRow>
{!account ? (
<TYPE.largeHeader>You must connect an account.</TYPE.largeHeader>
) : userLiquidityBalance && token && exchangeContract ? (
<V1PairRemoval
exchangeContract={exchangeContract}
liquidityTokenAmount={userLiquidityBalance}
token={token}
/>
) : (
<EmptyState message="Loading..." />
)}
</AutoColumn>
</BodyWrapper>
)
}

@ -1,119 +0,0 @@
import { Token } from '@uniswap/sdk-core'
import { JSBI } from '@uniswap/v2-sdk'
import React, { useCallback, useContext, useMemo, useState, useEffect } from 'react'
import { ThemeContext } from 'styled-components'
import { AutoColumn } from '../../components/Column'
import { AutoRow } from '../../components/Row'
import { SearchInput } from '../../components/SearchModal/styleds'
import { useAllTokenV1Exchanges } from '../../data/V1'
import { useActiveWeb3React } from '../../hooks'
import { useAllTokens, useToken } from '../../hooks/Tokens'
import { useTokenBalancesWithLoadingIndicator } from '../../state/wallet/hooks'
import { BackArrow, TYPE } from '../../theme'
import { LightCard } from '../../components/Card'
import { BodyWrapper } from '../AppBody'
import { EmptyState } from './EmptyState'
import V1PositionCard from '../../components/PositionCard/V1'
import QuestionHelper from '../../components/QuestionHelper'
import { Dots } from '../../components/swap/styleds'
import { useAddUserToken } from '../../state/user/hooks'
import { isTokenOnList } from '../../utils'
import { useCombinedActiveList } from '../../state/lists/hooks'
export default function MigrateV1() {
const theme = useContext(ThemeContext)
const { account, chainId } = useActiveWeb3React()
const [tokenSearch, setTokenSearch] = useState<string>('')
const handleTokenSearchChange = useCallback((e) => setTokenSearch(e.target.value), [setTokenSearch])
// automatically add the search token
const token = useToken(tokenSearch)
const selectedTokenListTokens = useCombinedActiveList()
const isOnSelectedList = isTokenOnList(selectedTokenListTokens, token ?? undefined)
const allTokens = useAllTokens()
const addToken = useAddUserToken()
useEffect(() => {
if (token && !isOnSelectedList && !allTokens[token.address]) {
addToken(token)
}
}, [token, isOnSelectedList, addToken, allTokens])
// get V1 LP balances
const V1Exchanges = useAllTokenV1Exchanges()
const V1LiquidityTokens: Token[] = useMemo(() => {
return chainId
? Object.keys(V1Exchanges).map(
(exchangeAddress) => new Token(chainId, exchangeAddress, 18, 'UNI-V1', 'Uniswap V1')
)
: []
}, [chainId, V1Exchanges])
const [V1LiquidityBalances, V1LiquidityBalancesLoading] = useTokenBalancesWithLoadingIndicator(
account ?? undefined,
V1LiquidityTokens
)
const allV1PairsWithLiquidity = V1LiquidityTokens.filter((V1LiquidityToken) => {
const balance = V1LiquidityBalances?.[V1LiquidityToken.address]
return balance && JSBI.greaterThan(balance.raw, JSBI.BigInt(0))
}).map((V1LiquidityToken) => {
const balance = V1LiquidityBalances[V1LiquidityToken.address]
return balance ? (
<V1PositionCard
key={V1LiquidityToken.address}
token={V1Exchanges[V1LiquidityToken.address]}
V1LiquidityBalance={balance}
/>
) : null
})
// should never always be false, because a V1 exhchange exists for WETH on all testnets
const isLoading = Object.keys(V1Exchanges)?.length === 0 || V1LiquidityBalancesLoading
return (
<BodyWrapper style={{ padding: 24 }}>
<AutoColumn gap="16px">
<AutoRow style={{ alignItems: 'center', justifyContent: 'space-between' }} gap="8px">
<BackArrow to="/pool" />
<TYPE.mediumHeader>Migrate V1 Liquidity</TYPE.mediumHeader>
<div>
<QuestionHelper text="Migrate your liquidity tokens from Uniswap V1 to Uniswap V2." />
</div>
</AutoRow>
<TYPE.body style={{ marginBottom: 8, fontWeight: 400 }}>
For each pool shown below, click migrate to remove your liquidity from Uniswap V1 and deposit it into Uniswap
V2.
</TYPE.body>
{!account ? (
<LightCard padding="40px">
<TYPE.body color={theme.text3} textAlign="center">
Connect to a wallet to view your V1 liquidity.
</TYPE.body>
</LightCard>
) : isLoading ? (
<LightCard padding="40px">
<TYPE.body color={theme.text3} textAlign="center">
<Dots>Loading</Dots>
</TYPE.body>
</LightCard>
) : (
<>
<AutoRow>
<SearchInput
value={tokenSearch}
onChange={handleTokenSearchChange}
placeholder="Enter a token address to find liquidity"
/>
</AutoRow>
{allV1PairsWithLiquidity?.length > 0 ? (
<>{allV1PairsWithLiquidity}</>
) : (
<EmptyState message="No V1 Liquidity found." />
)}
</>
)}
</AutoColumn>
</BodyWrapper>
)
}

@ -17,7 +17,6 @@ import { 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 { V2_MIGRATOR_ADDRESSES } from 'constants/v3'
import { PoolState, usePool } from 'data/Pools'
import { FeeAmount, Pool, Position, priceToClosestTick, TickMath } from '@uniswap/v3-sdk'
@ -43,6 +42,14 @@ import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
const ZERO = JSBI.BigInt(0)
function EmptyState({ message }: { message: string }) {
return (
<AutoColumn style={{ minHeight: 200, justifyContent: 'center', alignItems: 'center' }}>
<TYPE.body>{message}</TYPE.body>
</AutoColumn>
)
}
function LiquidityInfo({ token0Amount, token1Amount }: { token0Amount: TokenAmount; token1Amount: TokenAmount }) {
return (
<>
@ -198,10 +205,9 @@ function V2PairMigration({
const [signatureData, setSignatureData] = useState<{ v: number; r: string; s: string; deadline: BigNumber } | null>(
null
)
const [approval, approveManually] = useApproveCallback(
pairBalance,
chainId ? V2_MIGRATOR_ADDRESSES[chainId] : undefined
)
const migratorAddress = chainId && V2_MIGRATOR_ADDRESSES[chainId]
const [approval, approveManually] = useApproveCallback(pairBalance, migratorAddress)
const isArgentWallet = useIsArgentWallet()
const approve = useCallback(async () => {
@ -389,8 +395,8 @@ function V2PairMigration({
<AutoColumn gap="20px">
<TYPE.body my={9} style={{ fontWeight: 400 }}>
This tool will safely migrate your V2 liquidity to V3. The process is completely trustless thanks to the{' '}
{chainId && (
<ExternalLink href={getEtherscanLink(chainId, V2_MIGRATOR_ADDRESSES[chainId], 'address')}>
{chainId && migratorAddress && (
<ExternalLink href={getEtherscanLink(chainId, migratorAddress, 'address')}>
<TYPE.blue display="inline">Uniswap migration contract</TYPE.blue>
</ExternalLink>
)}

@ -9,7 +9,6 @@ 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'
@ -18,6 +17,13 @@ import { usePairs } from 'data/V2'
// TODO there's a bug in loading where "No V2 Liquidity found" flashes
// TODO add support for more pairs
function EmptyState({ message }: { message: string }) {
return (
<AutoColumn style={{ minHeight: 200, justifyContent: 'center', alignItems: 'center' }}>
<TYPE.body>{message}</TYPE.body>
</AutoColumn>
)
}
export default function MigrateV2() {
const theme = useContext(ThemeContext)

@ -3,11 +3,9 @@ import styled, { ThemeContext } from 'styled-components'
import JSBI from 'jsbi'
import { Link } from 'react-router-dom'
import { SwapPoolTabs } from '../../components/NavigationTabs'
import FullPositionCard from '../../components/PositionCard'
import { useUserHasLiquidityInAllTokens } from '../../data/V1'
import { useTokenBalancesWithLoadingIndicator } from '../../state/wallet/hooks'
import { StyledInternalLink, ExternalLink, TYPE, HideSmall } from '../../theme'
import { ExternalLink, TYPE, HideSmall } from '../../theme'
import { Text } from 'rebass'
import Card from '../../components/Card'
import { RowBetween, RowFixed } from '../../components/Row'
@ -108,8 +106,6 @@ export default function Pool() {
const allV2PairsWithLiquidity = v2Pairs.map(([, pair]) => pair).filter((v2Pair): v2Pair is Pair => Boolean(v2Pair))
const hasV1Liquidity = useUserHasLiquidityInAllTokens()
// show liquidity even if its deposited in rewards contract
const stakingInfo = useStakingInfo()
const stakingInfosWithBalance = stakingInfo?.filter((pool) => JSBI.greaterThan(pool.stakedAmount.raw, BIG_INT_ZERO))
@ -223,15 +219,6 @@ export default function Pool() {
</TYPE.body>
</EmptyProposals>
)}
<AutoColumn justify={'center'} gap="md">
<Text textAlign="center" fontSize={14} style={{ padding: '.5rem 0 .5rem 0' }}>
{hasV1Liquidity ? 'Uniswap V1 liquidity found!' : "Don't see a pool you joined?"}{' '}
<StyledInternalLink id="import-pool-link" to={hasV1Liquidity ? '/migrate/v1' : '/find'}>
{hasV1Liquidity ? 'Migrate now.' : 'Import it.'}
</StyledInternalLink>
</Text>
</AutoColumn>
</AutoColumn>
</AutoColumn>
</PageWrapper>

@ -14,7 +14,6 @@ import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import { SwapPoolTabs } from '../../components/NavigationTabs'
import { AutoRow, RowBetween } from '../../components/Row'
import AdvancedSwapDetailsDropdown from '../../components/swap/AdvancedSwapDetailsDropdown'
import BetterTradeLink, { DefaultVersionLink } from '../../components/swap/BetterTradeLink'
import confirmPriceImpactWithoutFee from '../../components/swap/confirmPriceImpactWithoutFee'
import { ArrowWrapper, BottomGrouping, SwapCallbackError, Wrapper } from '../../components/swap/styleds'
import TradePrice from '../../components/swap/TradePrice'
@ -23,13 +22,13 @@ import ProgressSteps from '../../components/ProgressSteps'
import SwapHeader from '../../components/swap/SwapHeader'
import { INITIAL_ALLOWED_SLIPPAGE } from '../../constants'
import { getTradeVersion } from '../../data/V1'
import { getTradeVersion } from '../../data/getTradeVersion'
import { useActiveWeb3React } from '../../hooks'
import { useCurrency, useAllTokens } from '../../hooks/Tokens'
import { ApprovalState, useApproveCallbackFromTrade } from '../../hooks/useApproveCallback'
import useENSAddress from '../../hooks/useENSAddress'
import { useSwapCallback } from '../../hooks/useSwapCallback'
import useToggledVersion, { DEFAULT_VERSION, Version } from '../../hooks/useToggledVersion'
import useToggledVersion, { Version } from '../../hooks/useToggledVersion'
import useWrapCallback, { WrapType } from '../../hooks/useWrapCallback'
import { useToggleSettingsMenu, useWalletModalToggle } from '../../state/application/hooks'
import { Field } from '../../state/swap/actions'
@ -48,7 +47,6 @@ import { ClickableText } from '../Pool/styleds'
import Loader from '../../components/Loader'
import { useIsTransactionUnsupported } from 'hooks/Trades'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
import { isTradeBetter } from 'utils/trades'
import { RouteComponentProps } from 'react-router-dom'
export default function Swap({ history }: RouteComponentProps) {
@ -91,14 +89,7 @@ export default function Swap({ history }: RouteComponentProps) {
// swap state
const { independentField, typedValue, recipient } = useSwapState()
const {
v1Trade,
v2Trade,
currencyBalances,
parsedAmount,
currencies,
inputError: swapInputError,
} = useDerivedSwapInfo()
const { v2Trade, currencyBalances, parsedAmount, currencies, inputError: swapInputError } = useDerivedSwapInfo()
const { wrapType, execute: onWrap, inputError: wrapInputError } = useWrapCallback(
currencies[Field.INPUT],
@ -109,14 +100,9 @@ export default function Swap({ history }: RouteComponentProps) {
const { address: recipientAddress } = useENSAddress(recipient)
const toggledVersion = useToggledVersion()
const tradesByVersion = {
[Version.v1]: v1Trade,
[Version.v2]: v2Trade,
}
const trade = showWrap ? undefined : tradesByVersion[toggledVersion]
const defaultTrade = showWrap ? undefined : tradesByVersion[DEFAULT_VERSION]
const betterTradeLinkV2: Version | undefined =
toggledVersion === Version.v1 && isTradeBetter(v1Trade, v2Trade) ? Version.v2 : undefined
const parsedAmounts = showWrap
? {
@ -509,11 +495,6 @@ export default function Swap({ history }: RouteComponentProps) {
</Column>
)}
{isExpertMode && swapErrorMessage ? <SwapCallbackError error={swapErrorMessage} /> : null}
{betterTradeLinkV2 && !swapIsUnsupported && toggledVersion === Version.v1 ? (
<BetterTradeLink version={betterTradeLinkV2} />
) : toggledVersion !== DEFAULT_VERSION && defaultTrade ? (
<DefaultVersionLink />
) : null}
</BottomGrouping>
</Wrapper>
</AppBody>

@ -1,12 +1,10 @@
import useENS from '../../hooks/useENS'
import { Version } from '../../hooks/useToggledVersion'
import { parseUnits } from '@ethersproject/units'
import { Currency, CurrencyAmount, ETHER, Token, TokenAmount } from '@uniswap/sdk-core'
import { JSBI, Trade } from '@uniswap/v2-sdk'
import { ParsedQs } from 'qs'
import { useCallback, useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useV1Trade } from '../../data/V1'
import { useActiveWeb3React } from '../../hooks'
import { useCurrency } from '../../hooks/Tokens'
import { useTradeExactIn, useTradeExactOut } from '../../hooks/Trades'
@ -16,7 +14,6 @@ import { AppDispatch, AppState } from '../index'
import { useCurrencyBalances } from '../wallet/hooks'
import { Field, replaceSwapState, selectCurrency, setRecipient, switchCurrencies, typeInput } from './actions'
import { SwapState } from './reducer'
import useToggledVersion from '../../hooks/useToggledVersion'
import { useUserSlippageTolerance } from '../user/hooks'
import { computeSlippageAdjustedAmounts } from '../../utils/prices'
@ -89,11 +86,11 @@ export function tryParseAmount(value?: string, currency?: Currency): CurrencyAmo
return undefined
}
const BAD_RECIPIENT_ADDRESSES: string[] = [
'0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f', // v2 factory
'0xf164fC0Ec4E93095b804a4795bBe1e041497b92a', // v2 router 01
'0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', // v2 router 02
]
const BAD_RECIPIENT_ADDRESSES: { [address: string]: true } = {
'0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f': true, // v2 factory
'0xf164fC0Ec4E93095b804a4795bBe1e041497b92a': true, // v2 router 01
'0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D': true, // v2 router 02
}
/**
* Returns true if any of the pairs or tokens in a trade have the given checksummed address
@ -114,12 +111,9 @@ export function useDerivedSwapInfo(): {
parsedAmount: CurrencyAmount | undefined
v2Trade: Trade | undefined
inputError?: string
v1Trade: Trade | undefined
} {
const { account } = useActiveWeb3React()
const toggledVersion = useToggledVersion()
const {
independentField,
typedValue,
@ -156,9 +150,6 @@ export function useDerivedSwapInfo(): {
[Field.OUTPUT]: outputCurrency ?? undefined,
}
// get link to trade on v1, if a better rate exists
const v1Trade = useV1Trade(isExactIn, currencies[Field.INPUT], currencies[Field.OUTPUT], parsedAmount)
let inputError: string | undefined
if (!account) {
inputError = 'Connect Wallet'
@ -177,7 +168,7 @@ export function useDerivedSwapInfo(): {
inputError = inputError ?? 'Enter a recipient'
} else {
if (
BAD_RECIPIENT_ADDRESSES.indexOf(formattedTo) !== -1 ||
BAD_RECIPIENT_ADDRESSES[formattedTo] ||
(bestTradeExactIn && involvesAddress(bestTradeExactIn, formattedTo)) ||
(bestTradeExactOut && involvesAddress(bestTradeExactOut, formattedTo))
) {
@ -189,19 +180,10 @@ export function useDerivedSwapInfo(): {
const slippageAdjustedAmounts = v2Trade && allowedSlippage && computeSlippageAdjustedAmounts(v2Trade, allowedSlippage)
const slippageAdjustedAmountsV1 =
v1Trade && allowedSlippage && computeSlippageAdjustedAmounts(v1Trade, allowedSlippage)
// compare input balance to max input based on version
const [balanceIn, amountIn] = [
currencyBalances[Field.INPUT],
toggledVersion === Version.v1
? slippageAdjustedAmountsV1
? slippageAdjustedAmountsV1[Field.INPUT]
: null
: slippageAdjustedAmounts
? slippageAdjustedAmounts[Field.INPUT]
: null,
slippageAdjustedAmounts ? slippageAdjustedAmounts[Field.INPUT] : null,
]
if (balanceIn && amountIn && balanceIn.lessThan(amountIn)) {
@ -214,7 +196,6 @@ export function useDerivedSwapInfo(): {
parsedAmount,
v2Trade: v2Trade ?? undefined,
inputError,
v1Trade,
}
}

@ -1,98 +0,0 @@
import { CurrencyAmount, ETHER, Percent, TokenAmount } from '@uniswap/sdk-core'
import { Route, Trade } from '@uniswap/v2-sdk'
import { DAI, USDC } from '../constants'
import { MockV1Pair } from '../data/V1'
import v1SwapArguments from './v1SwapArguments'
describe('v1SwapArguments', () => {
const USDC_WETH = new MockV1Pair('1000000', new TokenAmount(USDC, '1000000'))
const DAI_WETH = new MockV1Pair('1000000', new TokenAmount(DAI, '1000000'))
// just some random address
const TEST_RECIPIENT_ADDRESS = USDC_WETH.liquidityToken.address
it('exact eth to token', () => {
const trade = Trade.exactIn(new Route([USDC_WETH], ETHER), CurrencyAmount.ether('100'))
const result = v1SwapArguments(trade, {
recipient: TEST_RECIPIENT_ADDRESS,
allowedSlippage: new Percent('1', '100'),
deadline: 20 * 60,
})
expect(result.methodName).toEqual('ethToTokenTransferInput')
expect(result.args).toEqual(['0x62', '0x4b0', TEST_RECIPIENT_ADDRESS])
expect(result.value).toEqual('0x64')
})
it('exact token to eth', () => {
const trade = Trade.exactIn(new Route([USDC_WETH], USDC, ETHER), new TokenAmount(USDC, '100'))
const result = v1SwapArguments(trade, {
recipient: TEST_RECIPIENT_ADDRESS,
allowedSlippage: new Percent('1', '100'),
deadline: 40 * 60,
})
expect(result.methodName).toEqual('tokenToEthTransferInput')
expect(result.args[0]).toEqual('0x64')
expect(result.args[1]).toEqual('0x62')
expect(result.args[2]).toEqual('0x960')
expect(result.args[3]).toEqual(TEST_RECIPIENT_ADDRESS)
expect(result.value).toEqual('0x0')
})
it('exact token to token', () => {
const trade = Trade.exactIn(new Route([USDC_WETH, DAI_WETH], USDC), new TokenAmount(USDC, '100'))
const result = v1SwapArguments(trade, {
recipient: TEST_RECIPIENT_ADDRESS,
allowedSlippage: new Percent('1', '100'),
deadline: 20 * 60,
})
expect(result.methodName).toEqual('tokenToTokenTransferInput')
expect(result.args[0]).toEqual('0x64')
expect(result.args[1]).toEqual('0x61')
expect(result.args[2]).toEqual('0x1')
expect(result.args[3]).toEqual('0x4b0')
expect(result.args[4]).toEqual(TEST_RECIPIENT_ADDRESS)
expect(result.args[5]).toEqual(DAI.address)
expect(result.value).toEqual('0x0')
})
it('eth to exact token', () => {
const trade = Trade.exactOut(new Route([USDC_WETH], ETHER), new TokenAmount(USDC, '100'))
const result = v1SwapArguments(trade, {
recipient: TEST_RECIPIENT_ADDRESS,
allowedSlippage: new Percent('1', '100'),
deadline: 20 * 60,
})
expect(result.methodName).toEqual('ethToTokenTransferOutput')
expect(result.args[0]).toEqual('0x64')
expect(result.args[1]).toEqual('0x4b0')
expect(result.args[2]).toEqual(TEST_RECIPIENT_ADDRESS)
expect(result.value).toEqual('0x66')
})
it('token to exact eth', () => {
const trade = Trade.exactOut(new Route([USDC_WETH], USDC, ETHER), CurrencyAmount.ether('100'))
const result = v1SwapArguments(trade, {
recipient: TEST_RECIPIENT_ADDRESS,
allowedSlippage: new Percent('1', '100'),
deadline: 20 * 60,
})
expect(result.methodName).toEqual('tokenToEthTransferOutput')
expect(result.args[0]).toEqual('0x64')
expect(result.args[1]).toEqual('0x66')
expect(result.args[2]).toEqual('0x4b0')
expect(result.args[3]).toEqual(TEST_RECIPIENT_ADDRESS)
expect(result.value).toEqual('0x0')
})
it('token to exact token', () => {
const trade = Trade.exactOut(new Route([USDC_WETH, DAI_WETH], USDC), new TokenAmount(DAI, '100'))
const result = v1SwapArguments(trade, {
recipient: TEST_RECIPIENT_ADDRESS,
allowedSlippage: new Percent('1', '100'),
deadline: 20 * 60,
})
expect(result.methodName).toEqual('tokenToTokenTransferOutput')
expect(result.args[0]).toEqual('0x64')
expect(result.args[1]).toEqual('0x67')
expect(result.args[2]).toEqual(`0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`)
expect(result.args[3]).toEqual('0x4b0')
expect(result.args[4]).toEqual(TEST_RECIPIENT_ADDRESS)
expect(result.args[5]).toEqual(DAI.address)
expect(result.value).toEqual('0x0')
})
})

@ -1,91 +0,0 @@
import { MaxUint256 } from '@ethersproject/constants'
import { CurrencyAmount, ETHER, Token, TradeType } from '@uniswap/sdk-core'
import { Trade, TradeOptionsDeadline, SwapParameters } from '@uniswap/v2-sdk'
import { getTradeVersion } from '../data/V1'
import { Version } from '../hooks/useToggledVersion'
function toHex(currencyAmount: CurrencyAmount): string {
return `0x${currencyAmount.raw.toString(16)}`
}
/**
* Get the arguments to make for a swap
* @param trade trade to get v1 arguments for swapping
* @param options options for swapping
*/
export default function v1SwapArguments(
trade: Trade,
options: Omit<TradeOptionsDeadline, 'feeOnTransfer'>
): SwapParameters {
if (getTradeVersion(trade) !== Version.v1) {
throw new Error('invalid trade version')
}
if (trade.route.pairs.length > 2) {
throw new Error('too many pairs')
}
const isExactIn = trade.tradeType === TradeType.EXACT_INPUT
const inputETH = trade.inputAmount.currency === ETHER
const outputETH = trade.outputAmount.currency === ETHER
if (inputETH && outputETH) throw new Error('ETHER to ETHER')
const minimumAmountOut = toHex(trade.minimumAmountOut(options.allowedSlippage))
const maximumAmountIn = toHex(trade.maximumAmountIn(options.allowedSlippage))
const deadline = `0x${options.deadline.toString(16)}`
if (isExactIn) {
if (inputETH) {
return {
methodName: 'ethToTokenTransferInput',
args: [minimumAmountOut, deadline, options.recipient],
value: maximumAmountIn,
}
} else if (outputETH) {
return {
methodName: 'tokenToEthTransferInput',
args: [maximumAmountIn, minimumAmountOut, deadline, options.recipient],
value: '0x0',
}
} else {
const outputToken = trade.outputAmount.currency
// should never happen, needed for type check
if (!(outputToken instanceof Token)) {
throw new Error('token to token')
}
return {
methodName: 'tokenToTokenTransferInput',
args: [maximumAmountIn, minimumAmountOut, '0x1', deadline, options.recipient, outputToken.address],
value: '0x0',
}
}
} else {
if (inputETH) {
return {
methodName: 'ethToTokenTransferOutput',
args: [minimumAmountOut, deadline, options.recipient],
value: maximumAmountIn,
}
} else if (outputETH) {
return {
methodName: 'tokenToEthTransferOutput',
args: [minimumAmountOut, maximumAmountIn, deadline, options.recipient],
value: '0x0',
}
} else {
const output = trade.outputAmount.currency
if (!(output instanceof Token)) {
throw new Error('invalid output amount currency')
}
return {
methodName: 'tokenToTokenTransferOutput',
args: [
minimumAmountOut,
maximumAmountIn,
MaxUint256.toHexString(),
deadline,
options.recipient,
output.address,
],
value: '0x0',
}
}
}
}

@ -4149,25 +4149,25 @@
"@uniswap/v2-core" "1.0.1"
"@uniswap/v3-core" "1.0.0-rc.2"
"@uniswap/v3-periphery@^1.0.0-beta.21":
version "1.0.0-beta.21"
resolved "https://registry.yarnpkg.com/@uniswap/v3-periphery/-/v3-periphery-1.0.0-beta.21.tgz#0b8510bebca4b74aabdca72c5545bd5bec5128cd"
integrity sha512-o4U+lyH6qtlG2RTy3H/mtUGJuflkmVJ0pnXyrThZKC1KV/avlVgf4hmlG2PvOCV0yfwGMjQARKQ4jv6OpLFVqA==
"@uniswap/v3-periphery@^1.0.0-beta.22":
version "1.0.0-beta.22"
resolved "https://registry.yarnpkg.com/@uniswap/v3-periphery/-/v3-periphery-1.0.0-beta.22.tgz#b084926f290494ba3e4fcfa2b6a4ca11cd6631be"
integrity sha512-kDi5d1/7j++JDdfr0eemZo/2ZYtMmxRaq7O6cfaOW5Pv5j2wCYsauwC3SKzjpXsFD6Z0SdPPS3pVAu7UygnJYg==
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.2"
"@uniswap/v3-sdk@^1.0.0-alpha.13":
version "1.0.0-alpha.13"
resolved "https://registry.yarnpkg.com/@uniswap/v3-sdk/-/v3-sdk-1.0.0-alpha.13.tgz#5634bb361e6bcb8e1a05d79a2aa61ffe13412f0c"
integrity sha512-OEEMhMoJlRQyqcd6u4r37cyifVy5jKJf/afW1jD8iqRqg/qAiYG0aR9Y/s7HiDA9WVHyaL/0YsWG8f0hv2UK/w==
"@uniswap/v3-sdk@^1.0.0-alpha.17":
version "1.0.0-alpha.17"
resolved "https://registry.yarnpkg.com/@uniswap/v3-sdk/-/v3-sdk-1.0.0-alpha.17.tgz#5d21d2fc9f7d265af241933353661824e54b3fe4"
integrity sha512-fdLMwC+YrXV1KMf2nCzPqbl6309hn0Z74GpWGa3L2ow4Ntp6e4VHQu6uWffCRIwoRbFbhIQaoWlYasCuOU+o2Q==
dependencies:
"@ethersproject/abi" "^5.0.12"
"@ethersproject/solidity" "^5.0.9"
"@uniswap/sdk-core" "^1.0.9"
"@uniswap/v3-periphery" "^1.0.0-beta.21"
"@uniswap/v3-periphery" "^1.0.0-beta.22"
tiny-invariant "^1.1.0"
tiny-warning "^1.0.3"