Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useSetAtom } from 'jotai'
import { useEffect } from 'react'

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

interface Erc20ApproveProps {
isPartialApprovalEnabled: boolean
}

export function Erc20ApproveWidget({ isPartialApprovalEnabled }: Erc20ApproveProps): null {
const setIsPartialApproveEnabled = useSetAtom(isPartialApproveEnabledAtom)

useEffect(() => {
setIsPartialApproveEnabled(isPartialApprovalEnabled)
}, [setIsPartialApproveEnabled, isPartialApprovalEnabled])

return null
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useAtomValue } from 'jotai'
import { ReactNode } from 'react'

import {
Expand All @@ -8,15 +9,16 @@ import {
} from '../../hooks'
import { TradeAllowanceDisplay } from '../../pure/TradeAllowanceDisplay'
import { useSetUserApproveAmountModalState } from '../../state'
import { isPartialApproveEnabledAtom } from '../../state/isPartialApproveEnabledAtom'
import { isMaxAmountToApprove } from '../../utils'
import { ActiveOrdersWithAffectedPermit } from '../ActiveOrdersWithAffectedPermit'
import { TradeApproveToggle } from '../TradeApproveToggle'

export function TradeApproveWithAffectedOrderList(): ReactNode {
const {
reason: isApproveRequired,
currentAllowance
} = useIsApprovalOrPermitRequired({ isBundlingSupportedOrEnabledForContext: false })
const { reason: isApproveRequired, currentAllowance } = useIsApprovalOrPermitRequired({
isBundlingSupportedOrEnabledForContext: false,
})
const isPartialApprovalEnabledInSettings = useAtomValue(isPartialApproveEnabledAtom)

const setUserApproveAmountModalState = useSetUserApproveAmountModalState()

Expand All @@ -30,7 +32,7 @@ export function TradeApproveWithAffectedOrderList(): ReactNode {
isApproveRequired === ApproveRequiredReason.Required ||
isApproveRequired === ApproveRequiredReason.Eip2612PermitRequired

if (!partialAmountToApprove) return null
if (!partialAmountToApprove || !isPartialApprovalEnabledInSettings) return null

const currencyToApprove = partialAmountToApprove.currency

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './TradeApproveWithAffectedOrderList'
export * from './TradeChangeApproveAmountModal'
export * from './PartialApproveAmountModal'
export * from './PartialApproveContainer'
export * from './Erc20ApproveWidget'
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useAtomValue } from 'jotai'

import { useFeatureFlags } from '@cowprotocol/common-hooks'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'

Expand All @@ -8,10 +10,14 @@ import { useNeedsApproval } from 'common/hooks/useNeedsApproval'
import { useGetAmountToSignApprove } from './useGetAmountToSignApprove'
import { useGetPartialAmountToSignApprove } from './useGetPartialAmountToSignApprove'

import { useSwapPartialApprovalToggleState } from '../../swap/hooks/useSwapSettings'
import { MAX_APPROVE_AMOUNT } from '../constants'
import { useIsPartialApproveSelectedByUser } from '../state'

jest.mock('jotai', () => ({
...jest.requireActual('jotai'),
useAtomValue: jest.fn(),
}))

jest.mock('@cowprotocol/common-hooks', () => ({
useFeatureFlags: jest.fn(),
}))
Expand All @@ -24,22 +30,16 @@ jest.mock('./useGetPartialAmountToSignApprove', () => ({
useGetPartialAmountToSignApprove: jest.fn(),
}))

jest.mock('../../swap/hooks/useSwapSettings', () => ({
useSwapPartialApprovalToggleState: jest.fn(),
}))

jest.mock('../state', () => ({
useIsPartialApproveSelectedByUser: jest.fn(),
}))

const mockUseAtomValue = useAtomValue as jest.MockedFunction<typeof useAtomValue>
const mockUseFeatureFlags = useFeatureFlags as jest.MockedFunction<typeof useFeatureFlags>
const mockUseNeedsApproval = useNeedsApproval as jest.MockedFunction<typeof useNeedsApproval>
const mockUseGetPartialAmountToSignApprove = useGetPartialAmountToSignApprove as jest.MockedFunction<
typeof useGetPartialAmountToSignApprove
>
const mockUseSwapPartialApprovalToggleState = useSwapPartialApprovalToggleState as jest.MockedFunction<
typeof useSwapPartialApprovalToggleState
>
const mockUseIsPartialApproveSelectedByUser = useIsPartialApproveSelectedByUser as jest.MockedFunction<
typeof useIsPartialApproveSelectedByUser
>
Expand All @@ -57,7 +57,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseIsPartialApproveSelectedByUser.mockReturnValue(false)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: true })
mockUseSwapPartialApprovalToggleState.mockReturnValue([true, jest.fn()])
mockUseAtomValue.mockReturnValue(true)
})

