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:
Ian Lapham 2022-01-24 17:56:12 -05:00 committed by GitHub
parent 7f9c56b68c
commit bbdb5f3f56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 42 additions and 78 deletions

@ -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