Merge remote-tracking branch 'refs/remotes/origin/main'
This commit is contained in:
commit
6b99309fab
@ -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,28 +72,44 @@ 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)
|
||||
|
||||
// still loading data
|
||||
if (slot0Loading || addressesLoading || liquidityLoading || tickLoading) return [PoolState.LOADING, null]
|
||||
return useMemo(() => {
|
||||
// still loading data
|
||||
if (slot0Loading || addressesLoading || liquidityLoading || tickLoading || tickSyncing)
|
||||
return [PoolState.LOADING, null]
|
||||
|
||||
// invalid pool setup
|
||||
if (!tokenA || !tokenB || !feeAmount || tokenA.equals(tokenB)) return [PoolState.INVALID, null]
|
||||
// invalid pool setup
|
||||
if (!tokenA || !tokenB || !feeAmount || tokenA.equals(tokenB)) return [PoolState.INVALID, null]
|
||||
|
||||
// pool has not been created or not initialized yet
|
||||
if (poolAddressFromFactory === ZERO_ADDRESS || !slot0 || !liquidity || slot0.sqrtPriceX96 === 0) {
|
||||
return [PoolState.NOT_EXISTS, null]
|
||||
}
|
||||
// pool has not been created or not initialized yet
|
||||
if (poolAddressFromFactory === ZERO_ADDRESS || !slot0 || !liquidity || slot0.sqrtPriceX96 === 0) {
|
||||
return [PoolState.NOT_EXISTS, null]
|
||||
}
|
||||
|
||||
const tickList: Tick[] = tickData
|
||||
.map((tick) => {
|
||||
return new Tick({
|
||||
index: tick.tick,
|
||||
liquidityGross: tick.liquidityGross,
|
||||
liquidityNet: tick.liquidityNet,
|
||||
const tickList: Tick[] = tickData
|
||||
.map((tick) => {
|
||||
return new Tick({
|
||||
index: tick.tick,
|
||||
liquidityGross: tick.liquidityGross,
|
||||
liquidityNet: tick.liquidityNet,
|
||||
})
|
||||
})
|
||||
})
|
||||
.sort((tickA, tickB) => (tickA.index > tickB.index ? 1 : -1))
|
||||
.sort((tickA, tickB) => (tickA.index > tickB.index ? 1 : -1))
|
||||
|
||||
return [PoolState.EXISTS, new Pool(tokenA, tokenB, feeAmount, slot0.sqrtPriceX96, liquidity, slot0.tick, tickList)]
|
||||
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,
|
||||
])
|
||||
}
|
||||
|
157
src/data/V1.ts
157
src/data/V1.ts
@ -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)
|
||||
}
|
7
src/data/getTradeVersion.ts
Normal file
7
src/data/getTradeVersion.ts
Normal file
@ -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,54 +53,37 @@ 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,
|
||||
allowedSlippage: new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE),
|
||||
recipient,
|
||||
deadline: deadline.toNumber(),
|
||||
})
|
||||
)
|
||||
swapMethods.push(
|
||||
Router.swapCallParameters(trade, {
|
||||
feeOnTransfer: false,
|
||||
allowedSlippage: new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE),
|
||||
recipient,
|
||||
deadline: deadline.toNumber(),
|
||||
})
|
||||
)
|
||||
|
||||
if (trade.tradeType === TradeType.EXACT_INPUT) {
|
||||
swapMethods.push(
|
||||
Router.swapCallParameters(trade, {
|
||||
feeOnTransfer: true,
|
||||
allowedSlippage: new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE),
|
||||
recipient,
|
||||
deadline: deadline.toNumber(),
|
||||
})
|
||||
)
|
||||
}
|
||||
break
|
||||
case Version.v1:
|
||||
swapMethods.push(
|
||||
v1SwapArguments(trade, {
|
||||
allowedSlippage: new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE),
|
||||
recipient,
|
||||
deadline: deadline.toNumber(),
|
||||
})
|
||||
)
|
||||
break
|
||||
if (trade.tradeType === TradeType.EXACT_INPUT) {
|
||||
swapMethods.push(
|
||||
Router.swapCallParameters(trade, {
|
||||
feeOnTransfer: true,
|
||||
allowedSlippage: new Percent(JSBI.BigInt(allowedSlippage), BIPS_BASE),
|
||||
recipient,
|
||||
deadline: deadline.toNumber(),
|
||||
})
|
||||
)
|
||||
}
|
||||
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',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
yarn.lock
18
yarn.lock
@ -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"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user