describe('when partialAmountToSign is null', () => {
Expand All @@ -82,7 +82,7 @@ describe('useGetAmountToSignApprove', () => {
it('should return zero amount when approval is not needed regardless of partial approval settings', () => {
mockUseNeedsApproval.mockReturnValue(false)
mockUseIsPartialApproveSelectedByUser.mockReturnValue(true)
mockUseSwapPartialApprovalToggleState.mockReturnValue([true, jest.fn()])
mockUseAtomValue.mockReturnValue(true)

const { result } = renderHook(() => useGetAmountToSignApprove())

Expand All @@ -95,7 +95,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseIsPartialApproveSelectedByUser.mockReturnValue(true)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: true })
mockUseSwapPartialApprovalToggleState.mockReturnValue([true, jest.fn()])
mockUseAtomValue.mockReturnValue(true)

const { result } = renderHook(() => useGetAmountToSignApprove())

Expand All @@ -106,7 +106,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseIsPartialApproveSelectedByUser.mockReturnValue(false)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: true })
mockUseSwapPartialApprovalToggleState.mockReturnValue([true, jest.fn()])
mockUseAtomValue.mockReturnValue(true)

const { result } = renderHook(() => useGetAmountToSignApprove())

Expand All @@ -117,7 +117,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseIsPartialApproveSelectedByUser.mockReturnValue(true)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: true })
mockUseSwapPartialApprovalToggleState.mockReturnValue([false, jest.fn()])
mockUseAtomValue.mockReturnValue(false)

const { result } = renderHook(() => useGetAmountToSignApprove())

Expand All @@ -129,7 +129,7 @@ describe('useGetAmountToSignApprove', () => {
it('should return max amount when isPartialApproveEnabled is false', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: false })
mockUseSwapPartialApprovalToggleState.mockReturnValue([null, null])
mockUseAtomValue.mockReturnValue(false)

const { result } = renderHook(() => useGetAmountToSignApprove())

Expand All @@ -140,7 +140,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseIsPartialApproveSelectedByUser.mockReturnValue(true)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: false })
mockUseSwapPartialApprovalToggleState.mockReturnValue([null, null])
mockUseAtomValue.mockReturnValue(false)

const { result } = renderHook(() => useGetAmountToSignApprove())

Expand All @@ -153,7 +153,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseIsPartialApproveSelectedByUser.mockReturnValue(true)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: false })
mockUseSwapPartialApprovalToggleState.mockReturnValue([null, null])
mockUseAtomValue.mockReturnValue(false)

const { result } = renderHook(() => useGetAmountToSignApprove())

Expand All @@ -164,7 +164,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseIsPartialApproveSelectedByUser.mockReturnValue(true)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: true })
mockUseSwapPartialApprovalToggleState.mockReturnValue([false, jest.fn()])
mockUseAtomValue.mockReturnValue(false)

const { result } = renderHook(() => useGetAmountToSignApprove())

Expand All @@ -175,7 +175,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseIsPartialApproveSelectedByUser.mockReturnValue(false)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: true })
mockUseSwapPartialApprovalToggleState.mockReturnValue([true, jest.fn()])
mockUseAtomValue.mockReturnValue(true)

const { result } = renderHook(() => useGetAmountToSignApprove())

Expand Down Expand Up @@ -250,7 +250,7 @@ describe('useGetAmountToSignApprove', () => {

const firstResult = result.current

mockUseSwapPartialApprovalToggleState.mockReturnValue([false, jest.fn()])
mockUseAtomValue.mockReturnValue(false)
rerender()

expect(result.current).not.toBe(firstResult)
Expand All @@ -263,7 +263,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseIsPartialApproveSelectedByUser.mockReturnValue(true)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: true })
mockUseSwapPartialApprovalToggleState.mockReturnValue([true, jest.fn()])
mockUseAtomValue.mockReturnValue(true)

const { result } = renderHook(() => useGetAmountToSignApprove())

