fix: add support for full range positions in add liquidity (#2090)
* remove arbitrary range buttons and move full range button * better align full range to deposit * support dragging range when in full range * hack to support full range brushing * restore zoom levels * adjusted for mocks * fix styling * simplify type * restore rate toggle change * fix lower bound by looking at isSorted * better align bottoms * add reset button for full range positions * only flip when not at limit * fix +/- buttons in range selector * add help link
This commit is contained in:
parent
9b7637e012
commit
952cc98df3
@ -43,7 +43,8 @@ const FLIP_HANDLE_THRESHOLD_PX = 20
|
|||||||
// margin to prevent tick snapping from putting the brush off screen
|
// margin to prevent tick snapping from putting the brush off screen
|
||||||
const BRUSH_EXTENT_MARGIN_PX = 2
|
const BRUSH_EXTENT_MARGIN_PX = 2
|
||||||
|
|
||||||
const compare = (a1: [number, number], a2: [number, number]): boolean => a1[0] !== a2[0] || a1[1] !== a2[1]
|
const compare = (a1: [number, number], a2: [number, number], xScale: ScaleLinear<number, number>): boolean =>
|
||||||
|
xScale(a1[0]) !== xScale(a2[0]) || xScale(a1[1]) !== xScale(a2[1])
|
||||||
|
|
||||||
export const Brush = ({
|
export const Brush = ({
|
||||||
id,
|
id,
|
||||||
@ -62,7 +63,7 @@ export const Brush = ({
|
|||||||
interactive: boolean
|
interactive: boolean
|
||||||
brushLabelValue: (d: 'w' | 'e', x: number) => string
|
brushLabelValue: (d: 'w' | 'e', x: number) => string
|
||||||
brushExtent: [number, number]
|
brushExtent: [number, number]
|
||||||
setBrushExtent: (extent: [number, number]) => void
|
setBrushExtent: (extent: [number, number], mode: string | undefined) => void
|
||||||
innerWidth: number
|
innerWidth: number
|
||||||
innerHeight: number
|
innerHeight: number
|
||||||
westHandleColor: string
|
westHandleColor: string
|
||||||
@ -79,7 +80,9 @@ export const Brush = ({
|
|||||||
const previousBrushExtent = usePrevious(brushExtent)
|
const previousBrushExtent = usePrevious(brushExtent)
|
||||||
|
|
||||||
const brushed = useCallback(
|
const brushed = useCallback(
|
||||||
({ type, selection }: D3BrushEvent<unknown>) => {
|
(event: D3BrushEvent<unknown>) => {
|
||||||
|
const { type, selection, mode } = event
|
||||||
|
|
||||||
if (!selection) {
|
if (!selection) {
|
||||||
setLocalBrushExtent(null)
|
setLocalBrushExtent(null)
|
||||||
return
|
return
|
||||||
@ -88,8 +91,8 @@ export const Brush = ({
|
|||||||
const scaled = (selection as [number, number]).map(xScale.invert) as [number, number]
|
const scaled = (selection as [number, number]).map(xScale.invert) as [number, number]
|
||||||
|
|
||||||
// avoid infinite render loop by checking for change
|
// avoid infinite render loop by checking for change
|
||||||
if (type === 'end' && compare(brushExtent, scaled)) {
|
if (type === 'end' && compare(brushExtent, scaled, xScale)) {
|
||||||
setBrushExtent(scaled)
|
setBrushExtent(scaled, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
setLocalBrushExtent(scaled)
|
setLocalBrushExtent(scaled)
|
||||||
@ -118,7 +121,7 @@ export const Brush = ({
|
|||||||
|
|
||||||
brushBehavior.current(select(brushRef.current))
|
brushBehavior.current(select(brushRef.current))
|
||||||
|
|
||||||
if (previousBrushExtent && compare(brushExtent, previousBrushExtent)) {
|
if (previousBrushExtent && compare(brushExtent, previousBrushExtent, xScale)) {
|
||||||
select(brushRef.current)
|
select(brushRef.current)
|
||||||
.transition()
|
.transition()
|
||||||
.call(brushBehavior.current.move as any, brushExtent.map(xScale))
|
.call(brushBehavior.current.move as any, brushExtent.map(xScale))
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { max, scaleLinear, ZoomTransform } from 'd3'
|
import { max, scaleLinear, ZoomTransform } from 'd3'
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
import { Bound } from 'state/mint/v3/actions'
|
||||||
import { Area } from './Area'
|
import { Area } from './Area'
|
||||||
import { AxisBottom } from './AxisBottom'
|
import { AxisBottom } from './AxisBottom'
|
||||||
import { Brush } from './Brush'
|
import { Brush } from './Brush'
|
||||||
@ -13,6 +14,7 @@ export const yAccessor = (d: ChartEntry) => d.activeLiquidity
|
|||||||
export function Chart({
|
export function Chart({
|
||||||
id = 'liquidityChartRangeInput',
|
id = 'liquidityChartRangeInput',
|
||||||
data: { series, current },
|
data: { series, current },
|
||||||
|
ticksAtLimit,
|
||||||
styles,
|
styles,
|
||||||
dimensions: { width, height },
|
dimensions: { width, height },
|
||||||
margins,
|
margins,
|
||||||
@ -56,7 +58,7 @@ export function Chart({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!brushDomain) {
|
if (!brushDomain) {
|
||||||
onBrushDomainChange(xScale.domain() as [number, number])
|
onBrushDomainChange(xScale.domain() as [number, number], undefined)
|
||||||
}
|
}
|
||||||
}, [brushDomain, onBrushDomainChange, xScale])
|
}, [brushDomain, onBrushDomainChange, xScale])
|
||||||
|
|
||||||
@ -71,7 +73,13 @@ export function Chart({
|
|||||||
// allow zooming inside the x-axis
|
// allow zooming inside the x-axis
|
||||||
height
|
height
|
||||||
}
|
}
|
||||||
showClear={false}
|
resetBrush={() => {
|
||||||
|
onBrushDomainChange(
|
||||||
|
[current * zoomLevels.initialMin, current * zoomLevels.initialMax] as [number, number],
|
||||||
|
'reset'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
showResetButton={Boolean(ticksAtLimit[Bound.LOWER] || ticksAtLimit[Bound.UPPER])}
|
||||||
zoomLevels={zoomLevels}
|
zoomLevels={zoomLevels}
|
||||||
/>
|
/>
|
||||||
<svg width="100%" height="100%" viewBox={`0 0 ${width} ${height}`} style={{ overflow: 'visible' }}>
|
<svg width="100%" height="100%" viewBox={`0 0 ${width} ${height}`} style={{ overflow: 'visible' }}>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useMemo, useRef } from 'react'
|
import React, { useEffect, useMemo, useRef } from 'react'
|
||||||
import { ButtonGray } from 'components/Button'
|
import { ButtonGray } from 'components/Button'
|
||||||
import styled from 'styled-components/macro'
|
import styled from 'styled-components/macro'
|
||||||
import { ScaleLinear, select, ZoomBehavior, zoom, ZoomTransform } from 'd3'
|
import { ScaleLinear, select, ZoomBehavior, zoom, ZoomTransform, zoomIdentity } from 'd3'
|
||||||
import { RefreshCcw, ZoomIn, ZoomOut } from 'react-feather'
|
import { RefreshCcw, ZoomIn, ZoomOut } from 'react-feather'
|
||||||
import { ZoomLevels } from './types'
|
import { ZoomLevels } from './types'
|
||||||
|
|
||||||
@ -41,7 +41,8 @@ export default function Zoom({
|
|||||||
setZoom,
|
setZoom,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
showClear,
|
resetBrush,
|
||||||
|
showResetButton,
|
||||||
zoomLevels,
|
zoomLevels,
|
||||||
}: {
|
}: {
|
||||||
svg: SVGElement | null
|
svg: SVGElement | null
|
||||||
@ -49,12 +50,13 @@ export default function Zoom({
|
|||||||
setZoom: (transform: ZoomTransform) => void
|
setZoom: (transform: ZoomTransform) => void
|
||||||
width: number
|
width: number
|
||||||
height: number
|
height: number
|
||||||
showClear: boolean
|
resetBrush: () => void
|
||||||
|
showResetButton: boolean
|
||||||
zoomLevels: ZoomLevels
|
zoomLevels: ZoomLevels
|
||||||
}) {
|
}) {
|
||||||
const zoomBehavior = useRef<ZoomBehavior<Element, unknown>>()
|
const zoomBehavior = useRef<ZoomBehavior<Element, unknown>>()
|
||||||
|
|
||||||
const [zoomIn, zoomOut, reset, initial] = useMemo(
|
const [zoomIn, zoomOut, zoomInitial, zoomReset] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
() =>
|
() =>
|
||||||
svg &&
|
svg &&
|
||||||
@ -73,15 +75,16 @@ export default function Zoom({
|
|||||||
zoomBehavior.current &&
|
zoomBehavior.current &&
|
||||||
select(svg as Element)
|
select(svg as Element)
|
||||||
.transition()
|
.transition()
|
||||||
.call(zoomBehavior.current.scaleTo, 1),
|
.call(zoomBehavior.current.scaleTo, 0.5),
|
||||||
() =>
|
() =>
|
||||||
svg &&
|
svg &&
|
||||||
zoomBehavior.current &&
|
zoomBehavior.current &&
|
||||||
select(svg as Element)
|
select(svg as Element)
|
||||||
|
.call(zoomBehavior.current.transform, zoomIdentity.translate(0, 0).scale(1))
|
||||||
.transition()
|
.transition()
|
||||||
.call(zoomBehavior.current.scaleTo, 0.5),
|
.call(zoomBehavior.current.scaleTo, 0.5),
|
||||||
],
|
],
|
||||||
[svg, zoomBehavior]
|
[svg]
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -100,13 +103,19 @@ export default function Zoom({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// reset zoom to initial on zoomLevel change
|
// reset zoom to initial on zoomLevel change
|
||||||
initial()
|
zoomInitial()
|
||||||
}, [initial, zoomLevels])
|
}, [zoomInitial, zoomLevels])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper count={showClear ? 3 : 2}>
|
<Wrapper count={showResetButton ? 3 : 2}>
|
||||||
{showClear && (
|
{showResetButton && (
|
||||||
<Button onClick={reset} disabled={false}>
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
resetBrush()
|
||||||
|
zoomReset()
|
||||||
|
}}
|
||||||
|
disabled={false}
|
||||||
|
>
|
||||||
<RefreshCcw size={16} />
|
<RefreshCcw size={16} />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
@ -87,6 +87,8 @@ export default function LiquidityChartRangeInput({
|
|||||||
const tokenAColor = useColor(currencyA?.wrapped)
|
const tokenAColor = useColor(currencyA?.wrapped)
|
||||||
const tokenBColor = useColor(currencyB?.wrapped)
|
const tokenBColor = useColor(currencyB?.wrapped)
|
||||||
|
|
||||||
|
const isSorted = currencyA && currencyB && currencyA?.wrapped.sortsBefore(currencyB?.wrapped)
|
||||||
|
|
||||||
const { isLoading, isUninitialized, isError, error, formattedData } = useDensityChartData({
|
const { isLoading, isUninitialized, isError, error, formattedData } = useDensityChartData({
|
||||||
currencyA,
|
currencyA,
|
||||||
currencyB,
|
currencyB,
|
||||||
@ -94,7 +96,7 @@ export default function LiquidityChartRangeInput({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const onBrushDomainChangeEnded = useCallback(
|
const onBrushDomainChangeEnded = useCallback(
|
||||||
(domain) => {
|
(domain, mode) => {
|
||||||
let leftRangeValue = Number(domain[0])
|
let leftRangeValue = Number(domain[0])
|
||||||
const rightRangeValue = Number(domain[1])
|
const rightRangeValue = Number(domain[1])
|
||||||
|
|
||||||
@ -104,40 +106,48 @@ export default function LiquidityChartRangeInput({
|
|||||||
|
|
||||||
batch(() => {
|
batch(() => {
|
||||||
// simulate user input for auto-formatting and other validations
|
// simulate user input for auto-formatting and other validations
|
||||||
leftRangeValue > 0 && onLeftRangeInput(leftRangeValue.toFixed(6))
|
if (
|
||||||
rightRangeValue > 0 && onRightRangeInput(rightRangeValue.toFixed(6))
|
(!ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER] || mode === 'handle' || mode === 'reset') &&
|
||||||
|
leftRangeValue > 0
|
||||||
|
) {
|
||||||
|
onLeftRangeInput(leftRangeValue.toFixed(6))
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER] || mode === 'reset') && rightRangeValue > 0) {
|
||||||
|
// todo: remove this check. Upper bound for large numbers
|
||||||
|
// sometimes fails to parse to tick.
|
||||||
|
if (rightRangeValue < 1e35) {
|
||||||
|
onRightRangeInput(rightRangeValue.toFixed(6))
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[onLeftRangeInput, onRightRangeInput]
|
[isSorted, onLeftRangeInput, onRightRangeInput, ticksAtLimit]
|
||||||
)
|
)
|
||||||
|
|
||||||
interactive = interactive && Boolean(formattedData?.length)
|
interactive = interactive && Boolean(formattedData?.length)
|
||||||
|
|
||||||
const brushDomain: [number, number] | undefined = useMemo(() => {
|
const brushDomain: [number, number] | undefined = useMemo(() => {
|
||||||
const isSorted = currencyA && currencyB && currencyA?.wrapped.sortsBefore(currencyB?.wrapped)
|
|
||||||
|
|
||||||
const leftPrice = isSorted ? priceLower : priceUpper?.invert()
|
const leftPrice = isSorted ? priceLower : priceUpper?.invert()
|
||||||
const rightPrice = isSorted ? priceUpper : priceLower?.invert()
|
const rightPrice = isSorted ? priceUpper : priceLower?.invert()
|
||||||
|
|
||||||
return leftPrice && rightPrice
|
return leftPrice && rightPrice
|
||||||
? [parseFloat(leftPrice?.toSignificant(5)), parseFloat(rightPrice?.toSignificant(5))]
|
? [parseFloat(leftPrice?.toSignificant(6)), parseFloat(rightPrice?.toSignificant(6))]
|
||||||
: undefined
|
: undefined
|
||||||
}, [currencyA, currencyB, priceLower, priceUpper])
|
}, [isSorted, priceLower, priceUpper])
|
||||||
|
|
||||||
const brushLabelValue = useCallback(
|
const brushLabelValue = useCallback(
|
||||||
(d: 'w' | 'e', x: number) => {
|
(d: 'w' | 'e', x: number) => {
|
||||||
if (!price) return ''
|
if (!price) return ''
|
||||||
|
|
||||||
if (d === 'w' && ticksAtLimit[Bound.LOWER]) return '0'
|
if (d === 'w' && ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER]) return '0'
|
||||||
if (d === 'e' && ticksAtLimit[Bound.UPPER]) return '∞'
|
if (d === 'e' && ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER]) return '∞'
|
||||||
|
|
||||||
//const percent = (((x < price ? -1 : 1) * (Math.max(x, price) - Math.min(x, price))) / Math.min(x, price)) * 100
|
|
||||||
|
|
||||||
const percent = (x < price ? -1 : 1) * ((Math.max(x, price) - Math.min(x, price)) / price) * 100
|
const percent = (x < price ? -1 : 1) * ((Math.max(x, price) - Math.min(x, price)) / price) * 100
|
||||||
|
|
||||||
return price ? `${format(Math.abs(percent) > 1 ? '.2~s' : '.2~f')(percent)}%` : ''
|
return price ? `${format(Math.abs(percent) > 1 ? '.2~s' : '.2~f')(percent)}%` : ''
|
||||||
},
|
},
|
||||||
[price, ticksAtLimit]
|
[isSorted, price, ticksAtLimit]
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
@ -189,6 +199,7 @@ export default function LiquidityChartRangeInput({
|
|||||||
brushDomain={brushDomain}
|
brushDomain={brushDomain}
|
||||||
onBrushDomainChange={onBrushDomainChangeEnded}
|
onBrushDomainChange={onBrushDomainChangeEnded}
|
||||||
zoomLevels={ZOOM_LEVELS[feeAmount ?? FeeAmount.MEDIUM]}
|
zoomLevels={ZOOM_LEVELS[feeAmount ?? FeeAmount.MEDIUM]}
|
||||||
|
ticksAtLimit={ticksAtLimit}
|
||||||
/>
|
/>
|
||||||
</ChartWrapper>
|
</ChartWrapper>
|
||||||
)}
|
)}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { Bound } from 'state/mint/v3/actions'
|
||||||
|
|
||||||
export interface ChartEntry {
|
export interface ChartEntry {
|
||||||
activeLiquidity: number
|
activeLiquidity: number
|
||||||
price0: number
|
price0: number
|
||||||
@ -30,6 +32,7 @@ export interface LiquidityChartRangeInputProps {
|
|||||||
series: ChartEntry[]
|
series: ChartEntry[]
|
||||||
current: number
|
current: number
|
||||||
}
|
}
|
||||||
|
ticksAtLimit: { [bound in Bound]?: boolean | undefined }
|
||||||
|
|
||||||
styles: {
|
styles: {
|
||||||
area: {
|
area: {
|
||||||
@ -52,7 +55,7 @@ export interface LiquidityChartRangeInputProps {
|
|||||||
|
|
||||||
brushLabels: (d: 'w' | 'e', x: number) => string
|
brushLabels: (d: 'w' | 'e', x: number) => string
|
||||||
brushDomain: [number, number] | undefined
|
brushDomain: [number, number] | undefined
|
||||||
onBrushDomainChange: (domain: [number, number]) => void
|
onBrushDomainChange: (domain: [number, number], mode: string | undefined) => void
|
||||||
|
|
||||||
zoomLevels: ZoomLevels
|
zoomLevels: ZoomLevels
|
||||||
}
|
}
|
||||||
|
@ -4,76 +4,18 @@ import { AutoRow } from 'components/Row'
|
|||||||
import { TYPE } from 'theme'
|
import { TYPE } from 'theme'
|
||||||
import styled from 'styled-components/macro'
|
import styled from 'styled-components/macro'
|
||||||
import { Trans } from '@lingui/macro'
|
import { Trans } from '@lingui/macro'
|
||||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
|
||||||
import ReactGA from 'react-ga'
|
|
||||||
|
|
||||||
const Button = styled(ButtonOutlined).attrs(() => ({
|
const Button = styled(ButtonOutlined).attrs(() => ({
|
||||||
padding: '4px',
|
padding: '8px',
|
||||||
borderRadius: '8px',
|
$borderRadius: '8px',
|
||||||
}))`
|
}))`
|
||||||
color: ${({ theme }) => theme.text1};
|
color: ${({ theme }) => theme.text1};
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: ${({ theme }) => theme.bg2};
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const RANGES = {
|
export default function PresetsButtons({ setFullRange }: { setFullRange: () => void }) {
|
||||||
[FeeAmount.LOW]: [
|
|
||||||
{ label: '0.05', ticks: 5 },
|
|
||||||
{ label: '0.1', ticks: 10 },
|
|
||||||
{ label: '0.2', ticks: 20 },
|
|
||||||
],
|
|
||||||
[FeeAmount.MEDIUM]: [
|
|
||||||
{ label: '1', ticks: 100 },
|
|
||||||
{ label: '10', ticks: 953 },
|
|
||||||
{ label: '50', ticks: 4055 },
|
|
||||||
],
|
|
||||||
[FeeAmount.HIGH]: [
|
|
||||||
{ label: '2', ticks: 198 },
|
|
||||||
{ label: '10', ticks: 953 },
|
|
||||||
{ label: '80', ticks: 5878 },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PresetsButtonProps {
|
|
||||||
feeAmount: FeeAmount | undefined
|
|
||||||
setRange: (numTicks: number) => void
|
|
||||||
setFullRange: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const PresetButton = ({
|
|
||||||
values: { label, ticks },
|
|
||||||
setRange,
|
|
||||||
}: {
|
|
||||||
values: {
|
|
||||||
label: string
|
|
||||||
ticks: number
|
|
||||||
}
|
|
||||||
setRange: (numTicks: number) => void
|
|
||||||
}) => (
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setRange(ticks)
|
|
||||||
ReactGA.event({
|
|
||||||
category: 'Liquidity',
|
|
||||||
action: 'Preset clicked',
|
|
||||||
label: label,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TYPE.body fontSize={12}>
|
|
||||||
<Trans>+/- {label}%</Trans>
|
|
||||||
</TYPE.body>
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default function PresetsButtons({ feeAmount, setRange, setFullRange }: PresetsButtonProps) {
|
|
||||||
feeAmount = feeAmount ?? FeeAmount.LOW
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AutoRow gap="4px" width="auto">
|
<AutoRow gap="4px" width="auto">
|
||||||
<PresetButton values={RANGES[feeAmount][0]} setRange={setRange} />
|
|
||||||
<PresetButton values={RANGES[feeAmount][1]} setRange={setRange} />
|
|
||||||
<PresetButton values={RANGES[feeAmount][2]} setRange={setRange} />
|
|
||||||
<Button onClick={() => setFullRange()}>
|
<Button onClick={() => setFullRange()}>
|
||||||
<TYPE.body fontSize={12}>
|
<TYPE.body fontSize={12}>
|
||||||
<Trans>Full Range</Trans>
|
<Trans>Full Range</Trans>
|
||||||
|
@ -44,13 +44,13 @@ export default function RangeSelector({
|
|||||||
<AutoColumn gap="md">
|
<AutoColumn gap="md">
|
||||||
<RowBetween>
|
<RowBetween>
|
||||||
<StepCounter
|
<StepCounter
|
||||||
value={ticksAtLimit[Bound.LOWER] ? '0' : leftPrice?.toSignificant(5) ?? ''}
|
value={ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER] ? '0' : leftPrice?.toSignificant(5) ?? ''}
|
||||||
onUserInput={onLeftRangeInput}
|
onUserInput={onLeftRangeInput}
|
||||||
width="48%"
|
width="48%"
|
||||||
decrement={isSorted ? getDecrementLower : getIncrementUpper}
|
decrement={isSorted ? getDecrementLower : getIncrementUpper}
|
||||||
increment={isSorted ? getIncrementLower : getDecrementUpper}
|
increment={isSorted ? getIncrementLower : getDecrementUpper}
|
||||||
decrementDisabled={ticksAtLimit[Bound.LOWER]}
|
decrementDisabled={ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER]}
|
||||||
incrementDisabled={ticksAtLimit[Bound.LOWER]}
|
incrementDisabled={ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER]}
|
||||||
feeAmount={feeAmount}
|
feeAmount={feeAmount}
|
||||||
label={leftPrice ? `${currencyB?.symbol}` : '-'}
|
label={leftPrice ? `${currencyB?.symbol}` : '-'}
|
||||||
title={<Trans>Min Price</Trans>}
|
title={<Trans>Min Price</Trans>}
|
||||||
@ -58,13 +58,13 @@ export default function RangeSelector({
|
|||||||
tokenB={currencyB?.symbol}
|
tokenB={currencyB?.symbol}
|
||||||
/>
|
/>
|
||||||
<StepCounter
|
<StepCounter
|
||||||
value={ticksAtLimit[Bound.UPPER] ? '∞' : rightPrice?.toSignificant(5) ?? ''}
|
value={ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER] ? '∞' : rightPrice?.toSignificant(5) ?? ''}
|
||||||
onUserInput={onRightRangeInput}
|
onUserInput={onRightRangeInput}
|
||||||
width="48%"
|
width="48%"
|
||||||
decrement={isSorted ? getDecrementUpper : getIncrementLower}
|
decrement={isSorted ? getDecrementUpper : getIncrementLower}
|
||||||
increment={isSorted ? getIncrementUpper : getDecrementLower}
|
increment={isSorted ? getIncrementUpper : getDecrementLower}
|
||||||
incrementDisabled={ticksAtLimit[Bound.UPPER]}
|
incrementDisabled={ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER]}
|
||||||
decrementDisabled={ticksAtLimit[Bound.UPPER]}
|
decrementDisabled={ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER]}
|
||||||
feeAmount={feeAmount}
|
feeAmount={feeAmount}
|
||||||
label={rightPrice ? `${currencyB?.symbol}` : '-'}
|
label={rightPrice ? `${currencyB?.symbol}` : '-'}
|
||||||
tokenA={currencyA?.symbol}
|
tokenA={currencyA?.symbol}
|
||||||
|
@ -63,6 +63,7 @@ import { useDerivedPositionInfo } from 'hooks/useDerivedPositionInfo'
|
|||||||
import { PositionPreview } from 'components/PositionPreview'
|
import { PositionPreview } from 'components/PositionPreview'
|
||||||
import FeeSelector from 'components/FeeSelector'
|
import FeeSelector from 'components/FeeSelector'
|
||||||
import RangeSelector from 'components/RangeSelector'
|
import RangeSelector from 'components/RangeSelector'
|
||||||
|
import PresetsButtons from 'components/RangeSelector/PresetsButtons'
|
||||||
import RateToggle from 'components/RateToggle'
|
import RateToggle from 'components/RateToggle'
|
||||||
import { BigNumber } from '@ethersproject/bignumber'
|
import { BigNumber } from '@ethersproject/bignumber'
|
||||||
import { AddRemoveTabs } from 'components/NavigationTabs'
|
import { AddRemoveTabs } from 'components/NavigationTabs'
|
||||||
@ -604,9 +605,11 @@ export default function AddLiquidity({
|
|||||||
currencyA={baseCurrency}
|
currencyA={baseCurrency}
|
||||||
currencyB={quoteCurrency}
|
currencyB={quoteCurrency}
|
||||||
handleRateToggle={() => {
|
handleRateToggle={() => {
|
||||||
|
if (!ticksAtLimit[Bound.LOWER] && !ticksAtLimit[Bound.UPPER]) {
|
||||||
onLeftRangeInput((invertPrice ? priceLower : priceUpper?.invert())?.toSignificant(6) ?? '')
|
onLeftRangeInput((invertPrice ? priceLower : priceUpper?.invert())?.toSignificant(6) ?? '')
|
||||||
onRightRangeInput((invertPrice ? priceUpper : priceLower?.invert())?.toSignificant(6) ?? '')
|
onRightRangeInput((invertPrice ? priceUpper : priceLower?.invert())?.toSignificant(6) ?? '')
|
||||||
onFieldAInput(formattedAmounts[Field.CURRENCY_B] ?? '')
|
onFieldAInput(formattedAmounts[Field.CURRENCY_B] ?? '')
|
||||||
|
}
|
||||||
history.push(
|
history.push(
|
||||||
`/add/${currencyIdB as string}/${currencyIdA as string}${feeAmount ? '/' + feeAmount : ''}`
|
`/add/${currencyIdB as string}/${currencyIdA as string}${feeAmount ? '/' + feeAmount : ''}`
|
||||||
)
|
)
|
||||||
@ -863,6 +866,13 @@ export default function AddLiquidity({
|
|||||||
feeAmount={feeAmount}
|
feeAmount={feeAmount}
|
||||||
ticksAtLimit={ticksAtLimit}
|
ticksAtLimit={ticksAtLimit}
|
||||||
/>
|
/>
|
||||||
|
{!noLiquidity && (
|
||||||
|
<PresetsButtons
|
||||||
|
setFullRange={() => {
|
||||||
|
setShowCapitalEfficiencyWarning(true)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</AutoColumn>
|
</AutoColumn>
|
||||||
</StackedItem>
|
</StackedItem>
|
||||||
|
|
||||||
@ -890,7 +900,7 @@ export default function AddLiquidity({
|
|||||||
Full range positions may earn less fees than concentrated positions. Learn more{' '}
|
Full range positions may earn less fees than concentrated positions. Learn more{' '}
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
style={{ color: theme.yellow3, textDecoration: 'underline' }}
|
style={{ color: theme.yellow3, textDecoration: 'underline' }}
|
||||||
href={''}
|
href={'https://help.uniswap.org/en/articles/5406286-v3-faq-liquidity-providing'}
|
||||||
>
|
>
|
||||||
here
|
here
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
|
@ -542,34 +542,9 @@ export function useRangeHopCallbacks(
|
|||||||
return ''
|
return ''
|
||||||
}, [baseToken, quoteToken, tickUpper, feeAmount, pool])
|
}, [baseToken, quoteToken, tickUpper, feeAmount, pool])
|
||||||
|
|
||||||
const getSetRange = useCallback(
|
|
||||||
(numTicks: number) => {
|
|
||||||
if (baseToken && quoteToken && feeAmount && pool) {
|
|
||||||
// calculate range around current price given `numTicks`
|
|
||||||
const newPriceLower = tickToPrice(
|
|
||||||
baseToken,
|
|
||||||
quoteToken,
|
|
||||||
Math.max(TickMath.MIN_TICK, pool.tickCurrent - numTicks)
|
|
||||||
)
|
|
||||||
const newPriceUpper = tickToPrice(
|
|
||||||
baseToken,
|
|
||||||
quoteToken,
|
|
||||||
Math.min(TickMath.MAX_TICK, pool.tickCurrent + numTicks)
|
|
||||||
)
|
|
||||||
|
|
||||||
return [
|
|
||||||
newPriceLower.toSignificant(5, undefined, Rounding.ROUND_UP),
|
|
||||||
newPriceUpper.toSignificant(5, undefined, Rounding.ROUND_UP),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
return ['', '']
|
|
||||||
},
|
|
||||||
[baseToken, quoteToken, feeAmount, pool]
|
|
||||||
)
|
|
||||||
|
|
||||||
const getSetFullRange = useCallback(() => {
|
const getSetFullRange = useCallback(() => {
|
||||||
dispatch(setFullRange())
|
dispatch(setFullRange())
|
||||||
}, [dispatch])
|
}, [dispatch])
|
||||||
|
|
||||||
return { getDecrementLower, getIncrementLower, getDecrementUpper, getIncrementUpper, getSetRange, getSetFullRange }
|
return { getDecrementLower, getIncrementLower, getDecrementUpper, getIncrementUpper, getSetFullRange }
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user