Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af6098bfe5 | ||
|
|
fce29bb36f | ||
|
|
4517af39ba | ||
|
|
b40163ce05 |
3
.env
3
.env
@@ -1,5 +1,4 @@
|
||||
REACT_APP_CHAIN_ID="1"
|
||||
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_WALLETCONNECT_BRIDGE_URL="https://uniswap.bridge.walletconnect.org"
|
||||
# Because we use storybook which has its own babel-loader dependency @ 8.2.2, where react-scripts uses 8.1.0
|
||||
SKIP_PREFLIGHT_CHECK=true
|
||||
@@ -1,5 +1,4 @@
|
||||
REACT_APP_CHAIN_ID="1"
|
||||
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/099fc58e0de9451d80b18d7c74caa7c1"
|
||||
REACT_APP_INFURA_KEY="099fc58e0de9451d80b18d7c74caa7c1"
|
||||
REACT_APP_PORTIS_ID="c0e2bf01-4b08-4fd5-ac7b-8e26b58cd236"
|
||||
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID="UA-128182339-4"
|
||||
|
||||
15
README.md
15
README.md
@@ -27,7 +27,7 @@ or visit [app.uniswap.org](https://app.uniswap.org).
|
||||
### Install Dependencies
|
||||
|
||||
```bash
|
||||
yarn
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Run
|
||||
@@ -36,19 +36,6 @@ yarn
|
||||
yarn start
|
||||
```
|
||||
|
||||
### Configuring the environment (optional)
|
||||
|
||||
To have the interface default to a different network when a wallet is not connected:
|
||||
|
||||
1. Make a copy of `.env` named `.env.local`
|
||||
2. Change `REACT_APP_NETWORK_ID` to `"{YOUR_NETWORK_ID}"`
|
||||
3. Change `REACT_APP_NETWORK_URL` to e.g. `"https://{YOUR_NETWORK_ID}.infura.io/v3/{YOUR_INFURA_KEY}"`
|
||||
|
||||
Note that the interface only works on testnets where both
|
||||
[Uniswap V2](https://uniswap.org/docs/v2/smart-contracts/factory/) and
|
||||
[multicall](https://github.com/makerdao/multicall) are deployed.
|
||||
The interface will not work on other networks.
|
||||
|
||||
## Contributions
|
||||
|
||||
**Please open all pull requests against the `main` branch.**
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"@web3-react/fortmatic-connector": "^6.0.9",
|
||||
"@web3-react/injected-connector": "^6.0.7",
|
||||
"@web3-react/portis-connector": "^6.0.9",
|
||||
"@web3-react/walletconnect-connector": "^6.1.1",
|
||||
"@web3-react/walletconnect-connector": "^6.2.0",
|
||||
"@web3-react/walletlink-connector": "^6.2.0",
|
||||
"ajv": "^6.12.3",
|
||||
"cids": "^1.0.0",
|
||||
|
||||
@@ -1,40 +1,53 @@
|
||||
import { Web3Provider } from '@ethersproject/providers'
|
||||
import { ChainId } from '@uniswap/sdk-core'
|
||||
import { InjectedConnector } from '@web3-react/injected-connector'
|
||||
import { WalletConnectConnector } from '@web3-react/walletconnect-connector'
|
||||
import { WalletLinkConnector } from '@web3-react/walletlink-connector'
|
||||
import { PortisConnector } from '@web3-react/portis-connector'
|
||||
import getLibrary from '../utils/getLibrary'
|
||||
|
||||
import { FortmaticConnector } from './Fortmatic'
|
||||
import { NetworkConnector } from './NetworkConnector'
|
||||
import UNISWAP_LOGO_URL from '../assets/svg/logo.svg'
|
||||
|
||||
const NETWORK_URL = process.env.REACT_APP_NETWORK_URL
|
||||
const INFURA_KEY = process.env.REACT_APP_INFURA_KEY
|
||||
const FORMATIC_KEY = process.env.REACT_APP_FORTMATIC_KEY
|
||||
const PORTIS_ID = process.env.REACT_APP_PORTIS_ID
|
||||
const WALLETCONNECT_BRIDGE_URL = process.env.REACT_APP_WALLETCONNECT_BRIDGE_URL
|
||||
|
||||
export const NETWORK_CHAIN_ID: number = parseInt(process.env.REACT_APP_CHAIN_ID ?? '1')
|
||||
|
||||
if (typeof NETWORK_URL === 'undefined') {
|
||||
throw new Error(`REACT_APP_NETWORK_URL must be a defined environment variable`)
|
||||
if (typeof INFURA_KEY === 'undefined') {
|
||||
throw new Error(`REACT_APP_INFURA_KEY must be a defined environment variable`)
|
||||
}
|
||||
|
||||
const NETWORK_URLS: {
|
||||
[chainId in ChainId]: string
|
||||
} = {
|
||||
[ChainId.MAINNET]: `https://mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[ChainId.RINKEBY]: `https://rinkeby.infura.io/v3/${INFURA_KEY}`,
|
||||
[ChainId.ROPSTEN]: `https://ropsten.infura.io/v3/${INFURA_KEY}`,
|
||||
[ChainId.GÖRLI]: `https://goerli.infura.io/v3/${INFURA_KEY}`,
|
||||
[ChainId.KOVAN]: `https://kovan.infura.io/v3/${INFURA_KEY}`,
|
||||
}
|
||||
|
||||
const SUPPORTED_CHAIN_IDS = [ChainId.MAINNET, ChainId.RINKEBY, ChainId.ROPSTEN, ChainId.KOVAN, ChainId.GÖRLI]
|
||||
|
||||
export const network = new NetworkConnector({
|
||||
urls: { [NETWORK_CHAIN_ID]: NETWORK_URL },
|
||||
urls: NETWORK_URLS,
|
||||
defaultChainId: ChainId.MAINNET,
|
||||
})
|
||||
|
||||
let networkLibrary: Web3Provider | undefined
|
||||
export function getNetworkLibrary(): Web3Provider {
|
||||
return (networkLibrary = networkLibrary ?? new Web3Provider(network.provider as any))
|
||||
return (networkLibrary = networkLibrary ?? getLibrary(network.provider))
|
||||
}
|
||||
|
||||
export const injected = new InjectedConnector({
|
||||
supportedChainIds: [1, 3, 4, 5, 42],
|
||||
supportedChainIds: SUPPORTED_CHAIN_IDS,
|
||||
})
|
||||
|
||||
// mainnet only
|
||||
export const walletconnect = new WalletConnectConnector({
|
||||
rpc: { 1: NETWORK_URL },
|
||||
supportedChainIds: SUPPORTED_CHAIN_IDS,
|
||||
infuraId: INFURA_KEY, // obviously a hack
|
||||
bridge: WALLETCONNECT_BRIDGE_URL,
|
||||
qrcode: true,
|
||||
pollingInterval: 15000,
|
||||
@@ -54,7 +67,7 @@ export const portis = new PortisConnector({
|
||||
|
||||
// mainnet only
|
||||
export const walletlink = new WalletLinkConnector({
|
||||
url: NETWORK_URL,
|
||||
url: NETWORK_URLS[ChainId.MAINNET],
|
||||
appName: 'Uniswap',
|
||||
appLogoUrl: UNISWAP_LOGO_URL,
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ChainId } from '@uniswap/sdk-core'
|
||||
import { TokenList } from '@uniswap/token-lists'
|
||||
import { useCallback } from 'react'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { getNetworkLibrary, NETWORK_CHAIN_ID } from '../connectors'
|
||||
import { getNetworkLibrary } from '../connectors'
|
||||
import { AppDispatch } from '../state'
|
||||
import { fetchTokenList } from '../state/lists/actions'
|
||||
import getTokenList from '../utils/getTokenList'
|
||||
@@ -15,13 +15,12 @@ export function useFetchListCallback(): (listUrl: string, sendDispatch?: boolean
|
||||
const dispatch = useDispatch<AppDispatch>()
|
||||
|
||||
const ensResolver = useCallback(
|
||||
(ensName: string) => {
|
||||
async (ensName: string) => {
|
||||
if (!library || chainId !== ChainId.MAINNET) {
|
||||
if (NETWORK_CHAIN_ID === ChainId.MAINNET) {
|
||||
const networkLibrary = getNetworkLibrary()
|
||||
if (networkLibrary) {
|
||||
return resolveENSContentHash(ensName, networkLibrary)
|
||||
}
|
||||
const networkLibrary = getNetworkLibrary()
|
||||
const network = await networkLibrary.getNetwork()
|
||||
if (networkLibrary && network.chainId === ChainId.MAINNET) {
|
||||
return resolveENSContentHash(ensName, networkLibrary)
|
||||
}
|
||||
throw new Error('Could not construct mainnet ENS resolver')
|
||||
}
|
||||
|
||||
@@ -56,7 +56,9 @@ export default function useUSDCPrice(currency?: Currency): Price<Currency, Token
|
||||
|
||||
const ethPairETHAmount = ethPair?.reserveOf(weth)
|
||||
const ethPairETHUSDCValue: JSBI =
|
||||
ethPairETHAmount && usdcEthPair ? usdcEthPair.priceOf(weth).quote(ethPairETHAmount).quotient : JSBI.BigInt(0)
|
||||
ethPairETHAmount?.greaterThan(0) && usdcEthPair?.reserveOf(weth)?.greaterThan(0)
|
||||
? usdcEthPair.priceOf(weth).quote(ethPairETHAmount).quotient
|
||||
: JSBI.BigInt(0)
|
||||
|
||||
// all other tokens
|
||||
// first try the usdc pair
|
||||
@@ -81,6 +83,10 @@ export function useUSDCValue(currencyAmount: CurrencyAmount<Currency> | undefine
|
||||
|
||||
return useMemo(() => {
|
||||
if (!price || !currencyAmount) return null
|
||||
return price.quote(currencyAmount)
|
||||
try {
|
||||
return price.quote(currencyAmount)
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}, [currencyAmount, price])
|
||||
}
|
||||
|
||||
@@ -3,16 +3,18 @@ import { useEffect, useState } from 'react'
|
||||
import { useV3NFTPositionManagerContract } from './useContract'
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { Pool } from '@uniswap/v3-sdk'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { CurrencyAmount, Token, currencyEquals, ETHER, Ether } from '@uniswap/sdk-core'
|
||||
import { useBlockNumber } from 'state/application/hooks'
|
||||
import { unwrappedToken } from 'utils/wrappedCurrency'
|
||||
|
||||
const MAX_UINT128 = BigNumber.from(2).pow(128).sub(1)
|
||||
|
||||
// compute current + counterfactual fees for a v3 position
|
||||
export function useV3PositionFees(
|
||||
pool?: Pool,
|
||||
tokenId?: BigNumber
|
||||
): [CurrencyAmount<Token>, CurrencyAmount<Token>] | [undefined, undefined] {
|
||||
tokenId?: BigNumber,
|
||||
asWETH = false
|
||||
): [CurrencyAmount<Token | Ether>, CurrencyAmount<Token | Ether>] | [undefined, undefined] {
|
||||
const positionManager = useV3NFTPositionManagerContract(false)
|
||||
const owner = useSingleCallResult(tokenId ? positionManager : null, 'ownerOf', [tokenId]).result?.[0]
|
||||
|
||||
@@ -43,8 +45,12 @@ export function useV3PositionFees(
|
||||
|
||||
if (pool && amounts) {
|
||||
return [
|
||||
CurrencyAmount.fromRawAmount(pool.token0, amounts[0].toString()),
|
||||
CurrencyAmount.fromRawAmount(pool.token1, amounts[1].toString()),
|
||||
!asWETH && currencyEquals(unwrappedToken(pool.token0), ETHER)
|
||||
? CurrencyAmount.ether(amounts[0].toString())
|
||||
: CurrencyAmount.fromRawAmount(pool.token0, amounts[0].toString()),
|
||||
!asWETH && currencyEquals(unwrappedToken(pool.token1), ETHER)
|
||||
? CurrencyAmount.ether(amounts[1].toString())
|
||||
: CurrencyAmount.fromRawAmount(pool.token1, amounts[1].toString()),
|
||||
]
|
||||
} else {
|
||||
return [undefined, undefined]
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { SyntheticEvent, useCallback, useMemo, useRef, useState } from 'react'
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
||||
import { NonfungiblePositionManager, Pool, Position } from '@uniswap/v3-sdk'
|
||||
|
||||
import { PoolState, usePool } from 'hooks/usePools'
|
||||
import { useToken } from 'hooks/Tokens'
|
||||
import { useV3PositionFromTokenId } from 'hooks/useV3Positions'
|
||||
import { Link, RouteComponentProps } from 'react-router-dom'
|
||||
import { unwrappedToken } from 'utils/wrappedCurrency'
|
||||
import { unwrappedToken, wrappedCurrencyAmount } from 'utils/wrappedCurrency'
|
||||
import { usePositionTokenURI } from '../../hooks/usePositionTokenURI'
|
||||
import { LoadingRows } from './styleds'
|
||||
import styled from 'styled-components/macro'
|
||||
@@ -23,7 +23,7 @@ import { currencyId } from 'utils/currencyId'
|
||||
import { formatTokenAmount } from 'utils/formatTokenAmount'
|
||||
import { useV3PositionFees } from 'hooks/useV3PositionFees'
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { Token, WETH9, Currency, CurrencyAmount, Percent, Fraction, Price, currencyEquals } from '@uniswap/sdk-core'
|
||||
import { Token, Currency, CurrencyAmount, Percent, Fraction, Price, Ether } from '@uniswap/sdk-core'
|
||||
import { useActiveWeb3React } from 'hooks'
|
||||
import { useV3NFTPositionManagerContract } from 'hooks/useContract'
|
||||
import { useIsTransactionPending, useTransactionAdder } from 'state/transactions/hooks'
|
||||
@@ -38,6 +38,7 @@ import { useSingleCallResult } from 'state/multicall/hooks'
|
||||
import RangeBadge from '../../components/Badge/RangeBadge'
|
||||
import useUSDCPrice from 'hooks/useUSDCPrice'
|
||||
import Loader from 'components/Loader'
|
||||
import Toggle from 'components/Toggle'
|
||||
|
||||
const PageWrapper = styled.div`
|
||||
min-width: 800px;
|
||||
@@ -200,20 +201,11 @@ function getRatio(
|
||||
}
|
||||
}
|
||||
|
||||
function NFT({ image, height: targetHeight }: { image: string; height: number }) {
|
||||
const [animate, setAnimate] = useState(false)
|
||||
|
||||
const canvasRef = useRef<HTMLCanvasElement>()
|
||||
const imageRef = useRef<HTMLImageElement>()
|
||||
|
||||
const getSnapshot = (src: HTMLImageElement) => {
|
||||
if (!canvasRef.current) return
|
||||
|
||||
const { current: canvas } = canvasRef
|
||||
const context = canvas.getContext('2d')
|
||||
|
||||
if (!context) return
|
||||
// snapshots a src img into a canvas
|
||||
function getSnapshot(src: HTMLImageElement, canvas: HTMLCanvasElement, targetHeight: number) {
|
||||
const context = canvas.getContext('2d')
|
||||
|
||||
if (context) {
|
||||
let { width, height } = src
|
||||
|
||||
// src may be hidden and not have the target dimensions
|
||||
@@ -231,15 +223,39 @@ function NFT({ image, height: targetHeight }: { image: string; height: number })
|
||||
context.clearRect(0, 0, width, height)
|
||||
context.drawImage(src, 0, 0, width, height)
|
||||
}
|
||||
}
|
||||
|
||||
const onLoad = (e: SyntheticEvent<HTMLImageElement>) => {
|
||||
getSnapshot(e.target as HTMLImageElement)
|
||||
}
|
||||
function NFT({ image, height: targetHeight }: { image: string; height: number }) {
|
||||
const [animate, setAnimate] = useState(false)
|
||||
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||
const imageRef = useRef<HTMLImageElement>(null)
|
||||
|
||||
return (
|
||||
<NFTGrid onMouseEnter={() => setAnimate(true)} onMouseLeave={() => setAnimate(false)}>
|
||||
<NFTCanvas ref={canvasRef as any} />
|
||||
<NFTImage src={image} hidden={!animate} onLoad={onLoad} ref={imageRef as any} />
|
||||
<NFTGrid
|
||||
onMouseEnter={() => {
|
||||
setAnimate(true)
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
// snapshot the current frame so the transition to the canvas is smooth
|
||||
if (imageRef.current && canvasRef.current) {
|
||||
getSnapshot(imageRef.current, canvasRef.current, targetHeight)
|
||||
}
|
||||
setAnimate(false)
|
||||
}}
|
||||
>
|
||||
<NFTCanvas ref={canvasRef} />
|
||||
<NFTImage
|
||||
ref={imageRef}
|
||||
src={image}
|
||||
hidden={!animate}
|
||||
onLoad={() => {
|
||||
// snapshot for the canvas
|
||||
if (imageRef.current && canvasRef.current) {
|
||||
getSnapshot(imageRef.current, canvasRef.current, targetHeight)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</NFTGrid>
|
||||
)
|
||||
}
|
||||
@@ -269,8 +285,11 @@ export function PositionPage({
|
||||
const currency0 = token0 ? unwrappedToken(token0) : undefined
|
||||
const currency1 = token1 ? unwrappedToken(token1) : undefined
|
||||
|
||||
// flag for receiving WETH
|
||||
const [receiveWETH, setReceiveWETH] = useState(false)
|
||||
|
||||
// construct Position from details returned
|
||||
const [poolState, pool] = usePool(currency0 ?? undefined, currency1 ?? undefined, feeAmount)
|
||||
const [poolState, pool] = usePool(token0 ?? undefined, token1 ?? undefined, feeAmount)
|
||||
const position = useMemo(() => {
|
||||
if (pool && liquidity && typeof tickLower === 'number' && typeof tickUpper === 'number') {
|
||||
return new Position({ pool, liquidity: liquidity.toString(), tickLower, tickUpper })
|
||||
@@ -304,7 +323,7 @@ export function PositionPage({
|
||||
}, [inverted, pool, priceLower, priceUpper])
|
||||
|
||||
// fees
|
||||
const [feeValue0, feeValue1] = useV3PositionFees(pool ?? undefined, positionDetails?.tokenId)
|
||||
const [feeValue0, feeValue1] = useV3PositionFees(pool ?? undefined, positionDetails?.tokenId, receiveWETH)
|
||||
|
||||
const [collecting, setCollecting] = useState<boolean>(false)
|
||||
const [collectMigrationHash, setCollectMigrationHash] = useState<string | null>(null)
|
||||
@@ -320,12 +339,8 @@ export function PositionPage({
|
||||
|
||||
const { calldata, value } = NonfungiblePositionManager.collectCallParameters({
|
||||
tokenId: tokenId.toString(),
|
||||
expectedCurrencyOwed0: currencyEquals(feeValue0.currency, WETH9[chainId])
|
||||
? CurrencyAmount.ether(feeValue0.quotient)
|
||||
: feeValue0,
|
||||
expectedCurrencyOwed1: currencyEquals(feeValue1.currency, WETH9[chainId])
|
||||
? CurrencyAmount.ether(feeValue1.quotient)
|
||||
: feeValue1,
|
||||
expectedCurrencyOwed0: feeValue0,
|
||||
expectedCurrencyOwed1: feeValue1,
|
||||
recipient: account,
|
||||
})
|
||||
|
||||
@@ -371,15 +386,23 @@ export function PositionPage({
|
||||
const owner = useSingleCallResult(!!tokenId ? positionManager : null, 'ownerOf', [tokenId]).result?.[0]
|
||||
const ownsNFT = owner === account || positionDetails?.operator === account
|
||||
|
||||
// usdc prices always in terms of tokens
|
||||
const price0 = useUSDCPrice(token0 ?? undefined)
|
||||
const price1 = useUSDCPrice(token1 ?? undefined)
|
||||
|
||||
const fiatValueOfFees: CurrencyAmount<Token> | null = useMemo(() => {
|
||||
const fiatValueOfFees: CurrencyAmount<Token | Ether> | null = useMemo(() => {
|
||||
if (!price0 || !price1 || !feeValue0 || !feeValue1) return null
|
||||
const amount0 = price0.quote(feeValue0)
|
||||
const amount1 = price1.quote(feeValue1)
|
||||
|
||||
// we wrap because it doesn't matter, the quote returns a USDC amount
|
||||
const feeValue0Wrapped = wrappedCurrencyAmount(feeValue0, chainId)
|
||||
const feeValue1Wrapped = wrappedCurrencyAmount(feeValue1, chainId)
|
||||
|
||||
if (!feeValue0Wrapped || !feeValue1Wrapped) return null
|
||||
|
||||
const amount0 = price0.quote(feeValue0Wrapped)
|
||||
const amount1 = price1.quote(feeValue1Wrapped)
|
||||
return amount0.add(amount1)
|
||||
}, [price0, price1, feeValue0, feeValue1])
|
||||
}, [price0, price1, feeValue0, feeValue1, chainId])
|
||||
|
||||
const fiatValueOfLiquidity: CurrencyAmount<Token> | null = useMemo(() => {
|
||||
if (!price0 || !price1 || !position) return null
|
||||
@@ -388,6 +411,9 @@ export function PositionPage({
|
||||
return amount0.add(amount1)
|
||||
}, [price0, price1, position])
|
||||
|
||||
const feeValueUpper = inverted ? feeValue0 : feeValue1
|
||||
const feeValueLower = inverted ? feeValue1 : feeValue0
|
||||
|
||||
function modalHeader() {
|
||||
return (
|
||||
<AutoColumn gap={'md'} style={{ marginTop: '20px' }}>
|
||||
@@ -395,33 +421,17 @@ export function PositionPage({
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<CurrencyLogo currency={currencyQuote} size={'20px'} style={{ marginRight: '0.5rem' }} />
|
||||
<TYPE.main>
|
||||
{inverted
|
||||
? feeValue0
|
||||
? formatTokenAmount(feeValue0, 4)
|
||||
: '-'
|
||||
: feeValue1
|
||||
? formatTokenAmount(feeValue1, 4)
|
||||
: '-'}
|
||||
</TYPE.main>
|
||||
<CurrencyLogo currency={feeValueUpper?.currency} size={'20px'} style={{ marginRight: '0.5rem' }} />
|
||||
<TYPE.main>{feeValueUpper ? formatTokenAmount(feeValueUpper, 4) : '-'}</TYPE.main>
|
||||
</RowFixed>
|
||||
<TYPE.main>{currencyQuote?.symbol}</TYPE.main>
|
||||
<TYPE.main>{feeValueUpper?.currency?.symbol}</TYPE.main>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<CurrencyLogo currency={currencyBase} size={'20px'} style={{ marginRight: '0.5rem' }} />
|
||||
<TYPE.main>
|
||||
{inverted
|
||||
? feeValue0
|
||||
? formatTokenAmount(feeValue1, 4)
|
||||
: '-'
|
||||
: feeValue1
|
||||
? formatTokenAmount(feeValue0, 4)
|
||||
: '-'}
|
||||
</TYPE.main>
|
||||
<CurrencyLogo currency={feeValueLower?.currency} size={'20px'} style={{ marginRight: '0.5rem' }} />
|
||||
<TYPE.main>{feeValueLower ? formatTokenAmount(feeValueLower, 4) : '-'}</TYPE.main>
|
||||
</RowFixed>
|
||||
<TYPE.main>{currencyBase?.symbol}</TYPE.main>
|
||||
<TYPE.main>{feeValueLower?.currency?.symbol}</TYPE.main>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
@@ -640,40 +650,44 @@ export function PositionPage({
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<CurrencyLogo currency={currencyQuote} size={'20px'} style={{ marginRight: '0.5rem' }} />
|
||||
<TYPE.main>{currencyQuote?.symbol}</TYPE.main>
|
||||
<CurrencyLogo
|
||||
currency={feeValueUpper?.currency}
|
||||
size={'20px'}
|
||||
style={{ marginRight: '0.5rem' }}
|
||||
/>
|
||||
<TYPE.main>{feeValueUpper?.currency?.symbol}</TYPE.main>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<TYPE.main>
|
||||
{inverted
|
||||
? feeValue0
|
||||
? formatTokenAmount(feeValue0, 4)
|
||||
: '-'
|
||||
: feeValue1
|
||||
? formatTokenAmount(feeValue1, 4)
|
||||
: '-'}
|
||||
</TYPE.main>
|
||||
<TYPE.main>{feeValueUpper ? formatTokenAmount(feeValueUpper, 4) : '-'}</TYPE.main>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<CurrencyLogo currency={currencyBase} size={'20px'} style={{ marginRight: '0.5rem' }} />
|
||||
<TYPE.main>{currencyBase?.symbol}</TYPE.main>
|
||||
<CurrencyLogo
|
||||
currency={feeValueLower?.currency}
|
||||
size={'20px'}
|
||||
style={{ marginRight: '0.5rem' }}
|
||||
/>
|
||||
<TYPE.main>{feeValueLower?.currency?.symbol}</TYPE.main>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<TYPE.main>
|
||||
{inverted
|
||||
? feeValue0
|
||||
? formatTokenAmount(feeValue1, 4)
|
||||
: '-'
|
||||
: feeValue1
|
||||
? formatTokenAmount(feeValue0, 4)
|
||||
: '-'}
|
||||
</TYPE.main>
|
||||
<TYPE.main>{feeValueLower ? formatTokenAmount(feeValueLower, 4) : '-'}</TYPE.main>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
{ownsNFT && (feeValue0?.greaterThan(0) || feeValue1?.greaterThan(0)) && !collectMigrationHash ? (
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.main>Collect as WETH</TYPE.main>
|
||||
<Toggle
|
||||
id="receive-as-weth"
|
||||
isActive={receiveWETH}
|
||||
toggle={() => setReceiveWETH((receiveWETH) => !receiveWETH)}
|
||||
/>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
) : null}
|
||||
</AutoColumn>
|
||||
</DarkCard>
|
||||
</AutoColumn>
|
||||
|
||||
@@ -21,12 +21,10 @@ import ReactGA from 'react-ga'
|
||||
import { useActiveWeb3React } from 'hooks'
|
||||
import { TransactionResponse } from '@ethersproject/providers'
|
||||
import { useTransactionAdder } from 'state/transactions/hooks'
|
||||
import { WETH9, CurrencyAmount, currencyEquals, Percent } from '@uniswap/sdk-core'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import { TYPE } from 'theme'
|
||||
import { Wrapper, SmallMaxButton, ResponsiveHeaderText } from './styled'
|
||||
import Loader from 'components/Loader'
|
||||
import { useToken } from 'hooks/Tokens'
|
||||
import { unwrappedToken } from 'utils/wrappedCurrency'
|
||||
import DoubleCurrencyLogo from 'components/DoubleLogo'
|
||||
import { Break } from 'components/earn/styled'
|
||||
import { NonfungiblePositionManager } from '@uniswap/v3-sdk'
|
||||
@@ -34,6 +32,7 @@ import { calculateGasMargin } from 'utils'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { AddRemoveTabs } from 'components/NavigationTabs'
|
||||
import RangeBadge from 'components/Badge/RangeBadge'
|
||||
import Toggle from 'components/Toggle'
|
||||
|
||||
export const UINT128MAX = BigNumber.from(2).pow(128).sub(1)
|
||||
|
||||
@@ -65,11 +64,8 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
|
||||
const theme = useTheme()
|
||||
const { account, chainId, library } = useActiveWeb3React()
|
||||
|
||||
// currencies from position
|
||||
const token0 = useToken(position?.token0)
|
||||
const token1 = useToken(position?.token1)
|
||||
const currency0 = token0 ? unwrappedToken(token0) : undefined
|
||||
const currency1 = token1 ? unwrappedToken(token1) : undefined
|
||||
// flag for receiving WETH
|
||||
const [receiveWETH, setReceiveWETH] = useState(false)
|
||||
|
||||
// burn state
|
||||
const { percent } = useBurnV3State()
|
||||
@@ -82,7 +78,7 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
|
||||
feeValue1,
|
||||
outOfRange,
|
||||
error,
|
||||
} = useDerivedV3BurnInfo(position)
|
||||
} = useDerivedV3BurnInfo(position, receiveWETH)
|
||||
const { onPercentSelect } = useBurnV3ActionHandlers()
|
||||
|
||||
const removed = position?.liquidity?.eq(0)
|
||||
@@ -122,12 +118,8 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
|
||||
slippageTolerance: allowedSlippage,
|
||||
deadline: deadline.toString(),
|
||||
collectOptions: {
|
||||
expectedCurrencyOwed0: currencyEquals(liquidityValue0.currency, WETH9[chainId])
|
||||
? CurrencyAmount.ether(feeValue0.quotient)
|
||||
: feeValue0,
|
||||
expectedCurrencyOwed1: currencyEquals(liquidityValue1.currency, WETH9[chainId])
|
||||
? CurrencyAmount.ether(feeValue1.quotient)
|
||||
: feeValue1,
|
||||
expectedCurrencyOwed0: feeValue0,
|
||||
expectedCurrencyOwed1: feeValue1,
|
||||
recipient: account,
|
||||
},
|
||||
})
|
||||
@@ -195,32 +187,32 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
|
||||
}, [onPercentSelectForSlider, txnHash])
|
||||
|
||||
const pendingText = `Removing ${liquidityValue0?.toSignificant(6)} ${
|
||||
currency0?.symbol
|
||||
} and ${liquidityValue1?.toSignificant(6)} ${currency1?.symbol}`
|
||||
liquidityValue0?.currency?.symbol
|
||||
} and ${liquidityValue1?.toSignificant(6)} ${liquidityValue1?.currency?.symbol}`
|
||||
|
||||
function modalHeader() {
|
||||
return (
|
||||
<AutoColumn gap={'sm'} style={{ padding: '16px' }}>
|
||||
<RowBetween align="flex-end">
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
{currency0?.symbol}:
|
||||
Pooled {liquidityValue0?.currency?.symbol}:
|
||||
</Text>
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{liquidityValue0 && <FormattedCurrencyAmount currencyAmount={liquidityValue0} />}
|
||||
</Text>
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency0} />
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={liquidityValue0?.currency} />
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<RowBetween align="flex-end">
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
{currency1?.symbol}:
|
||||
Pooled {liquidityValue1?.currency?.symbol}:
|
||||
</Text>
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{liquidityValue1 && <FormattedCurrencyAmount currencyAmount={liquidityValue1} />}
|
||||
</Text>
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency1} />
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={liquidityValue1?.currency} />
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
{feeValue0?.greaterThan(0) || feeValue1?.greaterThan(0) ? (
|
||||
@@ -230,24 +222,24 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
|
||||
</TYPE.italic>
|
||||
<RowBetween>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
{currency0?.symbol} from fees:
|
||||
{feeValue0?.currency?.symbol} Fees Earned:
|
||||
</Text>
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{feeValue0 && <FormattedCurrencyAmount currencyAmount={feeValue0} />}
|
||||
</Text>
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency0} />
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={feeValue0?.currency} />
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
{currency1?.symbol} from fees:
|
||||
{feeValue1?.currency?.symbol} Fees Earned:
|
||||
</Text>
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{feeValue1 && <FormattedCurrencyAmount currencyAmount={feeValue1} />}
|
||||
</Text>
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency1} />
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={feeValue1?.currency} />
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
</>
|
||||
@@ -287,8 +279,16 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
|
||||
<AutoColumn gap="lg">
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<DoubleCurrencyLogo currency0={currency1} currency1={currency0} size={20} margin={true} />
|
||||
<TYPE.label ml="10px" fontSize="20px">{`${currency0?.symbol}/${currency1?.symbol}`}</TYPE.label>
|
||||
<DoubleCurrencyLogo
|
||||
currency0={feeValue0?.currency}
|
||||
currency1={feeValue1?.currency}
|
||||
size={20}
|
||||
margin={true}
|
||||
/>
|
||||
<TYPE.label
|
||||
ml="10px"
|
||||
fontSize="20px"
|
||||
>{`${feeValue0?.currency?.symbol}/${feeValue1?.currency?.symbol}`}</TYPE.label>
|
||||
</RowFixed>
|
||||
<RangeBadge removed={removed} inRange={!outOfRange} />
|
||||
</RowBetween>
|
||||
@@ -319,24 +319,24 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
Pooled {currency0?.symbol}:
|
||||
Pooled {liquidityValue0?.currency?.symbol}:
|
||||
</Text>
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{liquidityValue0 && <FormattedCurrencyAmount currencyAmount={liquidityValue0} />}
|
||||
</Text>
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency0} />
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={liquidityValue0?.currency} />
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
Pooled {currency1?.symbol}:
|
||||
Pooled {liquidityValue1?.currency?.symbol}:
|
||||
</Text>
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{liquidityValue1 && <FormattedCurrencyAmount currencyAmount={liquidityValue1} />}
|
||||
</Text>
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency1} />
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={liquidityValue1?.currency} />
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
{feeValue0?.greaterThan(0) || feeValue1?.greaterThan(0) ? (
|
||||
@@ -344,30 +344,40 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
|
||||
<Break />
|
||||
<RowBetween>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
{currency0?.symbol} Fees Earned:
|
||||
{feeValue0?.currency?.symbol} Fees Earned:
|
||||
</Text>
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{feeValue0 && <FormattedCurrencyAmount currencyAmount={feeValue0} />}
|
||||
</Text>
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency0} />
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={feeValue0?.currency} />
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
{currency1?.symbol} Fees Earned:
|
||||
{feeValue1?.currency?.symbol} Fees Earned:
|
||||
</Text>
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{feeValue1 && <FormattedCurrencyAmount currencyAmount={feeValue1} />}
|
||||
</Text>
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency1} />
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={feeValue1?.currency} />
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
</>
|
||||
) : null}
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
|
||||
<RowBetween>
|
||||
<TYPE.main>Collect as WETH</TYPE.main>
|
||||
<Toggle
|
||||
id="receive-as-weth"
|
||||
isActive={receiveWETH}
|
||||
toggle={() => setReceiveWETH((receiveWETH) => !receiveWETH)}
|
||||
/>
|
||||
</RowBetween>
|
||||
|
||||
<div style={{ display: 'flex' }}>
|
||||
<AutoColumn gap="12px" style={{ flex: '1' }}>
|
||||
<ButtonConfirmed
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Token, CurrencyAmount, Percent } from '@uniswap/sdk-core'
|
||||
import { Token, CurrencyAmount, Percent, Ether, currencyEquals, ETHER } from '@uniswap/sdk-core'
|
||||
import { Position } from '@uniswap/v3-sdk'
|
||||
import { usePool } from 'hooks/usePools'
|
||||
import { useActiveWeb3React } from 'hooks'
|
||||
@@ -10,20 +10,22 @@ import { PositionDetails } from 'types/position'
|
||||
|
||||
import { AppDispatch, AppState } from '../../index'
|
||||
import { selectPercent } from './actions'
|
||||
import { unwrappedToken } from 'utils/wrappedCurrency'
|
||||
|
||||
export function useBurnV3State(): AppState['burnV3'] {
|
||||
return useSelector<AppState, AppState['burnV3']>((state) => state.burnV3)
|
||||
}
|
||||
|
||||
export function useDerivedV3BurnInfo(
|
||||
position?: PositionDetails
|
||||
position?: PositionDetails,
|
||||
asWETH = false
|
||||
): {
|
||||
position?: Position
|
||||
liquidityPercentage?: Percent
|
||||
liquidityValue0?: CurrencyAmount<Token>
|
||||
liquidityValue1?: CurrencyAmount<Token>
|
||||
feeValue0?: CurrencyAmount<Token>
|
||||
feeValue1?: CurrencyAmount<Token>
|
||||
liquidityValue0?: CurrencyAmount<Token | Ether>
|
||||
liquidityValue1?: CurrencyAmount<Token | Ether>
|
||||
feeValue0?: CurrencyAmount<Token | Ether>
|
||||
feeValue1?: CurrencyAmount<Token | Ether>
|
||||
outOfRange: boolean
|
||||
error?: string
|
||||
} {
|
||||
@@ -50,20 +52,27 @@ export function useDerivedV3BurnInfo(
|
||||
|
||||
const liquidityPercentage = new Percent(percent, 100)
|
||||
|
||||
const liquidityValue0 =
|
||||
positionSDK &&
|
||||
CurrencyAmount.fromRawAmount(
|
||||
positionSDK.amount0.currency,
|
||||
liquidityPercentage.multiply(positionSDK.amount0.quotient).quotient
|
||||
)
|
||||
const liquidityValue1 =
|
||||
positionSDK &&
|
||||
CurrencyAmount.fromRawAmount(
|
||||
positionSDK.amount1.currency,
|
||||
liquidityPercentage.multiply(positionSDK.amount1.quotient).quotient
|
||||
)
|
||||
const discountedAmount0 = positionSDK
|
||||
? liquidityPercentage.multiply(positionSDK.amount0.quotient).quotient
|
||||
: undefined
|
||||
const discountedAmount1 = positionSDK
|
||||
? liquidityPercentage.multiply(positionSDK.amount1.quotient).quotient
|
||||
: undefined
|
||||
|
||||
const [feeValue0, feeValue1] = useV3PositionFees(pool ?? undefined, position?.tokenId)
|
||||
const liquidityValue0 =
|
||||
token0 && discountedAmount0
|
||||
? currencyEquals(unwrappedToken(token0), ETHER) && !asWETH
|
||||
? CurrencyAmount.ether(discountedAmount0)
|
||||
: CurrencyAmount.fromRawAmount(token0, discountedAmount0)
|
||||
: undefined
|
||||
const liquidityValue1 =
|
||||
token1 && discountedAmount1
|
||||
? currencyEquals(unwrappedToken(token1), ETHER) && !asWETH
|
||||
? CurrencyAmount.ether(discountedAmount1)
|
||||
: CurrencyAmount.fromRawAmount(token1, discountedAmount1)
|
||||
: undefined
|
||||
|
||||
const [feeValue0, feeValue1] = useV3PositionFees(pool ?? undefined, position?.tokenId, asWETH)
|
||||
|
||||
const outOfRange =
|
||||
pool && position ? pool.tickCurrent < position.tickLower || pool.tickCurrent > position.tickUpper : false
|
||||
|
||||
12
yarn.lock
12
yarn.lock
@@ -4582,7 +4582,7 @@
|
||||
js-sha3 "0.8.0"
|
||||
query-string "6.13.5"
|
||||
|
||||
"@walletconnect/web3-provider@^1.3.6":
|
||||
"@walletconnect/web3-provider@^1.4.1":
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@walletconnect/web3-provider/-/web3-provider-1.4.1.tgz#34f6319ab2473ab9ff0fcf1e8bc280c697fa01ff"
|
||||
integrity sha512-gUoBGM5hdtcXSoLXDTG1/WTamnUNpEWfaYMIVkfVnvVFd4whIjb0iOW5ywvDOf/49wq0C2+QThZL2Wc+r+jKLA==
|
||||
@@ -4646,12 +4646,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@web3-react/types/-/types-6.0.7.tgz#34a6204224467eedc6123abaf55fbb6baeb2809f"
|
||||
integrity sha512-ofGmfDhxmNT1/P/MgVa8IKSkCStFiyvXe+U5tyZurKdrtTDFU+wJ/LxClPDtFerWpczNFPUSrKcuhfPX1sI6+A==
|
||||
|
||||
"@web3-react/walletconnect-connector@^6.1.1":
|
||||
version "6.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@web3-react/walletconnect-connector/-/walletconnect-connector-6.1.9.tgz#3459ccf2a2ac7ae8f155645d29a517712afeb731"
|
||||
integrity sha512-gPtcFFRAnRgqhmBjhH+dtuG3cx23X+JOX+mRO1D7dN+8yxLZhLhjOZlJFECH5hkC20KMM/sk+rq2yy6Vqp76PQ==
|
||||
"@web3-react/walletconnect-connector@^6.2.0":
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@web3-react/walletconnect-connector/-/walletconnect-connector-6.2.0.tgz#5451f332a25b94cf7e615a20cc7d22a27532629d"
|
||||
integrity sha512-F6xYwI3MKiSdKa0248y2wBW0kTDddc2/IGn4CjMSYe0DJFggtxFsAAGHQTRmvwDcLlgQwtemJJ0cTA82MOVfEg==
|
||||
dependencies:
|
||||
"@walletconnect/web3-provider" "^1.3.6"
|
||||
"@walletconnect/web3-provider" "^1.4.1"
|
||||
"@web3-react/abstract-connector" "^6.0.7"
|
||||
"@web3-react/types" "^6.0.7"
|
||||
tiny-invariant "^1.0.6"
|
||||
|
||||
Reference in New Issue
Block a user