Expand Down Expand Up @@ -293,7 +293,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: scenario.isPartialApproveEnabled })
mockUseIsPartialApproveSelectedByUser.mockReturnValue(scenario.isPartialApprovalSelectedByUser)
mockUseSwapPartialApprovalToggleState.mockReturnValue([scenario.isPartialApprovalEnabledInSettings, jest.fn()])
mockUseAtomValue.mockReturnValue(scenario.isPartialApprovalEnabledInSettings)

const { result } = renderHook(() => useGetAmountToSignApprove())

Expand All @@ -305,7 +305,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: true })
mockUseIsPartialApproveSelectedByUser.mockReturnValue(true)
mockUseSwapPartialApprovalToggleState.mockReturnValue([false, jest.fn()])
mockUseAtomValue.mockReturnValue(false)

const { result } = renderHook(() => useGetAmountToSignApprove())

Expand Down Expand Up @@ -337,7 +337,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseIsPartialApproveSelectedByUser.mockReturnValue(true)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: true })
mockUseSwapPartialApprovalToggleState.mockReturnValue([true, jest.fn()])
mockUseAtomValue.mockReturnValue(true)

const { result } = renderHook(() => useGetAmountToSignApprove())

Expand All @@ -351,7 +351,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseIsPartialApproveSelectedByUser.mockReturnValue(true)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: true })
mockUseSwapPartialApprovalToggleState.mockReturnValue([true, jest.fn()])
mockUseAtomValue.mockReturnValue(true)

const { result, rerender } = renderHook(() => useGetAmountToSignApprove())

