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'
import { OutlineCard } from 'components/Card'
import { RowBetween } from 'components/Row'
import { Input as NumericalInput } from '../NumericalInput'
import styled, { keyframes, css } from 'styled-components'
import styled, { keyframes } from 'styled-components'
import { TYPE } from 'theme'
import { AutoColumn } from 'components/Column'
import { ButtonSecondary } from 'components/Button'
......@@ -34,11 +34,7 @@ const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boo
border-color: ${({ active, theme }) => active && theme.blue1};
padding: 12px;
${({ pulsing, theme }) =>
pulsing &&
css`
animation: ${pulse(theme.blue1)} 0.8s linear;
`}
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.blue1)} 0.8s linear;
`
const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
......@@ -54,8 +50,8 @@ const ContentWrapper = styled(RowBetween)`
interface StepCounterProps {
value: string
onUserInput: (value: string) => void
getDecrementValue?: () => string
getIncrementValue?: () => string
decrement: () => string
increment: () => string
feeAmount?: FeeAmount
label?: string
width?: string
......@@ -64,13 +60,13 @@ interface StepCounterProps {
const StepCounter = ({
value,
onUserInput,
getDecrementValue,
getIncrementValue,
decrement,
increment,
feeAmount,
label,
width,
locked,
onUserInput,
}: StepCounterProps) => {
// for focus state, styled components doesnt let you select input parent container
const [active, setActive] = useState(false)
......@@ -83,7 +79,7 @@ const StepCounter = ({
const [pulsing, setPulsing] = useState<boolean>(false)
// format fee amount
const feeAmountFormatted = feeAmount ? formattedFeeAmount(feeAmount) : ''
const feeAmountFormatted = feeAmount ? formattedFeeAmount(feeAmount * 2) : ''
const handleOnFocus = () => {
setUseLocalValue(true)
......@@ -92,18 +88,14 @@ const StepCounter = ({
// for button clicks
const handleDecrement = useCallback(() => {
if (getDecrementValue) {
setLocalValue(getDecrementValue())
onUserInput(getDecrementValue())
}
}, [getDecrementValue, onUserInput])
setLocalValue(decrement())
onUserInput(decrement())
}, [decrement, onUserInput])
const handleIncrement = useCallback(() => {
if (getIncrementValue) {
setLocalValue(getIncrementValue())
onUserInput(getIncrementValue())
}
}, [getIncrementValue, onUserInput])
setLocalValue(increment())
onUserInput(increment())
}, [increment, onUserInput])
const handleOnBlur = useCallback(() => {
setUseLocalValue(false)
......@@ -138,7 +130,7 @@ const StepCounter = ({
/>
</ContentWrapper>
{label && <TYPE.label fontSize="12px">{label}</TYPE.label>}
{getDecrementValue && getIncrementValue && !locked ? (
{!locked ? (
<RowBetween>
<SmallButton onClick={handleDecrement}>
<TYPE.main fontSize="12px">-{feeAmountFormatted}%</TYPE.main>
......
......@@ -159,10 +159,10 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
const formattedAmount1 = formatTokenAmount(amount1, 4)
// prices
const price0Lower = position ? position.token0PriceLower : undefined
const price0Upper = position ? position.token0PriceUpper : undefined
const price1Lower = price0Upper ? price0Upper.invert() : undefined
const price1Upper = price0Lower ? price0Lower.invert() : undefined
const price0Lower = position?.token0PriceLower
const price0Upper = position?.token0PriceUpper
const price1Lower = price0Lower?.invert()
const price1Upper = price0Upper?.invert()
// fees
const [feeValue0, feeValue1] = useV3PositionFees(pool ?? undefined, positionDetails)
......@@ -205,13 +205,13 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
<>
<DataLineItem>
{formatPrice(price0Lower, 4)} <DoubleArrow></DoubleArrow> {formatPrice(price0Upper, 4)}{' '}
{currency0?.symbol}&nbsp;/&nbsp;
{currency1?.symbol}
{currency1?.symbol}&nbsp;/&nbsp;
{currency0?.symbol}
</DataLineItem>
<DataLineItem>
{formatPrice(price1Lower, 4)} <DoubleArrow></DoubleArrow> {formatPrice(price1Upper, 4)}{' '}
{currency1?.symbol}&nbsp;/&nbsp;
{currency0?.symbol}
{currency0?.symbol}&nbsp;/&nbsp;
{currency1?.symbol}
</DataLineItem>
</>
) : (
......
import React from 'react'
import { Currency, Price } from '@uniswap/sdk-core'
import StepCounter from 'components/InputStepCounter/InputStepCounter'
import { RowBetween } from 'components/Row'
import React from 'react'
import { useActiveWeb3React } from 'hooks'
import { wrappedCurrency } from 'utils/wrappedCurrency'
// currencyA is the base token
export default function RangeSelector({
priceLower,
priceUpper,
onUpperRangeInput,
onLowerRangeInput,
getLowerDecrement,
getLowerIncrement,
getUpperDecrement,
getUpperIncrement,
onLeftRangeInput,
onRightRangeInput,
getDecrementLower,
getIncrementLower,
getDecrementUpper,
getIncrementUpper,
currencyA,
currencyB,
feeAmount,
fixedValueLower,
fixedValueUpper,
}: {
priceLower?: Price
priceUpper?: Price
getLowerIncrement?: () => string
getLowerDecrement?: () => string
getUpperIncrement?: () => string
getUpperDecrement?: () => string
onLowerRangeInput: (typedValue: string) => void
onUpperRangeInput: (typedValue: string) => void
getDecrementLower: () => string
getIncrementLower: () => string
getDecrementUpper: () => string
getIncrementUpper: () => string
onLeftRangeInput: (typedValue: string) => void
onRightRangeInput: (typedValue: string) => void
currencyA?: Currency | null
currencyB?: Currency | null
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 (
<RowBetween>
<StepCounter
value={fixedValueLower ?? priceLower?.toSignificant(5) ?? ''}
onUserInput={onLowerRangeInput}
value={leftPrice?.toSignificant(5) ?? ''}
onUserInput={onLeftRangeInput}
width="48%"
getIncrementValue={getLowerIncrement}
getDecrementValue={getLowerDecrement}
decrement={isSorted ? getDecrementLower : getIncrementUpper}
increment={isSorted ? getIncrementLower : getDecrementUpper}
feeAmount={feeAmount}
label={
priceLower && currencyA && currencyB
? `${priceLower.toSignificant(4)} ${currencyB.symbol} / 1 ${currencyA.symbol}`
: '-'
}
locked={!!fixedValueLower}
label={leftPrice ? `${leftPrice.toSignificant(5)} ${currencyB?.symbol}/${currencyA?.symbol}` : '-'}
/>
<StepCounter
value={fixedValueUpper ?? priceUpper?.toSignificant(5) ?? ''}
onUserInput={onUpperRangeInput}
value={rightPrice?.toSignificant(5) ?? ''}
onUserInput={onRightRangeInput}
width="48%"
getDecrementValue={getUpperDecrement}
getIncrementValue={getUpperIncrement}
decrement={isSorted ? getDecrementUpper : getIncrementLower}
increment={isSorted ? getIncrementUpper : getDecrementLower}
feeAmount={feeAmount}
label={
priceUpper && currencyA && currencyB
? `${priceUpper.toSignificant(4)} ${currencyB?.symbol} / 1 ${currencyA?.symbol}`
: '-'
}
locked={!!fixedValueUpper}
label={rightPrice ? `${rightPrice.toSignificant(5)} ${currencyB?.symbol}/${currencyA?.symbol}` : '-'}
/>
</RowBetween>
)
......
This diff is collapsed.
......@@ -29,10 +29,10 @@ import { useUserSlippageTolerance } from 'state/user/hooks'
import ReactGA from 'react-ga'
import { TransactionResponse } from '@ethersproject/providers'
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 { useTranslation } from 'react-i18next'
import { ChevronDown } from 'react-feather'
import { AlertTriangle, ChevronDown } from 'react-feather'
import FeeSelector from 'components/FeeSelector'
import RangeSelector from 'components/RangeSelector'
import RateToggle from 'components/RateToggle'
......@@ -42,6 +42,7 @@ import { splitSignature } from '@ethersproject/bytes'
import { BigNumber } from '@ethersproject/bignumber'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import { formatTokenAmount } from 'utils/formatTokenAmount'
import useTheme from 'hooks/useTheme'
const ZERO = JSBI.BigInt(0)
......@@ -104,6 +105,7 @@ function V2PairMigration({
}) {
const { t } = useTranslation()
const { chainId, account, library } = useActiveWeb3React()
const theme = useTheme()
const deadline = useTransactionDeadline() // custom from users settings
const blockTimestamp = useCurrentBlockTimestamp()
......@@ -141,31 +143,39 @@ function V2PairMigration({
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
const { ticks, pricesAtTicks } = useDerivedMintInfo(
invertPrice ? token1 : token0,
invertPrice ? token0 : token1,
feeAmount
const [baseToken, setBaseToken] = useState(token0)
const { ticks, pricesAtTicks, invertPrice, invalidRange, outOfRange } = useDerivedMintInfo(
token0,
token1,
feeAmount,
baseToken
)
// get value and prices at ticks
const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks
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
const tick = pool?.tickCurrent ?? priceToClosestTick(v2SpotPrice)
// the price is either the current v3 price, or the price at the tick
const sqrtPrice = pool?.sqrtRatioX96 ?? TickMath.getSqrtRatioAtTick(tick)
const position =
typeof tickLower === 'number' && typeof tickUpper === 'number'
typeof tickLower === 'number' && typeof tickUpper === 'number' && !invalidRange
? Position.fromAmounts({
pool: pool ?? new Pool(token0, token1, feeAmount, sqrtPrice, 0, tick, []),
tickLower: invertPrice ? tickUpper : tickLower,
tickUpper: invertPrice ? tickLower : tickUpper,
tickLower,
tickUpper,
amount0: token0Value.raw,
amount1: token1Value.raw,
})
......@@ -340,8 +350,8 @@ function V2PairMigration({
token0: token0.address,
token1: token1.address,
fee: feeAmount,
tickLower: invertPrice ? tickUpper : tickLower,
tickUpper: invertPrice ? tickLower : tickUpper,
tickLower,
tickUpper,
amount0Min: `0x${v3Amount0Min.raw.toString(16)}`,
amount1Min: `0x${v3Amount1Min.raw.toString(16)}`,
recipient: account,
......@@ -379,7 +389,6 @@ function V2PairMigration({
token1,
feeAmount,
pairBalance,
invertPrice,
tickLower,
tickUpper,
sqrtPrice,
......@@ -475,9 +484,9 @@ function V2PairMigration({
currencyA={invertPrice ? token1 : token0}
currencyB={invertPrice ? token0 : token1}
handleRateToggle={() => {
onLowerRangeInput('')
onUpperRangeInput('')
setInvertPrice((invertPrice) => !invertPrice)
onLeftRangeInput('')
onRightRangeInput('')
setBaseToken((base) => (base.equals(token0) ? token1 : token0))
}}
/>
</RowBetween>
......@@ -485,12 +494,39 @@ function V2PairMigration({
<RangeSelector
priceLower={priceLower}
priceUpper={priceUpper}
onLowerRangeInput={onLowerRangeInput}
onUpperRangeInput={onUpperRangeInput}
getDecrementLower={getDecrementLower}
getIncrementLower={getIncrementLower}
getDecrementUpper={getDecrementUpper}
getIncrementUpper={getIncrementUpper}
onLeftRangeInput={onLeftRangeInput}
onRightRangeInput={onRightRangeInput}
currencyA={invertPrice ? token1 : token0}
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>
{v3Amount0Min && v3Amount1Min ? (
<LiquidityInfo token0Amount={v3Amount0Min} token1Amount={v3Amount1Min} />
......@@ -506,6 +542,7 @@ function V2PairMigration({
signatureData !== null ||
!v3Amount0Min ||
!v3Amount1Min ||
invalidRange ||
confirmingMigration
}
onClick={approve}
......@@ -526,6 +563,7 @@ function V2PairMigration({
disabled={
!v3Amount0Min ||
!v3Amount1Min ||
invalidRange ||
(approval !== ApprovalState.APPROVED && signatureData === null) ||
confirmingMigration ||
isMigrationPending ||
......
......@@ -122,10 +122,10 @@ export function PositionPage({
return undefined
}, [liquidity, pool, tickLower, tickUpper])
const price0Lower = position ? position.token0PriceLower : undefined
const price0Upper = position ? position.token0PriceUpper : undefined
const price1Lower = price0Upper ? price0Upper.invert() : undefined
const price1Upper = price0Lower ? price0Lower.invert() : undefined
const price0Lower = position?.token0PriceLower
const price0Upper = position?.token0PriceUpper
const price1Lower = price0Lower?.invert()
const price1Upper = price0Upper?.invert()
// check if price is within range
const outOfRange: boolean =
......@@ -322,13 +322,13 @@ export function PositionPage({
<RowFixed>
<TYPE.label>{price0Lower?.toSignificant(4)}</TYPE.label>
<TYPE.label ml="10px">
{currency0?.symbol} / {currency1?.symbol}
{currency1?.symbol} / {currency0?.symbol}
</TYPE.label>
</RowFixed>
<RowFixed>
<TYPE.label>{price1Lower?.toSignificant(4)}</TYPE.label>
<TYPE.label ml="10px">
{currency1?.symbol} / {currency0?.symbol}
{currency0?.symbol} / {currency1?.symbol}
</TYPE.label>
</RowFixed>
<DarkBadge>
......@@ -342,17 +342,17 @@ export function PositionPage({
</DarkGreyCard>
<DarkGreyCard width="49%">
<AutoColumn gap="sm" justify="flex-start">
<TYPE.main>Lower Limit</TYPE.main>
<TYPE.main>Upper Limit</TYPE.main>
<RowFixed>
<TYPE.label>{price0Upper?.toSignificant(4)}</TYPE.label>
<TYPE.label ml="10px">
{currency0?.symbol} / {currency1?.symbol}
{currency1?.symbol} / {currency0?.symbol}
</TYPE.label>
</RowFixed>
<RowFixed>
<TYPE.label>{price1Upper?.toSignificant(4)}</TYPE.label>
<TYPE.label ml="10px">
{currency1?.symbol} / {currency0?.symbol}
{currency0?.symbol} / {currency1?.symbol}
</TYPE.label>
</RowFixed>
<DarkBadge>
......
......@@ -17,7 +17,7 @@ export enum RangeType {
}
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 typeLeftRangeInput = createAction<{ typedValue: string }>('mint/typeLeftRangeInput')
export const typeRightRangeInput = createAction<{ typedValue: string }>('mint/typeRightRangeInput')
export const resetMintState = createAction<void>('mint/resetMintState')
This diff is collapsed.
......@@ -3,9 +3,9 @@ import {
Field,
resetMintState,
typeInput,
typeLowerRangeInput,
typeUpperRangeInput,
typeStartPriceInput,
typeLeftRangeInput,
typeRightRangeInput,
} from './actions'
export interface MintState {
......@@ -13,8 +13,8 @@ export interface MintState {
readonly typedValue: string
readonly otherTypedValue: string // for the case when there's no liquidity
readonly startPriceTypedValue: string // for the case when there's no liquidity
readonly lowerRangeTypedValue: string
readonly upperRangeTypedValue: string
readonly leftRangeTypedValue: string
readonly rightRangeTypedValue: string
}
export const initialState: MintState = {
......@@ -22,29 +22,29 @@ export const initialState: MintState = {
typedValue: '',
otherTypedValue: '',
startPriceTypedValue: '',
lowerRangeTypedValue: '',
upperRangeTypedValue: '',
leftRangeTypedValue: '',
rightRangeTypedValue: '',
}
export default createReducer<MintState>(initialState, (builder) =>
builder
.addCase(resetMintState, () => initialState)
.addCase(typeLowerRangeInput, (state, { payload: { typedValue } }) => {
.addCase(typeStartPriceInput, (state, { payload: { typedValue } }) => {
return {
...state,
lowerRangeTypedValue: typedValue,
startPriceTypedValue: typedValue,
}
})
.addCase(typeUpperRangeInput, (state, { payload: { typedValue } }) => {
.addCase(typeLeftRangeInput, (state, { payload: { typedValue } }) => {
return {
...state,
upperRangeTypedValue: typedValue,
leftRangeTypedValue: typedValue,
}
})
.addCase(typeStartPriceInput, (state, { payload: { typedValue } }) => {
.addCase(typeRightRangeInput, (state, { payload: { typedValue } }) => {
return {
...state,
startPriceTypedValue: typedValue,
rightRangeTypedValue: typedValue,
}
})
.addCase(typeInput, (state, { payload: { field, typedValue, noLiquidity } }) => {
......
......@@ -18,9 +18,10 @@ export function tryParseTick(
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)
// this function is agnostic to the base, will always return the correct tick
const tick = priceToClosestTick(price)
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