Skip to content

Commit

Permalink
fix: full range lp shortcut (Uniswap#6136)
Browse files Browse the repository at this point in the history
* fix: fix full range LP

* make a dedicated hook for syncing query parameters

* sync full range button to url

* add comment explaining lint rule disable
  • Loading branch information
JFrankfurt authored Mar 13, 2023
1 parent b5f665b commit 10eda00
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 63 deletions.
18 changes: 6 additions & 12 deletions src/components/RangeSelector/PresetsButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Trans } from '@lingui/macro'
import { sendEvent } from 'components/analytics'
import { ButtonOutlined } from 'components/Button'
import { AutoRow } from 'components/Row'
import React from 'react'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'

Expand All @@ -14,18 +12,14 @@ const Button = styled(ButtonOutlined).attrs(() => ({
flex: 1;
`

export default function PresetsButtons({ setFullRange }: { setFullRange: () => void }) {
interface PresetsButtonsProps {
onSetFullRange: () => void
}

export default function PresetsButtons({ onSetFullRange }: PresetsButtonsProps) {
return (
<AutoRow gap="4px" width="auto">
<Button
onClick={() => {
setFullRange()
sendEvent({
category: 'Liquidity',
action: 'Full Range Clicked',
})
}}
>
<Button onClick={onSetFullRange}>
<ThemedText.DeprecatedBody fontSize={12}>
<Trans>Full Range</Trans>
</ThemedText.DeprecatedBody>
Expand Down
89 changes: 57 additions & 32 deletions src/pages/AddLiquidity/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import { FeeAmount, NonfungiblePositionManager } from '@uniswap/v3-sdk'
import { useWeb3React } from '@web3-react/core'
import { sendEvent } from 'components/analytics'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
import useParsedQueryString from 'hooks/useParsedQueryString'
import usePrevious from 'hooks/usePrevious'
import { useCallback, useEffect, useState } from 'react'
import { AlertTriangle } from 'react-feather'
import { useNavigate, useParams } from 'react-router-dom'
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
import { Text } from 'rebass'
import {
useRangeHopCallbacks,
Expand Down Expand Up @@ -92,7 +92,6 @@ export default function AddLiquidity() {
const expertMode = useIsExpertMode()
const addTransaction = useTransactionAdder()
const positionManager = useV3NFTPositionManagerContract()
const parsedQs = useParsedQueryString()

// check for existing position if tokenId in url
const { position: existingPositionDetails, loading: positionLoading } = useV3PositionFromTokenId(
Expand All @@ -114,15 +113,15 @@ export default function AddLiquidity() {
baseCurrency && currencyB && baseCurrency.wrapped.equals(currencyB.wrapped) ? undefined : currencyB

// mint state
const { independentField, typedValue, startPriceTypedValue, rightRangeTypedValue, leftRangeTypedValue } =
useV3MintState()
const { independentField, typedValue, startPriceTypedValue } = useV3MintState()

const {
pool,
ticks,
dependentField,
price,
pricesAtTicks,
pricesAtLimit,
parsedAmounts,
currencyBalances,
position,
Expand Down Expand Up @@ -153,26 +152,6 @@ export default function AddLiquidity() {
const [showConfirm, setShowConfirm] = useState<boolean>(false)
const [attemptingTxn, setAttemptingTxn] = useState<boolean>(false) // clicked confirm

useEffect(() => {
if (
parsedQs.minPrice &&
typeof parsedQs.minPrice === 'string' &&
parsedQs.minPrice !== leftRangeTypedValue &&
!isNaN(parsedQs.minPrice as any)
) {
onLeftRangeInput(parsedQs.minPrice)
}

if (
parsedQs.maxPrice &&
typeof parsedQs.maxPrice === 'string' &&
parsedQs.maxPrice !== rightRangeTypedValue &&
!isNaN(parsedQs.maxPrice as any)
) {
onRightRangeInput(parsedQs.maxPrice)
}
}, [parsedQs, rightRangeTypedValue, leftRangeTypedValue, onRightRangeInput, onLeftRangeInput])

// txn values
const deadline = useTransactionDeadline() // custom from users settings

Expand Down Expand Up @@ -427,6 +406,58 @@ export default function AddLiquidity() {
!depositBDisabled ? currencies[Field.CURRENCY_B]?.symbol : ''
}`

const [searchParams, setSearchParams] = useSearchParams()

const handleSetFullRange = useCallback(() => {
getSetFullRange()

const minPrice = pricesAtLimit[Bound.LOWER]
if (minPrice) searchParams.set('minPrice', minPrice.toSignificant(5))
const maxPrice = pricesAtLimit[Bound.UPPER]
if (maxPrice) searchParams.set('maxPrice', maxPrice.toSignificant(5))
setSearchParams(searchParams)

sendEvent({
category: 'Liquidity',
action: 'Full Range Clicked',
})
}, [getSetFullRange, pricesAtLimit, searchParams, setSearchParams])

// START: sync values with query string
const oldSearchParams = usePrevious(searchParams)
// use query string as an input to onInput handlers
useEffect(() => {
const minPrice = searchParams.get('minPrice')
const oldMinPrice = oldSearchParams?.get('minPrice')
if (
minPrice &&
typeof minPrice === 'string' &&
!isNaN(minPrice as any) &&
(!oldMinPrice || oldMinPrice !== minPrice)
) {
onLeftRangeInput(minPrice)
}
// disable eslint rule because this hook only cares about the url->input state data flow
// input state -> url updates are handled in the input handlers
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchParams])
useEffect(() => {
const maxPrice = searchParams.get('maxPrice')
const oldMaxPrice = oldSearchParams?.get('maxPrice')
if (
maxPrice &&
typeof maxPrice === 'string' &&
!isNaN(maxPrice as any) &&
(!oldMaxPrice || oldMaxPrice !== maxPrice)
) {
onRightRangeInput(maxPrice)
}
// disable eslint rule because this hook only cares about the url->input state data flow
// input state -> url updates are handled in the input handlers
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchParams])
// END: sync values with query string

const Buttons = () =>
addIsUnsupported ? (
<ButtonPrimary disabled={true} $borderRadius="12px" padding="12px">
Expand Down Expand Up @@ -825,13 +856,7 @@ export default function AddLiquidity() {
feeAmount={feeAmount}
ticksAtLimit={ticksAtLimit}
/>
{!noLiquidity && (
<PresetsButtons
setFullRange={() => {
getSetFullRange()
}}
/>
)}
{!noLiquidity && <PresetsButtons onSetFullRange={handleSetFullRange} />}
</AutoColumn>
</StackedItem>
</StackedContainer>
Expand Down
41 changes: 27 additions & 14 deletions src/state/mint/v3/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ import { usePool } from 'hooks/usePools'
import JSBI from 'jsbi'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { ReactNode, useCallback, useMemo } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { useSearchParams } from 'react-router-dom'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import { getTickToPrice } from 'utils/getTickToPrice'
import { replaceURLParam } from 'utils/routes'

import { BIG_INT_ZERO } from '../../../constants/misc'
import { PoolState } from '../../../hooks/usePools'
Expand Down Expand Up @@ -48,7 +47,6 @@ export function useV3MintActionHandlers(noLiquidity: boolean | undefined): {
onStartPriceInput: (typedValue: string) => void
} {
const dispatch = useAppDispatch()
const navigate = useNavigate()

const onFieldAInput = useCallback(
(typedValue: string) => {
Expand All @@ -64,22 +62,30 @@ export function useV3MintActionHandlers(noLiquidity: boolean | undefined): {
[dispatch, noLiquidity]
)

const { search } = useLocation()
const [searchParams, setSearchParams] = useSearchParams()

const onLeftRangeInput = useCallback(
(typedValue: string) => {
dispatch(typeLeftRangeInput({ typedValue }))
navigate({ search: replaceURLParam(search, 'minPrice', typedValue) }, { replace: true })
const paramMinPrice = searchParams.get('minPrice')
if (!paramMinPrice || (paramMinPrice && paramMinPrice !== typedValue)) {
searchParams.set('minPrice', typedValue)
setSearchParams(searchParams)
}
},
[dispatch, navigate, search]
[dispatch, searchParams, setSearchParams]
)

const onRightRangeInput = useCallback(
(typedValue: string) => {
dispatch(typeRightRangeInput({ typedValue }))
navigate({ search: replaceURLParam(search, 'maxPrice', typedValue) }, { replace: true })
const paramMaxPrice = searchParams.get('maxPrice')
if (!paramMaxPrice || (paramMaxPrice && paramMaxPrice !== typedValue)) {
searchParams.set('maxPrice', typedValue)
setSearchParams(searchParams)
}
},
[dispatch, navigate, search]
[dispatch, searchParams, setSearchParams]
)

const onStartPriceInput = useCallback(
Expand Down Expand Up @@ -113,6 +119,9 @@ export function useV3DerivedMintInfo(
pricesAtTicks: {
[bound in Bound]?: Price<Token, Token> | undefined
}
pricesAtLimit: {
[bound in Bound]?: Price<Token, Token> | undefined
}
currencies: { [field in Field]?: Currency }
currencyBalances: { [field in Field]?: CurrencyAmount<Currency> }
dependentField: Field
Expand Down Expand Up @@ -226,9 +235,7 @@ export function useV3DerivedMintInfo(
const poolForPosition: Pool | undefined = pool ?? mockPool

// lower and upper limits in the tick space for `feeAmoun<Trans>
const tickSpaceLimits: {
[bound in Bound]: number | undefined
} = useMemo(
const tickSpaceLimits = useMemo(
() => ({
[Bound.LOWER]: feeAmount ? nearestUsableTick(TickMath.MIN_TICK, TICK_SPACINGS[feeAmount]) : undefined,
[Bound.UPPER]: feeAmount ? nearestUsableTick(TickMath.MAX_TICK, TICK_SPACINGS[feeAmount]) : undefined,
Expand All @@ -238,9 +245,7 @@ export function useV3DerivedMintInfo(

// parse typed range values and determine closest ticks
// lower should always be a smaller tick
const ticks: {
[key: string]: number | undefined
} = useMemo(() => {
const ticks = useMemo(() => {
return {
[Bound.LOWER]:
typeof existingPosition?.tickLower === 'number'
Expand Down Expand Up @@ -286,6 +291,13 @@ export function useV3DerivedMintInfo(
// mark invalid range
const invalidRange = Boolean(typeof tickLower === 'number' && typeof tickUpper === 'number' && tickLower >= tickUpper)

const pricesAtLimit = useMemo(() => {
return {
[Bound.LOWER]: getTickToPrice(token0, token1, tickSpaceLimits.LOWER),
[Bound.UPPER]: getTickToPrice(token0, token1, tickSpaceLimits.UPPER),
}
}, [token0, token1, tickSpaceLimits.LOWER, tickSpaceLimits.UPPER])

// always returns the price with 0 as base token
const pricesAtTicks = useMemo(() => {
return {
Expand Down Expand Up @@ -472,6 +484,7 @@ export function useV3DerivedMintInfo(
ticks,
price,
pricesAtTicks,
pricesAtLimit,
position,
noLiquidity,
errorMessage,
Expand Down
5 changes: 0 additions & 5 deletions src/utils/routes.ts

This file was deleted.

0 comments on commit 10eda00

Please sign in to comment.