Commit cd3c4846 authored by Noah Zinsmeister's avatar Noah Zinsmeister

separate v2 + v3 mint reducers

reset v3 mint state on migrate mount/unmount
parent 40f0e619
......@@ -10,6 +10,7 @@ import Settings from '../Settings'
import { useDispatch } from 'react-redux'
import { AppDispatch } from 'state'
import { resetMintState } from 'state/mint/actions'
import { resetMintState as resetMintV3State } from 'state/mint/v3/actions'
import { TYPE } from 'theme'
import useTheme from 'hooks/useTheme'
......@@ -105,7 +106,11 @@ export function AddRemoveTabs({
<HistoryLink
to={'/pool' + (!!positionID ? `/${positionID.toString()}` : '')}
onClick={() => {
adding && dispatch(resetMintState())
if (adding) {
// not 100% sure both of these are needed
dispatch(resetMintState())
dispatch(resetMintV3State())
}
}}
>
<StyledArrowLeft stroke={theme.text2} />
......
......@@ -5,7 +5,7 @@ import { ThemeContext } from 'styled-components'
import { AutoColumn } from '../../components/Column'
import { AutoRow } from '../../components/Row'
import { ONE_BIPS } from '../../constants'
import { Field } from '../../state/mint/actions'
import { Field } from '../../state/mint/v3/actions'
import { TYPE } from '../../theme'
export function PoolPriceBar({
......
import React from 'react'
import { Field } from '../../state/mint/actions'
import { Field } from '../../state/mint/v3/actions'
import { AutoColumn } from 'components/Column'
import Card from 'components/Card'
import styled from 'styled-components'
......
......@@ -21,7 +21,7 @@ import { useCurrency } from '../../hooks/Tokens'
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
import useTransactionDeadline from '../../hooks/useTransactionDeadline'
import { useWalletModalToggle } from '../../state/application/hooks'
import { Field, Bound } from '../../state/mint/actions'
import { Field, Bound } from '../../state/mint/v3/actions'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { useIsExpertMode, useUserSlippageTolerance } from '../../state/user/hooks'
......@@ -33,7 +33,12 @@ import { currencyId } from '../../utils/currencyId'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
import { DynamicSection, CurrencyDropdown, StyledInput, Wrapper, ScrollablePage } from './styled'
import { useTranslation } from 'react-i18next'
import { useMintState, useMintActionHandlers, useDerivedMintInfo, useRangeHopCallbacks } from 'state/mint/hooks'
import {
useV3MintState,
useV3MintActionHandlers,
useRangeHopCallbacks,
useV3DerivedMintInfo,
} from 'state/mint/v3/hooks'
import { FeeAmount, NonfungiblePositionManager } from '@uniswap/v3-sdk'
import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES } from 'constants/v3'
import { useV3PositionFromTokenId } from 'hooks/useV3Positions'
......@@ -98,7 +103,7 @@ export default function AddLiquidity({
}, [currencyA, currencyB])
// mint state
const { independentField, typedValue, startPriceTypedValue } = useMintState()
const { independentField, typedValue, startPriceTypedValue } = useV3MintState()
const {
ticks,
......@@ -117,7 +122,7 @@ export default function AddLiquidity({
depositADisabled,
depositBDisabled,
invertPrice,
} = useDerivedMintInfo(
} = useV3DerivedMintInfo(
currencyA ?? undefined,
currencyB ?? undefined,
feeAmount,
......@@ -131,7 +136,7 @@ export default function AddLiquidity({
onLeftRangeInput,
onRightRangeInput,
onStartPriceInput,
} = useMintActionHandlers(noLiquidity)
} = useV3MintActionHandlers(noLiquidity)
const isValid = !errorMessage && !invalidRange
......
......@@ -27,7 +27,7 @@ import { useIsSwapUnsupported } from '../../hooks/useIsSwapUnsupported'
import useTransactionDeadline from '../../hooks/useTransactionDeadline'
import { useWalletModalToggle } from '../../state/application/hooks'
import { Field } from '../../state/mint/actions'
import { useMintActionHandlers, useMintState } from '../../state/mint/hooks'
import { useDerivedMintInfo, useMintActionHandlers, useMintState } from '../../state/mint/hooks'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { useIsExpertMode, useUserSlippageTolerance } from '../../state/user/hooks'
......@@ -41,7 +41,6 @@ import { ConfirmAddModalBottom } from './ConfirmAddModalBottom'
import { currencyId } from '../../utils/currencyId'
import { PoolPriceBar } from './PoolPriceBar'
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
import { useV2DerivedMintInfo } from 'state/mint/v2'
export default function AddLiquidity({
match: {
......@@ -79,7 +78,7 @@ export default function AddLiquidity({
liquidityMinted,
poolTokenPercentage,
error,
} = useV2DerivedMintInfo(currencyA ?? undefined, currencyB ?? undefined)
} = useDerivedMintInfo(currencyA ?? undefined, currencyB ?? undefined)
const { onFieldAInput, onFieldBInput } = useMintActionHandlers(noLiquidity)
......
import React, { useCallback, useMemo, useState } from 'react'
import React, { useCallback, useMemo, useState, useEffect } from 'react'
import { Fraction, Price, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core'
import { FACTORY_ADDRESS, JSBI } from '@uniswap/v2-sdk'
import { Redirect, RouteComponentProps } from 'react-router'
......@@ -30,8 +30,8 @@ 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, useRangeHopCallbacks } from 'state/mint/hooks'
import { Bound } from 'state/mint/actions'
import { useV3DerivedMintInfo, useRangeHopCallbacks, useV3MintActionHandlers } from 'state/mint/v3/hooks'
import { Bound, resetMintState } from 'state/mint/v3/actions'
import { useTranslation } from 'react-i18next'
import { AlertCircle, AlertTriangle, ArrowDown } from 'react-feather'
import FeeSelector from 'components/FeeSelector'
......@@ -44,6 +44,8 @@ import useTheme from 'hooks/useTheme'
import { unwrappedToken } from 'utils/wrappedCurrency'
import DoubleCurrencyLogo from 'components/DoubleLogo'
import Badge, { BadgeVariant } from 'components/Badge'
import { useDispatch } from 'react-redux'
import { AppDispatch } from 'state'
const ZERO = JSBI.BigInt(0)
......@@ -156,7 +158,7 @@ function V2PairMigration({
// the following is a small hack to get access to price range data/input handlers
const [baseToken, setBaseToken] = useState(token0)
const { ticks, pricesAtTicks, invertPrice, invalidRange, outOfRange } = useDerivedMintInfo(
const { ticks, pricesAtTicks, invertPrice, invalidRange, outOfRange } = useV3DerivedMintInfo(
token0,
token1,
feeAmount,
......@@ -175,7 +177,7 @@ function V2PairMigration({
tickUpper
)
const { onLeftRangeInput, onRightRangeInput } = useMintActionHandlers(noLiquidity)
const { onLeftRangeInput, onRightRangeInput } = useV3MintActionHandlers(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)
......@@ -585,6 +587,15 @@ export default function MigrateV2Pair({
params: { address },
},
}: RouteComponentProps<{ address: string }>) {
// reset mint state on component mount, and as a cleanup (on unmount)
const dispatch = useDispatch<AppDispatch>()
useEffect(() => {
dispatch(resetMintState())
return () => {
dispatch(resetMintState())
}
}, [dispatch])
const { chainId, account } = useActiveWeb3React()
// get pair contract
......
......@@ -7,6 +7,7 @@ import user from './user/reducer'
import transactions from './transactions/reducer'
import swap from './swap/reducer'
import mint from './mint/reducer'
import mintV3 from './mint/v3/reducer'
import lists from './lists/reducer'
import burn from './burn/reducer'
import burnV3 from './burn/v3/reducer'
......@@ -21,6 +22,7 @@ const store = configureStore({
transactions,
swap,
mint,
mintV3,
burn,
burnV3,
multicall,
......
......@@ -5,19 +5,5 @@ export enum Field {
CURRENCY_B = 'CURRENCY_B',
}
export enum Bound {
LOWER = 'LOWER',
UPPER = 'UPPER',
}
// save for % inputs
export enum RangeType {
PERCENT = 'PERCENT',
RATE = 'RATE',
}
export const typeInput = createAction<{ field: Field; typedValue: string; noLiquidity: boolean }>('mint/typeInputMint')
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.
import { createReducer } from '@reduxjs/toolkit'
import {
Field,
resetMintState,
typeInput,
typeStartPriceInput,
typeLeftRangeInput,
typeRightRangeInput,
} from './actions'
import { Field, resetMintState, typeInput } from './actions'
export interface MintState {
readonly independentField: Field
......@@ -29,24 +22,6 @@ export const initialState: MintState = {
export default createReducer<MintState>(initialState, (builder) =>
builder
.addCase(resetMintState, () => initialState)
.addCase(typeStartPriceInput, (state, { payload: { typedValue } }) => {
return {
...state,
startPriceTypedValue: typedValue,
}
})
.addCase(typeLeftRangeInput, (state, { payload: { typedValue } }) => {
return {
...state,
leftRangeTypedValue: typedValue,
}
})
.addCase(typeRightRangeInput, (state, { payload: { typedValue } }) => {
return {
...state,
rightRangeTypedValue: typedValue,
}
})
.addCase(typeInput, (state, { payload: { field, typedValue, noLiquidity } }) => {
if (noLiquidity) {
// they're typing into the field they've last typed in
......
import { useMemo } from 'react'
import { Pair } from '@uniswap/v2-sdk'
import { Currency, CurrencyAmount, ETHER, Percent, Price, TokenAmount } from '@uniswap/sdk-core'
import JSBI from 'jsbi'
import { PairState, useV2Pair } from '../../hooks/useV2Pairs'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useActiveWeb3React } from '../../hooks'
import { wrappedCurrency, wrappedCurrencyAmount } from '../../utils/wrappedCurrency'
import { tryParseAmount } from '../swap/hooks'
import { useCurrencyBalances } from '../wallet/hooks'
import { Field } from './actions'
import { useMintState } from './hooks'
const ZERO = JSBI.BigInt(0)
export function useV2DerivedMintInfo(
currencyA: Currency | undefined,
currencyB: Currency | undefined
): {
dependentField: Field
currencies: { [field in Field]?: Currency }
pair?: Pair | null
pairState: PairState
currencyBalances: { [field in Field]?: CurrencyAmount }
parsedAmounts: { [field in Field]?: CurrencyAmount }
price?: Price
noLiquidity?: boolean
liquidityMinted?: TokenAmount
poolTokenPercentage?: Percent
error?: string
} {
const { account, chainId } = useActiveWeb3React()
const { independentField, typedValue, otherTypedValue } = useMintState()
const dependentField = independentField === Field.CURRENCY_A ? Field.CURRENCY_B : Field.CURRENCY_A
// tokens
const currencies: { [field in Field]?: Currency } = useMemo(
() => ({
[Field.CURRENCY_A]: currencyA ?? undefined,
[Field.CURRENCY_B]: currencyB ?? undefined,
}),
[currencyA, currencyB]
)
// pair
const [pairState, pair] = useV2Pair(currencies[Field.CURRENCY_A], currencies[Field.CURRENCY_B])
const totalSupply = useTotalSupply(pair?.liquidityToken)
const noLiquidity: boolean =
pairState === PairState.NOT_EXISTS || Boolean(totalSupply && JSBI.equal(totalSupply.raw, ZERO))
// balances
const balances = useCurrencyBalances(account ?? undefined, [
currencies[Field.CURRENCY_A],
currencies[Field.CURRENCY_B],
])
const currencyBalances: { [field in Field]?: CurrencyAmount } = {
[Field.CURRENCY_A]: balances[0],
[Field.CURRENCY_B]: balances[1],
}
// amounts
const independentAmount: CurrencyAmount | undefined = tryParseAmount(typedValue, currencies[independentField])
const dependentAmount: CurrencyAmount | undefined = useMemo(() => {
if (noLiquidity) {
if (otherTypedValue && currencies[dependentField]) {
return tryParseAmount(otherTypedValue, currencies[dependentField])
}
return undefined
} else if (independentAmount) {
// we wrap the currencies just to get the price in terms of the other token
const wrappedIndependentAmount = wrappedCurrencyAmount(independentAmount, chainId)
const [tokenA, tokenB] = [wrappedCurrency(currencyA, chainId), wrappedCurrency(currencyB, chainId)]
if (tokenA && tokenB && wrappedIndependentAmount && pair) {
const dependentCurrency = dependentField === Field.CURRENCY_B ? currencyB : currencyA
const dependentTokenAmount =
dependentField === Field.CURRENCY_B
? pair.priceOf(tokenA).quote(wrappedIndependentAmount)
: pair.priceOf(tokenB).quote(wrappedIndependentAmount)
return dependentCurrency === ETHER ? CurrencyAmount.ether(dependentTokenAmount.raw) : dependentTokenAmount
}
return undefined
} else {
return undefined
}
}, [noLiquidity, otherTypedValue, currencies, dependentField, independentAmount, currencyA, chainId, currencyB, pair])
const parsedAmounts: { [field in Field]: CurrencyAmount | undefined } = useMemo(() => {
return {
[Field.CURRENCY_A]: independentField === Field.CURRENCY_A ? independentAmount : dependentAmount,
[Field.CURRENCY_B]: independentField === Field.CURRENCY_A ? dependentAmount : independentAmount,
}
}, [dependentAmount, independentAmount, independentField])
const price = useMemo(() => {
if (noLiquidity) {
const { [Field.CURRENCY_A]: currencyAAmount, [Field.CURRENCY_B]: currencyBAmount } = parsedAmounts
if (currencyAAmount && currencyBAmount) {
return new Price(currencyAAmount.currency, currencyBAmount.currency, currencyAAmount.raw, currencyBAmount.raw)
}
return undefined
} else {
const wrappedCurrencyA = wrappedCurrency(currencyA, chainId)
return pair && wrappedCurrencyA ? pair.priceOf(wrappedCurrencyA) : undefined
}
}, [chainId, currencyA, noLiquidity, pair, parsedAmounts])
// liquidity minted
const liquidityMinted = useMemo(() => {
const { [Field.CURRENCY_A]: currencyAAmount, [Field.CURRENCY_B]: currencyBAmount } = parsedAmounts
const [tokenAmountA, tokenAmountB] = [
wrappedCurrencyAmount(currencyAAmount, chainId),
wrappedCurrencyAmount(currencyBAmount, chainId),
]
if (pair && totalSupply && tokenAmountA && tokenAmountB) {
return pair.getLiquidityMinted(totalSupply, tokenAmountA, tokenAmountB)
} else {
return undefined
}
}, [parsedAmounts, chainId, pair, totalSupply])
const poolTokenPercentage = useMemo(() => {
if (liquidityMinted && totalSupply) {
return new Percent(liquidityMinted.raw, totalSupply.add(liquidityMinted).raw)
} else {
return undefined
}
}, [liquidityMinted, totalSupply])
let error: string | undefined
if (!account) {
error = 'Connect Wallet'
}
if (pairState === PairState.INVALID) {
error = error ?? 'Invalid pair'
}
if (!parsedAmounts[Field.CURRENCY_A] || !parsedAmounts[Field.CURRENCY_B]) {
error = error ?? 'Enter an amount'
}
const { [Field.CURRENCY_A]: currencyAAmount, [Field.CURRENCY_B]: currencyBAmount } = parsedAmounts
if (currencyAAmount && currencyBalances?.[Field.CURRENCY_A]?.lessThan(currencyAAmount)) {
error = 'Insufficient ' + currencies[Field.CURRENCY_A]?.symbol + ' balance'
}
if (currencyBAmount && currencyBalances?.[Field.CURRENCY_B]?.lessThan(currencyBAmount)) {
error = 'Insufficient ' + currencies[Field.CURRENCY_B]?.symbol + ' balance'
}
return {
dependentField,
currencies,
pair,
pairState,
currencyBalances,
parsedAmounts,
price,
noLiquidity,
liquidityMinted,
poolTokenPercentage,
error,
}
}
import { createAction } from '@reduxjs/toolkit'
export enum Field {
CURRENCY_A = 'CURRENCY_A',
CURRENCY_B = 'CURRENCY_B',
}
export enum Bound {
LOWER = 'LOWER',
UPPER = 'UPPER',
}
export const typeInput = createAction<{ field: Field; typedValue: string; noLiquidity: boolean }>(
'mintV3/typeInputMint'
)
export const typeStartPriceInput = createAction<{ typedValue: string }>('mintV3/typeStartPriceInput')
export const typeLeftRangeInput = createAction<{ typedValue: string }>('mintV3/typeLeftRangeInput')
export const typeRightRangeInput = createAction<{ typedValue: string }>('mintV3/typeRightRangeInput')
export const resetMintState = createAction<void>('mintV3/resetMintState')
This diff is collapsed.
import { createReducer } from '@reduxjs/toolkit'
import {
Field,
resetMintState,
typeInput,
typeStartPriceInput,
typeLeftRangeInput,
typeRightRangeInput,
} from './actions'
export interface MintState {
readonly independentField: Field
readonly typedValue: string
readonly startPriceTypedValue: string // for the case when there's no liquidity
readonly leftRangeTypedValue: string
readonly rightRangeTypedValue: string
}
export const initialState: MintState = {
independentField: Field.CURRENCY_A,
typedValue: '',
startPriceTypedValue: '',
leftRangeTypedValue: '',
rightRangeTypedValue: '',
}
export default createReducer<MintState>(initialState, (builder) =>
builder
.addCase(resetMintState, () => initialState)
.addCase(typeStartPriceInput, (state, { payload: { typedValue } }) => {
return {
...state,
startPriceTypedValue: typedValue,
}
})
.addCase(typeLeftRangeInput, (state, { payload: { typedValue } }) => {
return {
...state,
leftRangeTypedValue: typedValue,
}
})
.addCase(typeRightRangeInput, (state, { payload: { typedValue } }) => {
return {
...state,
rightRangeTypedValue: typedValue,
}
})
.addCase(typeInput, (state, { payload: { field, typedValue, noLiquidity } }) => {
if (noLiquidity) {
// they're typing into the field they've last typed in
if (field === state.independentField) {
return {
...state,
independentField: field,
typedValue,
}
}
// they're typing into a new field, store the other value
else {
return {
...state,
independentField: field,
typedValue,
}
}
} else {
return {
...state,
independentField: field,
typedValue,
}
}
})
)
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