Commit da33ec9d authored by Ian Lapham's avatar Ian Lapham Committed by GitHub

use token0 as base in all calculations (#51)

* use token0 as base in all calculations

* refactor

* fix price order

* fix existing position ticks

* remove empty space
Co-authored-by: default avatarNoah Zinsmeister <noahwz@gmail.com>
parent 60536862
...@@ -2,7 +2,7 @@ import React, { useState, useCallback, useEffect } from 'react' ...@@ -2,7 +2,7 @@ import React, { useState, useCallback, useEffect } from 'react'
import { OutlineCard } from 'components/Card' import { OutlineCard } from 'components/Card'
import { RowBetween } from 'components/Row' import { RowBetween } from 'components/Row'
import { Input as NumericalInput } from '../NumericalInput' import { Input as NumericalInput } from '../NumericalInput'
import styled, { keyframes, css } from 'styled-components' import styled, { keyframes } 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 { ButtonSecondary } from 'components/Button'
...@@ -34,11 +34,7 @@ const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boo ...@@ -34,11 +34,7 @@ const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boo
border-color: ${({ active, theme }) => active && theme.blue1}; border-color: ${({ active, theme }) => active && theme.blue1};
padding: 12px; padding: 12px;
${({ pulsing, theme }) => animation: ${({ pulsing, theme }) => pulsing && pulse(theme.blue1)} 0.8s linear;
pulsing &&
css`
animation: ${pulse(theme.blue1)} 0.8s linear;
`}
` `
const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>` const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
...@@ -54,8 +50,8 @@ const ContentWrapper = styled(RowBetween)` ...@@ -54,8 +50,8 @@ const ContentWrapper = styled(RowBetween)`
interface StepCounterProps { interface StepCounterProps {
value: string value: string
onUserInput: (value: string) => void onUserInput: (value: string) => void
getDecrementValue?: () => string decrement: () => string
getIncrementValue?: () => string increment: () => string
feeAmount?: FeeAmount feeAmount?: FeeAmount
label?: string label?: string
width?: string width?: string
...@@ -64,13 +60,13 @@ interface StepCounterProps { ...@@ -64,13 +60,13 @@ interface StepCounterProps {
const StepCounter = ({ const StepCounter = ({
value, value,
onUserInput, decrement,
getDecrementValue, increment,
getIncrementValue,
feeAmount, feeAmount,
label, label,
width, width,
locked, locked,
onUserInput,
}: StepCounterProps) => { }: 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)
...@@ -83,7 +79,7 @@ const StepCounter = ({ ...@@ -83,7 +79,7 @@ const StepCounter = ({
const [pulsing, setPulsing] = useState<boolean>(false) const [pulsing, setPulsing] = useState<boolean>(false)
// format fee amount // format fee amount
const feeAmountFormatted = feeAmount ? formattedFeeAmount(feeAmount) : '' const feeAmountFormatted = feeAmount ? formattedFeeAmount(feeAmount * 2) : ''
const handleOnFocus = () => { const handleOnFocus = () => {
setUseLocalValue(true) setUseLocalValue(true)
...@@ -92,18 +88,14 @@ const StepCounter = ({ ...@@ -92,18 +88,14 @@ const StepCounter = ({
// for button clicks // for button clicks
const handleDecrement = useCallback(() => { const handleDecrement = useCallback(() => {
if (getDecrementValue) { setLocalValue(decrement())
setLocalValue(getDecrementValue()) onUserInput(decrement())
onUserInput(getDecrementValue()) }, [decrement, onUserInput])
}
}, [getDecrementValue, onUserInput])
const handleIncrement = useCallback(() => { const handleIncrement = useCallback(() => {
if (getIncrementValue) { setLocalValue(increment())
setLocalValue(getIncrementValue()) onUserInput(increment())
onUserInput(getIncrementValue()) }, [increment, onUserInput])
}
}, [getIncrementValue, onUserInput])
const handleOnBlur = useCallback(() => { const handleOnBlur = useCallback(() => {
setUseLocalValue(false) setUseLocalValue(false)
...@@ -138,7 +130,7 @@ const StepCounter = ({ ...@@ -138,7 +130,7 @@ const StepCounter = ({
/> />
</ContentWrapper> </ContentWrapper>
{label && <TYPE.label fontSize="12px">{label}</TYPE.label>} {label && <TYPE.label fontSize="12px">{label}</TYPE.label>}
{getDecrementValue && getIncrementValue && !locked ? ( {!locked ? (
<RowBetween> <RowBetween>
<SmallButton onClick={handleDecrement}> <SmallButton onClick={handleDecrement}>
<TYPE.main fontSize="12px">-{feeAmountFormatted}%</TYPE.main> <TYPE.main fontSize="12px">-{feeAmountFormatted}%</TYPE.main>
......
...@@ -159,10 +159,10 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr ...@@ -159,10 +159,10 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
const formattedAmount1 = formatTokenAmount(amount1, 4) const formattedAmount1 = formatTokenAmount(amount1, 4)
// prices // prices
const price0Lower = position ? position.token0PriceLower : undefined const price0Lower = position?.token0PriceLower
const price0Upper = position ? position.token0PriceUpper : undefined const price0Upper = position?.token0PriceUpper
const price1Lower = price0Upper ? price0Upper.invert() : undefined const price1Lower = price0Lower?.invert()
const price1Upper = price0Lower ? price0Lower.invert() : undefined const price1Upper = price0Upper?.invert()
// fees // fees
const [feeValue0, feeValue1] = useV3PositionFees(pool ?? undefined, positionDetails) const [feeValue0, feeValue1] = useV3PositionFees(pool ?? undefined, positionDetails)
...@@ -205,13 +205,13 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr ...@@ -205,13 +205,13 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
<> <>
<DataLineItem> <DataLineItem>
{formatPrice(price0Lower, 4)} <DoubleArrow></DoubleArrow> {formatPrice(price0Upper, 4)}{' '} {formatPrice(price0Lower, 4)} <DoubleArrow></DoubleArrow> {formatPrice(price0Upper, 4)}{' '}
{currency0?.symbol}&nbsp;/&nbsp; {currency1?.symbol}&nbsp;/&nbsp;
{currency1?.symbol} {currency0?.symbol}
</DataLineItem> </DataLineItem>
<DataLineItem> <DataLineItem>
{formatPrice(price1Lower, 4)} <DoubleArrow></DoubleArrow> {formatPrice(price1Upper, 4)}{' '} {formatPrice(price1Lower, 4)} <DoubleArrow></DoubleArrow> {formatPrice(price1Upper, 4)}{' '}
{currency1?.symbol}&nbsp;/&nbsp; {currency0?.symbol}&nbsp;/&nbsp;
{currency0?.symbol} {currency1?.symbol}
</DataLineItem> </DataLineItem>
</> </>
) : ( ) : (
......
import React from 'react'
import { Currency, Price } from '@uniswap/sdk-core' import { Currency, Price } from '@uniswap/sdk-core'
import StepCounter from 'components/InputStepCounter/InputStepCounter' import StepCounter from 'components/InputStepCounter/InputStepCounter'
import { RowBetween } from 'components/Row' import { RowBetween } from 'components/Row'
import React from 'react' import { useActiveWeb3React } from 'hooks'
import { wrappedCurrency } from 'utils/wrappedCurrency'
// currencyA is the base token // currencyA is the base token
export default function RangeSelector({ export default function RangeSelector({
priceLower, priceLower,
priceUpper, priceUpper,
onUpperRangeInput, onLeftRangeInput,
onLowerRangeInput, onRightRangeInput,
getLowerDecrement, getDecrementLower,
getLowerIncrement, getIncrementLower,
getUpperDecrement, getDecrementUpper,
getUpperIncrement, getIncrementUpper,
currencyA, currencyA,
currencyB, currencyB,
feeAmount, feeAmount,
fixedValueLower,
fixedValueUpper,
}: { }: {
priceLower?: Price priceLower?: Price
priceUpper?: Price priceUpper?: Price
getLowerIncrement?: () => string getDecrementLower: () => string
getLowerDecrement?: () => string getIncrementLower: () => string
getUpperIncrement?: () => string getDecrementUpper: () => string
getUpperDecrement?: () => string getIncrementUpper: () => string
onLowerRangeInput: (typedValue: string) => void onLeftRangeInput: (typedValue: string) => void
onUpperRangeInput: (typedValue: string) => void onRightRangeInput: (typedValue: string) => void
currencyA?: Currency | null currencyA?: Currency | null
currencyB?: Currency | null currencyB?: Currency | null
feeAmount?: number feeAmount?: number
fixedValueLower?: string
fixedValueUpper?: string
}) { }) {
const { chainId } = useActiveWeb3React()
const tokenA = wrappedCurrency(currencyA ?? undefined, chainId)
const tokenB = wrappedCurrency(currencyB ?? undefined, chainId)
const isSorted = tokenA && tokenB && tokenA.sortsBefore(tokenB)
const leftPrice = isSorted ? priceLower : priceUpper?.invert()
const rightPrice = isSorted ? priceUpper : priceLower?.invert()
return ( return (
<RowBetween> <RowBetween>
<StepCounter <StepCounter
value={fixedValueLower ?? priceLower?.toSignificant(5) ?? ''} value={leftPrice?.toSignificant(5) ?? ''}
onUserInput={onLowerRangeInput} onUserInput={onLeftRangeInput}
width="48%" width="48%"
getIncrementValue={getLowerIncrement} decrement={isSorted ? getDecrementLower : getIncrementUpper}
getDecrementValue={getLowerDecrement} increment={isSorted ? getIncrementLower : getDecrementUpper}
feeAmount={feeAmount} feeAmount={feeAmount}
label={ label={leftPrice ? `${leftPrice.toSignificant(5)} ${currencyB?.symbol}/${currencyA?.symbol}` : '-'}
priceLower && currencyA && currencyB
? `${priceLower.toSignificant(4)} ${currencyB.symbol} / 1 ${currencyA.symbol}`
: '-'
}
locked={!!fixedValueLower}
/> />
<StepCounter <StepCounter
value={fixedValueUpper ?? priceUpper?.toSignificant(5) ?? ''} value={rightPrice?.toSignificant(5) ?? ''}
onUserInput={onUpperRangeInput} onUserInput={onRightRangeInput}
width="48%" width="48%"
getDecrementValue={getUpperDecrement} decrement={isSorted ? getDecrementUpper : getIncrementLower}
getIncrementValue={getUpperIncrement} increment={isSorted ? getIncrementUpper : getDecrementLower}
feeAmount={feeAmount} feeAmount={feeAmount}
label={ label={rightPrice ? `${rightPrice.toSignificant(5)} ${currencyB?.symbol}/${currencyA?.symbol}` : '-'}
priceUpper && currencyA && currencyB
? `${priceUpper.toSignificant(4)} ${currencyB?.symbol} / 1 ${currencyA?.symbol}`
: '-'
}
locked={!!fixedValueUpper}
/> />
</RowBetween> </RowBetween>
) )
......
import { TransactionResponse } from '@ethersproject/providers' import { TransactionResponse } from '@ethersproject/providers'
import { Currency, TokenAmount, Percent, ETHER } from '@uniswap/sdk-core' import { Currency, TokenAmount, Percent, ETHER } from '@uniswap/sdk-core'
import React, { useCallback, useContext, useState } from 'react' import React, { useCallback, useContext, useMemo, useState } from 'react'
import { Link2, AlertTriangle } from 'react-feather' import { Link2, AlertTriangle } from 'react-feather'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
import { useV3NFTPositionManagerContract } from '../../hooks/useContract' import { useV3NFTPositionManagerContract } from '../../hooks/useContract'
...@@ -26,7 +26,6 @@ import { useTransactionAdder } from '../../state/transactions/hooks' ...@@ -26,7 +26,6 @@ import { useTransactionAdder } from '../../state/transactions/hooks'
import { useIsExpertMode, useUserSlippageTolerance } from '../../state/user/hooks' import { useIsExpertMode, useUserSlippageTolerance } from '../../state/user/hooks'
import { TYPE } from '../../theme' import { TYPE } from '../../theme'
import { maxAmountSpend } from '../../utils/maxAmountSpend' import { maxAmountSpend } from '../../utils/maxAmountSpend'
import { wrappedCurrency } from '../../utils/wrappedCurrency'
import AppBody from '../AppBody' import AppBody from '../AppBody'
import { Dots } from '../Pool/styleds' import { Dots } from '../Pool/styleds'
import { currencyId } from '../../utils/currencyId' import { currencyId } from '../../utils/currencyId'
...@@ -42,8 +41,8 @@ import { ...@@ -42,8 +41,8 @@ import {
ScrollablePage, ScrollablePage,
} from './styled' } from './styled'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useMintState, useMintActionHandlers, useDerivedMintInfo } from 'state/mint/hooks' import { useMintState, useMintActionHandlers, useDerivedMintInfo, useRangeHopCallbacks } from 'state/mint/hooks'
import { FeeAmount, NonfungiblePositionManager, tickToPrice, TICK_SPACINGS } from '@uniswap/v3-sdk' import { FeeAmount, NonfungiblePositionManager } from '@uniswap/v3-sdk'
import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES } from 'constants/v3' import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES } from 'constants/v3'
import JSBI from 'jsbi' import JSBI from 'jsbi'
import { useV3PositionFromTokenId } from 'hooks/useV3Positions' import { useV3PositionFromTokenId } from 'hooks/useV3Positions'
...@@ -76,22 +75,6 @@ export default function AddLiquidity({ ...@@ -76,22 +75,6 @@ export default function AddLiquidity({
) )
const hasExistingPosition = !!existingPositionDetails && !positionLoading const hasExistingPosition = !!existingPositionDetails && !positionLoading
const { position: existingPosition } = useDerivedPositionInfo(existingPositionDetails) const { position: existingPosition } = useDerivedPositionInfo(existingPositionDetails)
const fixedValueLower =
hasExistingPosition && existingPosition
? tickToPrice(
existingPosition.pool.token0,
existingPosition.pool.token1,
existingPosition.tickLower
).toSignificant(4)
: undefined
const fixedValueUpper =
hasExistingPosition && existingPosition
? tickToPrice(
existingPosition.pool.token0,
existingPosition.pool.token1,
existingPosition.tickUpper
).toSignificant(4)
: undefined
// fee selection from url // fee selection from url
const feeAmount: FeeAmount | undefined = const feeAmount: FeeAmount | undefined =
...@@ -102,6 +85,14 @@ export default function AddLiquidity({ ...@@ -102,6 +85,14 @@ export default function AddLiquidity({
const currencyA = useCurrency(currencyIdA) const currencyA = useCurrency(currencyIdA)
const currencyB = useCurrency(currencyIdB) const currencyB = useCurrency(currencyIdB)
// keep track for UI display purposes of user selected base currency
const [baseCurrency, setBaseCurrency] = useState(currencyA)
const quoteCurrency = useMemo(() => (baseCurrency === currencyA ? currencyB : currencyA), [
baseCurrency,
currencyA,
currencyB,
])
// mint state // mint state
const { independentField, typedValue, startPriceTypedValue } = useMintState() const { independentField, typedValue, startPriceTypedValue } = useMintState()
...@@ -121,17 +112,24 @@ export default function AddLiquidity({ ...@@ -121,17 +112,24 @@ export default function AddLiquidity({
outOfRange, outOfRange,
depositADisabled, depositADisabled,
depositBDisabled, depositBDisabled,
} = useDerivedMintInfo(currencyA ?? undefined, currencyB ?? undefined, feeAmount, existingPosition) invertPrice,
} = useDerivedMintInfo(
currencyA ?? undefined,
currencyB ?? undefined,
feeAmount,
baseCurrency ?? undefined,
existingPosition
)
const { const {
onFieldAInput, onFieldAInput,
onFieldBInput, onFieldBInput,
onLowerRangeInput, onLeftRangeInput,
onUpperRangeInput, onRightRangeInput,
onStartPriceInput, onStartPriceInput,
} = useMintActionHandlers(noLiquidity) } = useMintActionHandlers(noLiquidity)
const isValid = !errorMessage const isValid = !errorMessage && !invalidRange
// modal and loading // modal and loading
const [showConfirm, setShowConfirm] = useState<boolean>(false) const [showConfirm, setShowConfirm] = useState<boolean>(false)
...@@ -322,90 +320,22 @@ export default function AddLiquidity({ ...@@ -322,90 +320,22 @@ export default function AddLiquidity({
const clearAll = useCallback(() => { const clearAll = useCallback(() => {
onFieldAInput('') onFieldAInput('')
onFieldBInput('') onFieldBInput('')
onLowerRangeInput('') onLeftRangeInput('')
onUpperRangeInput('') onRightRangeInput('')
history.push(`/add/`) history.push(`/add/`)
}, [history, onFieldAInput, onFieldBInput, onLowerRangeInput, onUpperRangeInput]) }, [history, onFieldAInput, onFieldBInput, onLeftRangeInput, onRightRangeInput])
// get value and prices at ticks // get value and prices at ticks
const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks
const { [Bound.LOWER]: priceLower, [Bound.UPPER]: priceUpper } = pricesAtTicks const { [Bound.LOWER]: priceLower, [Bound.UPPER]: priceUpper } = pricesAtTicks
const getLowerDecrement = () => { const { getDecrementLower, getIncrementLower, getDecrementUpper, getIncrementUpper } = useRangeHopCallbacks(
if (tickLower && feeAmount && currencyA && currencyB && chainId) { baseCurrency ?? undefined,
const tokenA = wrappedCurrency(currencyA, chainId) quoteCurrency ?? undefined,
const tokenB = wrappedCurrency(currencyB, chainId)
const tickAmount = TICK_SPACINGS[feeAmount]
const newTick = tickLower - tickAmount
const newPrice = tokenA && tokenB ? tickToPrice(tokenA, tokenB, newTick) : undefined
return newPrice ? newPrice.toFixed(10) : ''
} else {
return ''
}
}
const getLowerIncrement = () => {
if (tickLower && feeAmount && currencyA && currencyB && chainId) {
const tokenA = wrappedCurrency(currencyA, chainId)
const tokenB = wrappedCurrency(currencyB, chainId)
const tickAmount = TICK_SPACINGS[feeAmount]
const newTick = tickLower + tickAmount
const newPrice = tokenA && tokenB ? tickToPrice(tokenA, tokenB, newTick) : undefined
return newPrice ? newPrice.toFixed(10) : ''
} else {
return ''
}
}
const getUpperDecrement = () => {
if (tickUpper && feeAmount && currencyA && currencyB && chainId) {
const tokenA = wrappedCurrency(currencyA, chainId)
const tokenB = wrappedCurrency(currencyB, chainId)
const tickAmount = TICK_SPACINGS[feeAmount]
const newTick = tickUpper - tickAmount
const newPrice = tokenA && tokenB ? tickToPrice(tokenA, tokenB, newTick) : undefined
return newPrice ? newPrice.toFixed(10) : ''
} else {
return ''
}
}
const getUpperIncrement = () => {
if (tickUpper && feeAmount && currencyA && currencyB && chainId) {
const tokenA = wrappedCurrency(currencyA, chainId)
const tokenB = wrappedCurrency(currencyB, chainId)
const tickAmount = TICK_SPACINGS[feeAmount]
const newTick = tickUpper + tickAmount
const newPrice = tokenA && tokenB ? tickToPrice(tokenA, tokenB, newTick) : undefined
return newPrice ? newPrice.toFixed(10) : ''
} else {
return ''
}
}
const handleRateToggle = useCallback(() => {
if (currencyA && currencyB) {
const currencyIdA = currencyId(currencyA)
const currencyIdB = currencyId(currencyB)
// reset inputs
onLowerRangeInput('')
onUpperRangeInput('')
onStartPriceInput('')
onFieldAInput('')
onFieldBInput('')
history.push(`/add/${currencyIdB}/${currencyIdA}/${feeAmount ?? ''}`)
}
}, [
currencyA,
currencyB,
feeAmount, feeAmount,
history, tickLower,
onFieldAInput, tickUpper
onFieldBInput, )
onLowerRangeInput,
onStartPriceInput,
onUpperRangeInput,
])
return ( return (
<ScrollablePage> <ScrollablePage>
...@@ -523,11 +453,15 @@ export default function AddLiquidity({ ...@@ -523,11 +453,15 @@ export default function AddLiquidity({
</BlueCard> </BlueCard>
<RowBetween> <RowBetween>
<TYPE.label>{t('selectStartingPrice')}</TYPE.label> <TYPE.label>{t('selectStartingPrice')}</TYPE.label>
{currencyA && currencyB ? ( {baseCurrency && quoteCurrency ? (
<RateToggle <RateToggle
currencyA={currencyA} currencyA={baseCurrency}
currencyB={currencyB} currencyB={quoteCurrency}
handleRateToggle={handleRateToggle} handleRateToggle={() => {
onLeftRangeInput('')
onRightRangeInput('')
setBaseCurrency(quoteCurrency)
}}
/> />
) : null} ) : null}
</RowBetween> </RowBetween>
...@@ -543,7 +477,9 @@ export default function AddLiquidity({ ...@@ -543,7 +477,9 @@ export default function AddLiquidity({
<TYPE.main>Starting Price</TYPE.main> <TYPE.main>Starting Price</TYPE.main>
{price ? ( {price ? (
<TYPE.main> <TYPE.main>
1 {currencyA?.symbol} = {price?.toSignificant(8)} {currencyB?.symbol} 1 {currencyA?.symbol} ={' '}
{invertPrice ? price?.invert()?.toSignificant(8) : price?.toSignificant(8)}{' '}
{currencyB?.symbol}
</TYPE.main> </TYPE.main>
) : ( ) : (
'-' '-'
...@@ -559,16 +495,25 @@ export default function AddLiquidity({ ...@@ -559,16 +495,25 @@ export default function AddLiquidity({
> >
<RowBetween> <RowBetween>
<TYPE.label>{t('selectLiquidityRange')}</TYPE.label> <TYPE.label>{t('selectLiquidityRange')}</TYPE.label>
{currencyA && currencyB && !noLiquidity && ( {baseCurrency && quoteCurrency ? (
<RateToggle currencyA={currencyA} currencyB={currencyB} handleRateToggle={handleRateToggle} /> <RateToggle
)} currencyA={baseCurrency}
currencyB={quoteCurrency}
handleRateToggle={() => {
onLeftRangeInput('')
onRightRangeInput('')
setBaseCurrency(quoteCurrency)
}}
/>
) : null}
</RowBetween> </RowBetween>
{price && currencyA && !noLiquidity && ( {price && baseCurrency && quoteCurrency && !noLiquidity && (
<RowBetween style={{ backgroundColor: theme.bg6, padding: '12px', borderRadius: '12px' }}> <RowBetween style={{ backgroundColor: theme.bg6, padding: '12px', borderRadius: '12px' }}>
<TYPE.main>{t('currentRate', { label: currencyA.symbol })}</TYPE.main> <TYPE.main>Current Price</TYPE.main>
<TYPE.main> <TYPE.main>
{price.toSignificant(3)} {currencyB?.symbol} {invertPrice ? price.invert().toSignificant(3) : price.toSignificant(3)}{' '}
{quoteCurrency?.symbol} = 1 {baseCurrency.symbol}
</TYPE.main> </TYPE.main>
</RowBetween> </RowBetween>
)} )}
...@@ -576,17 +521,15 @@ export default function AddLiquidity({ ...@@ -576,17 +521,15 @@ export default function AddLiquidity({
<RangeSelector <RangeSelector
priceLower={priceLower} priceLower={priceLower}
priceUpper={priceUpper} priceUpper={priceUpper}
getLowerDecrement={getLowerDecrement} getDecrementLower={getDecrementLower}
getLowerIncrement={getLowerIncrement} getIncrementLower={getIncrementLower}
getUpperDecrement={getUpperDecrement} getDecrementUpper={getDecrementUpper}
getUpperIncrement={getUpperIncrement} getIncrementUpper={getIncrementUpper}
onLowerRangeInput={onLowerRangeInput} onLeftRangeInput={onLeftRangeInput}
onUpperRangeInput={onUpperRangeInput} onRightRangeInput={onRightRangeInput}
currencyA={currencyA} currencyA={baseCurrency}
currencyB={currencyB} currencyB={quoteCurrency}
feeAmount={feeAmount} feeAmount={feeAmount}
fixedValueLower={fixedValueLower}
fixedValueUpper={fixedValueUpper}
/> />
{outOfRange ? ( {outOfRange ? (
...@@ -634,9 +577,11 @@ export default function AddLiquidity({ ...@@ -634,9 +577,11 @@ export default function AddLiquidity({
showCommonBases showCommonBases
locked={depositADisabled} locked={depositADisabled}
/> />
<ColumnCenter> <ColumnCenter>
<Link2 stroke={theme.text2} size={'24px'} /> <Link2 stroke={theme.text2} size={'24px'} />
</ColumnCenter> </ColumnCenter>
<CurrencyInputPanel <CurrencyInputPanel
value={formattedAmounts[Field.CURRENCY_B]} value={formattedAmounts[Field.CURRENCY_B]}
disableCurrencySelect={true} disableCurrencySelect={true}
......
...@@ -29,10 +29,10 @@ import { useUserSlippageTolerance } from 'state/user/hooks' ...@@ -29,10 +29,10 @@ import { useUserSlippageTolerance } from 'state/user/hooks'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
import { TransactionResponse } from '@ethersproject/providers' import { TransactionResponse } from '@ethersproject/providers'
import { useIsTransactionPending, useTransactionAdder } from 'state/transactions/hooks' import { useIsTransactionPending, useTransactionAdder } from 'state/transactions/hooks'
import { useDerivedMintInfo, useMintActionHandlers } from 'state/mint/hooks' import { useDerivedMintInfo, useMintActionHandlers, useRangeHopCallbacks } 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 { AlertTriangle, ChevronDown } from 'react-feather'
import FeeSelector from 'components/FeeSelector' import FeeSelector from 'components/FeeSelector'
import RangeSelector from 'components/RangeSelector' import RangeSelector from 'components/RangeSelector'
import RateToggle from 'components/RateToggle' import RateToggle from 'components/RateToggle'
...@@ -42,6 +42,7 @@ import { splitSignature } from '@ethersproject/bytes' ...@@ -42,6 +42,7 @@ import { splitSignature } from '@ethersproject/bytes'
import { BigNumber } from '@ethersproject/bignumber' import { BigNumber } from '@ethersproject/bignumber'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp' import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import { formatTokenAmount } from 'utils/formatTokenAmount' import { formatTokenAmount } from 'utils/formatTokenAmount'
import useTheme from 'hooks/useTheme'
const ZERO = JSBI.BigInt(0) const ZERO = JSBI.BigInt(0)
...@@ -104,6 +105,7 @@ function V2PairMigration({ ...@@ -104,6 +105,7 @@ function V2PairMigration({
}) { }) {
const { t } = useTranslation() const { t } = useTranslation()
const { chainId, account, library } = useActiveWeb3React() const { chainId, account, library } = useActiveWeb3React()
const theme = useTheme()
const deadline = useTransactionDeadline() // custom from users settings const deadline = useTransactionDeadline() // custom from users settings
const blockTimestamp = useCurrentBlockTimestamp() const blockTimestamp = useCurrentBlockTimestamp()
...@@ -141,31 +143,39 @@ function V2PairMigration({ ...@@ -141,31 +143,39 @@ function V2PairMigration({
const largePriceDifference = priceDifferenceFraction && !priceDifferenceFraction?.lessThan(JSBI.BigInt(4)) const largePriceDifference = priceDifferenceFraction && !priceDifferenceFraction?.lessThan(JSBI.BigInt(4))
const [invertPrice, setInvertPrice] = useState(false)
// the following is a small hack to get access to price range data/input handlers // the following is a small hack to get access to price range data/input handlers
const { ticks, pricesAtTicks } = useDerivedMintInfo( const [baseToken, setBaseToken] = useState(token0)
invertPrice ? token1 : token0, const { ticks, pricesAtTicks, invertPrice, invalidRange, outOfRange } = useDerivedMintInfo(
invertPrice ? token0 : token1, token0,
feeAmount token1,
feeAmount,
baseToken
) )
// get value and prices at ticks // get value and prices at ticks
const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks
const { [Bound.LOWER]: priceLower, [Bound.UPPER]: priceUpper } = pricesAtTicks const { [Bound.LOWER]: priceLower, [Bound.UPPER]: priceUpper } = pricesAtTicks
const { onLowerRangeInput, onUpperRangeInput } = useMintActionHandlers(noLiquidity) const { getDecrementLower, getIncrementLower, getDecrementUpper, getIncrementUpper } = useRangeHopCallbacks(
baseToken,
baseToken.equals(token0) ? token1 : token0,
feeAmount,
tickLower,
tickUpper
)
const { onLeftRangeInput, onRightRangeInput } = useMintActionHandlers(noLiquidity)
// the v3 tick is either the pool's tickCurrent, or the tick closest to the v2 spot price // the v3 tick is either the pool's tickCurrent, or the tick closest to the v2 spot price
const tick = pool?.tickCurrent ?? priceToClosestTick(v2SpotPrice) const tick = pool?.tickCurrent ?? priceToClosestTick(v2SpotPrice)
// the price is either the current v3 price, or the price at the tick // the price is either the current v3 price, or the price at the tick
const sqrtPrice = pool?.sqrtRatioX96 ?? TickMath.getSqrtRatioAtTick(tick) const sqrtPrice = pool?.sqrtRatioX96 ?? TickMath.getSqrtRatioAtTick(tick)
const position = const position =
typeof tickLower === 'number' && typeof tickUpper === 'number' typeof tickLower === 'number' && typeof tickUpper === 'number' && !invalidRange
? Position.fromAmounts({ ? Position.fromAmounts({
pool: pool ?? new Pool(token0, token1, feeAmount, sqrtPrice, 0, tick, []), pool: pool ?? new Pool(token0, token1, feeAmount, sqrtPrice, 0, tick, []),
tickLower: invertPrice ? tickUpper : tickLower, tickLower,
tickUpper: invertPrice ? tickLower : tickUpper, tickUpper,
amount0: token0Value.raw, amount0: token0Value.raw,
amount1: token1Value.raw, amount1: token1Value.raw,
}) })
...@@ -340,8 +350,8 @@ function V2PairMigration({ ...@@ -340,8 +350,8 @@ function V2PairMigration({
token0: token0.address, token0: token0.address,
token1: token1.address, token1: token1.address,
fee: feeAmount, fee: feeAmount,
tickLower: invertPrice ? tickUpper : tickLower, tickLower,
tickUpper: invertPrice ? tickLower : tickUpper, tickUpper,
amount0Min: `0x${v3Amount0Min.raw.toString(16)}`, amount0Min: `0x${v3Amount0Min.raw.toString(16)}`,
amount1Min: `0x${v3Amount1Min.raw.toString(16)}`, amount1Min: `0x${v3Amount1Min.raw.toString(16)}`,
recipient: account, recipient: account,
...@@ -379,7 +389,6 @@ function V2PairMigration({ ...@@ -379,7 +389,6 @@ function V2PairMigration({
token1, token1,
feeAmount, feeAmount,
pairBalance, pairBalance,
invertPrice,
tickLower, tickLower,
tickUpper, tickUpper,
sqrtPrice, sqrtPrice,
...@@ -475,9 +484,9 @@ function V2PairMigration({ ...@@ -475,9 +484,9 @@ function V2PairMigration({
currencyA={invertPrice ? token1 : token0} currencyA={invertPrice ? token1 : token0}
currencyB={invertPrice ? token0 : token1} currencyB={invertPrice ? token0 : token1}
handleRateToggle={() => { handleRateToggle={() => {
onLowerRangeInput('') onLeftRangeInput('')
onUpperRangeInput('') onRightRangeInput('')
setInvertPrice((invertPrice) => !invertPrice) setBaseToken((base) => (base.equals(token0) ? token1 : token0))
}} }}
/> />
</RowBetween> </RowBetween>
...@@ -485,12 +494,39 @@ function V2PairMigration({ ...@@ -485,12 +494,39 @@ function V2PairMigration({
<RangeSelector <RangeSelector
priceLower={priceLower} priceLower={priceLower}
priceUpper={priceUpper} priceUpper={priceUpper}
onLowerRangeInput={onLowerRangeInput} getDecrementLower={getDecrementLower}
onUpperRangeInput={onUpperRangeInput} getIncrementLower={getIncrementLower}
getDecrementUpper={getDecrementUpper}
getIncrementUpper={getIncrementUpper}
onLeftRangeInput={onLeftRangeInput}
onRightRangeInput={onRightRangeInput}
currencyA={invertPrice ? token1 : token0} currencyA={invertPrice ? token1 : token0}
currencyB={invertPrice ? token0 : token1} currencyB={invertPrice ? token0 : token1}
feeAmount={feeAmount}
/> />
{outOfRange ? (
<YellowCard padding="8px 12px" borderRadius="12px">
<RowBetween>
<AlertTriangle stroke={theme.yellow3} size="16px" />
<TYPE.yellow ml="12px" fontSize="12px">
{t('inactiveRangeWarning')}
</TYPE.yellow>
</RowBetween>
</YellowCard>
) : null}
{invalidRange ? (
<YellowCard padding="8px 12px" borderRadius="12px">
<RowBetween>
<AlertTriangle stroke={theme.yellow3} size="16px" />
<TYPE.yellow ml="12px" fontSize="12px">
{t('invalidRangeWarning')}
</TYPE.yellow>
</RowBetween>
</YellowCard>
) : null}
<LightCard> <LightCard>
{v3Amount0Min && v3Amount1Min ? ( {v3Amount0Min && v3Amount1Min ? (
<LiquidityInfo token0Amount={v3Amount0Min} token1Amount={v3Amount1Min} /> <LiquidityInfo token0Amount={v3Amount0Min} token1Amount={v3Amount1Min} />
...@@ -506,6 +542,7 @@ function V2PairMigration({ ...@@ -506,6 +542,7 @@ function V2PairMigration({
signatureData !== null || signatureData !== null ||
!v3Amount0Min || !v3Amount0Min ||
!v3Amount1Min || !v3Amount1Min ||
invalidRange ||
confirmingMigration confirmingMigration
} }
onClick={approve} onClick={approve}
...@@ -526,6 +563,7 @@ function V2PairMigration({ ...@@ -526,6 +563,7 @@ function V2PairMigration({
disabled={ disabled={
!v3Amount0Min || !v3Amount0Min ||
!v3Amount1Min || !v3Amount1Min ||
invalidRange ||
(approval !== ApprovalState.APPROVED && signatureData === null) || (approval !== ApprovalState.APPROVED && signatureData === null) ||
confirmingMigration || confirmingMigration ||
isMigrationPending || isMigrationPending ||
......
...@@ -122,10 +122,10 @@ export function PositionPage({ ...@@ -122,10 +122,10 @@ export function PositionPage({
return undefined return undefined
}, [liquidity, pool, tickLower, tickUpper]) }, [liquidity, pool, tickLower, tickUpper])
const price0Lower = position ? position.token0PriceLower : undefined const price0Lower = position?.token0PriceLower
const price0Upper = position ? position.token0PriceUpper : undefined const price0Upper = position?.token0PriceUpper
const price1Lower = price0Upper ? price0Upper.invert() : undefined const price1Lower = price0Lower?.invert()
const price1Upper = price0Lower ? price0Lower.invert() : undefined const price1Upper = price0Upper?.invert()
// check if price is within range // check if price is within range
const outOfRange: boolean = const outOfRange: boolean =
...@@ -322,13 +322,13 @@ export function PositionPage({ ...@@ -322,13 +322,13 @@ export function PositionPage({
<RowFixed> <RowFixed>
<TYPE.label>{price0Lower?.toSignificant(4)}</TYPE.label> <TYPE.label>{price0Lower?.toSignificant(4)}</TYPE.label>
<TYPE.label ml="10px"> <TYPE.label ml="10px">
{currency0?.symbol} / {currency1?.symbol} {currency1?.symbol} / {currency0?.symbol}
</TYPE.label> </TYPE.label>
</RowFixed> </RowFixed>
<RowFixed> <RowFixed>
<TYPE.label>{price1Lower?.toSignificant(4)}</TYPE.label> <TYPE.label>{price1Lower?.toSignificant(4)}</TYPE.label>
<TYPE.label ml="10px"> <TYPE.label ml="10px">
{currency1?.symbol} / {currency0?.symbol} {currency0?.symbol} / {currency1?.symbol}
</TYPE.label> </TYPE.label>
</RowFixed> </RowFixed>
<DarkBadge> <DarkBadge>
...@@ -342,17 +342,17 @@ export function PositionPage({ ...@@ -342,17 +342,17 @@ export function PositionPage({
</DarkGreyCard> </DarkGreyCard>
<DarkGreyCard width="49%"> <DarkGreyCard width="49%">
<AutoColumn gap="sm" justify="flex-start"> <AutoColumn gap="sm" justify="flex-start">
<TYPE.main>Lower Limit</TYPE.main> <TYPE.main>Upper Limit</TYPE.main>
<RowFixed> <RowFixed>
<TYPE.label>{price0Upper?.toSignificant(4)}</TYPE.label> <TYPE.label>{price0Upper?.toSignificant(4)}</TYPE.label>
<TYPE.label ml="10px"> <TYPE.label ml="10px">
{currency0?.symbol} / {currency1?.symbol} {currency1?.symbol} / {currency0?.symbol}
</TYPE.label> </TYPE.label>
</RowFixed> </RowFixed>
<RowFixed> <RowFixed>
<TYPE.label>{price1Upper?.toSignificant(4)}</TYPE.label> <TYPE.label>{price1Upper?.toSignificant(4)}</TYPE.label>
<TYPE.label ml="10px"> <TYPE.label ml="10px">
{currency1?.symbol} / {currency0?.symbol} {currency0?.symbol} / {currency1?.symbol}
</TYPE.label> </TYPE.label>
</RowFixed> </RowFixed>
<DarkBadge> <DarkBadge>
......
...@@ -17,7 +17,7 @@ export enum RangeType { ...@@ -17,7 +17,7 @@ export enum RangeType {
} }
export const typeInput = createAction<{ field: Field; typedValue: string; noLiquidity: boolean }>('mint/typeInputMint') export const typeInput = createAction<{ field: Field; typedValue: string; noLiquidity: boolean }>('mint/typeInputMint')
export const typeLowerRangeInput = createAction<{ typedValue: string }>('mint/typeLowerRangeInput')
export const typeUpperRangeInput = createAction<{ typedValue: string }>('mint/typeUpperRangeInput')
export const typeStartPriceInput = createAction<{ typedValue: string }>('mint/typeStartPriceInput') export const typeStartPriceInput = createAction<{ typedValue: string }>('mint/typeStartPriceInput')
export const typeLeftRangeInput = createAction<{ typedValue: string }>('mint/typeLeftRangeInput')
export const typeRightRangeInput = createAction<{ typedValue: string }>('mint/typeRightRangeInput')
export const resetMintState = createAction<void>('mint/resetMintState') export const resetMintState = createAction<void>('mint/resetMintState')
...@@ -2,8 +2,16 @@ import { BIG_INT_ZERO } from './../../constants/index' ...@@ -2,8 +2,16 @@ import { BIG_INT_ZERO } from './../../constants/index'
import { getTickToPrice } from 'utils/getTickToPrice' import { getTickToPrice } from 'utils/getTickToPrice'
import JSBI from 'jsbi' import JSBI from 'jsbi'
import { PoolState } from '../../hooks/usePools' import { PoolState } from '../../hooks/usePools'
import { Pool, FeeAmount, Position, priceToClosestTick, TickMath } from '@uniswap/v3-sdk/dist/' import {
import { Currency, CurrencyAmount, ETHER, Price } from '@uniswap/sdk-core' Pool,
FeeAmount,
Position,
priceToClosestTick,
TickMath,
tickToPrice,
TICK_SPACINGS,
} from '@uniswap/v3-sdk/dist/'
import { Currency, CurrencyAmount, ETHER, Price, Rounding } from '@uniswap/sdk-core'
import { useCallback, useMemo } from 'react' import { useCallback, useMemo } from 'react'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { useActiveWeb3React } from '../../hooks' import { useActiveWeb3React } from '../../hooks'
...@@ -11,7 +19,7 @@ import { wrappedCurrency, wrappedCurrencyAmount } from '../../utils/wrappedCurre ...@@ -11,7 +19,7 @@ import { wrappedCurrency, wrappedCurrencyAmount } from '../../utils/wrappedCurre
import { AppDispatch, AppState } from '../index' import { AppDispatch, AppState } from '../index'
import { tryParseAmount } from '../swap/hooks' import { tryParseAmount } from '../swap/hooks'
import { useCurrencyBalances } from '../wallet/hooks' import { useCurrencyBalances } from '../wallet/hooks'
import { Field, Bound, typeInput, typeLowerRangeInput, typeUpperRangeInput, typeStartPriceInput } from './actions' import { Field, Bound, typeInput, typeStartPriceInput, typeLeftRangeInput, typeRightRangeInput } from './actions'
import { tryParseTick } from './utils' import { tryParseTick } from './utils'
import { usePool } from 'hooks/usePools' import { usePool } from 'hooks/usePools'
...@@ -24,8 +32,8 @@ export function useMintActionHandlers( ...@@ -24,8 +32,8 @@ export function useMintActionHandlers(
): { ): {
onFieldAInput: (typedValue: string) => void onFieldAInput: (typedValue: string) => void
onFieldBInput: (typedValue: string) => void onFieldBInput: (typedValue: string) => void
onLowerRangeInput: (typedValue: string) => void onLeftRangeInput: (typedValue: string) => void
onUpperRangeInput: (typedValue: string) => void onRightRangeInput: (typedValue: string) => void
onStartPriceInput: (typedValue: string) => void onStartPriceInput: (typedValue: string) => void
} { } {
const dispatch = useDispatch<AppDispatch>() const dispatch = useDispatch<AppDispatch>()
...@@ -44,16 +52,16 @@ export function useMintActionHandlers( ...@@ -44,16 +52,16 @@ export function useMintActionHandlers(
[dispatch, noLiquidity] [dispatch, noLiquidity]
) )
const onLowerRangeInput = useCallback( const onLeftRangeInput = useCallback(
(typedValue: string) => { (typedValue: string) => {
dispatch(typeLowerRangeInput({ typedValue })) dispatch(typeLeftRangeInput({ typedValue }))
}, },
[dispatch] [dispatch]
) )
const onUpperRangeInput = useCallback( const onRightRangeInput = useCallback(
(typedValue: string) => { (typedValue: string) => {
dispatch(typeUpperRangeInput({ typedValue })) dispatch(typeRightRangeInput({ typedValue }))
}, },
[dispatch] [dispatch]
) )
...@@ -68,18 +76,19 @@ export function useMintActionHandlers( ...@@ -68,18 +76,19 @@ export function useMintActionHandlers(
return { return {
onFieldAInput, onFieldAInput,
onFieldBInput, onFieldBInput,
onLowerRangeInput, onLeftRangeInput,
onUpperRangeInput, onRightRangeInput,
onStartPriceInput, onStartPriceInput,
} }
} }
export function useDerivedMintInfo( export function useDerivedMintInfo(
currencyA: Currency | undefined, currencyA?: Currency,
currencyB: Currency | undefined, currencyB?: Currency,
feeAmount: FeeAmount | undefined, feeAmount?: FeeAmount,
baseCurrency?: Currency,
// override for existing position // override for existing position
existingPosition?: Position | undefined existingPosition?: Position
): { ): {
pool?: Pool | null pool?: Pool | null
poolState: PoolState poolState: PoolState
...@@ -100,14 +109,15 @@ export function useDerivedMintInfo( ...@@ -100,14 +109,15 @@ export function useDerivedMintInfo(
invalidRange: boolean invalidRange: boolean
depositADisabled: boolean depositADisabled: boolean
depositBDisabled: boolean depositBDisabled: boolean
invertPrice: boolean
} { } {
const { account, chainId } = useActiveWeb3React() const { account, chainId } = useActiveWeb3React()
const { const {
independentField, independentField,
typedValue, typedValue,
lowerRangeTypedValue, leftRangeTypedValue,
upperRangeTypedValue, rightRangeTypedValue,
startPriceTypedValue, startPriceTypedValue,
} = useMintState() } = useMintState()
...@@ -116,25 +126,33 @@ export function useDerivedMintInfo( ...@@ -116,25 +126,33 @@ export function useDerivedMintInfo(
// currencies // currencies
const currencies: { [field in Field]?: Currency } = useMemo( const currencies: { [field in Field]?: Currency } = useMemo(
() => ({ () => ({
[Field.CURRENCY_A]: currencyA ?? undefined, [Field.CURRENCY_A]: currencyA,
[Field.CURRENCY_B]: currencyB ?? undefined, [Field.CURRENCY_B]: currencyB,
}), }),
[currencyA, currencyB] [currencyA, currencyB]
) )
// formatted with tokens // formatted with tokens
const [tokenA, tokenB] = useMemo(() => [wrappedCurrency(currencyA, chainId), wrappedCurrency(currencyB, chainId)], [ const [tokenA, tokenB, baseToken] = useMemo(
chainId, () => [
currencyA, wrappedCurrency(currencyA, chainId),
currencyB, wrappedCurrency(currencyB, chainId),
]) wrappedCurrency(baseCurrency, chainId),
],
[chainId, currencyA, currencyB, baseCurrency]
)
const [token0, token1] = useMemo(
() =>
tokenA && tokenB ? (tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]) : [undefined, undefined],
[tokenA, tokenB]
)
// balances // balances
const balances = useCurrencyBalances(account ?? undefined, [ const balances = useCurrencyBalances(account ?? undefined, [
currencies[Field.CURRENCY_A], currencies[Field.CURRENCY_A],
currencies[Field.CURRENCY_B], currencies[Field.CURRENCY_B],
]) ])
const currencyBalances: { [field in Field]?: CurrencyAmount } = { const currencyBalances: { [field in Field]?: CurrencyAmount } = {
[Field.CURRENCY_A]: balances[0], [Field.CURRENCY_A]: balances[0],
[Field.CURRENCY_B]: balances[1], [Field.CURRENCY_B]: balances[1],
...@@ -144,20 +162,28 @@ export function useDerivedMintInfo( ...@@ -144,20 +162,28 @@ export function useDerivedMintInfo(
const [poolState, pool] = usePool(currencies[Field.CURRENCY_A], currencies[Field.CURRENCY_B], feeAmount) const [poolState, pool] = usePool(currencies[Field.CURRENCY_A], currencies[Field.CURRENCY_B], feeAmount)
const noLiquidity = poolState === PoolState.NOT_EXISTS const noLiquidity = poolState === PoolState.NOT_EXISTS
// note to parse inputs in reverse
const invertPrice = Boolean(baseToken && token0 && !baseToken.equals(token0))
// always returns the price with 0 as base token
const price = useMemo(() => { const price = useMemo(() => {
// if no liquidity use typed value // if no liquidity use typed value
if (noLiquidity) { if (noLiquidity) {
const parsedAmount = tryParseAmount(startPriceTypedValue, tokenB) const parsedQuoteAmount = tryParseAmount(startPriceTypedValue, invertPrice ? token0 : token1)
if (parsedAmount && tokenA && tokenB) { if (parsedQuoteAmount && token0 && token1) {
const amountOne = tryParseAmount('1', tokenA) const baseAmount = tryParseAmount('1', invertPrice ? token1 : token0)
return amountOne ? new Price(tokenA, tokenB, amountOne.raw, parsedAmount.raw) : undefined const price =
baseAmount && parsedQuoteAmount
? new Price(baseAmount.currency, parsedQuoteAmount.currency, baseAmount.raw, parsedQuoteAmount.raw)
: undefined
return (invertPrice ? price?.invert() : price) ?? undefined
} }
return undefined return undefined
} else { } else {
// get the amount of quote currency // get the amount of quote currency
return pool && tokenA ? pool.priceOf(tokenA) : undefined return pool && token0 ? pool.priceOf(token0) : undefined
} }
}, [noLiquidity, startPriceTypedValue, tokenA, tokenB, pool]) }, [noLiquidity, startPriceTypedValue, invertPrice, token1, token0, pool])
// used for ratio calculation when pool not initialized // used for ratio calculation when pool not initialized
const mockPool = useMemo(() => { const mockPool = useMemo(() => {
...@@ -174,40 +200,42 @@ export function useDerivedMintInfo( ...@@ -174,40 +200,42 @@ export function useDerivedMintInfo(
const poolForPosition: Pool | undefined = pool ?? mockPool const poolForPosition: Pool | undefined = pool ?? mockPool
// parse typed range values and determine closest ticks // parse typed range values and determine closest ticks
// lower should always be a smaller tick
const ticks: { const ticks: {
[key: string]: number | undefined [key: string]: number | undefined
} = useMemo(() => { } = useMemo(() => {
return { return {
[Bound.LOWER]: existingPosition [Bound.LOWER]: existingPosition?.tickLower
? existingPosition.tickLower ? existingPosition?.tickLower
: tryParseTick(tokenA, tokenB, feeAmount, lowerRangeTypedValue), : invertPrice
[Bound.UPPER]: existingPosition ? tryParseTick(token1, token0, feeAmount, rightRangeTypedValue)
? existingPosition.tickUpper : tryParseTick(token0, token1, feeAmount, leftRangeTypedValue),
: tryParseTick(tokenA, tokenB, feeAmount, upperRangeTypedValue), [Bound.UPPER]: existingPosition?.tickUpper
? existingPosition?.tickUpper
: invertPrice
? tryParseTick(token1, token0, feeAmount, leftRangeTypedValue)
: tryParseTick(token0, token1, feeAmount, rightRangeTypedValue),
} }
}, [existingPosition, feeAmount, lowerRangeTypedValue, tokenA, tokenB, upperRangeTypedValue]) }, [existingPosition, feeAmount, invertPrice, leftRangeTypedValue, rightRangeTypedValue, token0, token1])
const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks || {} const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks || {}
const sortedTicks = useMemo(
() =>
tickLower !== undefined && tickUpper !== undefined
? tickLower < tickUpper
? [tickLower, tickUpper]
: [tickUpper, tickLower]
: undefined,
[tickLower, tickUpper]
)
// mark invalid range
const invalidRange = Boolean(typeof tickLower === 'number' && typeof tickUpper === 'number' && tickLower >= tickUpper)
// always returns the price with 0 as base token
const pricesAtTicks = useMemo(() => { const pricesAtTicks = useMemo(() => {
return { return {
[Bound.LOWER]: getTickToPrice(tokenA, tokenB, ticks[Bound.LOWER]), [Bound.LOWER]: getTickToPrice(token0, token1, ticks[Bound.LOWER]),
[Bound.UPPER]: getTickToPrice(tokenA, tokenB, ticks[Bound.UPPER]), [Bound.UPPER]: getTickToPrice(token0, token1, ticks[Bound.UPPER]),
} }
}, [tokenA, tokenB, ticks]) }, [token0, token1, ticks])
const { [Bound.LOWER]: lowerPrice, [Bound.UPPER]: upperPrice } = pricesAtTicks const { [Bound.LOWER]: lowerPrice, [Bound.UPPER]: upperPrice } = pricesAtTicks
// mark invalid range // liquidity range warning
const invalidRange = Boolean(lowerPrice && upperPrice && lowerPrice.greaterThan(upperPrice)) const outOfRange = Boolean(
!invalidRange && price && lowerPrice && upperPrice && (price.lessThan(lowerPrice) || price.greaterThan(upperPrice))
)
// amounts // amounts
const independentAmount: CurrencyAmount | undefined = tryParseAmount(typedValue, currencies[independentField]) const independentAmount: CurrencyAmount | undefined = tryParseAmount(typedValue, currencies[independentField])
...@@ -215,37 +243,30 @@ export function useDerivedMintInfo( ...@@ -215,37 +243,30 @@ export function useDerivedMintInfo(
const dependentAmount: CurrencyAmount | undefined = useMemo(() => { const dependentAmount: CurrencyAmount | undefined = useMemo(() => {
// we wrap the currencies just to get the price in terms of the other token // we wrap the currencies just to get the price in terms of the other token
const wrappedIndependentAmount = wrappedCurrencyAmount(independentAmount, chainId) const wrappedIndependentAmount = wrappedCurrencyAmount(independentAmount, chainId)
const dependentCurrency = dependentField === Field.CURRENCY_B ? currencyB : currencyA const dependentCurrency = dependentField === Field.CURRENCY_B ? currencyB : currencyA
if ( if (
feeAmount &&
independentAmount && independentAmount &&
tokenA &&
tokenB &&
wrappedIndependentAmount && wrappedIndependentAmount &&
price && typeof tickLower === 'number' &&
lowerPrice && typeof tickUpper === 'number' &&
upperPrice &&
sortedTicks &&
poolForPosition poolForPosition
) { ) {
// if price is out of range or invalid range - retun 0 (single deposit with be independent) // if price is out of range or invalid range - return 0 (single deposit will be independent)
if (price.lessThan(lowerPrice) || price.greaterThan(upperPrice) || invalidRange) { if (outOfRange || invalidRange) {
return undefined return undefined
} }
const position: Position | undefined = wrappedIndependentAmount.token.equals(poolForPosition.token0) const position: Position | undefined = wrappedIndependentAmount.token.equals(poolForPosition.token0)
? Position.fromAmount0({ ? Position.fromAmount0({
pool: poolForPosition, pool: poolForPosition,
tickLower: sortedTicks[0], tickLower,
tickUpper: sortedTicks[1], tickUpper,
amount0: independentAmount.raw, amount0: independentAmount.raw,
}) })
: Position.fromAmount1({ : Position.fromAmount1({
pool: poolForPosition, pool: poolForPosition,
tickLower: sortedTicks[0], tickLower,
tickUpper: sortedTicks[1], tickUpper,
amount1: independentAmount.raw, amount1: independentAmount.raw,
}) })
...@@ -259,16 +280,12 @@ export function useDerivedMintInfo( ...@@ -259,16 +280,12 @@ export function useDerivedMintInfo(
}, [ }, [
independentAmount, independentAmount,
chainId, chainId,
outOfRange,
dependentField, dependentField,
currencyB, currencyB,
currencyA, currencyA,
feeAmount, tickLower,
tokenA, tickUpper,
tokenB,
price,
sortedTicks,
lowerPrice,
upperPrice,
poolForPosition, poolForPosition,
invalidRange, invalidRange,
]) ])
...@@ -281,22 +298,37 @@ export function useDerivedMintInfo( ...@@ -281,22 +298,37 @@ export function useDerivedMintInfo(
}, [dependentAmount, independentAmount, independentField]) }, [dependentAmount, independentAmount, independentField])
// single deposit only if price is out of range // single deposit only if price is out of range
const deposit1Disabled = Boolean(sortedTicks && poolForPosition && poolForPosition.tickCurrent <= sortedTicks[0]) const deposit0Disabled = Boolean(
const deposit0Disabled = Boolean(sortedTicks && poolForPosition && poolForPosition.tickCurrent >= sortedTicks[1]) typeof tickUpper === 'number' && poolForPosition && poolForPosition.tickCurrent >= tickUpper
)
const deposit1Disabled = Boolean(
typeof tickLower === 'number' && poolForPosition && poolForPosition.tickCurrent <= tickLower
)
// sorted for token order // sorted for token order
const depositADisabled = Boolean( const depositADisabled =
invalidRange ||
Boolean(
(deposit0Disabled && poolForPosition && tokenA && poolForPosition.token0.equals(tokenA)) || (deposit0Disabled && poolForPosition && tokenA && poolForPosition.token0.equals(tokenA)) ||
(deposit1Disabled && poolForPosition && tokenA && poolForPosition.token1.equals(tokenA)) (deposit1Disabled && poolForPosition && tokenA && poolForPosition.token1.equals(tokenA))
) )
const depositBDisabled = Boolean( const depositBDisabled =
invalidRange ||
Boolean(
(deposit0Disabled && poolForPosition && tokenB && poolForPosition.token0.equals(tokenB)) || (deposit0Disabled && poolForPosition && tokenB && poolForPosition.token0.equals(tokenB)) ||
(deposit1Disabled && poolForPosition && tokenB && poolForPosition.token1.equals(tokenB)) (deposit1Disabled && poolForPosition && tokenB && poolForPosition.token1.equals(tokenB))
) )
// create position entity based on users selection // create position entity based on users selection
const position: Position | undefined = useMemo(() => { const position: Position | undefined = useMemo(() => {
if (!poolForPosition || !tokenA || !tokenB || !sortedTicks) { if (
!poolForPosition ||
!tokenA ||
!tokenB ||
typeof tickLower !== 'number' ||
typeof tickUpper !== 'number' ||
invalidRange
) {
return undefined return undefined
} }
...@@ -311,24 +343,25 @@ export function useDerivedMintInfo( ...@@ -311,24 +343,25 @@ export function useDerivedMintInfo(
if (amount0 !== undefined && amount1 !== undefined) { if (amount0 !== undefined && amount1 !== undefined) {
return Position.fromAmounts({ return Position.fromAmounts({
pool: poolForPosition, pool: poolForPosition,
tickLower: sortedTicks[0], tickLower,
tickUpper: sortedTicks[1], tickUpper,
amount0: amount0, amount0,
amount1: amount1, amount1,
}) })
} else { } else {
return undefined return undefined
} }
}, [parsedAmounts, poolForPosition, sortedTicks, tokenA, tokenB, deposit0Disabled, deposit1Disabled]) }, [
parsedAmounts,
// liquiidty range warning poolForPosition,
const outOfRange = Boolean( tokenA,
price && tokenB,
lowerPrice && deposit0Disabled,
upperPrice && deposit1Disabled,
!invalidRange && invalidRange,
(lowerPrice.greaterThan(price) || price.greaterThan(upperPrice)) tickLower,
) tickUpper,
])
let errorMessage: string | undefined let errorMessage: string | undefined
if (!account) { if (!account) {
...@@ -376,5 +409,52 @@ export function useDerivedMintInfo( ...@@ -376,5 +409,52 @@ export function useDerivedMintInfo(
outOfRange, outOfRange,
depositADisabled, depositADisabled,
depositBDisabled, depositBDisabled,
invertPrice,
}
}
export function useRangeHopCallbacks(
baseCurrency: Currency | undefined,
quoteCurrency: Currency | undefined,
feeAmount: FeeAmount | undefined,
tickLower: number | undefined,
tickUpper: number | undefined
) {
const { chainId } = useActiveWeb3React()
const baseToken = useMemo(() => wrappedCurrency(baseCurrency, chainId), [baseCurrency, chainId])
const quoteToken = useMemo(() => wrappedCurrency(quoteCurrency, chainId), [quoteCurrency, chainId])
const getDecrementLower = useCallback(() => {
if (baseToken && quoteToken && typeof tickLower === 'number' && feeAmount) {
const newPrice = tickToPrice(baseToken, quoteToken, tickLower - TICK_SPACINGS[feeAmount])
return newPrice.toSignificant(5, undefined, Rounding.ROUND_UP)
}
return ''
}, [baseToken, quoteToken, tickLower, feeAmount])
const getIncrementLower = useCallback(() => {
if (baseToken && quoteToken && typeof tickLower === 'number' && feeAmount) {
const newPrice = tickToPrice(baseToken, quoteToken, tickLower + TICK_SPACINGS[feeAmount])
return newPrice.toSignificant(5, undefined, Rounding.ROUND_UP)
}
return ''
}, [baseToken, quoteToken, tickLower, feeAmount])
const getDecrementUpper = useCallback(() => {
if (baseToken && quoteToken && typeof tickUpper === 'number' && feeAmount) {
const newPrice = tickToPrice(baseToken, quoteToken, tickUpper - TICK_SPACINGS[feeAmount])
return newPrice.toSignificant(5, undefined, Rounding.ROUND_UP)
} }
return ''
}, [baseToken, quoteToken, tickUpper, feeAmount])
const getIncrementUpper = useCallback(() => {
if (baseToken && quoteToken && typeof tickUpper === 'number' && feeAmount) {
const newPrice = tickToPrice(baseToken, quoteToken, tickUpper + TICK_SPACINGS[feeAmount])
return newPrice.toSignificant(5, undefined, Rounding.ROUND_UP)
}
return ''
}, [baseToken, quoteToken, tickUpper, feeAmount])
return { getDecrementLower, getIncrementLower, getDecrementUpper, getIncrementUpper }
} }
...@@ -3,9 +3,9 @@ import { ...@@ -3,9 +3,9 @@ import {
Field, Field,
resetMintState, resetMintState,
typeInput, typeInput,
typeLowerRangeInput,
typeUpperRangeInput,
typeStartPriceInput, typeStartPriceInput,
typeLeftRangeInput,
typeRightRangeInput,
} from './actions' } from './actions'
export interface MintState { export interface MintState {
...@@ -13,8 +13,8 @@ export interface MintState { ...@@ -13,8 +13,8 @@ export interface MintState {
readonly typedValue: string readonly typedValue: string
readonly otherTypedValue: string // for the case when there's no liquidity readonly otherTypedValue: string // for the case when there's no liquidity
readonly startPriceTypedValue: string // for the case when there's no liquidity readonly startPriceTypedValue: string // for the case when there's no liquidity
readonly lowerRangeTypedValue: string readonly leftRangeTypedValue: string
readonly upperRangeTypedValue: string readonly rightRangeTypedValue: string
} }
export const initialState: MintState = { export const initialState: MintState = {
...@@ -22,29 +22,29 @@ export const initialState: MintState = { ...@@ -22,29 +22,29 @@ export const initialState: MintState = {
typedValue: '', typedValue: '',
otherTypedValue: '', otherTypedValue: '',
startPriceTypedValue: '', startPriceTypedValue: '',
lowerRangeTypedValue: '', leftRangeTypedValue: '',
upperRangeTypedValue: '', rightRangeTypedValue: '',
} }
export default createReducer<MintState>(initialState, (builder) => export default createReducer<MintState>(initialState, (builder) =>
builder builder
.addCase(resetMintState, () => initialState) .addCase(resetMintState, () => initialState)
.addCase(typeLowerRangeInput, (state, { payload: { typedValue } }) => { .addCase(typeStartPriceInput, (state, { payload: { typedValue } }) => {
return { return {
...state, ...state,
lowerRangeTypedValue: typedValue, startPriceTypedValue: typedValue,
} }
}) })
.addCase(typeUpperRangeInput, (state, { payload: { typedValue } }) => { .addCase(typeLeftRangeInput, (state, { payload: { typedValue } }) => {
return { return {
...state, ...state,
upperRangeTypedValue: typedValue, leftRangeTypedValue: typedValue,
} }
}) })
.addCase(typeStartPriceInput, (state, { payload: { typedValue } }) => { .addCase(typeRightRangeInput, (state, { payload: { typedValue } }) => {
return { return {
...state, ...state,
startPriceTypedValue: typedValue, rightRangeTypedValue: typedValue,
} }
}) })
.addCase(typeInput, (state, { payload: { field, typedValue, noLiquidity } }) => { .addCase(typeInput, (state, { payload: { field, typedValue, noLiquidity } }) => {
......
...@@ -18,9 +18,10 @@ export function tryParseTick( ...@@ -18,9 +18,10 @@ export function tryParseTick(
if (!amount || !amountOne) return undefined if (!amount || !amountOne) return undefined
// parse the typed value into a price, token0 should always be base currency based on url // parse the typed value into a price
const price = new Price(baseToken, quoteToken, amountOne.raw, amount.raw) const price = new Price(baseToken, quoteToken, amountOne.raw, amount.raw)
// this function is agnostic to the base, will always return the correct tick
const tick = priceToClosestTick(price) const tick = priceToClosestTick(price)
return nearestUsableTick(tick, TICK_SPACINGS[feeAmount]) return nearestUsableTick(tick, TICK_SPACINGS[feeAmount])
......
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