Skip to content

Commit

Permalink
feat: auto show preview after approve EX-2852 (#2594)
Browse files Browse the repository at this point in the history
* faet: auto show preview after approve EX-2852

* fix: dont show preview if change input after cancel tx and back to approved amount

* feat: handle price impact is very high
  • Loading branch information
viet-nv authored Feb 20, 2025
1 parent 4e259a7 commit 962cc92
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export const SwapButtonWithPriceImpact = ({
showLoading,
onClick,
priceImpact,
isApproved,
route,
disabled,
showNoteGetRoute,
Expand Down Expand Up @@ -77,7 +76,7 @@ export const SwapButtonWithPriceImpact = ({
}

const shouldDisableByPriceImpact = checkShouldDisableByPriceImpact(isDegenMode, priceImpact)
const shouldDisable = !route || !isApproved || disabled
const shouldDisable = !route || disabled

if ((priceImpactResult.isVeryHigh || priceImpactResult.isInvalid) && isDegenMode) {
return (
Expand All @@ -104,7 +103,13 @@ export const SwapButtonWithPriceImpact = ({
}
}}
$minimal={minimal}
style={{ display: 'flex', alignItems: 'center', gap: '4px' }}
style={{
display: 'flex',
alignItems: 'center',
gap: '4px',
background: shouldDisableByPriceImpact ? theme.red : undefined,
color: shouldDisableByPriceImpact ? theme.text : undefined,
}}
>
{shouldDisableByPriceImpact && showTooltipPriceImpact ? (
<MouseoverTooltip
Expand Down
15 changes: 14 additions & 1 deletion src/components/SwapForm/SwapActionButton/SwapOnlyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export type Props = {
setProcessingSwap: React.Dispatch<React.SetStateAction<boolean>>
setErrorWhileSwap: (e: string) => void
buildRoute: () => Promise<BuildRouteResult>

autoShowPreview: boolean
setAutoShowPreview: (val: boolean) => void
}

const SwapOnlyButton: React.FC<Props> = ({
Expand All @@ -61,6 +64,9 @@ const SwapOnlyButton: React.FC<Props> = ({
setProcessingSwap,
setErrorWhileSwap,
buildRoute,

autoShowPreview,
setAutoShowPreview,
}) => {
const { mixpanelHandler } = useMixpanel({
[Field.INPUT]: currencyIn,
Expand Down Expand Up @@ -99,8 +105,15 @@ const SwapOnlyButton: React.FC<Props> = ({
})
}

const [slippage] = useUserSlippageTolerance()
useEffect(() => {
if (autoShowPreview && isApproved) {
handleClickSwapForNormalMode()
setAutoShowPreview(false)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [autoShowPreview, isApproved])

const [slippage] = useUserSlippageTolerance()
useEffect(() => {
if (Boolean(buildResult) && isProcessingSwap) handleClickSwapForNormalMode()
// eslint-disable-next-line
Expand Down
42 changes: 38 additions & 4 deletions src/components/SwapForm/SwapActionButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ChainId, Currency, CurrencyAmount } from '@kyberswap/ks-sdk-core'
import { Trans } from '@lingui/macro'
import { useEffect, useState } from 'react'
import { useSearchParams } from 'react-router-dom'
import { Flex } from 'rebass'
import styled from 'styled-components'

Expand All @@ -22,6 +23,7 @@ import { WrapType } from 'hooks/useWrapCallback'
import { useChangeNetwork } from 'hooks/web3/useChangeNetwork'
import { useWalletModalToggle } from 'state/application/hooks'
import { DetailedRouteSummary } from 'types/route'
import { checkPriceImpact } from 'utils/prices'

import { Props as SwapOnlyButtonProps } from './SwapOnlyButton'

Expand Down Expand Up @@ -105,11 +107,17 @@ const SwapActionButton: React.FC<Props> = ({
)

// check whether the user has approved the router on the input token
const [approval, approveCallback, currentAllowance] = useApproveCallback(
const [approval, approveCallback, currentAllowance, pendingApproval] = useApproveCallback(
parsedAmountFromTypedValue,
routeSummary?.routerAddress,
)

useEffect(() => {
if (pendingApproval) {
setAutoShowPreview(true)
}
}, [pendingApproval])

// check if user has gone through approval process, used to show two step buttons, reset on token change
const [approvalSubmitted, setApprovalSubmitted] = useState<boolean>(false)

Expand Down Expand Up @@ -145,8 +153,20 @@ const SwapActionButton: React.FC<Props> = ({
if (!showApproveFlow) setLoading(false)
}, [showApproveFlow])

const { priceImpact } = routeSummary || {}
const priceImpactResult = checkPriceImpact(priceImpact)

const [searchParams, setSearchParams] = useSearchParams()
const [approvalType, setApprovalType] = useState(AllowanceType.INFINITE)
const [autoShowPreview, setAutoShowPreview] = useState(false)

const handleApproveClick = () => {
if (!isDegenMode && (priceImpactResult.isVeryHigh || priceImpactResult.isInvalid)) {
searchParams.set('enableDegenMode', 'true')
setSearchParams(searchParams)
return
}

setLoading(true)
approveCallback(
approvalType === AllowanceType.EXACT && parsedAmountFromTypedValue ? parsedAmountFromTypedValue : undefined,
Expand Down Expand Up @@ -259,11 +279,13 @@ const SwapActionButton: React.FC<Props> = ({
buildRoute,

isApproved: approval === ApprovalState.APPROVED || permitState === PermitState.SIGNED,
autoShowPreview,
setAutoShowPreview,
}

const Approvebtn = permitState === PermitState.NOT_SIGNED ? ButtonLight : ButtonPrimary

if (showApproveFlow) {
if (showApproveFlow && (isDegenMode ? true : !priceImpactResult.isInvalid && !priceImpactResult.isVeryHigh)) {
return (
<div>
<RowBetween style={{ gap: '1rem' }}>
Expand Down Expand Up @@ -308,6 +330,12 @@ const SwapActionButton: React.FC<Props> = ({
style={{
border: 'none',
flex: 1,
...(priceImpactResult.isVeryHigh || priceImpactResult.isInvalid
? {
background: theme.red,
color: loading || approval === ApprovalState.PENDING ? theme.subText : theme.text,
}
: {}),
}}
>
{approval === ApprovalState.PENDING ? (
Expand All @@ -317,11 +345,17 @@ const SwapActionButton: React.FC<Props> = ({
) : (
<RowFit gap="4px">
<InfoHelper
color={!loading && permitState === PermitState.NOT_SIGNED ? theme.primary : theme.textReverse}
color={
priceImpactResult.isVeryHigh || priceImpactResult.isInvalid
? theme.text
: !loading && permitState === PermitState.NOT_SIGNED
? theme.primary
: theme.textReverse
}
placement="top"
text={approveTooltipText()}
/>
<Trans>Approve {currencyIn?.symbol}</Trans>
Approve & Swap
</RowFit>
)}
</Approvebtn>
Expand Down
30 changes: 22 additions & 8 deletions src/hooks/useApproveCallback.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { MaxUint256 } from '@ethersproject/constants'
import { Currency, CurrencyAmount, TokenAmount } from '@kyberswap/ks-sdk-core'
import { t } from '@lingui/macro'
import { BigNumber } from 'ethers'
import { Interface } from 'ethers/lib/utils'
import JSBI from 'jsbi'
import { useCallback, useMemo } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'

import { NotificationType } from 'components/Announcement/type'
import ERC20_ABI from 'constants/abis/erc20.json'
import { useTokenAllowance } from 'data/Allowances'
import { useNotify } from 'state/application/hooks'
import { Field } from 'state/swap/actions'
import { useHasPendingApproval, useTransactionAdder } from 'state/transactions/hooks'
Expand All @@ -20,7 +20,7 @@ import { computeSlippageAdjustedAmounts } from 'utils/prices'
import { paymasterExecute } from 'utils/sendTransaction'

import { useActiveWeb3React } from './index'
import { useTokenSigningContract } from './useContract'
import { useTokenReadingContract, useTokenSigningContract } from './useContract'

const ERC20Interface = new Interface(ERC20_ABI)

Expand All @@ -36,11 +36,26 @@ export function useApproveCallback(
amountToApprove?: CurrencyAmount<Currency>,
spender?: string,
forceApprove = false,
): [ApprovalState, (customAllowance?: CurrencyAmount<Currency>) => Promise<void>, TokenAmount | undefined] {
): [ApprovalState, (customAllowance?: CurrencyAmount<Currency>) => Promise<void>, TokenAmount | undefined, boolean] {
const { account } = useActiveWeb3React()
const token = amountToApprove?.currency.wrapped
const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)

const tokenContract = useTokenSigningContract(token?.address)
const readingTokenContract = useTokenReadingContract(token?.address)
const pendingApproval = useHasPendingApproval(token?.address, spender)

const [currentAllowance, setAllowance] = useState<TokenAmount | undefined>(undefined)
const getAllowance = useCallback(async () => {
if (!readingTokenContract || !token) return
readingTokenContract.allowance(account, spender).then((res: BigNumber) => {
setAllowance(TokenAmount.fromRawAmount(token, res.toString()))
})
}, [readingTokenContract, account, spender, token])

useEffect(() => {
getAllowance()
}, [getAllowance, pendingApproval])

// check the current approval status
const approvalState: ApprovalState = useMemo(() => {
if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
Expand All @@ -65,7 +80,6 @@ export function useApproveCallback(
}, [amountToApprove, currentAllowance, pendingApproval, spender])
const notify = useNotify()

const tokenContract = useTokenSigningContract(token?.address)
const addTransactionWithType = useTransactionAdder()
const [paymentToken] = usePaymentToken()

Expand Down Expand Up @@ -179,14 +193,14 @@ export function useApproveCallback(
],
)

return [approvalState, approve, currentAllowance]
return [approvalState, approve, currentAllowance, pendingApproval]
}

// wraps useApproveCallback in the context of a swap
export function useApproveCallbackFromTradeV2(
trade?: Aggregator,
allowedSlippage = 0,
): [ApprovalState, () => Promise<void>, TokenAmount | undefined] {
): [ApprovalState, () => Promise<void>, TokenAmount | undefined, boolean] {
const amountToApprove = useMemo(
() => (trade ? computeSlippageAdjustedAmounts(trade, allowedSlippage)[Field.INPUT] : undefined),
[trade, allowedSlippage],
Expand Down

0 comments on commit 962cc92

Please sign in to comment.