Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

178 enhance the add liquidity page with price range settings options #190

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions src/components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -344,19 +344,22 @@ const ResponsiveCheck = styled(Check)`
size: 13px;
`

export function ButtonRadioChecked({ active = false, children, ...rest }: { active?: boolean } & ButtonProps) {
const theme = useTheme()

export function ButtonRadioChecked({
active = false,
justifyContent,
children,
...rest
}: { active?: boolean } & ButtonProps) {
if (!active) {
return (
<ButtonOutlined $borderRadius="12px" padding="12px 8px" {...rest}>
<RowBetween>{children}</RowBetween>
<RowBetween style={{ justifyContent: justifyContent ?? 'flex-start' }}>{children}</RowBetween>
</ButtonOutlined>
)
}
return (
<ActiveOutlined {...rest} padding="12px 8px" $borderRadius="12px">
<RowBetween>
<RowBetween style={{ justifyContent: justifyContent ?? 'flex-start' }}>
{children}
<CheckboxWrapper>
{/* <Circle>
Expand Down
2 changes: 1 addition & 1 deletion src/components/FeeSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const FocusedOutlineCard = styled(Card)<{ pulsing: boolean }>`
align-self: center;
`

const Select = styled.div`
export const Select = styled.div`
align-items: flex-start;
display: grid;
grid-auto-flow: column;
Expand Down
46 changes: 36 additions & 10 deletions src/components/LiquidityChartRangeInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ThemedText } from 'theme/components'
import { Chart } from './Chart'
import { useDensityChartData } from './hooks'
import { ZoomLevels } from './types'
import JSBI from 'jsbi'

const ZOOM_LEVELS: Record<FeeAmount, ZoomLevels> = {
[FeeAmount.LOWEST]: {
Expand Down Expand Up @@ -66,6 +67,7 @@ function InfoBox({ message, icon }: { message?: ReactNode; icon: ReactNode }) {
}

export default function LiquidityChartRangeInput({
rangePercentage,
currencyA,
currencyB,
feeAmount,
Expand All @@ -77,6 +79,7 @@ export default function LiquidityChartRangeInput({
onRightRangeInput,
interactive,
}: {
rangePercentage: number | null
currencyA?: Currency
currencyB?: Currency
feeAmount?: FeeAmount
Expand Down Expand Up @@ -134,13 +137,34 @@ export default function LiquidityChartRangeInput({
interactive = interactive && Boolean(formattedData?.length)

const brushDomain: [number, number] | undefined = useMemo(() => {
const leftPrice = isSorted ? priceLower : priceUpper?.invert()
const rightPrice = isSorted ? priceUpper : priceLower?.invert()
if (priceLower && priceUpper && price) {
if (!rangePercentage) {
const leftPrice = isSorted ? priceLower : priceUpper?.invert()
const rightPrice = isSorted ? priceUpper : priceLower?.invert()
return [parseFloat(leftPrice?.toSignificant(6)), parseFloat(rightPrice?.toSignificant(6))]
} else {
const percentage = JSBI.BigInt(rangePercentage) // 20%
const base = JSBI.BigInt(100) // 100 to represent the whole
// Calculate the percentage value
const percentageValue = JSBI.divide(JSBI.multiply(JSBI.BigInt(Math.floor(price)), percentage), base)

const leftPriceWithPercentage = JSBI.subtract(JSBI.BigInt(Math.floor(price)), percentageValue)
const rightPriceWithPercentage = JSBI.add(JSBI.BigInt(Math.floor(price)), percentageValue)

// Combine with the base number
const leftPriceWithScaledPercentage =
Number(leftPriceWithPercentage) - Math.round(Math.random() * 10000) / 10000
const rightPriceWithScaledPercentage =
Number(rightPriceWithPercentage) + Math.round(Math.random() * 10000) / 10000
return [
parseFloat(leftPriceWithScaledPercentage.toString()),
parseFloat(rightPriceWithScaledPercentage.toString()),
]
}
}

return leftPrice && rightPrice
? [parseFloat(leftPrice?.toSignificant(6)), parseFloat(rightPrice?.toSignificant(6))]
: undefined
}, [isSorted, priceLower, priceUpper])
return undefined
}, [priceLower, priceUpper, rangePercentage])

const brushLabelValue = useCallback(
(d: 'w' | 'e', x: number) => {
Expand All @@ -157,10 +181,10 @@ export default function LiquidityChartRangeInput({
)

const isUninitialized = !currencyA || !currencyB || (formattedData === undefined && !isLoading)

console.log(formattedData, currencyA, currencyB)
return (
<AutoColumn gap="md" style={{ minHeight: '200px' }}>
{isUninitialized ? (
{/* {isUninitialized ? (
<InfoBox
message={<Trans>Your position will appear here.</Trans>}
icon={<Inbox size={56} stroke={theme.neutral1} />}
Expand All @@ -177,10 +201,11 @@ export default function LiquidityChartRangeInput({
message={<Trans>There is no liquidity data.</Trans>}
icon={<BarChart2 size={56} stroke={theme.neutral2} />}
/>
) : (
) : ( */}
{price && (
<ChartWrapper>
<Chart
data={{ series: formattedData, current: price }}
data={{ series: [], current: price }}
dimensions={{ width: 560, height: 200 }}
margins={{ top: 10, right: 2, bottom: 20, left: 0 }}
styles={{
Expand All @@ -203,6 +228,7 @@ export default function LiquidityChartRangeInput({
/>
</ChartWrapper>
)}
{/* )} */}
</AutoColumn>
)
}
41 changes: 37 additions & 4 deletions src/components/RangeSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import { Trans } from '@lingui/macro'
import { Currency, Price, Token } from '@vnaysn/jediswap-sdk-core'
import StepCounter from 'components/InputStepCounter/InputStepCounter'
import { AutoRow } from 'components/Row'
import JSBI from 'jsbi'
import { useMemo } from 'react'
import { Bound } from 'state/mint/v3/actions'

// currencyA is the base token
export default function RangeSelector({
price,
rangePercentage,
priceLower,
priceUpper,
onLeftRangeInput,
Expand All @@ -15,6 +19,8 @@ export default function RangeSelector({
feeAmount,
ticksAtLimit,
}: {
price: any
rangePercentage: number | null
priceLower?: Price<Token, Token>
priceUpper?: Price<Token, Token>
onLeftRangeInput: (typedValue: string) => void
Expand All @@ -28,13 +34,40 @@ export default function RangeSelector({
const tokenB = (currencyB ?? undefined)?.wrapped
const isSorted = tokenA && tokenB && tokenA.sortsBefore(tokenB)

const leftPrice = isSorted ? priceLower : priceUpper?.invert()
const rightPrice = isSorted ? priceUpper : priceLower?.invert()
const { leftPrice, rightPrice } = useMemo(() => {
if (priceLower && priceUpper) {
if (!rangePercentage) {
const leftPrice = isSorted ? priceLower : priceUpper?.invert()
const rightPrice = isSorted ? priceUpper : priceLower?.invert()
return { leftPrice: leftPrice.toSignificant(8), rightPrice: rightPrice.toSignificant(8) }
} else {
const percentage = JSBI.BigInt(rangePercentage) // 20%
const base = JSBI.BigInt(100) // 100 to represent the whole
// Calculate the percentage value
const percentageValue = JSBI.divide(JSBI.multiply(JSBI.BigInt(parseInt(price)), percentage), base)

const leftPriceWithPercentage = JSBI.subtract(JSBI.BigInt(parseInt(price)), percentageValue)
const rightPriceWithPercentage = JSBI.add(JSBI.BigInt(parseInt(price)), percentageValue)

// Combine with the base number
const leftPriceWithScaledPercentage =
Number(leftPriceWithPercentage) - Math.round(Math.random() * 10000) / 10000
const rightPriceWithScaledPercentage =
Number(rightPriceWithPercentage) + Math.round(Math.random() * 10000) / 10000
return {
leftPrice: leftPriceWithScaledPercentage.toString(),
rightPrice: rightPriceWithScaledPercentage.toString(),
}
}
}

return { leftPrice: undefined, rightPrice: undefined }
}, [priceLower, priceUpper, rangePercentage])

return (
<AutoRow gap="md">
<StepCounter
value={ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER] ? '0' : leftPrice?.toSignificant(8) ?? ''}
value={ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER] ? '0' : leftPrice ?? ''}
onUserInput={onLeftRangeInput}
feeAmount={feeAmount}
label={leftPrice ? `${currencyB?.symbol}` : '-'}
Expand All @@ -43,7 +76,7 @@ export default function RangeSelector({
tokenB={currencyB?.symbol}
/>
<StepCounter
value={ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER] ? '∞' : rightPrice?.toSignificant(8) ?? ''}
value={ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER] ? '∞' : rightPrice ?? ''}
onUserInput={onRightRangeInput}
feeAmount={feeAmount}
label={rightPrice ? `${currencyB?.symbol}` : '-'}
Expand Down
79 changes: 60 additions & 19 deletions src/pages/AddLiquidity/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ import {
import { ThemedText } from 'theme/components'
import { addressesAreEquivalent } from 'utils/addressesAreEquivalent'
import { WrongChainError } from 'utils/errors'
import { ButtonError, ButtonLight, ButtonPrimary, ButtonText } from '../../components/Button'
import { ButtonError, ButtonLight, ButtonPrimary, ButtonRadioChecked, ButtonText } from '../../components/Button'
import { BlueCard, LightCard, OutlineCard, YellowCard } from '../../components/Card'
import { AutoColumn } from '../../components/Column'
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import FeeSelector from '../../components/FeeSelector'
import FeeSelector, { Select } from '../../components/FeeSelector'
import HoverInlineText from '../../components/HoverInlineText'
import LiquidityChartRangeInput from '../../components/LiquidityChartRangeInput'
import { AddRemoveTabs } from '../../components/NavigationTabs'
Expand Down Expand Up @@ -86,6 +86,12 @@ export default function AddLiquidityWrapper() {
}
}

export const SelectRangePercentage = styled(Select)`
justify-items: center;
max-width: 50%;
width: 100%;
`

function AddLiquidity() {
const navigate = useNavigate()
const {
Expand All @@ -104,6 +110,7 @@ function AddLiquidity() {
const toggleWalletDrawer = useToggleAccountDrawer() // toggle wallet when disconnected
const positionManager = useV3NFTPositionManagerContract()
const parsedTokenId = tokenId ? parseInt(tokenId) : undefined
const rangePercentages: number[] = [5, 10, 20]

// check for existing position if tokenId in url
const { position: existingPositionDetails, loading: positionLoading } = useV3PosFromTokenId(parsedTokenId)
Expand All @@ -123,6 +130,7 @@ function AddLiquidity() {
const { independentField, typedValue, startPriceTypedValue } = useV3MintState()

const [showWarning, setShowWarning] = useState(true)
const [rangePercentage, setRangePercentage] = useState<number | null>(null)
const [mintCallData, setMintCallData] = useState<Call[]>([])

const {
Expand Down Expand Up @@ -156,7 +164,7 @@ function AddLiquidity() {
const invalidPool = false

const { onFieldAInput, onFieldBInput, onLeftRangeInput, onRightRangeInput, onStartPriceInput } =
useV3MintActionHandlers(noLiquidity)
useV3MintActionHandlers(noLiquidity, setRangePercentage)

const { writeAsync, data: txData } = useContractWrite({
calls: mintCallData,
Expand Down Expand Up @@ -394,6 +402,7 @@ function AddLiquidity() {

const handleFeePoolSelect = useCallback(
(newFeeAmount: FeeAmount) => {
setRangePercentage(null)
onLeftRangeInput('')
onRightRangeInput('')
navigate(`/add/${currencyIdA}/${currencyIdB}/${newFeeAmount}`)
Expand All @@ -415,6 +424,7 @@ function AddLiquidity() {
const addIsUnsupported = useIsSwapUnsupported(currencies?.CURRENCY_A, currencies?.CURRENCY_B)

const clearAll = useCallback(() => {
setRangePercentage(null)
onFieldAInput('')
onFieldBInput('')
onLeftRangeInput('')
Expand Down Expand Up @@ -530,10 +540,9 @@ function AddLiquidity() {
[usdcValueCurrencyB]
)

// const owner = useSingleCallResult(tokenId ? positionManager : null, 'ownerOf', [tokenId]).result?.[0]
// const ownsNFT =
// addressesAreEquivalent(owner, account) || addressesAreEquivalent(existingPositionDetails?.operator, account)
// const showOwnershipWarning = Boolean(hasExistingPosition && account && !ownsNFT)
const handleRange = (p: number) => {
setRangePercentage(p)
}

return (
<>
Expand Down Expand Up @@ -684,19 +693,11 @@ function AddLiquidity() {
</RowFixed>
)}
</RowBetween>
{/* <LiquidityChartRangeInput
currencyA={baseCurrency ?? undefined}
currencyB={quoteCurrency ?? undefined}
feeAmount={feeAmount}
ticksAtLimit={ticksAtLimit}
price={price ? parseFloat((invertPrice ? price.invert() : price).toSignificant(8)) : undefined}
priceLower={priceLower}
priceUpper={priceUpper}
onLeftRangeInput={onLeftRangeInput}
onRightRangeInput={onRightRangeInput}
interactive={!hasExistingPosition}
/> */}
<RangeSelector
price={
price ? (invertPrice ? price.invert().toSignificant(6) : price.toSignificant(6)) : undefined
}
rangePercentage={rangePercentage}
priceLower={priceLower}
priceUpper={priceUpper}
onLeftRangeInput={onLeftRangeInput}
Expand Down Expand Up @@ -757,6 +758,46 @@ function AddLiquidity() {
</Trans>
</AutoColumn>
)}
{baseCurrency && quoteCurrency && feeAmount && (
<div style={{ display: 'block', margin: '0 auto' }}>
<SelectRangePercentage>
{rangePercentages.map((percentage) => (
<ButtonRadioChecked
active={rangePercentage === percentage}
justifyContent="center"
onClick={() => handleRange(percentage)}
style={{
border: '1px solid #444',
borderRadius: '8px',
justifyContent: 'center',
width: 80,
height: 25,
}}
>
<AutoColumn gap="sm" justify="flex-start">
{percentage}%
</AutoColumn>
</ButtonRadioChecked>
))}
</SelectRangePercentage>
</div>
)}

<LiquidityChartRangeInput
rangePercentage={rangePercentage}
currencyA={baseCurrency ?? undefined}
currencyB={quoteCurrency ?? undefined}
feeAmount={feeAmount}
ticksAtLimit={ticksAtLimit}
price={
price ? parseFloat((invertPrice ? price.invert() : price).toSignificant(8)) : undefined
}
priceLower={priceLower}
priceUpper={priceUpper}
onLeftRangeInput={onLeftRangeInput}
onRightRangeInput={onRightRangeInput}
interactive={!hasExistingPosition}
/>
</>
) : (
<AutoColumn gap="md">
Expand Down
7 changes: 6 additions & 1 deletion src/state/mint/v3/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ export function useV3MintState(): AppState['mintV3'] {
return useAppSelector((state) => state.mintV3)
}

export function useV3MintActionHandlers(noLiquidity: boolean | undefined): {
export function useV3MintActionHandlers(
noLiquidity: boolean | undefined,
setRangePercentage: any
): {
onFieldAInput: (typedValue: string) => void
onFieldBInput: (typedValue: string) => void
onLeftRangeInput: (typedValue: string) => void
Expand All @@ -66,6 +69,7 @@ export function useV3MintActionHandlers(noLiquidity: boolean | undefined): {

const onLeftRangeInput = useCallback(
(typedValue: string) => {
setRangePercentage(null)
dispatch(typeLeftRangeInput({ typedValue }))
const paramMinPrice = searchParams.get('minPrice')
if (!paramMinPrice || (paramMinPrice && paramMinPrice !== typedValue)) {
Expand All @@ -78,6 +82,7 @@ export function useV3MintActionHandlers(noLiquidity: boolean | undefined): {

const onRightRangeInput = useCallback(
(typedValue: string) => {
setRangePercentage(null)
dispatch(typeRightRangeInput({ typedValue }))
const paramMaxPrice = searchParams.get('maxPrice')
if (!paramMaxPrice || (paramMaxPrice && paramMaxPrice !== typedValue)) {
Expand Down