Skip to content

Commit

Permalink
fix(twap): display fallback handler banner when it's relevant (#2757)
Browse files Browse the repository at this point in the history
  • Loading branch information
shoom3301 authored Jul 4, 2023
1 parent 65a1dde commit 19ed7ee
Show file tree
Hide file tree
Showing 14 changed files with 52 additions and 50 deletions.
5 changes: 3 additions & 2 deletions src/common/containers/TradeApprove/TradeApproveButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import { useTradeApproveState } from './useTradeApproveState'
export interface TradeApproveButtonProps {
amountToApprove: CurrencyAmount<Currency>
children?: React.ReactNode
isDisabled?: boolean
}

export function TradeApproveButton(props: TradeApproveButtonProps) {
const { amountToApprove, children } = props
const { amountToApprove, children, isDisabled } = props

const currency = amountToApprove.currency

Expand All @@ -33,7 +34,7 @@ export function TradeApproveButton(props: TradeApproveButtonProps) {

return (
<>
<ApproveButton currency={currency} onClick={handleApprove} state={approvalState} />
<ApproveButton isDisabled={isDisabled} currency={currency} onClick={handleApprove} state={approvalState} />

{children}
</>
Expand Down
5 changes: 3 additions & 2 deletions src/common/pure/ApproveButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ export interface ApproveButtonProps {
currency: Currency | undefined | null
state: ApprovalState
onClick: () => void
isDisabled?: boolean
}

export function ApproveButton(props: ApproveButtonProps) {
const { currency, state, onClick } = props
const { currency, state, onClick, isDisabled } = props

const theme = useContext(ThemeContext)
const isPending = state === ApprovalState.PENDING
const isConfirmed = state === ApprovalState.APPROVED
const disabled = state !== ApprovalState.NOT_APPROVED
const disabled = isDisabled || state !== ApprovalState.NOT_APPROVED

const content = useMemo(() => {
if (isConfirmed) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function TradeFormButtons(props: TradeFormButtonsProps) {
const buttonFactory = tradeButtonsMap[validation]

if (typeof buttonFactory === 'function') {
return buttonFactory(context)
return buttonFactory(context, isDisabled)
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface ButtonErrorConfig {
}

interface ButtonCallback {
(context: TradeFormButtonContext): JSX.Element | null
(context: TradeFormButtonContext, isDisabled?: boolean): JSX.Element | null
}

const CompatibilityIssuesWarningWrapper = styled.div`
Expand Down Expand Up @@ -113,35 +113,35 @@ export const tradeButtonsMap: Record<TradeFormValidation, ButtonErrorConfig | Bu
</TradeFormBlankButton>
)
},
[TradeFormValidation.ExpertApproveAndSwap]: (context) => {
[TradeFormValidation.ExpertApproveAndSwap]: (context, isDisabled = false) => {
const tokenToApprove = context.derivedState.slippageAdjustedSellAmount?.currency.wrapped

return (
<TradeFormBlankButton onClick={context.doTrade}>
<TradeFormBlankButton disabled={isDisabled} onClick={context.doTrade}>
<Trans>
Confirm (Approve&nbsp;{<TokenSymbol token={tokenToApprove} length={6} />}&nbsp;and {context.defaultText})
</Trans>
</TradeFormBlankButton>
)
},
[TradeFormValidation.ApproveAndSwap]: (context) => {
[TradeFormValidation.ApproveAndSwap]: (context, isDisabled = false) => {
const tokenToApprove = context.derivedState.slippageAdjustedSellAmount?.currency.wrapped

return (
<TradeFormBlankButton onClick={context.confirmTrade}>
<TradeFormBlankButton disabled={isDisabled} onClick={context.confirmTrade}>
<Trans>
Approve&nbsp;{<TokenSymbol token={tokenToApprove} length={6} />}&nbsp;and {context.defaultText}
</Trans>
</TradeFormBlankButton>
)
},
[TradeFormValidation.ApproveRequired]: (context) => {
[TradeFormValidation.ApproveRequired]: (context, isDisabled = false) => {
const amountToApprove = context.derivedState.slippageAdjustedSellAmount

if (!amountToApprove) return null

return (
<TradeApproveButton amountToApprove={amountToApprove}>
<TradeApproveButton isDisabled={isDisabled} amountToApprove={amountToApprove}>
<TradeFormBlankButton disabled={true}>
<Trans>{context.defaultText}</Trans>
</TradeFormBlankButton>
Expand Down
8 changes: 3 additions & 5 deletions src/modules/twap/containers/ActionButtons/index.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
import { useAtomValue } from 'jotai'
import React from 'react'

import { useTradeConfirmActions } from 'modules/trade'
import { TradeFormButtons, TradeFormValidation } from 'modules/tradeFormValidation'
import { useTradeFormButtonContext } from 'modules/tradeFormValidation'

import { useAreWarningsAccepted } from '../../hooks/useAreWarningsAccepted'
import { useTwapWarningsContext } from '../../hooks/useTwapWarningsContext'
import { PrimaryActionButton } from '../../pure/PrimaryActionButton'
import { TwapFormState } from '../../pure/PrimaryActionButton/getTwapFormState'
import { twapOrdersSettingsAtom } from '../../state/twapOrdersSettingsAtom'

interface ActionButtonsProps {
localFormValidation: TwapFormState | null
primaryFormValidation: TradeFormValidation | null
}

export function ActionButtons({ localFormValidation, primaryFormValidation }: ActionButtonsProps) {
const { isPriceImpactAccepted } = useAtomValue(twapOrdersSettingsAtom)
const tradeConfirmActions = useTradeConfirmActions()
const { walletIsNotConnected, showPriceImpactWarning } = useTwapWarningsContext()
const { walletIsNotConnected } = useTwapWarningsContext()

const confirmTrade = tradeConfirmActions.onOpen

const areWarningsAccepted = showPriceImpactWarning ? isPriceImpactAccepted : true
const areWarningsAccepted = useAreWarningsAccepted()

const primaryActionContext = {
confirmTrade,
Expand Down
7 changes: 3 additions & 4 deletions src/modules/twap/containers/TwapFormWarnings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ import {
UnsupportedWalletWarning,
} from './warnings'

import { useFallbackHandlerVerification } from '../../hooks/useFallbackHandlerVerification'
import { useIsFallbackHandlerRequired } from '../../hooks/useFallbackHandlerVerification'
import { useTwapWarningsContext } from '../../hooks/useTwapWarningsContext'
import { TwapFormState } from '../../pure/PrimaryActionButton/getTwapFormState'
import { ExtensibleFallbackVerification } from '../../services/verifyExtensibleFallback'
import { twapOrdersSettingsAtom, updateTwapOrdersSettingsAtom } from '../../state/twapOrdersSettingsAtom'

interface TwapFormWarningsProps {
Expand All @@ -27,7 +26,7 @@ export function TwapFormWarnings({ localFormValidation }: TwapFormWarningsProps)
const updateTwapOrdersSettings = useUpdateAtom(updateTwapOrdersSettingsAtom)

const { chainId } = useWalletInfo()
const fallbackHandlerVerification = useFallbackHandlerVerification()
const isFallbackHandlerRequired = useIsFallbackHandlerRequired()
const isSafeViaWc = useIsSafeViaWc()
const { canTrade, showPriceImpactWarning, walletIsNotConnected } = useTwapWarningsContext()

Expand Down Expand Up @@ -68,7 +67,7 @@ export function TwapFormWarnings({ localFormValidation }: TwapFormWarningsProps)
return <SmallPartTimeWarning />
}

if (canTrade && fallbackHandlerVerification !== ExtensibleFallbackVerification.HAS_DOMAIN_VERIFIER) {
if (canTrade && isFallbackHandlerRequired) {
return (
<FallbackHandlerWarning
isFallbackHandlerSetupAccepted={isFallbackHandlerSetupAccepted}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ export function UnsupportedWalletWarning({ isSafeViaWc }: { isSafeViaWc: boolean

return (
<InlineBanner type="alert">
<strong>Unsupported wallet detected:</strong> TWAP orders currently require a Safe with a special fallback
handler. <br />
<strong>Unsupported wallet detected</strong>
<br />
TWAP orders currently require a Safe with a special fallback handler. <br />
Have one? Switch to it! {/*Need setup? <HashLink to="/faq/limit-order#how-do-fees-work">Click here</HashLink>.*/}{' '}
<br />
Future updates may extend wallet support!
Expand Down
8 changes: 3 additions & 5 deletions src/modules/twap/containers/TwapFormWidget/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ import * as styledEl from './styled'
import { AMOUNT_PARTS_LABELS, LABELS_TOOLTIPS } from './tooltips'

import { DEFAULT_TWAP_SLIPPAGE, defaultNumOfParts, orderDeadlines } from '../../const'
import { useFallbackHandlerVerification } from '../../hooks/useFallbackHandlerVerification'
import { useIsFallbackHandlerRequired } from '../../hooks/useFallbackHandlerVerification'
import { useTwapFormState } from '../../hooks/useTwapFormState'
import { AmountParts } from '../../pure/AmountParts'
import { DeadlineSelector } from '../../pure/DeadlineSelector'
import { ExtensibleFallbackVerification } from '../../services/verifyExtensibleFallback'
import { partsStateAtom } from '../../state/partsStateAtom'
import { twapTimeIntervalAtom } from '../../state/twapOrderAtom'
import { twapOrdersSettingsAtom, updateTwapOrdersSettingsAtom } from '../../state/twapOrdersSettingsAtom'
Expand All @@ -46,7 +45,7 @@ export function TwapFormWidget() {
const { inputCurrencyAmount, outputCurrencyAmount } = useAdvancedOrdersDerivedState()
const { inputCurrencyAmount: rawInputCurrencyAmount } = useAdvancedOrdersRawState()
const { updateState } = useTradeState()
const fallbackHandlerVerification = useFallbackHandlerVerification()
const isFallbackHandlerRequired = useIsFallbackHandlerRequired()

const partsState = useAtomValue(partsStateAtom)
const timeInterval = useAtomValue(twapTimeIntervalAtom)
Expand All @@ -66,7 +65,6 @@ export function TwapFormWidget() {
isCustomDeadline,
}

const fallbackHandlerIsNotSet = fallbackHandlerVerification !== ExtensibleFallbackVerification.HAS_DOMAIN_VERIFIER
const shouldLoadTwapOrders = !!(isSafeApp && chainId && account && composableCowContract)

// Reset output amount when num of parts or input amount are changed
Expand All @@ -87,7 +85,7 @@ export function TwapFormWidget() {
{shouldLoadTwapOrders && (
<TwapOrdersUpdater composableCowContract={composableCowContract} safeAddress={account} chainId={chainId} />
)}
<TwapConfirmModal fallbackHandlerIsNotSet={fallbackHandlerIsNotSet} />
<TwapConfirmModal fallbackHandlerIsNotSet={isFallbackHandlerRequired} />

{!isWrapOrUnwrap && (
<styledEl.Row>
Expand Down
18 changes: 18 additions & 0 deletions src/modules/twap/hooks/useAreWarningsAccepted.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useAtomValue } from 'jotai'

import { useIsFallbackHandlerRequired } from './useFallbackHandlerVerification'
import { useTwapWarningsContext } from './useTwapWarningsContext'

import { twapOrdersSettingsAtom } from '../state/twapOrdersSettingsAtom'

export function useAreWarningsAccepted(): boolean {
const { isPriceImpactAccepted, isFallbackHandlerSetupAccepted } = useAtomValue(twapOrdersSettingsAtom)
const { showPriceImpactWarning } = useTwapWarningsContext()
const isFallbackHandlerRequired = useIsFallbackHandlerRequired()

if (showPriceImpactWarning) return isPriceImpactAccepted

if (isFallbackHandlerRequired) return isFallbackHandlerSetupAccepted

return true
}
5 changes: 5 additions & 0 deletions src/modules/twap/hooks/useFallbackHandlerVerification.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { useAtomValue } from 'jotai/utils'

import { ExtensibleFallbackVerification } from '../services/verifyExtensibleFallback'
import { fallbackHandlerVerificationAtom } from '../state/fallbackHandlerVerificationAtom'

export function useFallbackHandlerVerification() {
return useAtomValue(fallbackHandlerVerificationAtom)
}

export function useIsFallbackHandlerRequired() {
return useFallbackHandlerVerification() !== ExtensibleFallbackVerification.HAS_DOMAIN_VERIFIER
}
5 changes: 1 addition & 4 deletions src/modules/twap/hooks/useTwapFormState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ import { useFallbackHandlerVerification } from './useFallbackHandlerVerification
import { getTwapFormState, TwapFormState } from '../pure/PrimaryActionButton/getTwapFormState'
import { partsStateAtom } from '../state/partsStateAtom'
import { twapOrderAtom, twapTimeIntervalAtom } from '../state/twapOrderAtom'
import { twapOrdersSettingsAtom } from '../state/twapOrdersSettingsAtom'

export function useTwapFormState(): TwapFormState | null {
const { isFallbackHandlerSetupAccepted } = useAtomValue(twapOrdersSettingsAtom)
const { chainId } = useWalletInfo()

const twapOrder = useAtomValue(twapOrderAtom)
Expand All @@ -24,12 +22,11 @@ export function useTwapFormState(): TwapFormState | null {
return useMemo(() => {
return getTwapFormState({
isSafeApp,
isFallbackHandlerSetupAccepted,
verification,
twapOrder,
sellAmountPartFiat,
chainId,
partTime,
})
}, [isSafeApp, isFallbackHandlerSetupAccepted, verification, twapOrder, sellAmountPartFiat, chainId, partTime])
}, [isSafeApp, verification, twapOrder, sellAmountPartFiat, chainId, partTime])
}
2 changes: 1 addition & 1 deletion src/modules/twap/hooks/useTwapWarningsContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function useTwapWarningsContext(): TwapWarningsContext {
return useMemo(() => {
// TODO: bind to settings
const expertMode = false
const canTrade = !primaryFormValidation
const canTrade = !primaryFormValidation || primaryFormValidation >= TradeFormValidation.ExpertApproveAndSwap
const showPriceImpactWarning = canTrade && !expertMode && !!priceImpact.error
const walletIsNotConnected = primaryFormValidation === TradeFormValidation.WalletNotConnected

Expand Down
13 changes: 1 addition & 12 deletions src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { isSellAmountTooSmall } from '../../utils/isSellAmountTooSmall'

export interface TwapFormStateParams {
isSafeApp: boolean
isFallbackHandlerSetupAccepted: boolean
verification: ExtensibleFallbackVerification | null
twapOrder: TWAPOrder | null
sellAmountPartFiat: Nullish<CurrencyAmount<Currency>>
Expand All @@ -21,27 +20,17 @@ export interface TwapFormStateParams {
export enum TwapFormState {
LOADING_SAFE_INFO = 'LOADING_SAFE_INFO',
NOT_SAFE = 'NOT_SAFE',
NEED_FALLBACK_HANDLER = 'NEED_FALLBACK_HANDLER',
SELL_AMOUNT_TOO_SMALL = 'SELL_AMOUNT_TOO_SMALL',
PART_TIME_INTERVAL_TOO_SHORT = 'PART_TIME_INTERVAL_TOO_SHORT',
}

export function getTwapFormState(props: TwapFormStateParams): TwapFormState | null {
const { twapOrder, isSafeApp, isFallbackHandlerSetupAccepted, verification, sellAmountPartFiat, chainId, partTime } =
props
const { isSafeApp, verification, sellAmountPartFiat, chainId, partTime } = props

if (!isSafeApp) return TwapFormState.NOT_SAFE

if (verification === null) return TwapFormState.LOADING_SAFE_INFO

if (
twapOrder &&
verification !== ExtensibleFallbackVerification.HAS_DOMAIN_VERIFIER &&
!isFallbackHandlerSetupAccepted
) {
return TwapFormState.NEED_FALLBACK_HANDLER
}

if (isSellAmountTooSmall(sellAmountPartFiat, chainId)) {
return TwapFormState.SELL_AMOUNT_TOO_SMALL
}
Expand Down
5 changes: 0 additions & 5 deletions src/modules/twap/pure/PrimaryActionButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ const buttonsMap: Record<TwapFormState, (context: PrimaryActionButtonContext) =>
Unsupported wallet
</ButtonPrimary>
),
[TwapFormState.NEED_FALLBACK_HANDLER]: () => (
<ButtonPrimary disabled={true} buttonSize={ButtonSize.BIG}>
Unsupported Safe
</ButtonPrimary>
),
[TwapFormState.SELL_AMOUNT_TOO_SMALL]: () => (
<ButtonPrimary disabled={true} buttonSize={ButtonSize.BIG}>
Sell amount too small
Expand Down

0 comments on commit 19ed7ee

Please sign in to comment.