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 { InterfaceTrade } from 'state/routing/types'
|
||||
|
||||
import { useUserSlippageToleranceWithDefault } from '../state/user/hooks'
|
||||
import useGasPrice from './useGasPrice'
|
||||
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 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
|
||||
): Percent {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
@ -41,7 +43,7 @@ export default function useSwapSlippageTolerance(
|
||||
const nativeCurrency = useNativeCurrency()
|
||||
const nativeCurrencyPrice = useUSDCPrice(nativeCurrency ?? undefined)
|
||||
|
||||
const defaultSlippageTolerance = useMemo(() => {
|
||||
return useMemo(() => {
|
||||
if (!trade || onL2) return ONE_TENTHS_PERCENT
|
||||
|
||||
const nativeGasCost =
|
||||
@ -73,6 +75,4 @@ export default function useSwapSlippageTolerance(
|
||||
|
||||
return V3_SWAP_DEFAULT_SLIPPAGE
|
||||
}, [trade, onL2, nativeGasPrice, gasEstimate, nativeCurrency, nativeCurrencyPrice, chainId, outputDollarValue])
|
||||
|
||||
return useUserSlippageToleranceWithDefault(defaultSlippageTolerance)
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import { useAtom } from 'jotai'
|
||||
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 { ReactNode, useCallback, useRef } from 'react'
|
||||
|
||||
@ -54,36 +55,31 @@ function InputOption<T>({ value, children, selected, onSelect }: OptionProps<T>
|
||||
}
|
||||
|
||||
export default function MaxSlippageSelect() {
|
||||
const { P01, P05, CUSTOM } = MaxSlippage
|
||||
const [{ value: maxSlippage, custom }, setMaxSlippage] = useAtom(maxSlippageAtom)
|
||||
const [maxSlippage, setMaxSlippage] = useAtom(maxSlippageAtom)
|
||||
|
||||
const input = useRef<HTMLInputElement>(null)
|
||||
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(
|
||||
(custom) => {
|
||||
(custom: Percent | 'auto') => {
|
||||
focus()
|
||||
if (custom !== undefined) {
|
||||
setMaxSlippage({ value: CUSTOM, custom })
|
||||
setMaxSlippage(custom)
|
||||
}
|
||||
},
|
||||
[CUSTOM, focus, setMaxSlippage]
|
||||
[focus, setMaxSlippage]
|
||||
)
|
||||
|
||||
return (
|
||||
<Column gap={0.75}>
|
||||
<Label name={<Trans>Max slippage</Trans>} tooltip={tooltip} />
|
||||
<Row gap={0.5} grow>
|
||||
<Option value={P01} onSelect={setMaxSlippage} selected={maxSlippage === P01} />
|
||||
<Option value={P05} onSelect={setMaxSlippage} selected={maxSlippage === P05} />
|
||||
<InputOption value={custom} onSelect={onInputSelect} selected={maxSlippage === CUSTOM}>
|
||||
<DecimalInput
|
||||
size={custom === undefined ? undefined : 5}
|
||||
value={custom?.toString() ?? ''}
|
||||
onChange={(custom) => setMaxSlippage({ value: CUSTOM, custom: custom ? parseFloat(custom) : undefined })}
|
||||
placeholder={t`Custom`}
|
||||
ref={input}
|
||||
/>
|
||||
%
|
||||
<Option value={'auto'} onSelect={setMaxSlippage} selected={maxSlippage === 'auto'} />
|
||||
<InputOption value={maxSlippage} onSelect={onInputSelect} selected={maxSlippage !== 'auto'}>
|
||||
<DecimalInput size={5} value={''} onChange={() => null} placeholder={t`Custom`} ref={input} />%
|
||||
</InputOption>
|
||||
</Row>
|
||||
</Column>
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
|
||||
import { useClientSideV3Trade } from 'hooks/useClientSideV3Trade'
|
||||
import { atom } from 'jotai'
|
||||
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
|
||||
import { useCurrencyBalances } from 'lib/hooks/useCurrencyBalance'
|
||||
import { maxSlippageAtom } from 'lib/state/settings'
|
||||
import { Field, swapAtom } from 'lib/state/swap'
|
||||
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
|
||||
import { ReactNode, useEffect, useMemo } from 'react'
|
||||
@ -86,9 +88,16 @@ function useComputeSwapInfo(): SwapInfo {
|
||||
[trade.trade?.inputAmount, trade.trade?.outputAmount]
|
||||
)
|
||||
|
||||
// TODO(ianlapham): Fix swap slippage tolerance
|
||||
// const allowedSlippage = useSwapSlippageTolerance(trade.trade ?? undefined)
|
||||
const allowedSlippage = useMemo(() => new Percent(100), [])
|
||||
/*
|
||||
* If user has enabled 'auto' slippage, use the default best slippage calculated
|
||||
* 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(() => {
|
||||
let inputError: ReactNode | undefined
|
||||
@ -154,7 +163,7 @@ export function SwapInfoUpdater() {
|
||||
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 {
|
||||
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. */
|
||||
export function setTogglable(draft: boolean) {
|
||||
return !draft
|
||||
|
@ -1,34 +1,26 @@
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import { atomWithReset } from 'jotai/utils'
|
||||
|
||||
import { Customizable, pickAtom, setCustomizable, 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,
|
||||
}
|
||||
import { pickAtom, setTogglable } from './atoms'
|
||||
|
||||
export const TRANSACTION_TTL_DEFAULT = 40
|
||||
|
||||
interface Settings {
|
||||
maxSlippage: Customizable<MaxSlippage>
|
||||
maxSlippage: Percent | 'auto' // auto will cause slippage to resort to default calculation
|
||||
transactionTtl: number | undefined
|
||||
mockTogglable: boolean
|
||||
clientSideRouter: boolean // wether to use
|
||||
}
|
||||
|
||||
const initialSettings: Settings = {
|
||||
maxSlippage: { value: MaxSlippage.DEFAULT },
|
||||
maxSlippage: 'auto',
|
||||
transactionTtl: undefined,
|
||||
mockTogglable: true,
|
||||
clientSideRouter: false,
|
||||
}
|
||||
|
||||
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 mockTogglableAtom = pickAtom(settingsAtom, 'mockTogglable', setTogglable)
|
||||
export const clientSideRouterAtom = pickAtom(settingsAtom, 'clientSideRouter')
|
||||
|
@ -1,17 +1,18 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
|
||||
import { useBestTrade } from 'hooks/useBestTrade'
|
||||
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
|
||||
import { ParsedQs } from 'qs'
|
||||
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useAppDispatch, useAppSelector } from 'state/hooks'
|
||||
import { InterfaceTrade, TradeState } from 'state/routing/types'
|
||||
import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
|
||||
|
||||
import { useCurrency } from '../../hooks/Tokens'
|
||||
import useENS from '../../hooks/useENS'
|
||||
import useParsedQueryString from '../../hooks/useParsedQueryString'
|
||||
import useSwapSlippageTolerance from '../../hooks/useSwapSlippageTolerance'
|
||||
import { isAddress } from '../../utils'
|
||||
import { AppState } from '../index'
|
||||
import { useCurrencyBalances } from '../wallet/hooks'
|
||||
@ -133,7 +134,10 @@ export function useDerivedSwapInfo(): {
|
||||
[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(() => {
|
||||
let inputError: ReactNode | undefined
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user