feat: update slippage tolerance to use auto or custom (#3166)
* update slippage tolerance to use auto or custom * remove attempted styling for other PR * back out UI changes, small naming updates * remove UI work * small code style changes, fix typo * update comment to doc comment
This commit is contained in:
parent
7f9c56b68c
commit
bbdb5f3f56
@ -8,7 +8,6 @@ import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { InterfaceTrade } from 'state/routing/types'
|
import { InterfaceTrade } from 'state/routing/types'
|
||||||
|
|
||||||
import { useUserSlippageToleranceWithDefault } from '../state/user/hooks'
|
|
||||||
import useGasPrice from './useGasPrice'
|
import useGasPrice from './useGasPrice'
|
||||||
import useUSDCPrice, { useUSDCValue } from './useUSDCPrice'
|
import useUSDCPrice, { useUSDCValue } from './useUSDCPrice'
|
||||||
|
|
||||||
@ -29,7 +28,10 @@ function guesstimateGas(trade: Trade<Currency, Currency, TradeType> | undefined)
|
|||||||
const MIN_AUTO_SLIPPAGE_TOLERANCE = new Percent(5, 1000) // 0.5%
|
const MIN_AUTO_SLIPPAGE_TOLERANCE = new Percent(5, 1000) // 0.5%
|
||||||
const MAX_AUTO_SLIPPAGE_TOLERANCE = new Percent(25, 100) // 25%
|
const MAX_AUTO_SLIPPAGE_TOLERANCE = new Percent(25, 100) // 25%
|
||||||
|
|
||||||
export default function useSwapSlippageTolerance(
|
/**
|
||||||
|
* Returns slippage tolerance based on values from current trade, gas estimates from api, and active network.
|
||||||
|
*/
|
||||||
|
export default function useAutoSlippageTolerance(
|
||||||
trade: InterfaceTrade<Currency, Currency, TradeType> | undefined
|
trade: InterfaceTrade<Currency, Currency, TradeType> | undefined
|
||||||
): Percent {
|
): Percent {
|
||||||
const { chainId } = useActiveWeb3React()
|
const { chainId } = useActiveWeb3React()
|
||||||
@ -41,7 +43,7 @@ export default function useSwapSlippageTolerance(
|
|||||||
const nativeCurrency = useNativeCurrency()
|
const nativeCurrency = useNativeCurrency()
|
||||||
const nativeCurrencyPrice = useUSDCPrice(nativeCurrency ?? undefined)
|
const nativeCurrencyPrice = useUSDCPrice(nativeCurrency ?? undefined)
|
||||||
|
|
||||||
const defaultSlippageTolerance = useMemo(() => {
|
return useMemo(() => {
|
||||||
if (!trade || onL2) return ONE_TENTHS_PERCENT
|
if (!trade || onL2) return ONE_TENTHS_PERCENT
|
||||||
|
|
||||||
const nativeGasCost =
|
const nativeGasCost =
|
||||||
@ -73,6 +75,4 @@ export default function useSwapSlippageTolerance(
|
|||||||
|
|
||||||
return V3_SWAP_DEFAULT_SLIPPAGE
|
return V3_SWAP_DEFAULT_SLIPPAGE
|
||||||
}, [trade, onL2, nativeGasPrice, gasEstimate, nativeCurrency, nativeCurrencyPrice, chainId, outputDollarValue])
|
}, [trade, onL2, nativeGasPrice, gasEstimate, nativeCurrency, nativeCurrencyPrice, chainId, outputDollarValue])
|
||||||
|
|
||||||
return useUserSlippageToleranceWithDefault(defaultSlippageTolerance)
|
|
||||||
}
|
}
|
@ -1,7 +1,8 @@
|
|||||||
import { t, Trans } from '@lingui/macro'
|
import { t, Trans } from '@lingui/macro'
|
||||||
|
import { Percent } from '@uniswap/sdk-core'
|
||||||
import { useAtom } from 'jotai'
|
import { useAtom } from 'jotai'
|
||||||
import { Check, LargeIcon } from 'lib/icons'
|
import { Check, LargeIcon } from 'lib/icons'
|
||||||
import { MaxSlippage, maxSlippageAtom } from 'lib/state/settings'
|
import { maxSlippageAtom } from 'lib/state/settings'
|
||||||
import styled, { ThemedText } from 'lib/theme'
|
import styled, { ThemedText } from 'lib/theme'
|
||||||
import { ReactNode, useCallback, useRef } from 'react'
|
import { ReactNode, useCallback, useRef } from 'react'
|
||||||
|
|
||||||
@ -54,36 +55,31 @@ function InputOption<T>({ value, children, selected, onSelect }: OptionProps<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function MaxSlippageSelect() {
|
export default function MaxSlippageSelect() {
|
||||||
const { P01, P05, CUSTOM } = MaxSlippage
|
const [maxSlippage, setMaxSlippage] = useAtom(maxSlippageAtom)
|
||||||
const [{ value: maxSlippage, custom }, setMaxSlippage] = useAtom(maxSlippageAtom)
|
|
||||||
|
|
||||||
const input = useRef<HTMLInputElement>(null)
|
const input = useRef<HTMLInputElement>(null)
|
||||||
const focus = useCallback(() => input.current?.focus(), [input])
|
const focus = useCallback(() => input.current?.focus(), [input])
|
||||||
|
|
||||||
|
//@TODO(ianlapham): hook up inputs to either set custom slippage or update to auto
|
||||||
|
//@TODO(ianlapham): update UI to match designs in spec
|
||||||
|
|
||||||
const onInputSelect = useCallback(
|
const onInputSelect = useCallback(
|
||||||
(custom) => {
|
(custom: Percent | 'auto') => {
|
||||||
focus()
|
focus()
|
||||||
if (custom !== undefined) {
|
if (custom !== undefined) {
|
||||||
setMaxSlippage({ value: CUSTOM, custom })
|
setMaxSlippage(custom)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[CUSTOM, focus, setMaxSlippage]
|
[focus, setMaxSlippage]
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column gap={0.75}>
|
<Column gap={0.75}>
|
||||||
<Label name={<Trans>Max slippage</Trans>} tooltip={tooltip} />
|
<Label name={<Trans>Max slippage</Trans>} tooltip={tooltip} />
|
||||||
<Row gap={0.5} grow>
|
<Row gap={0.5} grow>
|
||||||
<Option value={P01} onSelect={setMaxSlippage} selected={maxSlippage === P01} />
|
<Option value={'auto'} onSelect={setMaxSlippage} selected={maxSlippage === 'auto'} />
|
||||||
<Option value={P05} onSelect={setMaxSlippage} selected={maxSlippage === P05} />
|
<InputOption value={maxSlippage} onSelect={onInputSelect} selected={maxSlippage !== 'auto'}>
|
||||||
<InputOption value={custom} onSelect={onInputSelect} selected={maxSlippage === CUSTOM}>
|
<DecimalInput size={5} value={''} onChange={() => null} placeholder={t`Custom`} ref={input} />%
|
||||||
<DecimalInput
|
|
||||||
size={custom === undefined ? undefined : 5}
|
|
||||||
value={custom?.toString() ?? ''}
|
|
||||||
onChange={(custom) => setMaxSlippage({ value: CUSTOM, custom: custom ? parseFloat(custom) : undefined })}
|
|
||||||
placeholder={t`Custom`}
|
|
||||||
ref={input}
|
|
||||||
/>
|
|
||||||
%
|
|
||||||
</InputOption>
|
</InputOption>
|
||||||
</Row>
|
</Row>
|
||||||
</Column>
|
</Column>
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { Trans } from '@lingui/macro'
|
import { Trans } from '@lingui/macro'
|
||||||
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
|
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
|
||||||
|
import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
|
||||||
import { useClientSideV3Trade } from 'hooks/useClientSideV3Trade'
|
import { useClientSideV3Trade } from 'hooks/useClientSideV3Trade'
|
||||||
import { atom } from 'jotai'
|
import { atom } from 'jotai'
|
||||||
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
|
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
|
||||||
import { useCurrencyBalances } from 'lib/hooks/useCurrencyBalance'
|
import { useCurrencyBalances } from 'lib/hooks/useCurrencyBalance'
|
||||||
|
import { maxSlippageAtom } from 'lib/state/settings'
|
||||||
import { Field, swapAtom } from 'lib/state/swap'
|
import { Field, swapAtom } from 'lib/state/swap'
|
||||||
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
|
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
|
||||||
import { ReactNode, useEffect, useMemo } from 'react'
|
import { ReactNode, useEffect, useMemo } from 'react'
|
||||||
@ -86,9 +88,16 @@ function useComputeSwapInfo(): SwapInfo {
|
|||||||
[trade.trade?.inputAmount, trade.trade?.outputAmount]
|
[trade.trade?.inputAmount, trade.trade?.outputAmount]
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(ianlapham): Fix swap slippage tolerance
|
/*
|
||||||
// const allowedSlippage = useSwapSlippageTolerance(trade.trade ?? undefined)
|
* If user has enabled 'auto' slippage, use the default best slippage calculated
|
||||||
const allowedSlippage = useMemo(() => new Percent(100), [])
|
* based on the trade. If user has entered custom slippage, use that instead.
|
||||||
|
*/
|
||||||
|
const autoSlippageTolerance = useAutoSlippageTolerance(trade.trade)
|
||||||
|
const maxSlippage = useAtomValue(maxSlippageAtom)
|
||||||
|
const allowedSlippage = useMemo(
|
||||||
|
() => (maxSlippage === 'auto' ? autoSlippageTolerance : maxSlippage),
|
||||||
|
[autoSlippageTolerance, maxSlippage]
|
||||||
|
)
|
||||||
|
|
||||||
const inputError = useMemo(() => {
|
const inputError = useMemo(() => {
|
||||||
let inputError: ReactNode | undefined
|
let inputError: ReactNode | undefined
|
||||||
@ -154,7 +163,7 @@ export function SwapInfoUpdater() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Requires that SwapInfoUpdater be installed in the DOM tree. */
|
/** Requires that SwapInfoUpdater be installed in the DOM tree. **/
|
||||||
export default function useSwapInfo(): SwapInfo {
|
export default function useSwapInfo(): SwapInfo {
|
||||||
return useAtomValue(swapInfoAtom)
|
return useAtomValue(swapInfoAtom)
|
||||||
}
|
}
|
||||||
|
@ -34,43 +34,6 @@ export function pickAtom<Value, Key extends keyof Value & keyof Draft<Value>, Up
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Typing for a customizable enum; see setCustomizable.
|
|
||||||
* This is not exported because an enum may not extend another interface.
|
|
||||||
*/
|
|
||||||
interface CustomizableEnum<T extends number> {
|
|
||||||
CUSTOM: -1
|
|
||||||
DEFAULT: T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Typing for a customizable enum; see setCustomizable.
|
|
||||||
* The first value is used, unless it is CUSTOM, in which case the second is used.
|
|
||||||
*/
|
|
||||||
export type Customizable<T> = { value: T; custom?: number }
|
|
||||||
|
|
||||||
/** Sets a customizable enum, validating the tuple and falling back to the default. */
|
|
||||||
export function setCustomizable<T extends number, Enum extends CustomizableEnum<T>>(customizable: Enum) {
|
|
||||||
return (draft: Customizable<T>, update: T | Customizable<T>) => {
|
|
||||||
// normalize the update
|
|
||||||
if (typeof update === 'number') {
|
|
||||||
update = { value: update }
|
|
||||||
}
|
|
||||||
|
|
||||||
draft.value = update.value
|
|
||||||
if (draft.value === customizable.CUSTOM) {
|
|
||||||
draft.custom = update.custom
|
|
||||||
|
|
||||||
// prevent invalid state
|
|
||||||
if (draft.custom === undefined) {
|
|
||||||
draft.value = customizable.DEFAULT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return draft
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets a togglable atom to invert its state at the next render. */
|
/** Sets a togglable atom to invert its state at the next render. */
|
||||||
export function setTogglable(draft: boolean) {
|
export function setTogglable(draft: boolean) {
|
||||||
return !draft
|
return !draft
|
||||||
|
@ -1,34 +1,26 @@
|
|||||||
|
import { Percent } from '@uniswap/sdk-core'
|
||||||
import { atomWithReset } from 'jotai/utils'
|
import { atomWithReset } from 'jotai/utils'
|
||||||
|
|
||||||
import { Customizable, pickAtom, setCustomizable, setTogglable } from './atoms'
|
import { pickAtom, setTogglable } from './atoms'
|
||||||
|
|
||||||
/** Max slippage, as a percentage. */
|
|
||||||
export enum MaxSlippage {
|
|
||||||
P01 = 0.1,
|
|
||||||
P05 = 0.5,
|
|
||||||
// Members to satisfy CustomizableEnum; see setCustomizable
|
|
||||||
CUSTOM = -1,
|
|
||||||
DEFAULT = P05,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TRANSACTION_TTL_DEFAULT = 40
|
export const TRANSACTION_TTL_DEFAULT = 40
|
||||||
|
|
||||||
interface Settings {
|
interface Settings {
|
||||||
maxSlippage: Customizable<MaxSlippage>
|
maxSlippage: Percent | 'auto' // auto will cause slippage to resort to default calculation
|
||||||
transactionTtl: number | undefined
|
transactionTtl: number | undefined
|
||||||
mockTogglable: boolean
|
mockTogglable: boolean
|
||||||
clientSideRouter: boolean // wether to use
|
clientSideRouter: boolean // wether to use
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialSettings: Settings = {
|
const initialSettings: Settings = {
|
||||||
maxSlippage: { value: MaxSlippage.DEFAULT },
|
maxSlippage: 'auto',
|
||||||
transactionTtl: undefined,
|
transactionTtl: undefined,
|
||||||
mockTogglable: true,
|
mockTogglable: true,
|
||||||
clientSideRouter: false,
|
clientSideRouter: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const settingsAtom = atomWithReset(initialSettings)
|
export const settingsAtom = atomWithReset(initialSettings)
|
||||||
export const maxSlippageAtom = pickAtom(settingsAtom, 'maxSlippage', setCustomizable(MaxSlippage))
|
export const maxSlippageAtom = pickAtom(settingsAtom, 'maxSlippage')
|
||||||
export const transactionTtlAtom = pickAtom(settingsAtom, 'transactionTtl')
|
export const transactionTtlAtom = pickAtom(settingsAtom, 'transactionTtl')
|
||||||
export const mockTogglableAtom = pickAtom(settingsAtom, 'mockTogglable', setTogglable)
|
export const mockTogglableAtom = pickAtom(settingsAtom, 'mockTogglable', setTogglable)
|
||||||
export const clientSideRouterAtom = pickAtom(settingsAtom, 'clientSideRouter')
|
export const clientSideRouterAtom = pickAtom(settingsAtom, 'clientSideRouter')
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
import { Trans } from '@lingui/macro'
|
import { Trans } from '@lingui/macro'
|
||||||
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
|
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
|
||||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||||
|
import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
|
||||||
import { useBestTrade } from 'hooks/useBestTrade'
|
import { useBestTrade } from 'hooks/useBestTrade'
|
||||||
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
|
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
|
||||||
import { ParsedQs } from 'qs'
|
import { ParsedQs } from 'qs'
|
||||||
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
|
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { useAppDispatch, useAppSelector } from 'state/hooks'
|
import { useAppDispatch, useAppSelector } from 'state/hooks'
|
||||||
import { InterfaceTrade, TradeState } from 'state/routing/types'
|
import { InterfaceTrade, TradeState } from 'state/routing/types'
|
||||||
|
import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
|
||||||
|
|
||||||
import { useCurrency } from '../../hooks/Tokens'
|
import { useCurrency } from '../../hooks/Tokens'
|
||||||
import useENS from '../../hooks/useENS'
|
import useENS from '../../hooks/useENS'
|
||||||
import useParsedQueryString from '../../hooks/useParsedQueryString'
|
import useParsedQueryString from '../../hooks/useParsedQueryString'
|
||||||
import useSwapSlippageTolerance from '../../hooks/useSwapSlippageTolerance'
|
|
||||||
import { isAddress } from '../../utils'
|
import { isAddress } from '../../utils'
|
||||||
import { AppState } from '../index'
|
import { AppState } from '../index'
|
||||||
import { useCurrencyBalances } from '../wallet/hooks'
|
import { useCurrencyBalances } from '../wallet/hooks'
|
||||||
@ -133,7 +134,10 @@ export function useDerivedSwapInfo(): {
|
|||||||
[inputCurrency, outputCurrency]
|
[inputCurrency, outputCurrency]
|
||||||
)
|
)
|
||||||
|
|
||||||
const allowedSlippage = useSwapSlippageTolerance(trade.trade ?? undefined)
|
// allowed slippage is either auto slippage, or custom user defined slippage if auto slippage disabled
|
||||||
|
const autoSlippageTolerance = useAutoSlippageTolerance(trade.trade)
|
||||||
|
const allowedSlippage = useUserSlippageToleranceWithDefault(autoSlippageTolerance)
|
||||||
|
|
||||||
const inputError = useMemo(() => {
|
const inputError = useMemo(() => {
|
||||||
let inputError: ReactNode | undefined
|
let inputError: ReactNode | undefined
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user