Commit 18408c9c authored by Ian Lapham's avatar Ian Lapham Committed by GitHub

Increase Liquidity, component cleanup, preview components (#50)

* basic +/- buttons

* start increase liquidity

* fix v3 remove
parent 39f018ba
...@@ -102,6 +102,7 @@ ...@@ -102,6 +102,7 @@
"connectWallet": "Connect Wallet", "connectWallet": "Connect Wallet",
"unsupportedAsset": "Unsupported Asset", "unsupportedAsset": "Unsupported Asset",
"feePool": "Fee Pool", "feePool": "Fee Pool",
"feeTier": "Fee Tier",
"rebalanceMessage": "Your underlying tokens will be automatically rebalanced when the rate of the pool changes and may be different when you withdraw the position.", "rebalanceMessage": "Your underlying tokens will be automatically rebalanced when the rate of the pool changes and may be different when you withdraw the position.",
"addEarnHelper": "You will earn fees from trades proportional to your share of the pool.", "addEarnHelper": "You will earn fees from trades proportional to your share of the pool.",
"learnMoreAboutFess": " Learn more about earning fees.", "learnMoreAboutFess": " Learn more about earning fees.",
......
import React from 'react'
import { FeeAmount } from '@uniswap/v3-sdk'
import { useTranslation } from 'react-i18next'
import { AutoColumn } from 'components/Column'
import { DynamicSection } from 'pages/AddLiquidity/styled'
import { TYPE } from 'theme'
import { RowBetween } from 'components/Row'
import { ButtonRadioChecked } from 'components/Button'
export default function FeeSelector({
disabled = false,
feeAmount,
handleFeePoolSelect,
}: {
disabled?: boolean
feeAmount?: FeeAmount
handleFeePoolSelect: (feeAmount: FeeAmount) => void
}) {
const { t } = useTranslation()
return (
<AutoColumn gap="16px">
<DynamicSection gap="md" disabled={disabled}>
<TYPE.label>{t('selectPool')}</TYPE.label>
<RowBetween>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.LOW}
onClick={() => handleFeePoolSelect(FeeAmount.LOW)}
>
<AutoColumn gap="sm" justify="flex-start">
<TYPE.label>0.05% {t('fee')}</TYPE.label>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
Optimized for stable assets.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.MEDIUM}
onClick={() => handleFeePoolSelect(FeeAmount.MEDIUM)}
>
<AutoColumn gap="sm" justify="flex-start">
<TYPE.label>0.3% {t('fee')}</TYPE.label>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
The classic Uniswap pool fee.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.HIGH}
onClick={() => handleFeePoolSelect(FeeAmount.HIGH)}
>
<AutoColumn gap="sm" justify="flex-start">
<TYPE.label>1% {t('fee')}</TYPE.label>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
Best for volatile assets.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
</RowBetween>
</DynamicSection>
</AutoColumn>
)
}
...@@ -5,6 +5,9 @@ import { Input as NumericalInput } from '../NumericalInput' ...@@ -5,6 +5,9 @@ import { Input as NumericalInput } from '../NumericalInput'
import styled, { keyframes, css } from 'styled-components' import styled, { keyframes, css } from 'styled-components'
import { TYPE } from 'theme' import { TYPE } from 'theme'
import { AutoColumn } from 'components/Column' import { AutoColumn } from 'components/Column'
import { ButtonSecondary } from 'components/Button'
import { FeeAmount } from '@uniswap/v3-sdk'
import { formattedFeeAmount } from 'utils'
const pulse = (color: string) => keyframes` const pulse = (color: string) => keyframes`
0% { 0% {
...@@ -20,6 +23,13 @@ const pulse = (color: string) => keyframes` ...@@ -20,6 +23,13 @@ const pulse = (color: string) => keyframes`
} }
` `
const SmallButton = styled(ButtonSecondary)`
background-color: ${({ theme }) => theme.bg2};
border-radius: 8px;
padding: 4px;
width: 48%;
`
const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boolean }>` const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boolean }>`
border-color: ${({ active, theme }) => active && theme.blue1}; border-color: ${({ active, theme }) => active && theme.blue1};
padding: 12px; padding: 12px;
...@@ -44,11 +54,24 @@ const ContentWrapper = styled(RowBetween)` ...@@ -44,11 +54,24 @@ const ContentWrapper = styled(RowBetween)`
interface StepCounterProps { interface StepCounterProps {
value: string value: string
onUserInput: (value: string) => void onUserInput: (value: string) => void
getDecrementValue?: () => string
getIncrementValue?: () => string
feeAmount?: FeeAmount
label?: string label?: string
width?: string width?: string
locked?: boolean // disable input
} }
const StepCounter = ({ value, onUserInput, label, width }: StepCounterProps) => { const StepCounter = ({
value,
onUserInput,
getDecrementValue,
getIncrementValue,
feeAmount,
label,
width,
locked,
}: StepCounterProps) => {
// for focus state, styled components doesnt let you select input parent container // for focus state, styled components doesnt let you select input parent container
const [active, setActive] = useState(false) const [active, setActive] = useState(false)
...@@ -59,11 +82,29 @@ const StepCounter = ({ value, onUserInput, label, width }: StepCounterProps) => ...@@ -59,11 +82,29 @@ const StepCounter = ({ value, onUserInput, label, width }: StepCounterProps) =>
// animation if parent value updates local value // animation if parent value updates local value
const [pulsing, setPulsing] = useState<boolean>(false) const [pulsing, setPulsing] = useState<boolean>(false)
// format fee amount
const feeAmountFormatted = feeAmount ? formattedFeeAmount(feeAmount) : ''
const handleOnFocus = () => { const handleOnFocus = () => {
setUseLocalValue(true) setUseLocalValue(true)
setActive(true) setActive(true)
} }
// for button clicks
const handleDecrement = useCallback(() => {
if (getDecrementValue) {
setLocalValue(getDecrementValue())
onUserInput(getDecrementValue())
}
}, [getDecrementValue, onUserInput])
const handleIncrement = useCallback(() => {
if (getIncrementValue) {
setLocalValue(getIncrementValue())
onUserInput(getIncrementValue())
}
}, [getIncrementValue, onUserInput])
const handleOnBlur = useCallback(() => { const handleOnBlur = useCallback(() => {
setUseLocalValue(false) setUseLocalValue(false)
setActive(false) setActive(false)
...@@ -90,12 +131,23 @@ const StepCounter = ({ value, onUserInput, label, width }: StepCounterProps) => ...@@ -90,12 +131,23 @@ const StepCounter = ({ value, onUserInput, label, width }: StepCounterProps) =>
className="rate-input-0" className="rate-input-0"
value={localValue} value={localValue}
fontSize="18px" fontSize="18px"
disabled={locked}
onUserInput={(val) => { onUserInput={(val) => {
setLocalValue(val) setLocalValue(val)
}} }}
/> />
</ContentWrapper> </ContentWrapper>
{label && <TYPE.label fontSize="12px">{label}</TYPE.label>} {label && <TYPE.label fontSize="12px">{label}</TYPE.label>}
{getDecrementValue && getIncrementValue && !locked ? (
<RowBetween>
<SmallButton onClick={handleDecrement}>
<TYPE.main fontSize="12px">-{feeAmountFormatted}%</TYPE.main>
</SmallButton>
<SmallButton onClick={handleIncrement}>
<TYPE.main fontSize="12px">+{feeAmountFormatted}%</TYPE.main>
</SmallButton>
</RowBetween>
) : null}
</AutoColumn> </AutoColumn>
</FocusedOutlineCard> </FocusedOutlineCard>
) )
......
import React, { useRef } from 'react' import React, { useRef } from 'react'
import { BookOpen, Code, Info, MessageCircle, PieChart } from 'react-feather' import { BookOpen, Code, Info, MessageCircle, PieChart } from 'react-feather'
import { Link } from 'react-router-dom'
import styled, { css } from 'styled-components' import styled, { css } from 'styled-components'
import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg' import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
import { useActiveWeb3React } from '../../hooks' import { useActiveWeb3React } from '../../hooks'
...@@ -96,6 +97,20 @@ const MenuItem = styled(ExternalLink)` ...@@ -96,6 +97,20 @@ const MenuItem = styled(ExternalLink)`
} }
` `
const InternalMenuItem = styled(Link)`
flex: 1;
padding: 0.5rem 0.5rem;
color: ${({ theme }) => theme.text2};
:hover {
color: ${({ theme }) => theme.text1};
cursor: pointer;
text-decoration: none;
}
> svg {
margin-right: 8px;
}
`
const CODE_LINK = 'https://github.com/Uniswap/uniswap-interface' const CODE_LINK = 'https://github.com/Uniswap/uniswap-interface'
export default function Menu() { export default function Menu() {
...@@ -153,14 +168,21 @@ interface NewMenuProps { ...@@ -153,14 +168,21 @@ interface NewMenuProps {
menuItems: { menuItems: {
content: any content: any
link: string link: string
external: boolean
}[] }[]
} }
const NewMenuFlyout = styled(MenuFlyout)` const NewMenuFlyout = styled(MenuFlyout)`
top: 3rem !important; top: 3rem !important;
` `
const NewMenuItem = styled(MenuItem)` const NewMenuItem = styled(InternalMenuItem)`
width: max-content;
text-decoration: none;
`
const ExternalMenuItem = styled(MenuItem)`
width: max-content; width: max-content;
text-decoration: none;
` `
export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, menuItems, ...rest }: NewMenuProps) => { export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, menuItems, ...rest }: NewMenuProps) => {
...@@ -174,11 +196,17 @@ export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, men ...@@ -174,11 +196,17 @@ export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, men
<ToggleElement onClick={toggle} /> <ToggleElement onClick={toggle} />
{open && ( {open && (
<NewMenuFlyout flyoutAlignment={flyoutAlignment}> <NewMenuFlyout flyoutAlignment={flyoutAlignment}>
{menuItems.map(({ content, link }, i) => ( {menuItems.map(({ content, link, external }, i) =>
<NewMenuItem href={link} key={i}> external ? (
<ExternalMenuItem id="link" href={link} key={link + i}>
{content}
</ExternalMenuItem>
) : (
<NewMenuItem id="link" to={link} key={link + i}>
{content} {content}
</NewMenuItem> </NewMenuItem>
))} )
)}
</NewMenuFlyout> </NewMenuFlyout>
)} )}
</StyledMenu> </StyledMenu>
......
import { BigNumber } from '@ethersproject/bignumber'
import PositionListItem from 'components/PositionListItem' import PositionListItem from 'components/PositionListItem'
import React from 'react' import React from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
...@@ -37,7 +36,7 @@ const MobileHeader = styled.div` ...@@ -37,7 +36,7 @@ const MobileHeader = styled.div`
` `
export type PositionListProps = React.PropsWithChildren<{ export type PositionListProps = React.PropsWithChildren<{
positions: (PositionDetails & { tokenId: BigNumber })[] positions: PositionDetails[]
}> }>
export default function PositionList({ positions }: PositionListProps) { export default function PositionList({ positions }: PositionListProps) {
...@@ -54,7 +53,7 @@ export default function PositionList({ positions }: PositionListProps) { ...@@ -54,7 +53,7 @@ export default function PositionList({ positions }: PositionListProps) {
<MobileHeader>Your positions</MobileHeader> <MobileHeader>Your positions</MobileHeader>
{positions.map((p, i) => { {positions.map((p, i) => {
const key = `${i}-${p.nonce.toString()} ${p.token0} ${p.token1} ${p.tokensOwed0} ${p.tokensOwed1}` const key = `${i}-${p.nonce.toString()} ${p.token0} ${p.token1} ${p.tokensOwed0} ${p.tokensOwed1}`
return <PositionListItem key={key} positionDetails={p} positionIndex={i} /> return <PositionListItem key={key} positionDetails={p} />
})} })}
</> </>
) )
......
...@@ -16,7 +16,6 @@ import { formatPrice, formatTokenAmount } from 'utils/formatTokenAmount' ...@@ -16,7 +16,6 @@ import { formatPrice, formatTokenAmount } from 'utils/formatTokenAmount'
import Loader from 'components/Loader' import Loader from 'components/Loader'
import { unwrappedToken } from 'utils/wrappedCurrency' import { unwrappedToken } from 'utils/wrappedCurrency'
import { useV3PositionFees } from 'hooks/useV3PositionFees' import { useV3PositionFees } from 'hooks/useV3PositionFees'
import { BigNumber } from '@ethersproject/bignumber'
const ActiveDot = styled.span` const ActiveDot = styled.span`
background-color: ${({ theme }) => theme.success}; background-color: ${({ theme }) => theme.success};
...@@ -121,11 +120,10 @@ const DataText = styled.div` ...@@ -121,11 +120,10 @@ const DataText = styled.div`
` `
export interface PositionListItemProps { export interface PositionListItemProps {
positionDetails: PositionDetails & { tokenId: BigNumber } positionDetails: PositionDetails
positionIndex: number
} }
export default function PositionListItem({ positionDetails, positionIndex }: PositionListItemProps) { export default function PositionListItem({ positionDetails }: PositionListItemProps) {
const { t } = useTranslation() const { t } = useTranslation()
const { const {
...@@ -172,7 +170,7 @@ export default function PositionListItem({ positionDetails, positionIndex }: Pos ...@@ -172,7 +170,7 @@ export default function PositionListItem({ positionDetails, positionIndex }: Pos
// check if price is within range // check if price is within range
const outOfRange: boolean = pool ? pool.tickCurrent < tickLower || pool.tickCurrent > tickUpper : false const outOfRange: boolean = pool ? pool.tickCurrent < tickLower || pool.tickCurrent > tickUpper : false
const positionSummaryLink = '/pool/' + positionIndex.toString() const positionSummaryLink = '/pool/' + positionDetails.tokenId
return ( return (
<Row to={positionSummaryLink}> <Row to={positionSummaryLink}>
......
import React, { useState, useCallback, useMemo } from 'react'
import { Position } from '@uniswap/v3-sdk'
import { DarkCard, DarkGreyCard } from 'components/Card'
import { AutoColumn } from 'components/Column'
import { TYPE } from 'theme'
import { RowBetween, RowFixed } from 'components/Row'
import CurrencyLogo from 'components/CurrencyLogo'
import { unwrappedToken } from 'utils/wrappedCurrency'
import { Break } from 'components/earn/styled'
import { useTranslation } from 'react-i18next'
import RateToggle from 'components/RateToggle'
export const PositionPreview = ({ position, title }: { position: Position; title?: string }) => {
const { t } = useTranslation()
const currency0 = unwrappedToken(position.pool.token0)
const currency1 = unwrappedToken(position.pool.token1)
// track which currency should be base
const [baseCurrency, setBaseCurrency] = useState(currency0)
const sorted = baseCurrency === currency0
const quoteCurrency = sorted ? currency1 : currency0
const quotePrice = useMemo(() => {
return sorted ? position.pool.priceOf(position.pool.token1) : position.pool.priceOf(position.pool.token0)
}, [sorted, position.pool])
const priceLower = sorted ? position.token0PriceUpper.invert() : position.token0PriceLower
const priceUpper = sorted ? position.token0PriceLower.invert() : position.token0PriceUpper
const handleRateChange = useCallback(() => {
setBaseCurrency(quoteCurrency)
}, [quoteCurrency])
return (
<DarkGreyCard>
<AutoColumn gap="md">
<RowBetween>
{title ? <TYPE.main>{title}</TYPE.main> : <div />}
<RateToggle
currencyA={sorted ? currency1 : currency0}
currencyB={sorted ? currency0 : currency1}
handleRateToggle={handleRateChange}
/>
</RowBetween>
<RowBetween>
<RowFixed>
<CurrencyLogo currency={currency0} />
<TYPE.label ml="8px">{currency0?.symbol}</TYPE.label>
</RowFixed>
<RowFixed>
<TYPE.label mr="8px">{position.amount0.toSignificant(4)}</TYPE.label>
</RowFixed>
</RowBetween>
<RowBetween>
<RowFixed>
<CurrencyLogo currency={currency1} />
<TYPE.label ml="8px">{currency1?.symbol}</TYPE.label>
</RowFixed>
<RowFixed>
<TYPE.label mr="8px">{position.amount1.toSignificant(4)}</TYPE.label>
</RowFixed>
</RowBetween>
<Break />
<RowBetween>
<TYPE.label>{t('feeTier')}</TYPE.label>
<TYPE.label>{position?.pool?.fee / 10000}%</TYPE.label>
</RowBetween>
<RowBetween>
<TYPE.label>Current Price</TYPE.label>
<TYPE.label>{`1 ${quoteCurrency?.symbol} = ${quotePrice.toSignificant(6)} ${
baseCurrency?.symbol
}`}</TYPE.label>
</RowBetween>
<RowBetween>
<DarkCard width="46%" padding="8px">
<AutoColumn gap="4px" justify="center">
<TYPE.main fontSize="12px">Min price</TYPE.main>
<TYPE.label textAlign="center">{`${priceLower.toSignificant(4)}`}</TYPE.label>
<TYPE.main
textAlign="center"
fontSize="12px"
>{` ${quoteCurrency.symbol}/${baseCurrency.symbol}`}</TYPE.main>
</AutoColumn>
</DarkCard>
<TYPE.main ml="4px" mr="4px">
</TYPE.main>
<DarkCard width="46%" padding="8px">
<AutoColumn gap="4px" justify="center">
<TYPE.main fontSize="12px">Max price</TYPE.main>
<TYPE.label textAlign="center">{`${priceUpper.toSignificant(4)}`}</TYPE.label>
<TYPE.main
textAlign="center"
fontSize="12px"
>{` ${quoteCurrency.symbol}/${baseCurrency.symbol}`}</TYPE.main>
</AutoColumn>
</DarkCard>
</RowBetween>
</AutoColumn>
</DarkGreyCard>
)
}
import { Currency, Price } from '@uniswap/sdk-core'
import StepCounter from 'components/InputStepCounter/InputStepCounter'
import { RowBetween } from 'components/Row'
import React from 'react'
// currencyA is the base token
export default function RangeSelector({
priceLower,
priceUpper,
onUpperRangeInput,
onLowerRangeInput,
getLowerDecrement,
getLowerIncrement,
getUpperDecrement,
getUpperIncrement,
currencyA,
currencyB,
feeAmount,
fixedValueLower,
fixedValueUpper,
}: {
priceLower?: Price
priceUpper?: Price
getLowerIncrement?: () => string
getLowerDecrement?: () => string
getUpperIncrement?: () => string
getUpperDecrement?: () => string
onLowerRangeInput: (typedValue: string) => void
onUpperRangeInput: (typedValue: string) => void
currencyA?: Currency | null
currencyB?: Currency | null
feeAmount?: number
fixedValueLower?: string
fixedValueUpper?: string
}) {
return (
<RowBetween>
<StepCounter
value={fixedValueLower ?? priceLower?.toSignificant(5) ?? ''}
onUserInput={onLowerRangeInput}
width="48%"
getIncrementValue={getLowerIncrement}
getDecrementValue={getLowerDecrement}
feeAmount={feeAmount}
label={
priceLower && currencyA && currencyB
? `${priceLower.toSignificant(4)} ${currencyB.symbol} / 1 ${currencyA.symbol}`
: '-'
}
locked={!!fixedValueLower}
/>
<StepCounter
value={fixedValueUpper ?? priceUpper?.toSignificant(5) ?? ''}
onUserInput={onUpperRangeInput}
width="48%"
getDecrementValue={getUpperDecrement}
getIncrementValue={getUpperIncrement}
feeAmount={feeAmount}
label={
priceUpper && currencyA && currencyB
? `${priceUpper.toSignificant(4)} ${currencyB?.symbol} / 1 ${currencyA?.symbol}`
: '-'
}
locked={!!fixedValueUpper}
/>
</RowBetween>
)
}
import React from 'react'
import { Currency } from '@uniswap/sdk-core'
import { ToggleElement, ToggleWrapper } from 'components/Toggle/MultiToggle'
import { useActiveWeb3React } from 'hooks'
import { useTranslation } from 'react-i18next'
import { wrappedCurrency } from 'utils/wrappedCurrency'
// the order of displayed base currencies from left to right is always in sort order
// currencyA is treated as the preferred base currency
export default function RateToggle({
currencyA,
currencyB,
handleRateToggle,
}: {
currencyA: Currency
currencyB: Currency
handleRateToggle: () => void
}) {
const { t } = useTranslation()
const { chainId } = useActiveWeb3React()
const tokenA = wrappedCurrency(currencyA, chainId)
const tokenB = wrappedCurrency(currencyB, chainId)
const isSorted = tokenA && tokenB && tokenA.sortsBefore(tokenB)
return tokenA && tokenB ? (
<ToggleWrapper width="fit-content">
<ToggleElement isActive={isSorted} fontSize="12px" onClick={handleRateToggle}>
{isSorted ? currencyA.symbol : currencyB.symbol} {t('rate')}
</ToggleElement>
<ToggleElement isActive={!isSorted} fontSize="12px" onClick={handleRateToggle}>
{isSorted ? currencyB.symbol : currencyA.symbol} {t('rate')}
</ToggleElement>
</ToggleWrapper>
) : null
}
import { Tick } from '@uniswap/v3-sdk' import { computePoolAddress, Tick } from '@uniswap/v3-sdk'
import { ZERO_ADDRESS } from './../constants/index' import { ZERO_ADDRESS } from './../constants/index'
import { Currency } from '@uniswap/sdk-core' import { Currency } from '@uniswap/sdk-core'
import { useMemo } from 'react' import { useMemo } from 'react'
import { useActiveWeb3React } from '../hooks' import { useActiveWeb3React } from '../hooks'
import { useSingleCallResult } from '../state/multicall/hooks' import { useSingleCallResult } from '../state/multicall/hooks'
import { wrappedCurrency } from '../utils/wrappedCurrency' import { wrappedCurrency } from '../utils/wrappedCurrency'
import { Pool, FeeAmount, computePoolAddress } from '@uniswap/v3-sdk' import { Pool, FeeAmount } from '@uniswap/v3-sdk'
import { useV3Factory, useV3Pool } from 'hooks/useContract' import { useV3Factory, useV3Pool } from 'hooks/useContract'
import { V3_CORE_FACTORY_ADDRESSES } from 'constants/v3' import { V3_CORE_FACTORY_ADDRESSES } from 'constants/v3'
import { useAllV3Ticks } from 'hooks/useAllV3Ticks' import { useAllV3Ticks } from 'hooks/useAllV3Ticks'
...@@ -52,11 +52,13 @@ export function usePool(currencyA?: Currency, currencyB?: Currency, feeAmount?: ...@@ -52,11 +52,13 @@ export function usePool(currencyA?: Currency, currencyB?: Currency, feeAmount?:
} }
}, [chainId, feeAmount, tokenA, tokenB]) }, [chainId, feeAmount, tokenA, tokenB])
const poolContract = useV3Pool(poolAddress)
// check factory if pools exists // check factory if pools exists
const addressParams = token0 && token1 && feeAmount ? [token0.address, token1.address, feeAmount] : undefined const addressParams = token0 && token1 && feeAmount ? [token0.address, token1.address, feeAmount] : undefined
const addressFromFactory = useSingleCallResult(addressParams ? factoryContract : undefined, 'getPool', addressParams) const addressFromFactory = useSingleCallResult(addressParams ? factoryContract : undefined, 'getPool', addressParams)
const { result: addressesResult, loading: addressesLoading } = addressFromFactory
const poolAddressFromFactory = addressesResult?.[0]
const poolContract = useV3Pool(poolAddress)
// attempt to fetch pool metadata // attempt to fetch pool metadata
const slot0Datas = useSingleCallResult(poolContract, 'slot0') const slot0Datas = useSingleCallResult(poolContract, 'slot0')
...@@ -66,10 +68,8 @@ export function usePool(currencyA?: Currency, currencyB?: Currency, feeAmount?: ...@@ -66,10 +68,8 @@ export function usePool(currencyA?: Currency, currencyB?: Currency, feeAmount?:
const { result: slot0, loading: slot0Loading } = slot0Datas const { result: slot0, loading: slot0Loading } = slot0Datas
const { result: liquidityResult, loading: liquidityLoading } = liquidityDatas const { result: liquidityResult, loading: liquidityLoading } = liquidityDatas
const { result: addressesResult, loading: addressesLoading } = addressFromFactory
const liquidity = liquidityResult?.[0] const liquidity = liquidityResult?.[0]
const poolAddressFromFactory = addressesResult?.[0]
// fetch tick data for pool // fetch tick data for pool
const { tickData, loading: tickLoading, syncing: tickSyncing } = useAllV3Ticks(token0, token1, feeAmount) const { tickData, loading: tickLoading, syncing: tickSyncing } = useAllV3Ticks(token0, token1, feeAmount)
......
import { Pool, Position } from '@uniswap/v3-sdk'
import { usePool } from 'data/Pools'
import { PositionDetails } from 'types/position'
import { useCurrency } from './Tokens'
export const useDerivedPositionInfo = (
positionDetails: PositionDetails | undefined
): {
position: Position | undefined
pool: Pool | undefined
} => {
const currency0 = useCurrency(positionDetails?.token0)
const currency1 = useCurrency(positionDetails?.token1)
// construct pool data
const [, pool] = usePool(currency0 ?? undefined, currency1 ?? undefined, positionDetails?.fee)
let position = undefined
if (pool && positionDetails) {
position = new Position({
pool,
liquidity: positionDetails.liquidity,
tickLower: positionDetails.tickLower,
tickUpper: positionDetails.tickUpper,
})
}
return {
position,
pool: pool ?? undefined,
}
}
...@@ -47,7 +47,7 @@ function getCounterfactualFees( ...@@ -47,7 +47,7 @@ function getCounterfactualFees(
// compute current + counterfactual fees for a v3 position // compute current + counterfactual fees for a v3 position
export function useV3PositionFees( export function useV3PositionFees(
pool?: Pool, pool?: Pool,
positionDetails?: PositionDetails & { tokenId: BigNumber } positionDetails?: PositionDetails
): [TokenAmount, TokenAmount] | [undefined, undefined] { ): [TokenAmount, TokenAmount] | [undefined, undefined] {
const { chainId } = useActiveWeb3React() const { chainId } = useActiveWeb3React()
......
...@@ -7,22 +7,24 @@ import { BigNumber } from '@ethersproject/bignumber' ...@@ -7,22 +7,24 @@ import { BigNumber } from '@ethersproject/bignumber'
interface UseV3PositionsResults { interface UseV3PositionsResults {
loading: boolean loading: boolean
error: boolean error: boolean
positions: (PositionDetails & { tokenId: BigNumber })[] | undefined positions: PositionDetails[] | undefined
} }
function useV3PositionsFromTokenIds(tokenIds: BigNumber[]): UseV3PositionsResults { function useV3PositionsFromTokenIds(tokenIds: BigNumber[] | undefined): UseV3PositionsResults {
const positionManager = useV3NFTPositionManagerContract() const positionManager = useV3NFTPositionManagerContract()
const inputs = useMemo(() => tokenIds.map((tokenId) => [tokenId]), [tokenIds]) const inputs = useMemo(() => (tokenIds ? tokenIds.map((tokenId) => [BigNumber.from(tokenId)]) : []), [tokenIds])
const results = useSingleContractMultipleData(positionManager ?? undefined, 'positions', inputs) const results = useSingleContractMultipleData(positionManager ?? undefined, 'positions', inputs)
const loading = useMemo(() => results.some(({ loading }) => loading), [results]) const loading = useMemo(() => results.some(({ loading }) => loading), [results])
const error = useMemo(() => results.some(({ error }) => error), [results]) const error = useMemo(() => results.some(({ error }) => error), [results])
const positions = useMemo(() => { const positions = useMemo(() => {
if (!loading && !error) { if (!loading && !error && tokenIds) {
return results.map((call) => { return results.map((call, i) => {
const tokenId = tokenIds[i]
const result = call.result as Result const result = call.result as Result
return { return {
tokenId,
fee: result.fee, fee: result.fee,
feeGrowthInside0LastX128: result.feeGrowthInside0LastX128, feeGrowthInside0LastX128: result.feeGrowthInside0LastX128,
feeGrowthInside1LastX128: result.feeGrowthInside1LastX128, feeGrowthInside1LastX128: result.feeGrowthInside1LastX128,
...@@ -39,7 +41,7 @@ function useV3PositionsFromTokenIds(tokenIds: BigNumber[]): UseV3PositionsResult ...@@ -39,7 +41,7 @@ function useV3PositionsFromTokenIds(tokenIds: BigNumber[]): UseV3PositionsResult
}) })
} }
return undefined return undefined
}, [loading, error, results]) }, [loading, error, results, tokenIds])
return { return {
loading, loading,
...@@ -51,11 +53,11 @@ function useV3PositionsFromTokenIds(tokenIds: BigNumber[]): UseV3PositionsResult ...@@ -51,11 +53,11 @@ function useV3PositionsFromTokenIds(tokenIds: BigNumber[]): UseV3PositionsResult
interface UseV3PositionResults { interface UseV3PositionResults {
loading: boolean loading: boolean
error: boolean error: boolean
position: (PositionDetails & { tokenId: BigNumber }) | undefined position: PositionDetails | undefined
} }
export function useV3PositionFromTokenId(tokenId: BigNumber): UseV3PositionResults { export function useV3PositionFromTokenId(tokenId: BigNumber | undefined): UseV3PositionResults {
const position = useV3PositionsFromTokenIds([tokenId]) const position = useV3PositionsFromTokenIds(tokenId ? [tokenId] : undefined)
return { return {
loading: position.loading, loading: position.loading,
error: position.error, error: position.error,
...@@ -75,7 +77,7 @@ export function useV3Positions(account: string | null | undefined): UseV3Positio ...@@ -75,7 +77,7 @@ export function useV3Positions(account: string | null | undefined): UseV3Positio
// we don't expect any account balance to ever exceed the bounds of max safe int // we don't expect any account balance to ever exceed the bounds of max safe int
const accountBalance: number | undefined = balanceResult?.[0] ? Number.parseInt(balanceResult[0]) : undefined const accountBalance: number | undefined = balanceResult?.[0] ? Number.parseInt(balanceResult[0]) : undefined
const positionIndicesArgs = useMemo(() => { const tokenIdsArgs = useMemo(() => {
if (accountBalance && account) { if (accountBalance && account) {
const tokenRequests = [] const tokenRequests = []
for (let i = 0; i < accountBalance; i++) { for (let i = 0; i < accountBalance; i++) {
...@@ -86,33 +88,27 @@ export function useV3Positions(account: string | null | undefined): UseV3Positio ...@@ -86,33 +88,27 @@ export function useV3Positions(account: string | null | undefined): UseV3Positio
return [] return []
}, [account, accountBalance]) }, [account, accountBalance])
const positionIndicesResults = useSingleContractMultipleData( const tokenIdResults = useSingleContractMultipleData(
positionManager ?? undefined, positionManager ?? undefined,
'tokenOfOwnerByIndex', 'tokenOfOwnerByIndex',
positionIndicesArgs tokenIdsArgs
) )
const positionIndicesLoading = useMemo(() => positionIndicesResults.some(({ loading }) => loading), [
positionIndicesResults,
])
const positionIndicesError = useMemo(() => positionIndicesResults.some(({ error }) => error), [
positionIndicesResults,
])
const tokenIds = useMemo(() => { const tokenIds = useMemo(() => {
if (account) { if (account) {
return positionIndicesResults return tokenIdResults
.map(({ result }) => result) .map(({ result }) => result)
.filter((result): result is Result => !!result) .filter((result): result is Result => !!result)
.map((result) => BigNumber.from(result[0])) .map((result) => BigNumber.from(result[0]))
} }
return [] return []
}, [account, positionIndicesResults]) }, [account, tokenIdResults])
const positionsResults = useV3PositionsFromTokenIds(tokenIds) const positionsResults = useV3PositionsFromTokenIds(tokenIds)
// wrap the return value // wrap the return value
const loading = balanceLoading || positionIndicesLoading const loading = balanceLoading || positionsResults.loading
const error = balanceError || positionIndicesError const error = balanceError || positionsResults.error
return { return {
loading: loading || positionsResults.loading, loading: loading || positionsResults.loading,
......
import React, { useMemo } from 'react' import React from 'react'
import { RowBetween, RowFixed } from '../../components/Row' import { RowBetween, RowFixed } from '../../components/Row'
import CurrencyLogo from '../../components/CurrencyLogo'
import { Field } from '../../state/mint/actions' import { Field } from '../../state/mint/actions'
import { TYPE } from '../../theme' import { TYPE } from '../../theme'
import { AutoColumn } from 'components/Column' import { AutoColumn } from 'components/Column'
import Card, { DarkGreyCard } from 'components/Card' import Card from 'components/Card'
import styled from 'styled-components' import styled from 'styled-components'
import { Break } from 'components/earn/styled'
import { useTranslation } from 'react-i18next'
import { Currency, CurrencyAmount, Price } from '@uniswap/sdk-core' import { Currency, CurrencyAmount, Price } from '@uniswap/sdk-core'
import { Position } from '@uniswap/v3-sdk' import { Position } from '@uniswap/v3-sdk'
import DoubleCurrencyLogo from 'components/DoubleLogo' import DoubleCurrencyLogo from 'components/DoubleLogo'
import { wrappedCurrency } from 'utils/wrappedCurrency' import { PositionPreview } from 'components/PositionPreview'
import { useActiveWeb3React } from 'hooks' import { RangeBadge } from './styled'
// import QuestionHelper from 'components/QuestionHelper'
// import { WarningBadge } from 'components/Badge/Badge.stories'
// import { AlertCircle } from 'react-feather'
// import useTheme from 'hooks/useTheme'
const Wrapper = styled.div` const Wrapper = styled.div`
padding: 20px; padding: 20px;
min-width: 460px; min-width: 460px;
` `
const Badge = styled(Card)<{ inRange?: boolean }>` export const Badge = styled(Card)<{ inRange?: boolean }>`
width: fit-content; width: fit-content;
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
...@@ -35,32 +28,19 @@ const Badge = styled(Card)<{ inRange?: boolean }>` ...@@ -35,32 +28,19 @@ const Badge = styled(Card)<{ inRange?: boolean }>`
export function Review({ export function Review({
position, position,
currencies, currencies,
parsedAmounts,
priceLower,
priceUpper,
outOfRange, outOfRange,
}: { }: {
position?: Position position?: Position
existingPosition?: Position
currencies: { [field in Field]?: Currency } currencies: { [field in Field]?: Currency }
parsedAmounts: { [field in Field]?: CurrencyAmount } parsedAmounts: { [field in Field]?: CurrencyAmount }
priceLower?: Price priceLower?: Price
priceUpper?: Price priceUpper?: Price
outOfRange: boolean outOfRange: boolean
}) { }) {
const { t } = useTranslation()
const { chainId } = useActiveWeb3React()
// const theme = useTheme()
const currencyA: Currency | undefined = currencies[Field.CURRENCY_A] const currencyA: Currency | undefined = currencies[Field.CURRENCY_A]
const currencyB: Currency | undefined = currencies[Field.CURRENCY_B] const currencyB: Currency | undefined = currencies[Field.CURRENCY_B]
// formatted with tokens
const [tokenA, tokenB] = useMemo(() => [wrappedCurrency(currencyA, chainId), wrappedCurrency(currencyB, chainId)], [
chainId,
currencyA,
currencyB,
])
return ( return (
<Wrapper> <Wrapper>
<AutoColumn gap="lg"> <AutoColumn gap="lg">
...@@ -71,56 +51,9 @@ export function Review({ ...@@ -71,56 +51,9 @@ export function Review({
{currencyA?.symbol} / {currencyB?.symbol} {currencyA?.symbol} / {currencyB?.symbol}
</TYPE.label> </TYPE.label>
</RowFixed> </RowFixed>
<Badge inRange={!outOfRange}>{outOfRange ? 'Out of range' : 'In Range'}</Badge> <RangeBadge inRange={!outOfRange}>{outOfRange ? 'Out of range' : 'In Range'}</RangeBadge>
</RowBetween>
{position && tokenA && tokenB && (
<DarkGreyCard>
<AutoColumn gap="md">
<TYPE.label>Deposit Amounts</TYPE.label>
{parsedAmounts[Field.CURRENCY_A] && (
<RowBetween>
<RowFixed>
<CurrencyLogo currency={currencyA} />
<TYPE.label ml="8px">{currencyA?.symbol}</TYPE.label>
</RowFixed>
<RowFixed>
<TYPE.label mr="8px">{parsedAmounts[Field.CURRENCY_A]?.toSignificant(6)}</TYPE.label>
</RowFixed>
</RowBetween>
)}
{parsedAmounts[Field.CURRENCY_B] && (
<RowBetween>
<RowFixed>
<CurrencyLogo currency={currencyB} />
<TYPE.label ml="8px">{currencyB?.symbol}</TYPE.label>
</RowFixed>
<RowFixed>
<TYPE.label mr="8px">{parsedAmounts[Field.CURRENCY_B]?.toSignificant(6)}</TYPE.label>
</RowFixed>
</RowBetween>
)}
<Break />
<RowBetween>
<TYPE.label>{t('feePool')}</TYPE.label>
<TYPE.label>{position?.pool?.fee / 10000}%</TYPE.label>
</RowBetween> </RowBetween>
<RowBetween> {position ? <PositionPreview position={position} title={'Tokens To Add'} /> : null}
<TYPE.label>Current Price</TYPE.label>
<TYPE.label>{`1 ${currencyA?.symbol} = ${position?.pool
?.priceOf(position.pool?.token0.equals(tokenA) ? position.pool?.token0 : position.pool?.token1)
.toSignificant(6)} ${position.pool.token1?.symbol}`}</TYPE.label>
</RowBetween>
<RowBetween>
<TYPE.label>Active Range</TYPE.label>
<TYPE.label>{`1 ${
position.pool?.token0.equals(tokenA) ? currencyA?.symbol : currencyB?.symbol
} = ${priceLower?.toSignificant(4)} ⟷ ${priceUpper?.toSignificant(6)} ${
position.pool?.token0.equals(tokenA) ? currencyB?.symbol : currencyA?.symbol
}`}</TYPE.label>
</RowBetween>
</AutoColumn>
</DarkGreyCard>
)}
{/* <YellowCard> {/* <YellowCard>
<AutoColumn gap="md"> <AutoColumn gap="md">
<RowBetween> <RowBetween>
......
This diff is collapsed.
import styled from 'styled-components' import styled from 'styled-components'
import { AutoColumn } from 'components/Column' import { AutoColumn } from 'components/Column'
import CurrencyInputPanel from 'components/CurrencyInputPanel' import CurrencyInputPanel from 'components/CurrencyInputPanel'
import { DarkGreyCard } from 'components/Card' import Card, { DarkGreyCard } from 'components/Card'
import Input from 'components/NumericalInput' import Input from 'components/NumericalInput'
export const Wrapper = styled.div`
position: relative;
padding: 20px;
min-width: 460px;
`
export const ScrollablePage = styled.div` export const ScrollablePage = styled.div`
position: relative; position: relative;
display: flex; display: flex;
...@@ -14,6 +20,15 @@ export const ScrollableContent = styled.div` ...@@ -14,6 +20,15 @@ export const ScrollableContent = styled.div`
margin-right: 16px; margin-right: 16px;
` `
export const RangeBadge = styled(Card)<{ inRange?: boolean }>`
width: fit-content;
font-size: 14px;
font-weight: 500;
border-radius: 8px;
padding: 4px 6px;
background-color: ${({ inRange, theme }) => (inRange ? theme.green1 : theme.yellow2)};
`
export const FixedPreview = styled.div` export const FixedPreview = styled.div`
position: relative; position: relative;
padding: 16px; padding: 16px;
......
...@@ -27,6 +27,7 @@ import Vote from './Vote' ...@@ -27,6 +27,7 @@ import Vote from './Vote'
import VotePage from './Vote/VotePage' import VotePage from './Vote/VotePage'
import { RedirectDuplicateTokenIdsV2 } from './AddLiquidityV2/redirects' import { RedirectDuplicateTokenIdsV2 } from './AddLiquidityV2/redirects'
import { PositionPage } from './Pool/PositionPage' import { PositionPage } from './Pool/PositionPage'
import AddLiquidity from './AddLiquidity'
const AppWrapper = styled.div` const AppWrapper = styled.div`
display: flex; display: flex;
...@@ -101,7 +102,7 @@ export default function App() { ...@@ -101,7 +102,7 @@ export default function App() {
<Route exact strict path="/find" component={PoolFinder} /> <Route exact strict path="/find" component={PoolFinder} />
<Route exact strict path="/pool/v2" component={PoolV2} /> <Route exact strict path="/pool/v2" component={PoolV2} />
<Route exact strict path="/pool" component={Pool} /> <Route exact strict path="/pool" component={Pool} />
<Route exact strict path="/pool/:positionIndex" component={PositionPage} /> <Route exact strict path="/pool/:tokenId" component={PositionPage} />
<Route exact strict path="/add/v2/:currencyIdA?/:currencyIdB?" component={RedirectDuplicateTokenIdsV2} /> <Route exact strict path="/add/v2/:currencyIdA?/:currencyIdB?" component={RedirectDuplicateTokenIdsV2} />
<Route <Route
...@@ -111,6 +112,13 @@ export default function App() { ...@@ -111,6 +112,13 @@ export default function App() {
component={RedirectDuplicateTokenIds} component={RedirectDuplicateTokenIds}
/> />
<Route
exact
strict
path="/increase/:currencyIdA?/:currencyIdB?/:feeAmount?/:tokenId?"
component={AddLiquidity}
/>
<Route exact strict path="/remove/v2/:currencyIdA/:currencyIdB" component={RemoveLiquidity} /> <Route exact strict path="/remove/v2/:currencyIdA/:currencyIdB" component={RemoveLiquidity} />
<Route exact strict path="/remove/:tokenId" component={RemoveLiquidityV3} /> <Route exact strict path="/remove/:tokenId" component={RemoveLiquidityV3} />
......
...@@ -20,7 +20,6 @@ import { BodyWrapper } from '../AppBody' ...@@ -20,7 +20,6 @@ import { BodyWrapper } from '../AppBody'
import { V2_MIGRATOR_ADDRESSES } from 'constants/v3' import { V2_MIGRATOR_ADDRESSES } from 'constants/v3'
import { PoolState, usePool } from 'data/Pools' import { PoolState, usePool } from 'data/Pools'
import { FeeAmount, Pool, Position, priceToClosestTick, TickMath } from '@uniswap/v3-sdk' import { FeeAmount, Pool, Position, priceToClosestTick, TickMath } from '@uniswap/v3-sdk'
import { FeeSelector, RangeSelector, RateToggle } from 'pages/AddLiquidity'
import { LightCard, PinkCard, YellowCard } from 'components/Card' import { LightCard, PinkCard, YellowCard } from 'components/Card'
import { ApprovalState, useApproveCallback } from 'hooks/useApproveCallback' import { ApprovalState, useApproveCallback } from 'hooks/useApproveCallback'
import { Dots } from 'components/swap/styleds' import { Dots } from 'components/swap/styleds'
...@@ -34,6 +33,9 @@ import { useDerivedMintInfo, useMintActionHandlers } from 'state/mint/hooks' ...@@ -34,6 +33,9 @@ import { useDerivedMintInfo, useMintActionHandlers } from 'state/mint/hooks'
import { Bound } from 'state/mint/actions' import { Bound } from 'state/mint/actions'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { ChevronDown } from 'react-feather' import { ChevronDown } from 'react-feather'
import FeeSelector from 'components/FeeSelector'
import RangeSelector from 'components/RangeSelector'
import RateToggle from 'components/RateToggle'
import useIsArgentWallet from 'hooks/useIsArgentWallet' import useIsArgentWallet from 'hooks/useIsArgentWallet'
import { Contract } from '@ethersproject/contracts' import { Contract } from '@ethersproject/contracts'
import { splitSignature } from '@ethersproject/bytes' import { splitSignature } from '@ethersproject/bytes'
......
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { Position } from '@uniswap/v3-sdk' import { Position } from '@uniswap/v3-sdk'
import { PoolState, usePool } from 'data/Pools' import { PoolState, usePool } from 'data/Pools'
import { useActiveWeb3React } from 'hooks'
import { useToken } from 'hooks/Tokens' import { useToken } from 'hooks/Tokens'
import { useV3Positions } from 'hooks/useV3Positions' import { useV3PositionFromTokenId } from 'hooks/useV3Positions'
import { RouteComponentProps, Link } from 'react-router-dom' import { Link, RouteComponentProps } from 'react-router-dom'
import { unwrappedToken } from 'utils/wrappedCurrency' import { unwrappedToken } from 'utils/wrappedCurrency'
import { LoadingRows } from './styleds' import { LoadingRows } from './styleds'
import styled from 'styled-components' import styled from 'styled-components'
...@@ -19,8 +18,10 @@ import { DarkCard, DarkGreyCard } from 'components/Card' ...@@ -19,8 +18,10 @@ import { DarkCard, DarkGreyCard } from 'components/Card'
import CurrencyLogo from 'components/CurrencyLogo' import CurrencyLogo from 'components/CurrencyLogo'
import { AlertTriangle } from 'react-feather' import { AlertTriangle } from 'react-feather'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useV3PositionFees } from 'hooks/useV3PositionFees' import { currencyId } from 'utils/currencyId'
import { formatTokenAmount } from 'utils/formatTokenAmount' import { formatTokenAmount } from 'utils/formatTokenAmount'
import { useV3PositionFees } from 'hooks/useV3PositionFees'
import { BigNumber } from '@ethersproject/bignumber'
const PageWrapper = styled.div` const PageWrapper = styled.div`
min-width: 800px; min-width: 800px;
...@@ -75,7 +76,7 @@ const ActiveDot = styled.span` ...@@ -75,7 +76,7 @@ const ActiveDot = styled.span`
margin-right: 4px; margin-right: 4px;
` `
const DarkBadge = styled.div` export const DarkBadge = styled.div`
widthL fit-content; widthL fit-content;
border-radius: 8px; border-radius: 8px;
background-color: ${({ theme }) => theme.bg0}; background-color: ${({ theme }) => theme.bg0};
...@@ -84,15 +85,13 @@ const DarkBadge = styled.div` ...@@ -84,15 +85,13 @@ const DarkBadge = styled.div`
export function PositionPage({ export function PositionPage({
match: { match: {
params: { positionIndex }, params: { tokenId: tokenIdFromUrl },
}, },
}: RouteComponentProps<{ positionIndex?: string }>) { }: RouteComponentProps<{ tokenId?: string }>) {
const { account } = useActiveWeb3React()
const { t } = useTranslation() const { t } = useTranslation()
const { loading, positions } = useV3Positions(account ?? undefined) const parsedTokenId = tokenIdFromUrl ? BigNumber.from(tokenIdFromUrl) : undefined
const { loading, position: positionDetails } = useV3PositionFromTokenId(parsedTokenId)
const positionDetails = positionIndex && positions ? positions[parseInt(positionIndex)] : undefined
const { token0: token0Address, token1: token1Address, fee: feeAmount, liquidity, tickLower, tickUpper, tokenId } = const { token0: token0Address, token1: token1Address, fee: feeAmount, liquidity, tickLower, tickUpper, tokenId } =
positionDetails || {} positionDetails || {}
...@@ -121,6 +120,10 @@ export function PositionPage({ ...@@ -121,6 +120,10 @@ export function PositionPage({
const outOfRange: boolean = const outOfRange: boolean =
pool && tickLower && tickUpper ? pool.tickCurrent < tickLower || pool.tickCurrent > tickUpper : false pool && tickLower && tickUpper ? pool.tickCurrent < tickLower || pool.tickCurrent > tickUpper : false
const linkToIncrease =
currency0 && currency1
? `/increase/${currencyId(currency0)}/${currencyId(currency1)}/${feeAmount}/${tokenId}`
: `/pool`
// fees // fees
const [feeValue0, feeValue1] = useV3PositionFees(pool ?? undefined, positionDetails) const [feeValue0, feeValue1] = useV3PositionFees(pool ?? undefined, positionDetails)
...@@ -153,11 +156,16 @@ export function PositionPage({ ...@@ -153,11 +156,16 @@ export function PositionPage({
<BadgeText>{basisPointsToPercent(feeAmount / 100).toSignificant()}%</BadgeText> <BadgeText>{basisPointsToPercent(feeAmount / 100).toSignificant()}%</BadgeText>
</Badge> </Badge>
</RowFixed> </RowFixed>
{tokenId && ( <RowFixed>
<ButtonPrimary width="200px" padding="8px" borderRadius="12px" as={Link} to={`/remove/${tokenId}`}> <Link to={linkToIncrease}>
<ButtonPrimary mr="20px" width="200px" padding="8px" borderRadius="12px">
Increase liquidity
</ButtonPrimary>
</Link>
<ButtonPrimary width="200px" padding="8px" borderRadius="12px">
Remove liquidity Remove liquidity
</ButtonPrimary> </ButtonPrimary>
)} </RowFixed>
</RowBetween> </RowBetween>
<RowBetween> <RowBetween>
<BadgeWrapper> <BadgeWrapper>
......
...@@ -105,7 +105,8 @@ export default function Pool() { ...@@ -105,7 +105,8 @@ export default function Pool() {
{t('Create a pool')} {t('Create a pool')}
</MenuItem> </MenuItem>
), ),
link: '/#/add', link: '/add/ETH',
external: false,
}, },
{ {
content: ( content: (
...@@ -115,6 +116,7 @@ export default function Pool() { ...@@ -115,6 +116,7 @@ export default function Pool() {
</MenuItem> </MenuItem>
), ),
link: 'https://uniswap.org/docs/v2/', link: 'https://uniswap.org/docs/v2/',
external: true,
}, },
] ]
if (showMigrateHeaderLink) { if (showMigrateHeaderLink) {
...@@ -125,7 +127,8 @@ export default function Pool() { ...@@ -125,7 +127,8 @@ export default function Pool() {
{t('Migrate v2 liquidity')} {t('Migrate v2 liquidity')}
</MenuItem> </MenuItem>
), ),
link: '/#/migrate/v2', link: '/migrate/v2',
external: false,
}) })
} }
return ( return (
......
...@@ -17,7 +17,7 @@ export function useBurnV3State(): AppState['burnV3'] { ...@@ -17,7 +17,7 @@ export function useBurnV3State(): AppState['burnV3'] {
} }
export function useDerivedV3BurnInfo( export function useDerivedV3BurnInfo(
position?: PositionDetails & { tokenId: BigNumber } position?: PositionDetails
): { ): {
liquidity?: BigNumber liquidity?: BigNumber
liquidityValue0?: TokenAmount liquidityValue0?: TokenAmount
......
...@@ -77,7 +77,9 @@ export function useMintActionHandlers( ...@@ -77,7 +77,9 @@ export function useMintActionHandlers(
export function useDerivedMintInfo( export function useDerivedMintInfo(
currencyA: Currency | undefined, currencyA: Currency | undefined,
currencyB: Currency | undefined, currencyB: Currency | undefined,
feeAmount: FeeAmount | undefined feeAmount: FeeAmount | undefined,
// override for existing position
existingPosition?: Position | undefined
): { ): {
pool?: Pool | null pool?: Pool | null
poolState: PoolState poolState: PoolState
...@@ -176,10 +178,14 @@ export function useDerivedMintInfo( ...@@ -176,10 +178,14 @@ export function useDerivedMintInfo(
[key: string]: number | undefined [key: string]: number | undefined
} = useMemo(() => { } = useMemo(() => {
return { return {
[Bound.LOWER]: tryParseTick(tokenA, tokenB, feeAmount, lowerRangeTypedValue), [Bound.LOWER]: existingPosition
[Bound.UPPER]: tryParseTick(tokenA, tokenB, feeAmount, upperRangeTypedValue), ? existingPosition.tickLower
: tryParseTick(tokenA, tokenB, feeAmount, lowerRangeTypedValue),
[Bound.UPPER]: existingPosition
? existingPosition.tickUpper
: tryParseTick(tokenA, tokenB, feeAmount, upperRangeTypedValue),
} }
}, [feeAmount, lowerRangeTypedValue, tokenA, tokenB, upperRangeTypedValue]) }, [existingPosition, feeAmount, lowerRangeTypedValue, tokenA, tokenB, upperRangeTypedValue])
const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks || {} const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks || {}
const sortedTicks = useMemo( const sortedTicks = useMemo(
......
import { BigNumber } from '@ethersproject/bignumber' import { BigNumberish } from '@ethersproject/bignumber'
export interface PositionDetails { export interface PositionDetails {
nonce: BigNumber nonce: BigNumber
tokenId: BigNumberish | undefined
operator: string operator: string
token0: string token0: string
token1: string token1: string
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment