Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: warn against high price impact at confirm step #5032

Merged
3 changes: 2 additions & 1 deletion src/assets/translations/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,8 @@
"unknownGas": "(unknown)",
"receiveAddress": "Receive Address",
"receiveAddressDescription": "No %{chainName} address found from connected wallet. Manually enter address to continue.",
"addressPlaceholder": "%{chainName} address"
"addressPlaceholder": "%{chainName} address",
"priceImpactWarning": "Due to the size of this trade relative to available liquidity, the expected price impact of this trade is %{priceImpactPercentage}%. Are you sure you want to trade?"
},
"modals": {
"popup": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { WalletActions } from 'context/WalletProvider/actions'
import { useErrorHandler } from 'hooks/useErrorToast/useErrorToast'
import { useLocaleFormatter } from 'hooks/useLocaleFormatter/useLocaleFormatter'
import { useWallet } from 'hooks/useWallet/useWallet'
import { bnOrZero, positiveOrZero } from 'lib/bignumber/bignumber'
import { bn, bnOrZero, positiveOrZero } from 'lib/bignumber/bignumber'
import { getTxLink } from 'lib/getTxLink'
import { firstNonZeroDecimal } from 'lib/math'
import { getMixPanel } from 'lib/mixpanel/mixPanelSingleton'
Expand Down Expand Up @@ -124,7 +124,7 @@ export const TradeConfirm = () => {
const swapperName = useAppSelector(selectActiveSwapperName)
const defaultFeeAsset = useAppSelector(selectFirstHopSellFeeAsset)
const netBuyAmountCryptoPrecision = useAppSelector(selectNetBuyAmountCryptoPrecision)
const slippage = useAppSelector(selectTradeSlippagePercentageDecimal)
const slippageDecimal = useAppSelector(selectTradeSlippagePercentageDecimal)
const netBuyAmountUserCurrency = useAppSelector(selectNetBuyAmountUserCurrency)
const sellAmountBeforeFeesUserCurrency = useAppSelector(selectSellAmountUserCurrency)
const networkFeeCryptoHuman = useAppSelector(selectFirstHopNetworkFeeCryptoPrecision)
Expand All @@ -150,6 +150,22 @@ export const TradeConfirm = () => {

const txHash = buyTxHash ?? sellTxHash

const priceImpactPercentage = useMemo(() => {
if (!sellAmountBeforeFeesUserCurrency || !netBuyAmountUserCurrency) return bn(0)

const tradeDifference = bn(sellAmountBeforeFeesUserCurrency)
.minus(netBuyAmountUserCurrency)
0xApotheosis marked this conversation as resolved.
Show resolved Hide resolved
.abs()

return tradeDifference.div(sellAmountBeforeFeesUserCurrency).times(100)
}, [sellAmountBeforeFeesUserCurrency, netBuyAmountUserCurrency])

const isHighPriceImpact = useMemo(() => {
if (!priceImpactPercentage) return false

return priceImpactPercentage.gt(10)
}, [priceImpactPercentage])

const getSellTxLink = useCallback(
(sellTxHash: string) =>
getTxLink({
Expand Down Expand Up @@ -206,6 +222,16 @@ export const TradeConfirm = () => {
return
}

const shouldContinueTrade =
!isHighPriceImpact ||
window.confirm(
translate('trade.priceImpactWarning', {
priceImpactPercentage: priceImpactPercentage.toFixed(2),
}),
)

if (!shouldContinueTrade) return

await executeTrade()
// only track after swapper successfully executes trade
// otherwise unsigned txs will be tracked as confirmed trades
Expand All @@ -224,8 +250,11 @@ export const TradeConfirm = () => {
handleBack,
history,
isConnected,
isHighPriceImpact,
mixpanel,
priceImpactPercentage,
showErrorToast,
translate,
wallet,
walletDispatch,
])
Expand Down Expand Up @@ -331,7 +360,7 @@ export const TradeConfirm = () => {
amountBeforeFeesCryptoPrecision={buyAmountBeforeFeesCryptoPrecision ?? ''}
protocolFees={tradeQuoteStep?.feeData.protocolFees}
shapeShiftFee='0'
slippage={slippage}
slippage={slippageDecimal}
fiatAmount={positiveOrZero(netBuyAmountUserCurrency).toFixed(2)}
swapperName={swapperName ?? ''}
intermediaryTransactionOutputs={tradeQuoteStep?.intermediaryTransactionOutputs}
Expand All @@ -349,7 +378,7 @@ export const TradeConfirm = () => {
buyAmountBeforeFeesCryptoPrecision,
tradeQuoteStep?.feeData.protocolFees,
tradeQuoteStep?.intermediaryTransactionOutputs,
slippage,
slippageDecimal,
netBuyAmountUserCurrency,
swapperName,
donationAmount,
Expand Down
5 changes: 5 additions & 0 deletions src/state/slices/tradeQuoteSlice/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,11 @@ export const selectTradeSlippagePercentageDecimal: Selector<ReduxState, string>
},
)

export const selectTradeSlippagePercentage: Selector<ReduxState, string> = createSelector(
0xApotheosis marked this conversation as resolved.
Show resolved Hide resolved
selectTradeSlippagePercentageDecimal,
tradeSlippagePercentageDecimal => bn(tradeSlippagePercentageDecimal).times(100).toString(),
)

const selectSellAssetUsdRate = createSelector(
selectFirstHopSellAsset,
selectCryptoMarketData,
Expand Down