fix: full range lp shortcut (#6136)
* 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
This commit is contained in:
parent
b5f665bc6e
commit
10eda002f5
@ -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'
|
||||
|
||||
@ -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>
|
||||
|
@ -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,
|
||||
@ -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(
|
||||
@ -114,8 +113,7 @@ 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,
|
||||
@ -123,6 +121,7 @@ export default function AddLiquidity() {
|
||||
dependentField,
|
||||
price,
|
||||
pricesAtTicks,
|
||||
pricesAtLimit,
|
||||
parsedAmounts,
|
||||
currencyBalances,
|
||||
position,
|
||||
@ -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
|
||||
|
||||
@ -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">
|
||||
@ -825,13 +856,7 @@ export default function AddLiquidity() {
|
||||
feeAmount={feeAmount}
|
||||
ticksAtLimit={ticksAtLimit}
|
||||
/>
|
||||
{!noLiquidity && (
|
||||
<PresetsButtons
|
||||
setFullRange={() => {
|
||||
getSetFullRange()
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!noLiquidity && <PresetsButtons onSetFullRange={handleSetFullRange} />}
|
||||
</AutoColumn>
|
||||
</StackedItem>
|
||||
</StackedContainer>
|
||||
|
@ -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'
|
||||
@ -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) => {
|
||||
@ -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(
|
||||
@ -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
|
||||
@ -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,
|
||||
@ -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'
|
||||
@ -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 {
|
||||
@ -472,6 +484,7 @@ export function useV3DerivedMintInfo(
|
||||
ticks,
|
||||
price,
|
||||
pricesAtTicks,
|
||||
pricesAtLimit,
|
||||
position,
|
||||
noLiquidity,
|
||||
errorMessage,
|
||||
|
@ -1,5 +0,0 @@
|
||||
export const replaceURLParam = (search: string, param: string, newValue: string) => {
|
||||
const searchParams = new URLSearchParams(search)
|
||||
searchParams.set(param, newValue)
|
||||
return searchParams.toString()
|
||||
}
|
Loading…
Reference in New Issue
Block a user