From 7a1ce48fb5ff7be92f263cf5a921d815b469c69d Mon Sep 17 00:00:00 2001 From: Alfetopito Date: Thu, 9 Jan 2025 10:44:00 +0000 Subject: [PATCH] Revert "feat(swap): partial approve (#5256)" This reverts commit f080ffdb098612e729f3a3f829410ce78697979f. --- .../containers/OrderHooksDetails/index.tsx | 41 +--------- .../TradeApprove/TradeApproveButton.tsx | 5 +- .../TradeApprove/useTradeApproveCallback.ts | 9 +-- .../src/common/hooks/useApproveCallback.ts | 15 ++-- .../src/common/pure/ApproveButton/index.tsx | 1 + .../common/pure/OrderProgressBarV2/index.tsx | 15 +--- .../common/pure/OrderProgressBarV2/styled.ts | 12 ++- .../src/common/utils/parsePermitData.ts | 15 ---- .../src/legacy/state/user/hooks.tsx | 19 ----- .../src/legacy/state/user/reducer.ts | 7 +- .../src/lib/hooks/useApproval.ts | 74 ++++++++++++++++++- .../operations/bundle/buildApproveTx.ts | 5 +- .../permit/hooks/useGeneratePermitHook.ts | 10 +-- .../permit/hooks/useGetCachedPermit.ts | 5 +- .../modules/permit/state/permitCacheAtom.ts | 31 ++------ .../src/modules/permit/types.ts | 4 +- .../src/modules/permit/utils/handlePermit.ts | 3 +- .../ConfirmSwapModalSetup/index.tsx | 8 +- .../modules/swap/containers/EthFlow/index.tsx | 8 +- .../swap/containers/SwapWidget/index.tsx | 10 +-- .../swap/hooks/useHandleSwapOrEthFlow.ts | 8 +- .../swap/hooks/useSwapButtonContext.ts | 4 - .../modules/swap/hooks/useSwapFlowContext.ts | 6 +- .../EthFlow/EthFlowModalContent/configs.ts | 4 +- .../swap/pure/SwapButtons/index.cosmos.tsx | 1 - .../modules/swap/pure/SwapButtons/index.tsx | 3 +- .../pure/banners/PartialApprovalBanner.tsx | 31 -------- .../trade/pure/TradeConfirmation/index.tsx | 18 +---- .../tradeFlow/hooks/useTradeFlowContext.ts | 8 +- .../safeBundleFlow/safeBundleApprovalFlow.ts | 11 +-- .../safeBundleFlow/safeBundleEthFlow.ts | 10 +-- .../tradeFlow/services/swapFlow/index.ts | 1 - .../tradeFlow/types/TradeFlowContext.ts | 1 - .../containers/SettingsTab/index.tsx | 41 +--------- .../pure/Row/RowSlippageContent/index.tsx | 7 +- .../state/settingsTabState.ts | 8 +- .../cowswap-frontend/src/pages/Swap/index.tsx | 5 +- .../utils/orderUtils/getOrderPermitAmount.ts | 7 +- libs/hook-dapp-lib/src/hookDappsRegistry.json | 2 +- .../src/lib/generatePermitHook.ts | 14 +--- libs/permit-utils/src/types.ts | 1 - libs/ui/src/containers/InlineBanner/index.tsx | 2 +- libs/ui/src/pure/LinkStyledButton/index.tsx | 9 --- 43 files changed, 149 insertions(+), 350 deletions(-) delete mode 100644 apps/cowswap-frontend/src/common/utils/parsePermitData.ts delete mode 100644 apps/cowswap-frontend/src/modules/swap/pure/banners/PartialApprovalBanner.tsx diff --git a/apps/cowswap-frontend/src/common/containers/OrderHooksDetails/index.tsx b/apps/cowswap-frontend/src/common/containers/OrderHooksDetails/index.tsx index 67e607b666..3c8a623983 100644 --- a/apps/cowswap-frontend/src/common/containers/OrderHooksDetails/index.tsx +++ b/apps/cowswap-frontend/src/common/containers/OrderHooksDetails/index.tsx @@ -3,8 +3,6 @@ import { ReactElement, useEffect, useMemo, useState } from 'react' import { latest } from '@cowprotocol/app-data' import { CowHookDetails, HookToDappMatch, matchHooksToDappsRegistry } from '@cowprotocol/hook-dapp-lib' import { InfoTooltip } from '@cowprotocol/ui' -import { useWalletInfo } from '@cowprotocol/wallet' -import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { ChevronDown, ChevronUp } from 'react-feather' @@ -16,27 +14,15 @@ import { HookItem } from './HookItem' import * as styledEl from './styled' import { CircleCount } from './styled' -import { parsePermitData } from '../../utils/parsePermitData' - interface OrderHooksDetailsProps { appData: string | AppDataInfo children: (content: ReactElement) => ReactElement margin?: string isTradeConfirmation?: boolean - slippageAdjustedSellAmount?: CurrencyAmount - isPartialApprove?: boolean } -export function OrderHooksDetails({ - appData, - children, - margin, - isTradeConfirmation, - slippageAdjustedSellAmount, - isPartialApprove, -}: OrderHooksDetailsProps) { +export function OrderHooksDetails({ appData, children, margin, isTradeConfirmation }: OrderHooksDetailsProps) { const [isOpen, setOpen] = useState(false) - const { account } = useWalletInfo() const appDataDoc = useMemo(() => { return typeof appData === 'string' ? decodeAppData(appData) : appData.doc }, [appData]) @@ -55,32 +41,9 @@ export function OrderHooksDetails({ const metadata = appDataDoc.metadata as latest.Metadata - /** - * AppData might include a hook with account agnostic permit which is used to fetch a quote. - * This hook should be ignored. - * Moreover, any hook with a permit which has owner !== current account will be excluded. - * We also remove the permit from appData before order signing (see filterPermitSignerPermit). - */ - const preHooks = account - ? metadata.hooks?.pre?.filter((hook) => { - try { - const permitHookData = parsePermitData(hook.callData) - const isOwnerMatched = permitHookData.owner.toLowerCase() === account.toLowerCase() - - // If the hook is a partial approve, we need to check if the value is equal to the slippageAdjustedSellAmount - // Because there might be a hook with an "infinite" permit from other widget - return isPartialApprove && slippageAdjustedSellAmount - ? isOwnerMatched && permitHookData.value.eq(slippageAdjustedSellAmount.quotient.toString()) - : isOwnerMatched - } catch { - return true - } - }) - : metadata.hooks?.pre - const hasSomeFailedSimulation = isTradeConfirmation && Object.values(data || {}).some((hook) => !hook.status) - const preHooksToDapp = matchHooksToDappsRegistry(preHooks || [], preCustomHookDapps) + const preHooksToDapp = matchHooksToDappsRegistry(metadata.hooks?.pre || [], preCustomHookDapps) const postHooksToDapp = matchHooksToDappsRegistry(metadata.hooks?.post || [], postCustomHookDapps) if (!preHooksToDapp.length && !postHooksToDapp.length) return null diff --git a/apps/cowswap-frontend/src/common/containers/TradeApprove/TradeApproveButton.tsx b/apps/cowswap-frontend/src/common/containers/TradeApprove/TradeApproveButton.tsx index 3e31105e1d..68e68ef2b3 100644 --- a/apps/cowswap-frontend/src/common/containers/TradeApprove/TradeApproveButton.tsx +++ b/apps/cowswap-frontend/src/common/containers/TradeApprove/TradeApproveButton.tsx @@ -14,16 +14,15 @@ export interface TradeApproveButtonProps { amountToApprove: CurrencyAmount children?: React.ReactNode isDisabled?: boolean - isPartialApprove?: boolean } export function TradeApproveButton(props: TradeApproveButtonProps) { - const { amountToApprove, children, isDisabled, isPartialApprove } = props + const { amountToApprove, children, isDisabled } = props const currency = amountToApprove.currency const { state: approvalState } = useApproveState(amountToApprove) - const tradeApproveCallback = useTradeApproveCallback(amountToApprove, isPartialApprove) + const tradeApproveCallback = useTradeApproveCallback(amountToApprove) const shouldZeroApprove = useShouldZeroApprove(amountToApprove) const zeroApprove = useZeroApprove(amountToApprove.currency) diff --git a/apps/cowswap-frontend/src/common/containers/TradeApprove/useTradeApproveCallback.ts b/apps/cowswap-frontend/src/common/containers/TradeApprove/useTradeApproveCallback.ts index 552a32d42b..3e5136344c 100644 --- a/apps/cowswap-frontend/src/common/containers/TradeApprove/useTradeApproveCallback.ts +++ b/apps/cowswap-frontend/src/common/containers/TradeApprove/useTradeApproveCallback.ts @@ -19,16 +19,13 @@ export interface TradeApproveCallback { (params?: TradeApproveCallbackParams): Promise } -export function useTradeApproveCallback( - amountToApprove?: CurrencyAmount, - isPartialApprove?: boolean, -): TradeApproveCallback { +export function useTradeApproveCallback(amountToApprove?: CurrencyAmount): TradeApproveCallback { const updateTradeApproveState = useUpdateTradeApproveState() const spender = useTradeSpenderAddress() const currency = amountToApprove?.currency const symbol = currency?.symbol - const approveCallback = useApproveCallback(amountToApprove, spender, isPartialApprove) + const approveCallback = useApproveCallback(amountToApprove, spender) return useCallback( async ({ useModals = true }: TradeApproveCallbackParams = { useModals: true }) => { @@ -61,6 +58,6 @@ export function useTradeApproveCallback( return undefined }) }, - [symbol, approveCallback, updateTradeApproveState, currency], + [symbol, approveCallback, updateTradeApproveState, currency] ) } diff --git a/apps/cowswap-frontend/src/common/hooks/useApproveCallback.ts b/apps/cowswap-frontend/src/common/hooks/useApproveCallback.ts index 8a07a9ef95..6fce1a2444 100644 --- a/apps/cowswap-frontend/src/common/hooks/useApproveCallback.ts +++ b/apps/cowswap-frontend/src/common/hooks/useApproveCallback.ts @@ -18,18 +18,14 @@ export async function estimateApprove( tokenContract: Erc20, spender: string, amountToApprove: CurrencyAmount, - isPartialApprove?: boolean, ): Promise<{ approveAmount: BigNumber | string gasLimit: BigNumber }> { - const approveAmount = - isPartialApprove && amountToApprove ? BigNumber.from(amountToApprove.quotient.toString()) : MaxUint256 - try { return { - approveAmount, - gasLimit: await tokenContract.estimateGas.approve(spender, approveAmount), + approveAmount: MaxUint256, + gasLimit: await tokenContract.estimateGas.approve(spender, MaxUint256), } } catch { // Fallback: Attempt to set an approval for the maximum wallet balance (instead of the MaxUint256). @@ -49,7 +45,7 @@ export async function estimateApprove( ) return { - approveAmount, + approveAmount: MaxUint256, gasLimit: GAS_LIMIT_DEFAULT, } } @@ -59,7 +55,6 @@ export async function estimateApprove( export function useApproveCallback( amountToApprove?: CurrencyAmount, spender?: string, - isPartialApprove?: boolean, ): (summary?: string) => Promise { const { chainId } = useWalletInfo() const currency = amountToApprove?.currency @@ -73,7 +68,7 @@ export function useApproveCallback( return } - const estimation = await estimateApprove(tokenContract, spender, amountToApprove, isPartialApprove) + const estimation = await estimateApprove(tokenContract, spender, amountToApprove) return tokenContract .approve(spender, estimation.approveAmount, { gasLimit: calculateGasMargin(estimation.gasLimit), @@ -86,5 +81,5 @@ export function useApproveCallback( }) return response }) - }, [chainId, token, tokenContract, amountToApprove, spender, addTransaction, isPartialApprove]) + }, [chainId, token, tokenContract, amountToApprove, spender, addTransaction]) } diff --git a/apps/cowswap-frontend/src/common/pure/ApproveButton/index.tsx b/apps/cowswap-frontend/src/common/pure/ApproveButton/index.tsx index 68bda945b0..13273df82e 100644 --- a/apps/cowswap-frontend/src/common/pure/ApproveButton/index.tsx +++ b/apps/cowswap-frontend/src/common/pure/ApproveButton/index.tsx @@ -50,6 +50,7 @@ export function ApproveButton(props: ApproveButtonProps) { content={ You must give the CoW Protocol smart contracts permission to use your . + If you approve the default amount, you will only have to do this once per token. } > diff --git a/apps/cowswap-frontend/src/common/pure/OrderProgressBarV2/index.tsx b/apps/cowswap-frontend/src/common/pure/OrderProgressBarV2/index.tsx index 605eb3faf3..81d1eaafc2 100644 --- a/apps/cowswap-frontend/src/common/pure/OrderProgressBarV2/index.tsx +++ b/apps/cowswap-frontend/src/common/pure/OrderProgressBarV2/index.tsx @@ -22,16 +22,7 @@ import { ExplorerDataType, getExplorerLink, getRandomInt, isSellOrder, shortenAd import { OrderKind, SupportedChainId } from '@cowprotocol/cow-sdk' import { TokenLogo } from '@cowprotocol/tokens' import { Command } from '@cowprotocol/types' -import { - Confetti, - ExternalLink, - InfoTooltip, - ProductLogo, - ProductVariant, - TokenAmount, - UI, - UnderlinedLinkStyledButton, -} from '@cowprotocol/ui' +import { Confetti, ExternalLink, InfoTooltip, ProductLogo, ProductVariant, TokenAmount, UI } from '@cowprotocol/ui' import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { AnimatePresence, motion } from 'framer-motion' @@ -1132,14 +1123,14 @@ function ExpiredStep(props: OrderProgressBarV2Props) {

The good news

Unlike on other exchanges, you won't be charged for this! Feel free to{' '} - { props.navigateToNewOrder?.() trackNewOrderClick() }} > place a new order - {' '} + {' '} without worry.

diff --git a/apps/cowswap-frontend/src/common/pure/OrderProgressBarV2/styled.ts b/apps/cowswap-frontend/src/common/pure/OrderProgressBarV2/styled.ts index cd1a6a3de0..198fe338ff 100644 --- a/apps/cowswap-frontend/src/common/pure/OrderProgressBarV2/styled.ts +++ b/apps/cowswap-frontend/src/common/pure/OrderProgressBarV2/styled.ts @@ -1,6 +1,6 @@ import IMAGE_STAR_SHINE from '@cowprotocol/assets/cow-swap/star-shine.svg' import { SingleLetterLogoWrapper } from '@cowprotocol/tokens' -import { ButtonPrimary, Font, Media, UI } from '@cowprotocol/ui' +import { ButtonPrimary, Font, LinkStyledButton, Media, UI } from '@cowprotocol/ui' import styled, { css, keyframes } from 'styled-components/macro' @@ -66,7 +66,6 @@ export const StepsContainer = styled.div<{ $height: number; $minHeight?: string; padding: 0; // implement a gradient to hide the bottom of the steps container using white to opacity white using pseudo element - &::after { content: ${({ bottomGradient }) => (bottomGradient ? '""' : 'none')}; position: absolute; @@ -144,6 +143,15 @@ export const CancelButton = styled(CancelButtonOriginal)` } ` +export const Button = styled(LinkStyledButton)` + font-size: 14px; + text-decoration: underline; + + &:hover { + text-decoration: none; + } +` + export const ProgressImageWrapper = styled.div<{ bgColor?: string; padding?: string; height?: string; gap?: string }>` width: 100%; height: ${({ height }) => height || '246px'}; diff --git a/apps/cowswap-frontend/src/common/utils/parsePermitData.ts b/apps/cowswap-frontend/src/common/utils/parsePermitData.ts deleted file mode 100644 index 6ba16f7c1a..0000000000 --- a/apps/cowswap-frontend/src/common/utils/parsePermitData.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Erc20__factory } from '@cowprotocol/abis' -import type { BigNumber } from '@ethersproject/bignumber' - -const erc20Interface = Erc20__factory.createInterface() - -export interface PermitParameters { - owner: string - spender: string - value: BigNumber - deadline: BigNumber -} - -export function parsePermitData(callData: string): PermitParameters { - return erc20Interface.decodeFunctionData('permit', callData) as unknown as PermitParameters -} diff --git a/apps/cowswap-frontend/src/legacy/state/user/hooks.tsx b/apps/cowswap-frontend/src/legacy/state/user/hooks.tsx index 24aeb1e878..2f86b41a32 100644 --- a/apps/cowswap-frontend/src/legacy/state/user/hooks.tsx +++ b/apps/cowswap-frontend/src/legacy/state/user/hooks.tsx @@ -8,11 +8,8 @@ import { Currency } from '@uniswap/sdk-core' import { shallowEqual } from 'react-redux' -import { useIsHooksTradeType } from 'modules/trade/hooks/useIsHooksTradeType' - import { updateHooksEnabled, - updatePartialApprove, updateRecipientToggleVisible, updateUserDarkMode, updateUserDeadline, @@ -121,22 +118,6 @@ export function useUserTransactionTTL(): [number, (slippage: number) => void] { return [deadline, setUserDeadline] } -export function usePartialApprove(): [boolean, (value: boolean) => void] { - const dispatch = useAppDispatch() - const isHookTradeType = useIsHooksTradeType() - const partialApprove = useAppSelector((state) => state.user.partialApprove) - - const setPartialApprove = useCallback( - (partialApprove: boolean) => { - dispatch(updatePartialApprove({ partialApprove })) - }, - [dispatch], - ) - - // Partial approve is disabled for Hooks store - return [isHookTradeType ? false : partialApprove, setPartialApprove] -} - export function useSelectedWallet(): string | undefined { return useAppSelector(({ user: { selectedWallet } }) => selectedWallet) } diff --git a/apps/cowswap-frontend/src/legacy/state/user/reducer.ts b/apps/cowswap-frontend/src/legacy/state/user/reducer.ts index 395811fa84..a9470d45b3 100644 --- a/apps/cowswap-frontend/src/legacy/state/user/reducer.ts +++ b/apps/cowswap-frontend/src/legacy/state/user/reducer.ts @@ -18,7 +18,6 @@ export interface UserState { // TODO: mod, shouldn't be here recipientToggleVisible: boolean hooksEnabled: boolean - partialApprove: boolean // deadline set by user in minutes, used in all txns userDeadline: number @@ -28,9 +27,9 @@ export const initialState: UserState = { selectedWallet: undefined, matchesDarkMode: false, userDarkMode: null, + // TODO: mod, shouldn't be here recipientToggleVisible: false, hooksEnabled: false, - partialApprove: false, userLocale: null, userDeadline: DEFAULT_DEADLINE_FROM_NOW, } @@ -57,9 +56,6 @@ const userSlice = createSlice({ updateUserDeadline(state, action) { state.userDeadline = action.payload.userDeadline }, - updatePartialApprove(state, action) { - state.partialApprove = action.payload.partialApprove - }, updateRecipientToggleVisible(state, action) { state.recipientToggleVisible = action.payload.recipientToggleVisible }, @@ -74,6 +70,5 @@ export const { updateUserDeadline, updateUserLocale, updateRecipientToggleVisible, - updatePartialApprove, } = userSlice.actions export default userSlice.reducer diff --git a/apps/cowswap-frontend/src/lib/hooks/useApproval.ts b/apps/cowswap-frontend/src/lib/hooks/useApproval.ts index bfe49fa6c2..285b4afb2a 100644 --- a/apps/cowswap-frontend/src/lib/hooks/useApproval.ts +++ b/apps/cowswap-frontend/src/lib/hooks/useApproval.ts @@ -1,7 +1,9 @@ -import { useMemo } from 'react' +import { useCallback, useMemo } from 'react' -import { getIsNativeToken } from '@cowprotocol/common-utils' +import { calculateGasMargin, getIsNativeToken } from '@cowprotocol/common-utils' import { useWalletInfo } from '@cowprotocol/wallet' +import { MaxUint256 } from '@ethersproject/constants' +import { TransactionResponse } from '@ethersproject/providers' import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' import { Nullish } from 'types' @@ -9,6 +11,7 @@ import { Nullish } from 'types' import { useTokenAllowance } from 'legacy/hooks/useTokenAllowance' import { ApprovalState } from 'common/hooks/useApproveState' +import { useTokenContract } from 'common/hooks/useContract' export interface ApprovalStateForSpenderResult { approvalState: ApprovalState @@ -19,7 +22,7 @@ function toApprovalState( amountToApprove: Nullish>, spender: string | undefined, currentAllowance?: CurrencyAmount, - pendingApproval?: boolean, + pendingApproval?: boolean ): ApprovalState { // Unknown amount or spender if (!amountToApprove || !spender) { @@ -47,7 +50,7 @@ function toApprovalState( export function useApprovalStateForSpender( amountToApprove: Nullish>, spender: string | undefined, - useIsPendingApproval: (token?: Token, spender?: string) => boolean, + useIsPendingApproval: (token?: Token, spender?: string) => boolean ): ApprovalStateForSpenderResult { const { account } = useWalletInfo() const currency = amountToApprove?.currency @@ -61,3 +64,66 @@ export function useApprovalStateForSpender( return { approvalState, currentAllowance } }, [amountToApprove, currentAllowance, pendingApproval, spender]) } + +export function useApproval( + amountToApprove: CurrencyAmount | undefined, + spender: string | undefined, + useIsPendingApproval: (token?: Token, spender?: string) => boolean +): [ + ApprovalState, + () => Promise<{ response: TransactionResponse; tokenAddress: string; spenderAddress: string } | undefined> +] { + const { chainId } = useWalletInfo() + const currency = amountToApprove?.currency + const token = currency && !getIsNativeToken(currency) ? currency : undefined + + // check the current approval status + const approvalState = useApprovalStateForSpender(amountToApprove, spender, useIsPendingApproval).approvalState + + const tokenContract = useTokenContract(token?.address) + + const approve = useCallback(async () => { + function logFailure(error: Error | string): undefined { + console.warn(`${token?.symbol || 'Token'} approval failed:`, error) + return + } + + // Bail early if there is an issue. + if (approvalState !== ApprovalState.NOT_APPROVED) { + return logFailure('approve was called unnecessarily') + } else if (!chainId) { + return logFailure('no chainId') + } else if (!token) { + return logFailure('no token') + } else if (!tokenContract) { + return logFailure('tokenContract is null') + } else if (!amountToApprove) { + return logFailure('missing amount to approve') + } else if (!spender) { + return logFailure('no spender') + } + + let useExact = false + const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => { + // general fallback for tokens which restrict approval amounts + useExact = true + return tokenContract.estimateGas.approve(spender, amountToApprove.quotient.toString()) + }) + + return tokenContract + .approve(spender, useExact ? amountToApprove.quotient.toString() : MaxUint256, { + gasLimit: calculateGasMargin(estimatedGas), + }) + .then((response) => ({ + response, + tokenAddress: token.address, + spenderAddress: spender, + })) + .catch((error: Error) => { + logFailure(error) + throw error + }) + }, [approvalState, token, tokenContract, amountToApprove, spender, chainId]) + + return [approvalState, approve] +} diff --git a/apps/cowswap-frontend/src/modules/operations/bundle/buildApproveTx.ts b/apps/cowswap-frontend/src/modules/operations/bundle/buildApproveTx.ts index 51aba39eea..abd22db931 100644 --- a/apps/cowswap-frontend/src/modules/operations/bundle/buildApproveTx.ts +++ b/apps/cowswap-frontend/src/modules/operations/bundle/buildApproveTx.ts @@ -7,16 +7,15 @@ export type BuildApproveTxParams = { erc20Contract: Erc20 spender: string amountToApprove: CurrencyAmount - isPartialApprove?: boolean } /** * Builds the approval tx, without sending it */ export async function buildApproveTx(params: BuildApproveTxParams) { - const { erc20Contract, spender, amountToApprove, isPartialApprove } = params + const { erc20Contract, spender, amountToApprove } = params - const estimatedAmount = await estimateApprove(erc20Contract, spender, amountToApprove, isPartialApprove) + const estimatedAmount = await estimateApprove(erc20Contract, spender, amountToApprove) return erc20Contract.populateTransaction.approve(spender, estimatedAmount.approveAmount) } diff --git a/apps/cowswap-frontend/src/modules/permit/hooks/useGeneratePermitHook.ts b/apps/cowswap-frontend/src/modules/permit/hooks/useGeneratePermitHook.ts index 2fbf6ba422..86d6b6bf65 100644 --- a/apps/cowswap-frontend/src/modules/permit/hooks/useGeneratePermitHook.ts +++ b/apps/cowswap-frontend/src/modules/permit/hooks/useGeneratePermitHook.ts @@ -19,7 +19,7 @@ import { GeneratePermitHook, GeneratePermitHookParams } from '../types' /** * Hook that returns callback to generate permit hook data */ -export function useGeneratePermitHook(isPartialApprove?: boolean): GeneratePermitHook { +export function useGeneratePermitHook(): GeneratePermitHook { const { chainId } = useWalletInfo() const storePermit = useSetAtom(storePermitCacheAtom) const getCachedPermit = useGetCachedPermit() @@ -44,15 +44,14 @@ export function useGeneratePermitHook(isPartialApprove?: boolean): GeneratePermi const eip2162Utils = getPermitUtilsInstance(chainId, provider, account) const spender = customSpender || COW_PROTOCOL_VAULT_RELAYER_ADDRESS[chainId] - const amount = isPartialApprove ? params.amount : undefined // Always get the nonce for the real account, to know whether the cache should be invalidated // Static account should never need to pre-check the nonce as it'll never change once cached const nonce = account ? await eip2162Utils.getTokenNonce(inputToken.address, account) : undefined - const permitParams = { chainId, tokenAddress: inputToken.address, account, nonce, amount } + const permitParams = { chainId, tokenAddress: inputToken.address, account, nonce } - const cachedPermit = await getCachedPermit(inputToken.address, spender, amount) + const cachedPermit = await getCachedPermit(inputToken.address, spender) if (cachedPermit) { return cachedPermit @@ -67,13 +66,12 @@ export function useGeneratePermitHook(isPartialApprove?: boolean): GeneratePermi eip2162Utils, account, nonce, - amount, }) hookData && storePermit({ ...permitParams, hookData, spender }) return hookData }, - [provider, chainId, getCachedPermit, storePermit, isPartialApprove], + [provider, chainId, getCachedPermit, storePermit], ) } diff --git a/apps/cowswap-frontend/src/modules/permit/hooks/useGetCachedPermit.ts b/apps/cowswap-frontend/src/modules/permit/hooks/useGetCachedPermit.ts index 4476e8c7c4..4f0ba0dc7a 100644 --- a/apps/cowswap-frontend/src/modules/permit/hooks/useGetCachedPermit.ts +++ b/apps/cowswap-frontend/src/modules/permit/hooks/useGetCachedPermit.ts @@ -13,14 +13,13 @@ import { getPermitCacheAtom } from '../state/permitCacheAtom' export function useGetCachedPermit(): ( tokenAddress: Nullish, customSpender?: string, - amount?: bigint, ) => Promise { const { chainId, account } = useWalletInfo() const provider = useWalletProvider() const getCachedPermit = useSetAtom(getPermitCacheAtom) return useCallback( - async (tokenAddress: Nullish, customSpender?: string, amount?: bigint) => { + async (tokenAddress: Nullish, customSpender?: string) => { if (!provider || !account || !tokenAddress) { return } @@ -33,7 +32,7 @@ export function useGetCachedPermit(): ( // Static account should never need to pre-check the nonce as it'll never change once cached const nonce = account ? await eip2162Utils.getTokenNonce(tokenAddress, account) : undefined - const permitParams = { chainId, tokenAddress, account, nonce, spender, amount } + const permitParams = { chainId, tokenAddress, account, nonce, spender } return getCachedPermit(permitParams) } catch (e) { diff --git a/apps/cowswap-frontend/src/modules/permit/state/permitCacheAtom.ts b/apps/cowswap-frontend/src/modules/permit/state/permitCacheAtom.ts index 1ab0198f52..e09aa938f4 100644 --- a/apps/cowswap-frontend/src/modules/permit/state/permitCacheAtom.ts +++ b/apps/cowswap-frontend/src/modules/permit/state/permitCacheAtom.ts @@ -1,12 +1,6 @@ import { atom } from 'jotai' import { atomWithStorage } from 'jotai/utils' -import { MaxUint256 } from '@ethersproject/constants' - -import { TradeType, tradeTypeAtom } from 'modules/trade' - -import { Routes } from 'common/constants/routes' - import { CachedPermitData, GetPermitCacheParams, @@ -20,14 +14,14 @@ import { * Should never change once it has been created. * Used exclusively for quote requests */ -export const staticPermitCacheAtom = atomWithStorage('staticPermitCache:v4', {}) +export const staticPermitCacheAtom = atomWithStorage('staticPermitCache:v3', {}) /** * Atom that stores permit data for user permit requests. * Should be updated whenever the permit nonce is updated. * Used exclusively for order requests */ -export const userPermitCacheAtom = atomWithStorage('userPermitCache:v2', {}) +export const userPermitCacheAtom = atomWithStorage('userPermitCache:v1', {}) /** * Atom to add/update permit cache data @@ -42,7 +36,6 @@ export const storePermitCacheAtom = atom(null, (get, set, params: StorePermitCac const dataToCache: CachedPermitData = { hookData: params.hookData, nonce: params.nonce, - amount: params.amount?.toString(), } set(atomToUpdate, (permitCache) => ({ ...permitCache, [key]: JSON.stringify(dataToCache) })) @@ -60,10 +53,6 @@ export const getPermitCacheAtom = atom(null, (get, set, params: GetPermitCachePa const atomToUpdate = params.account ? userPermitCacheAtom : staticPermitCacheAtom const permitCache = get(atomToUpdate) - const tradeType = get(tradeTypeAtom) - - const isSwap = tradeType?.tradeType === TradeType.SWAP && tradeType.route === Routes.SWAP - const key = buildKey(params) const cachedData = permitCache[key] @@ -72,7 +61,7 @@ export const getPermitCacheAtom = atom(null, (get, set, params: GetPermitCachePa } try { - const { hookData, nonce: storedNonce, amount }: CachedPermitData = JSON.parse(cachedData) + const { hookData, nonce: storedNonce }: CachedPermitData = JSON.parse(cachedData) if (params.account !== undefined) { // User type permit cache, check the nonce @@ -87,16 +76,6 @@ export const getPermitCacheAtom = atom(null, (get, set, params: GetPermitCachePa return undefined } - - // Only Swap might create partial amount permits - // Because of that, we skip cached permits with partial amount in other widgets - if (!isSwap && amount && amount !== MaxUint256.toString()) { - return undefined - } - - if (params.amount && params.amount.toString() !== amount) { - return undefined - } } // Cache hit for both static and user permit types @@ -112,8 +91,8 @@ export const getPermitCacheAtom = atom(null, (get, set, params: GetPermitCachePa } }) -function buildKey({ chainId, tokenAddress, account, spender, amount }: PermitCacheKeyParams) { - const base = `${chainId}-${tokenAddress.toLowerCase()}-${spender.toLowerCase()}-${amount ? amount.toString() : ''}` +function buildKey({ chainId, tokenAddress, account, spender }: PermitCacheKeyParams) { + const base = `${chainId}-${tokenAddress.toLowerCase()}-${spender.toLowerCase()}` return account ? `${base}-${account.toLowerCase()}` : base } diff --git a/apps/cowswap-frontend/src/modules/permit/types.ts b/apps/cowswap-frontend/src/modules/permit/types.ts index 2a42f1c941..c1b9cd215d 100644 --- a/apps/cowswap-frontend/src/modules/permit/types.ts +++ b/apps/cowswap-frontend/src/modules/permit/types.ts @@ -14,7 +14,7 @@ export type AddPermitTokenParams = { permitInfo: PermitInfo } -export type GeneratePermitHookParams = Pick & { +export type GeneratePermitHookParams = Pick & { customSpender?: string } @@ -33,7 +33,6 @@ export type PermitCache = Record export type CachedPermitData = { hookData: PermitHookData nonce: number | undefined - amount: string | undefined } export type PermitCacheKeyParams = { @@ -42,7 +41,6 @@ export type PermitCacheKeyParams = { account: string | undefined nonce: number | undefined spender: string - amount: bigint | undefined } export type StorePermitCacheParams = PermitCacheKeyParams & { hookData: PermitHookData } diff --git a/apps/cowswap-frontend/src/modules/permit/utils/handlePermit.ts b/apps/cowswap-frontend/src/modules/permit/utils/handlePermit.ts index 1be97e5ee9..1a13cb033c 100644 --- a/apps/cowswap-frontend/src/modules/permit/utils/handlePermit.ts +++ b/apps/cowswap-frontend/src/modules/permit/utils/handlePermit.ts @@ -22,7 +22,7 @@ import { HandlePermitParams } from '../types' * Returns the updated appData */ export async function handlePermit(params: HandlePermitParams): Promise { - const { permitInfo, inputToken, account, appData, typedHooks, generatePermitHook, amount } = params + const { permitInfo, inputToken, account, appData, typedHooks, generatePermitHook } = params if (isSupportedPermitInfo(permitInfo) && !getIsNativeToken(inputToken)) { // permitInfo will only be set if there's NOT enough allowance @@ -31,7 +31,6 @@ export async function handlePermit(params: HandlePermitParams): Promise {(restContent) => ( <> diff --git a/apps/cowswap-frontend/src/modules/swap/containers/EthFlow/index.tsx b/apps/cowswap-frontend/src/modules/swap/containers/EthFlow/index.tsx index 150a7eea22..a8b9760bd0 100644 --- a/apps/cowswap-frontend/src/modules/swap/containers/EthFlow/index.tsx +++ b/apps/cowswap-frontend/src/modules/swap/containers/EthFlow/index.tsx @@ -9,7 +9,6 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { useSingleActivityDescriptor } from 'legacy/hooks/useRecentActivity' import { WrapUnwrapCallback } from 'legacy/hooks/useWrapCallback' -import { usePartialApprove } from 'legacy/state/user/hooks' import { getDerivedEthFlowState } from 'modules/swap/containers/EthFlow/utils/getDerivedEthFlowState' import { EthFlowModalContent } from 'modules/swap/pure/EthFlow/EthFlowModalContent' @@ -26,6 +25,7 @@ import { useEthFlowActions } from './hooks/useEthFlowActions' import useRemainingNativeTxsAndCosts from './hooks/useRemainingNativeTxsAndCosts' import { useSetupEthFlow } from './hooks/useSetupEthFlow' + export interface EthFlowProps { nativeInput?: CurrencyAmount hasEnoughWrappedBalanceForSwap: boolean @@ -45,12 +45,10 @@ export function EthFlowModal({ const native = useNativeCurrency() const wrapped = useWrappedToken() const { state: approvalState } = useApproveState(nativeInput || null) - const [isPartialApprove] = usePartialApprove() const ethFlowContext = useAtomValue(ethFlowContextAtom) const approveCallback = useTradeApproveCallback( - (nativeInput && currencyAmountToTokenAmount(nativeInput)) || undefined, - isPartialApprove, + (nativeInput && currencyAmountToTokenAmount(nativeInput)) || undefined ) const ethFlowActions = useEthFlowActions({ wrap: wrapCallback, @@ -88,7 +86,7 @@ export function EthFlowModal({ approvalState, approveActivity, wrapActivity, - onDismiss, + onDismiss }) return ( diff --git a/apps/cowswap-frontend/src/modules/swap/containers/SwapWidget/index.tsx b/apps/cowswap-frontend/src/modules/swap/containers/SwapWidget/index.tsx index d4fff58b3d..a49a039628 100644 --- a/apps/cowswap-frontend/src/modules/swap/containers/SwapWidget/index.tsx +++ b/apps/cowswap-frontend/src/modules/swap/containers/SwapWidget/index.tsx @@ -14,12 +14,7 @@ import { NetworkAlert } from 'legacy/components/NetworkAlert/NetworkAlert' import { useModalIsOpen } from 'legacy/state/application/hooks' import { ApplicationModal } from 'legacy/state/application/reducer' import { Field } from 'legacy/state/types' -import { - useHooksEnabledManager, - usePartialApprove, - useRecipientToggleManager, - useUserTransactionTTL, -} from 'legacy/state/user/hooks' +import { useHooksEnabledManager, useRecipientToggleManager, useUserTransactionTTL } from 'legacy/state/user/hooks' import { useCurrencyAmountBalanceCombined } from 'modules/combinedBalances' import { useInjectedWidgetParams } from 'modules/injectedWidget' @@ -90,7 +85,6 @@ export function SwapWidget({ topContent, bottomContent }: SwapWidgetProps) { const recipientToggleState = useRecipientToggleManager() const hooksEnabledState = useHooksEnabledManager() const deadlineState = useUserTransactionTTL() - const partialApproveState = usePartialApprove() const isHookTradeType = useIsHooksTradeType() const isTradePriceUpdating = useTradePricesUpdate() @@ -244,8 +238,6 @@ export function SwapWidget({ topContent, bottomContent }: SwapWidgetProps) { recipientToggleState={recipientToggleState} hooksEnabledState={hooksEnabledState} deadlineState={deadlineState} - // Partial approve is disabled for Hooks store - partialApproveState={isHookTradeType ? undefined : partialApproveState} /> ), diff --git a/apps/cowswap-frontend/src/modules/swap/hooks/useHandleSwapOrEthFlow.ts b/apps/cowswap-frontend/src/modules/swap/hooks/useHandleSwapOrEthFlow.ts index 275a2798c3..f550702e03 100644 --- a/apps/cowswap-frontend/src/modules/swap/hooks/useHandleSwapOrEthFlow.ts +++ b/apps/cowswap-frontend/src/modules/swap/hooks/useHandleSwapOrEthFlow.ts @@ -1,7 +1,7 @@ import { useCallback } from 'react' import { Field } from 'legacy/state/types' -import { usePartialApprove, useUserTransactionTTL } from 'legacy/state/user/hooks' +import { useUserTransactionTTL } from 'legacy/state/user/hooks' import { TradeWidgetActions, useTradePriceImpact } from 'modules/trade' import { logTradeFlow } from 'modules/trade/utils/logger' @@ -25,11 +25,7 @@ export function useHandleSwapOrEthFlow(actions: TradeWidgetActions) { const { onUserInput, onChangeRecipient } = actions const [deadline] = useUserTransactionTTL() - const [isPartialApprove] = usePartialApprove() - const { callback: handleSwap, contextIsReady } = useHandleSwap( - useSafeMemoObject({ deadline, isPartialApprove }), - actions, - ) + const { callback: handleSwap, contextIsReady } = useHandleSwap(useSafeMemoObject({ deadline }), actions) const callback = useCallback(async () => { if (!swapFlowContext) return diff --git a/apps/cowswap-frontend/src/modules/swap/hooks/useSwapButtonContext.ts b/apps/cowswap-frontend/src/modules/swap/hooks/useSwapButtonContext.ts index 19906be3f4..91fe6ec473 100644 --- a/apps/cowswap-frontend/src/modules/swap/hooks/useSwapButtonContext.ts +++ b/apps/cowswap-frontend/src/modules/swap/hooks/useSwapButtonContext.ts @@ -14,7 +14,6 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { useToggleWalletModal } from 'legacy/state/application/hooks' import { useGetQuoteAndStatus, useIsBestQuoteLoading } from 'legacy/state/price/hooks' import { Field } from 'legacy/state/types' -import { usePartialApprove } from 'legacy/state/user/hooks' import { useCurrencyAmountBalanceCombined } from 'modules/combinedBalances' import { useInjectedWidgetParams } from 'modules/injectedWidget' @@ -64,7 +63,6 @@ export function useSwapButtonContext(input: SwapButtonInput, actions: TradeWidge const tradeConfirmActions = useTradeConfirmActions() const { standaloneMode } = useInjectedWidgetParams() const isHooksStore = useIsHooksTradeType() - const [isPartialApprove] = usePartialApprove() const currencyIn = currencies[Field.INPUT] const currencyOut = currencies[Field.OUTPUT] @@ -145,7 +143,6 @@ export function useSwapButtonContext(input: SwapButtonInput, actions: TradeWidge onCurrencySelection, widgetStandaloneMode: standaloneMode, quoteDeadlineParams, - isPartialApprove, }), [ swapButtonState, @@ -162,7 +159,6 @@ export function useSwapButtonContext(input: SwapButtonInput, actions: TradeWidge onCurrencySelection, standaloneMode, quoteDeadlineParams, - isPartialApprove, ], ) } diff --git a/apps/cowswap-frontend/src/modules/swap/hooks/useSwapFlowContext.ts b/apps/cowswap-frontend/src/modules/swap/hooks/useSwapFlowContext.ts index bc70dfb18f..afed5d89d7 100644 --- a/apps/cowswap-frontend/src/modules/swap/hooks/useSwapFlowContext.ts +++ b/apps/cowswap-frontend/src/modules/swap/hooks/useSwapFlowContext.ts @@ -1,4 +1,4 @@ -import { usePartialApprove, useUserTransactionTTL } from 'legacy/state/user/hooks' +import { useUserTransactionTTL } from 'legacy/state/user/hooks' import { useTradeFlowContext } from 'modules/tradeFlow' @@ -6,7 +6,5 @@ import { useSafeMemoObject } from 'common/hooks/useSafeMemo' export function useSwapFlowContext() { const [deadline] = useUserTransactionTTL() - const [isPartialApprove] = usePartialApprove() - - return useTradeFlowContext(useSafeMemoObject({ deadline, isPartialApprove })) + return useTradeFlowContext(useSafeMemoObject({ deadline })) } diff --git a/apps/cowswap-frontend/src/modules/swap/pure/EthFlow/EthFlowModalContent/configs.ts b/apps/cowswap-frontend/src/modules/swap/pure/EthFlow/EthFlowModalContent/configs.ts index e599b3e3f3..b5648ccb99 100644 --- a/apps/cowswap-frontend/src/modules/swap/pure/EthFlow/EthFlowModalContent/configs.ts +++ b/apps/cowswap-frontend/src/modules/swap/pure/EthFlow/EthFlowModalContent/configs.ts @@ -68,7 +68,9 @@ export const ethFlowConfigs: { [EthFlowState.ApproveNeeded]: ({ wrappedSymbol }) => ({ title: `Approve ${wrappedSymbol}`, buttonText: `Approve ${wrappedSymbol}`, - descriptions: [`It is required to do an approval of ${wrappedSymbol} via an on-chain ERC20 Approve transaction.`], + descriptions: [ + `It is required to do a one-time approval of ${wrappedSymbol} via an on-chain ERC20 Approve transaction.`, + ], }), [EthFlowState.SwapReady]: ({ wrappedSymbol }) => ({ title: `Continue swap with ${wrappedSymbol}`, diff --git a/apps/cowswap-frontend/src/modules/swap/pure/SwapButtons/index.cosmos.tsx b/apps/cowswap-frontend/src/modules/swap/pure/SwapButtons/index.cosmos.tsx index c64663d107..110dd701f1 100644 --- a/apps/cowswap-frontend/src/modules/swap/pure/SwapButtons/index.cosmos.tsx +++ b/apps/cowswap-frontend/src/modules/swap/pure/SwapButtons/index.cosmos.tsx @@ -26,7 +26,6 @@ const swapButtonsContext: SwapButtonsContext = { openSwapConfirm: () => void 0, toggleWalletModal: () => void 0, hasEnoughWrappedBalanceForSwap: true, - isPartialApprove: false, quoteDeadlineParams: { validFor: 0, quoteValidTo: 0, diff --git a/apps/cowswap-frontend/src/modules/swap/pure/SwapButtons/index.tsx b/apps/cowswap-frontend/src/modules/swap/pure/SwapButtons/index.tsx index a0dd7e1e41..28c5618757 100644 --- a/apps/cowswap-frontend/src/modules/swap/pure/SwapButtons/index.tsx +++ b/apps/cowswap-frontend/src/modules/swap/pure/SwapButtons/index.tsx @@ -40,7 +40,6 @@ export interface SwapButtonsContext { onCurrencySelection: (field: Field, currency: Currency) => void widgetStandaloneMode?: boolean quoteDeadlineParams: QuoteDeadlineParams - isPartialApprove: boolean } const swapButtonStateMap: { [key in SwapButtonState]: (props: SwapButtonsContext) => JSX.Element } = { @@ -127,7 +126,7 @@ const swapButtonStateMap: { [key in SwapButtonState]: (props: SwapButtonsContext {props.inputAmount && ( - + Swap diff --git a/apps/cowswap-frontend/src/modules/swap/pure/banners/PartialApprovalBanner.tsx b/apps/cowswap-frontend/src/modules/swap/pure/banners/PartialApprovalBanner.tsx deleted file mode 100644 index caddfb37b5..0000000000 --- a/apps/cowswap-frontend/src/modules/swap/pure/banners/PartialApprovalBanner.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import ICON_TOKENS from '@cowprotocol/assets/svg/tokens.svg' -import { Command } from '@cowprotocol/types' -import { BannerOrientation, ClosableBanner, InlineBanner, UnderlinedLinkStyledButton } from '@cowprotocol/ui' - -import styled from 'styled-components/macro' - -const BANNER_STORAGE_KEY = 'partialPermitBannerKey:v0' - -type PartialApprovalBannerProps = { - openSettings: Command -} - -export function PartialApprovalBanner({ openSettings }: PartialApprovalBannerProps) { - return ClosableBanner(BANNER_STORAGE_KEY, (onClose) => ( - -

- NEW: You can now choose to do minimal token approvals in the settings. -

-
- )) -} - -const Link = styled(UnderlinedLinkStyledButton)` - padding: 0; -` diff --git a/apps/cowswap-frontend/src/modules/trade/pure/TradeConfirmation/index.tsx b/apps/cowswap-frontend/src/modules/trade/pure/TradeConfirmation/index.tsx index 6edebb05e2..979c49913d 100644 --- a/apps/cowswap-frontend/src/modules/trade/pure/TradeConfirmation/index.tsx +++ b/apps/cowswap-frontend/src/modules/trade/pure/TradeConfirmation/index.tsx @@ -9,7 +9,6 @@ import { CustomRecipientWarningBanner, LongLoadText, } from '@cowprotocol/ui' -import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { Trans } from '@lingui/macro' import ms from 'ms.macro' @@ -50,9 +49,6 @@ export interface TradeConfirmationProps { recipient?: string | null buttonText?: React.ReactNode children?: (restContent: ReactElement) => ReactElement - slippageAdjustedSellAmount?: CurrencyAmount - isPartialApprove?: boolean - displayHookDetails?: boolean } export function TradeConfirmation(props: TradeConfirmationProps) { @@ -80,9 +76,6 @@ export function TradeConfirmation(props: TradeConfirmationProps) { recipient, isPriceStatic, appData, - isPartialApprove, - slippageAdjustedSellAmount, - displayHookDetails, } = frozenProps || props /** @@ -133,20 +126,15 @@ export function TradeConfirmation(props: TradeConfirmationProps) { onConfirm() } - const hookDetailsElement = displayHookDetails ? ( + const hookDetailsElement = ( <> {appData && ( - + {(hookChildren) => hookChildren} )} - ) : null + ) return ( e.key === 'Escape' && onDismiss()}> diff --git a/apps/cowswap-frontend/src/modules/tradeFlow/hooks/useTradeFlowContext.ts b/apps/cowswap-frontend/src/modules/tradeFlow/hooks/useTradeFlowContext.ts index 945cf69e0f..fafa6e7594 100644 --- a/apps/cowswap-frontend/src/modules/tradeFlow/hooks/useTradeFlowContext.ts +++ b/apps/cowswap-frontend/src/modules/tradeFlow/hooks/useTradeFlowContext.ts @@ -28,10 +28,9 @@ import { TradeFlowContext } from '../types/TradeFlowContext' export interface TradeFlowParams { deadline: number - isPartialApprove?: boolean } -export function useTradeFlowContext({ deadline, isPartialApprove }: TradeFlowParams): TradeFlowContext | null { +export function useTradeFlowContext({ deadline }: TradeFlowParams): TradeFlowContext | null { const { chainId, account } = useWalletInfo() const provider = useWalletProvider() const { allowsOffchainSigning } = useWalletDetails() @@ -50,7 +49,7 @@ export function useTradeFlowContext({ deadline, isPartialApprove }: TradeFlowPar const networkFee = receiveAmountInfo?.costs.networkFee.amountInSellCurrency const permitInfo = usePermitInfo(sellCurrency, tradeType) - const generatePermitHook = useGeneratePermitHook(isPartialApprove) + const generatePermitHook = useGeneratePermitHook() const getCachedPermit = useGetCachedPermit() const closeModals = useCloseModals() const dispatch = useDispatch() @@ -123,7 +122,6 @@ export function useTradeFlowContext({ deadline, isPartialApprove }: TradeFlowPar deadline, orderKind, uiOrderType, - isPartialApprove, ] : null, ([ @@ -154,7 +152,6 @@ export function useTradeFlowContext({ deadline, isPartialApprove }: TradeFlowPar deadline, orderKind, uiOrderType, - isPartialApprove, ]) => { return { context: { @@ -165,7 +162,6 @@ export function useTradeFlowContext({ deadline, isPartialApprove }: TradeFlowPar }, flags: { allowsOffchainSigning, - isPartialApprove, }, callbacks: { closeModals, diff --git a/apps/cowswap-frontend/src/modules/tradeFlow/services/safeBundleFlow/safeBundleApprovalFlow.ts b/apps/cowswap-frontend/src/modules/tradeFlow/services/safeBundleFlow/safeBundleApprovalFlow.ts index eafae28aff..cae474c566 100644 --- a/apps/cowswap-frontend/src/modules/tradeFlow/services/safeBundleFlow/safeBundleApprovalFlow.ts +++ b/apps/cowswap-frontend/src/modules/tradeFlow/services/safeBundleFlow/safeBundleApprovalFlow.ts @@ -33,15 +33,7 @@ export async function safeBundleApprovalFlow( return false } - const { - context, - callbacks, - orderParams, - swapFlowAnalyticsContext, - tradeConfirmActions, - typedHooks, - flags: { isPartialApprove }, - } = tradeContext + const { context, callbacks, orderParams, swapFlowAnalyticsContext, tradeConfirmActions, typedHooks } = tradeContext const { spender, settlementContract, safeAppsSdk, erc20Contract } = safeBundleContext @@ -60,7 +52,6 @@ export async function safeBundleApprovalFlow( erc20Contract, spender, amountToApprove: context.inputAmount, - isPartialApprove, }) orderParams.appData = await removePermitHookFromAppData(orderParams.appData, typedHooks) diff --git a/apps/cowswap-frontend/src/modules/tradeFlow/services/safeBundleFlow/safeBundleEthFlow.ts b/apps/cowswap-frontend/src/modules/tradeFlow/services/safeBundleFlow/safeBundleEthFlow.ts index c9b09bd002..0247b09d84 100644 --- a/apps/cowswap-frontend/src/modules/tradeFlow/services/safeBundleFlow/safeBundleEthFlow.ts +++ b/apps/cowswap-frontend/src/modules/tradeFlow/services/safeBundleFlow/safeBundleEthFlow.ts @@ -35,14 +35,7 @@ export async function safeBundleEthFlow( return false } - const { - context, - callbacks, - swapFlowAnalyticsContext, - tradeConfirmActions, - typedHooks, - flags: { isPartialApprove }, - } = tradeContext + const { context, callbacks, swapFlowAnalyticsContext, tradeConfirmActions, typedHooks } = tradeContext const { spender, settlementContract, safeAppsSdk, needsApproval, wrappedNativeContract } = safeBundleContext @@ -80,7 +73,6 @@ export async function safeBundleEthFlow( erc20Contract: wrappedNativeContract as unknown as Erc20, spender, amountToApprove: inputAmount, - isPartialApprove, }) txs.push({ diff --git a/apps/cowswap-frontend/src/modules/tradeFlow/services/swapFlow/index.ts b/apps/cowswap-frontend/src/modules/tradeFlow/services/swapFlow/index.ts index 1a8589d56c..d26e22b9d4 100644 --- a/apps/cowswap-frontend/src/modules/tradeFlow/services/swapFlow/index.ts +++ b/apps/cowswap-frontend/src/modules/tradeFlow/services/swapFlow/index.ts @@ -59,7 +59,6 @@ export async function swapFlow( inputToken: inputCurrency, permitInfo, generatePermitHook, - amount: BigInt(inputAmount.quotient.toString()), }) if (callDataContainsPermitSigner(orderParams.appData.fullAppData)) { diff --git a/apps/cowswap-frontend/src/modules/tradeFlow/types/TradeFlowContext.ts b/apps/cowswap-frontend/src/modules/tradeFlow/types/TradeFlowContext.ts index c129b37df6..16f6dc07b8 100644 --- a/apps/cowswap-frontend/src/modules/tradeFlow/types/TradeFlowContext.ts +++ b/apps/cowswap-frontend/src/modules/tradeFlow/types/TradeFlowContext.ts @@ -26,7 +26,6 @@ export interface TradeFlowContext { } flags: { allowsOffchainSigning: boolean - isPartialApprove?: boolean } callbacks: { closeModals: Command diff --git a/apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/SettingsTab/index.tsx b/apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/SettingsTab/index.tsx index 7090446fdb..fc7273935d 100644 --- a/apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/SettingsTab/index.tsx +++ b/apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/SettingsTab/index.tsx @@ -27,20 +27,12 @@ interface SettingsTabProps { className?: string recipientToggleState: StatefulValue hooksEnabledState?: StatefulValue - partialApproveState?: StatefulValue deadlineState: StatefulValue } -export function SettingsTab({ - className, - recipientToggleState, - hooksEnabledState, - deadlineState, - partialApproveState, -}: SettingsTabProps) { +export function SettingsTab({ className, recipientToggleState, hooksEnabledState, deadlineState }: SettingsTabProps) { const menuButtonRef = useRef(null) - const [isPartialApprove, setPartialApprove] = partialApproveState || [null, null] const [recipientToggleVisible, toggleRecipientVisibilityAux] = recipientToggleState const toggleRecipientVisibility = useCallback( (value?: boolean) => { @@ -112,7 +104,7 @@ export function SettingsTab({ Experimental: {' '} - Add DeFI interactions before and after your trade. + Add DeFI interactions before and after your trade } /> @@ -120,35 +112,6 @@ export function SettingsTab({ )} - - {isPartialApprove !== null && setPartialApprove && ( - - - - Minimal Approvals - - - By default, token approvals & permits are for an unlimited amount, which ensures you don't pay extra for subsequent trades. -
-
- When this setting is enabled, approvals & permits will be for the minimum amount instead of unlimited. - This incurs additional costs on every trade. -
-
- Existing approvals must be revoked manually before you can re-approve. - - } - /> -
- setPartialApprove(!isPartialApprove)} - /> -
- )}
diff --git a/apps/cowswap-frontend/src/modules/tradeWidgetAddons/pure/Row/RowSlippageContent/index.tsx b/apps/cowswap-frontend/src/modules/tradeWidgetAddons/pure/Row/RowSlippageContent/index.tsx index 4a9c0ec9d9..741bc17006 100644 --- a/apps/cowswap-frontend/src/modules/tradeWidgetAddons/pure/Row/RowSlippageContent/index.tsx +++ b/apps/cowswap-frontend/src/modules/tradeWidgetAddons/pure/Row/RowSlippageContent/index.tsx @@ -1,3 +1,4 @@ +import { useSetAtom } from 'jotai' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { Command } from '@cowprotocol/types' @@ -9,7 +10,7 @@ import styled from 'styled-components/macro' import { getNativeSlippageTooltip, getNonNativeSlippageTooltip } from 'common/utils/tradeSettingsTooltips' -import { useOpenSettingsTab } from '../../../state/settingsTabState' +import { settingsTabStateAtom } from '../../../state/settingsTabState' import { RowStyleProps, StyledInfoIcon, StyledRowBetween, TextWrapper, TransactionText } from '../styled' const DefaultSlippage = styled.span` @@ -64,7 +65,9 @@ export function RowSlippageContent(props: RowSlippageContentProps) { isSmartSlippageLoading, } = props - const openSettings = useOpenSettingsTab() + const setSettingTabState = useSetAtom(settingsTabStateAtom) + + const openSettings = () => setSettingTabState({ open: true }) const tooltipContent = slippageTooltip || diff --git a/apps/cowswap-frontend/src/modules/tradeWidgetAddons/state/settingsTabState.ts b/apps/cowswap-frontend/src/modules/tradeWidgetAddons/state/settingsTabState.ts index 61b3e2561a..659fb6321f 100644 --- a/apps/cowswap-frontend/src/modules/tradeWidgetAddons/state/settingsTabState.ts +++ b/apps/cowswap-frontend/src/modules/tradeWidgetAddons/state/settingsTabState.ts @@ -1,9 +1,3 @@ -import { atom, useSetAtom } from 'jotai' +import { atom } from 'jotai' export const settingsTabStateAtom = atom({ open: false }) - -export function useOpenSettingsTab() { - const setSettingTabState = useSetAtom(settingsTabStateAtom) - - return () => setSettingTabState({ open: true }) -} diff --git a/apps/cowswap-frontend/src/pages/Swap/index.tsx b/apps/cowswap-frontend/src/pages/Swap/index.tsx index c90c727003..d672663c10 100644 --- a/apps/cowswap-frontend/src/pages/Swap/index.tsx +++ b/apps/cowswap-frontend/src/pages/Swap/index.tsx @@ -5,16 +5,13 @@ import { useWalletInfo } from '@cowprotocol/wallet' import { Navigate, useLocation, useParams } from 'react-router-dom' import { SwapUpdaters, SwapWidget } from 'modules/swap' -import { PartialApprovalBanner } from 'modules/swap/pure/banners/PartialApprovalBanner' import { getDefaultTradeRawState } from 'modules/trade/types/TradeRawState' import { parameterizeTradeRoute } from 'modules/trade/utils/parameterizeTradeRoute' -import { useOpenSettingsTab } from 'modules/tradeWidgetAddons/state/settingsTabState' import { Routes } from 'common/constants/routes' export function SwapPage() { const params = useParams() - const openSettings = useOpenSettingsTab() if (!params.chainId) { return @@ -23,7 +20,7 @@ export function SwapPage() { return ( <> - } /> + ) } diff --git a/apps/cowswap-frontend/src/utils/orderUtils/getOrderPermitAmount.ts b/apps/cowswap-frontend/src/utils/orderUtils/getOrderPermitAmount.ts index ab4c3d7c8f..d579add099 100644 --- a/apps/cowswap-frontend/src/utils/orderUtils/getOrderPermitAmount.ts +++ b/apps/cowswap-frontend/src/utils/orderUtils/getOrderPermitAmount.ts @@ -1,11 +1,12 @@ +import { Erc20__factory } from '@cowprotocol/abis' import type { LatestAppDataDocVersion } from '@cowprotocol/app-data' import { COW_PROTOCOL_VAULT_RELAYER_ADDRESS, SupportedChainId } from '@cowprotocol/cow-sdk' import { BigNumber } from '@ethersproject/bignumber' -import { parsePermitData } from 'common/utils/parsePermitData' - import { ParsedOrder } from './parseOrder' +const erc20Interface = Erc20__factory.createInterface() + export function getOrderPermitAmount(chainId: SupportedChainId, order: ParsedOrder): BigNumber | null { if (!order.fullAppData) return null @@ -19,7 +20,7 @@ export function getOrderPermitAmount(chainId: SupportedChainId, order: ParsedOrd const permitData = preHooks .map((hook) => { try { - return parsePermitData(hook.callData) + return erc20Interface.decodeFunctionData('permit', hook.callData) } catch { return null } diff --git a/libs/hook-dapp-lib/src/hookDappsRegistry.json b/libs/hook-dapp-lib/src/hookDappsRegistry.json index 7f973f7768..4ce8c5e3e2 100644 --- a/libs/hook-dapp-lib/src/hookDappsRegistry.json +++ b/libs/hook-dapp-lib/src/hookDappsRegistry.json @@ -24,7 +24,7 @@ "PERMIT_TOKEN": { "type": "INTERNAL", "name": "Permit a token", - "descriptionShort": "Permit an address to spend one token on your behalf.", + "descriptionShort": "Infinite permit an address to spend one token on your behalf.", "description": "This hook allows you to permit an address to spend your tokens on your behalf. This is useful for allowing a smart contract to spend your tokens without needing to approve each transaction.", "image": "https://raw.githubusercontent.com/cowprotocol/cowswap/refs/heads/develop/apps/cowswap-frontend/src/modules/hooksStore/dapps/PermitHookApp/icon.png", "version": "v0.1.0", diff --git a/libs/permit-utils/src/lib/generatePermitHook.ts b/libs/permit-utils/src/lib/generatePermitHook.ts index f121a67d83..e9f2e32675 100644 --- a/libs/permit-utils/src/lib/generatePermitHook.ts +++ b/libs/permit-utils/src/lib/generatePermitHook.ts @@ -37,17 +37,7 @@ export async function generatePermitHook(params: PermitHookParams): Promise { - const { - inputToken, - spender, - chainId, - permitInfo, - provider, - account, - eip2162Utils, - nonce: preFetchedNonce, - amount, - } = params + const { inputToken, spender, chainId, permitInfo, provider, account, eip2162Utils, nonce: preFetchedNonce } = params const tokenAddress = inputToken.address // TODO: remove the need for `name` from input token. Should come from permitInfo instead @@ -68,7 +58,7 @@ async function generatePermitHookRaw(params: PermitHookParams): Promise