Expand All @@ -368,7 +368,7 @@ describe('useGetAmountToSignApprove', () => {
mockUseNeedsApproval.mockReturnValue(true)
mockUseIsPartialApproveSelectedByUser.mockReturnValue(true)
mockUseFeatureFlags.mockReturnValue({ isPartialApproveEnabled: true })
mockUseSwapPartialApprovalToggleState.mockReturnValue([true, jest.fn()])
mockUseAtomValue.mockReturnValue(true)
rerender()
expect(result.current).toEqual(mockPartialAmount)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useAtomValue } from 'jotai'
import { useMemo } from 'react'

import { useFeatureFlags } from '@cowprotocol/common-hooks'
Expand All @@ -7,9 +8,9 @@ import { useNeedsApproval } from 'common/hooks/useNeedsApproval'

import { useGetPartialAmountToSignApprove } from './useGetPartialAmountToSignApprove'

import { useSwapPartialApprovalToggleState } from '../../swap/hooks/useSwapSettings'
import { MAX_APPROVE_AMOUNT } from '../constants'
import { useIsPartialApproveSelectedByUser } from '../state'
import { isPartialApproveEnabledAtom } from '../state/isPartialApproveEnabledAtom'

/**
* Returns the amount to sign for the approval transaction/permit
Expand All @@ -23,7 +24,7 @@ export function useGetAmountToSignApprove(): CurrencyAmount<Currency> | null {
const isApprovalNeeded = useNeedsApproval(partialAmountToSign)
const isPartialApprovalSelectedByUser = useIsPartialApproveSelectedByUser()
const { isPartialApproveEnabled } = useFeatureFlags()
const [isPartialApprovalEnabledInSettings] = useSwapPartialApprovalToggleState(isPartialApproveEnabled)
const isPartialApprovalEnabledInSettings = useAtomValue(isPartialApproveEnabledAtom)

return useMemo(() => {
if (!partialAmountToSign) return null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { atom } from 'jotai'

export const isPartialApproveEnabledAtom = atom<boolean>(false)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'

import { useFeatureFlags } from '@cowprotocol/common-hooks'
import { getIsNativeToken, isInjectedWidget, isSellOrder } from '@cowprotocol/common-utils'
import { isInjectedWidget, isSellOrder } from '@cowprotocol/common-utils'
import { useIsEagerConnectInProgress, useIsSmartContractWallet, useWalletInfo } from '@cowprotocol/wallet'

import { Field } from 'legacy/state/types'
Expand Down Expand Up @@ -52,7 +52,6 @@ export interface SwapWidgetProps {
}

// TODO: Break down this large function into smaller functions
// TODO: Add proper return type annotation
// eslint-disable-next-line max-lines-per-function,complexity
export function SwapWidget({ topContent, bottomContent }: SwapWidgetProps): ReactNode {
const { showRecipient } = useSwapSettings()
Expand Down Expand Up @@ -87,7 +86,7 @@ export function SwapWidget({ topContent, bottomContent }: SwapWidgetProps): Reac
orderKind,
isUnlocked,
} = useSwapDerivedState()
const doTrade = useHandleSwap(useSafeMemoObject({ deadline: deadlineState[0] }), widgetActions)
const doTrade = useHandleSwap({ deadline: deadlineState[0] }, widgetActions)
const hasEnoughWrappedBalanceForSwap = useHasEnoughWrappedBalanceForSwap()
const isSmartContractWallet = useIsSmartContractWallet()
const { account } = useWalletInfo()
Expand Down Expand Up @@ -161,8 +160,6 @@ export function SwapWidget({ topContent, bottomContent }: SwapWidgetProps): Reac
const { isPartialApproveEnabled } = useFeatureFlags()
const enablePartialApprovalState = useSwapPartialApprovalToggleState(isPartialApproveEnabled)

const enablePartialApproval = enablePartialApprovalState[0] && inputCurrency && !getIsNativeToken(inputCurrency)

const isConnected = Boolean(account)
const isNetworkUnsupported = useIsProviderNetworkUnsupported()

Expand Down Expand Up @@ -190,7 +187,7 @@ export function SwapWidget({ topContent, bottomContent }: SwapWidgetProps): Reac
return (
<>
{bottomContent}
{enablePartialApproval ? <TradeApproveWithAffectedOrderList /> : null}
<TradeApproveWithAffectedOrderList />
<SwapRateDetails rateInfoParams={rateInfoParams} deadline={deadlineState[0]} />
<Warnings buyingFiatAmount={buyingFiatAmount} />
{tradeWarnings}
Expand All @@ -215,7 +212,6 @@ export function SwapWidget({ topContent, bottomContent }: SwapWidgetProps): Reac
hasEnoughWrappedBalanceForSwap,
toBeImported,
intermediateBuyToken,
enablePartialApproval,
],
),
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { useTradeFlowContext } from 'modules/tradeFlow'

import { useSafeMemoObject } from 'common/hooks/useSafeMemo'
import { TradeFlowContext, useTradeFlowContext } from 'modules/tradeFlow'

import { useSwapDeadlineState } from './useSwapSettings'

// TODO: Add proper return type annotation
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function useSwapFlowContext() {
export function useSwapFlowContext(): TradeFlowContext | null {
const [deadline] = useSwapDeadlineState()
return useTradeFlowContext(useSafeMemoObject({ deadline }))

return useTradeFlowContext({ deadline })
}
5 changes: 4 additions & 1 deletion apps/cowswap-frontend/src/modules/swap/updaters/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ReactNode } from 'react'
import { isSellOrder, percentToBps } from '@cowprotocol/common-utils'

import { AppDataUpdater } from 'modules/appData'
import { Erc20ApproveWidget } from 'modules/erc20Approve'
import { EthFlowDeadlineUpdater } from 'modules/ethFlow'
import { useIsHooksTradeType } from 'modules/trade'
import { useSetTradeQuoteParams } from 'modules/tradeQuote'
Expand All @@ -13,13 +14,14 @@ import { SetupSwapAmountsFromUrlUpdater } from './SetupSwapAmountsFromUrlUpdater
import { UnfillableSwapOrdersUpdater } from './UnfillableSwapOrdersUpdater'

import { useFillSwapDerivedState, useSwapDerivedState } from '../hooks/useSwapDerivedState'
import { useSwapDeadlineState } from '../hooks/useSwapSettings'
import { useSwapDeadlineState, useSwapSettings } from '../hooks/useSwapSettings'

export function SwapUpdaters(): ReactNode {
const { orderKind, inputCurrencyAmount, outputCurrencyAmount, slippage } = useSwapDerivedState()
const isSmartSlippageApplied = useIsSmartSlippageApplied()
const swapDeadlineState = useSwapDeadlineState()
const partiallyFillable = useIsHooksTradeType()
const { enablePartialApprovalBySettings } = useSwapSettings()

useFillSwapDerivedState()
useSetTradeQuoteParams({
Expand All @@ -34,6 +36,7 @@ export function SwapUpdaters(): ReactNode {
<EthFlowDeadlineUpdater deadlineState={swapDeadlineState} />
<SetupSwapAmountsFromUrlUpdater />
<QuoteObserverUpdater />
<Erc20ApproveWidget isPartialApprovalEnabled={enablePartialApprovalBySettings} />
{slippage && (
<AppDataUpdater
orderClass="market"
Expand Down
Loading