From 5f45d8e9ef753113b750e67bd35e7eb85a4ebb38 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Fri, 24 Nov 2023 10:12:20 +0000 Subject: [PATCH 1/2] docs: improve comment for fee (#3436) --- apps/widget-configurator/src/app/embedDialog/const.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/widget-configurator/src/app/embedDialog/const.ts b/apps/widget-configurator/src/app/embedDialog/const.ts index 4b7be2e4b5..4b70809e83 100644 --- a/apps/widget-configurator/src/app/embedDialog/const.ts +++ b/apps/widget-configurator/src/app/embedDialog/const.ts @@ -13,7 +13,7 @@ export const COMMENTS_BY_PARAM_NAME: Record = { sell: 'Sell token. Optionally add amount for sell orders', buy: 'Buy token. Optionally add amount for buy orders', enabledTradeTypes: 'swap, limit and/or advanced', - interfaceFeeBips: 'Fill the form above if you are interested', + interfaceFeeBips: '0.5% - COMING SOON! Fill the form above if you are interested', } export const COMMENTS_BY_PARAM_NAME_TYPESCRIPT: Record = { From 58b6ade450537d7bb304947697e7b3e47b34408e Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Fri, 24 Nov 2023 11:03:04 +0000 Subject: [PATCH 2/2] fix: do not send permit to quote when enough allowance (#3433) * fix: do not send permit to quote when enough allowance * fix: remove hook data, and remove unnecessary re-renders * docs: add permit state uml * fix: make sure we log to sentry if we sign with the default signer in permits --------- Co-authored-by: Alfetopito --- .../appData/updater/AppDataHooksUpdater.ts | 62 ++- .../appData/utils/buildAppDataHooks.ts | 9 +- .../limitOrders/hooks/useTradeFlowContext.ts | 13 + .../limitOrders/services/tradeFlow/index.ts | 11 + .../modules/permit/permit-state.drawio.svg | 489 ++++++++++++++++++ .../src/modules/permit/utils/handlePermit.ts | 4 +- .../src/modules/swap/hooks/useFlowContext.ts | 20 +- .../modules/swap/hooks/useSwapFlowContext.ts | 22 +- libs/permit-utils/src/index.ts | 2 + 9 files changed, 611 insertions(+), 21 deletions(-) create mode 100644 apps/cowswap-frontend/src/modules/permit/permit-state.drawio.svg diff --git a/apps/cowswap-frontend/src/modules/appData/updater/AppDataHooksUpdater.ts b/apps/cowswap-frontend/src/modules/appData/updater/AppDataHooksUpdater.ts index d0a7060837..eccc2a11a5 100644 --- a/apps/cowswap-frontend/src/modules/appData/updater/AppDataHooksUpdater.ts +++ b/apps/cowswap-frontend/src/modules/appData/updater/AppDataHooksUpdater.ts @@ -1,25 +1,67 @@ -import { useEffect, useRef } from 'react' +import { useEffect, useMemo, useRef } from 'react' + +import { PermitHookData } from '@cowprotocol/permit-utils' import { useAccountAgnosticPermitHookData } from 'modules/permit' +import { useDerivedSwapInfo } from 'modules/swap/hooks/useSwapState' +import { useLimitHasEnoughAllowance } from '../../limitOrders/hooks/useTradeFlowContext' +import { useSwapEnoughAllowance } from '../../swap/hooks/useSwapFlowContext' import { useUpdateAppDataHooks } from '../hooks' import { buildAppDataHooks } from '../utils/buildAppDataHooks' +// const count = 0 + +function usePermitDataIfNotAllowance(): PermitHookData | undefined { + const permitHookData = useAccountAgnosticPermitHookData() || {} + + // Remove permitData if the user has enough allowance for the current trade + const swapHasEnoughAllowance = useSwapEnoughAllowance() + const limitHasEnoughAllowance = useLimitHasEnoughAllowance() + const shouldUsePermit = swapHasEnoughAllowance === false || limitHasEnoughAllowance === false + + const { target, callData, gasLimit }: Partial = permitHookData || {} + + return useMemo(() => { + if (!target || !callData || !gasLimit) { + return undefined + } + + return shouldUsePermit ? { target, callData, gasLimit } : undefined + }, [shouldUsePermit, target, callData, gasLimit]) +} + export function AppDataHooksUpdater(): null { + const { v2Trade } = useDerivedSwapInfo() const updateAppDataHooks = useUpdateAppDataHooks() - const permitHookData = useAccountAgnosticPermitHookData() - - // To avoid dumb re-renders - const ref = useRef(permitHookData) - ref.current = permitHookData - const stableRef = JSON.stringify(permitHookData) + const permitData = usePermitDataIfNotAllowance() + const permitDataPrev = useRef(undefined) + const hasTradeInfo = !!v2Trade useEffect(() => { - if (stableRef) { - const hooks = buildAppDataHooks(ref.current ? [ref.current] : undefined) + if ( + !hasTradeInfo || // If there's no trade info, wait until we have one to update the hooks (i.e. missing quote) + JSON.stringify(permitDataPrev.current) === JSON.stringify(permitData) // Or if the permit data has not changed + ) { + return undefined + } + + const hooks = buildAppDataHooks({ + preInteractionHooks: permitData ? [permitData] : undefined, + }) + + if (hooks) { + // Update the hooks + console.log('[AppDataHooksUpdater]: Set hooks', hooks) updateAppDataHooks(hooks) + permitDataPrev.current = permitData + } else { + // There was a hook data, but not any more. The hook needs to be removed + console.log('[AppDataHooksUpdater] Clear hooks') + updateAppDataHooks(undefined) + permitDataPrev.current = undefined } - }, [stableRef, updateAppDataHooks]) + }, [updateAppDataHooks, permitData, hasTradeInfo]) return null } diff --git a/apps/cowswap-frontend/src/modules/appData/utils/buildAppDataHooks.ts b/apps/cowswap-frontend/src/modules/appData/utils/buildAppDataHooks.ts index a03512a3ae..b42dae9adc 100644 --- a/apps/cowswap-frontend/src/modules/appData/utils/buildAppDataHooks.ts +++ b/apps/cowswap-frontend/src/modules/appData/utils/buildAppDataHooks.ts @@ -1,9 +1,12 @@ import { AppDataHooks, PostHooks, PreHooks } from '../types' -export function buildAppDataHooks( - preInteractionHooks?: PreHooks, +export function buildAppDataHooks({ + preInteractionHooks, + postInteractionHooks, +}: { + preInteractionHooks?: PreHooks postInteractionHooks?: PostHooks -): AppDataHooks | undefined { +}): AppDataHooks | undefined { if (!preInteractionHooks && !postInteractionHooks) { return undefined } diff --git a/apps/cowswap-frontend/src/modules/limitOrders/hooks/useTradeFlowContext.ts b/apps/cowswap-frontend/src/modules/limitOrders/hooks/useTradeFlowContext.ts index 488fa09af8..f9aba04437 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/hooks/useTradeFlowContext.ts +++ b/apps/cowswap-frontend/src/modules/limitOrders/hooks/useTradeFlowContext.ts @@ -22,6 +22,19 @@ import { useTradeQuote } from 'modules/tradeQuote' import { useLimitOrdersDerivedState } from './useLimitOrdersDerivedState' +export function useLimitHasEnoughAllowance(): boolean | undefined { + const state = useLimitOrdersDerivedState() + const { chainId, account } = useWalletInfo() + + const checkAllowanceAddress = GP_VAULT_RELAYER[chainId] + const { enoughAllowance } = useEnoughBalanceAndAllowance({ + account, + amount: state.slippageAdjustedSellAmount || undefined, + checkAllowanceAddress, + }) + return enoughAllowance +} + export function useTradeFlowContext(): TradeFlowContext | null { const { provider } = useWeb3React() const { chainId, account } = useWalletInfo() diff --git a/apps/cowswap-frontend/src/modules/limitOrders/services/tradeFlow/index.ts b/apps/cowswap-frontend/src/modules/limitOrders/services/tradeFlow/index.ts index e6c7baf500..7d4a07a6f1 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/services/tradeFlow/index.ts +++ b/apps/cowswap-frontend/src/modules/limitOrders/services/tradeFlow/index.ts @@ -1,6 +1,9 @@ import { OrderClass } from '@cowprotocol/cow-sdk' +import { PERMIT_SIGNER } from '@cowprotocol/permit-utils' import { Percent } from '@uniswap/sdk-core' +import * as Sentry from '@sentry/browser' + import { PriceImpact } from 'legacy/hooks/usePriceImpact' import { partialOrderUpdate } from 'legacy/state/orders/utils' import { signAndPostOrder } from 'legacy/utils/trade' @@ -67,6 +70,14 @@ export async function tradeFlow( generatePermitHook, }) + if (postOrderParams.appData.fullAppData.includes(PERMIT_SIGNER.address)) { + // report this to sentry if we ever use the default signer in the permit + Sentry.captureException('User signed the permit using PERMIT_SIGNER instead of their account', { + tags: { errorType: 'permitWithDefaultSigner' }, + contexts: { params: { account } }, + }) + } + logTradeFlow('LIMIT ORDER FLOW', 'STEP 3: send transaction') tradeFlowAnalytics.trade(swapFlowAnalyticsContext) diff --git a/apps/cowswap-frontend/src/modules/permit/permit-state.drawio.svg b/apps/cowswap-frontend/src/modules/permit/permit-state.drawio.svg new file mode 100644 index 0000000000..2940e2efca --- /dev/null +++ b/apps/cowswap-frontend/src/modules/permit/permit-state.drawio.svg @@ -0,0 +1,489 @@ + + + + + + + + + +
+
+
+
+ + + ordersPermitStatusAtom + + +
+
+
+
+
+
+ + ordersPermitStatusAtom + +
+
+ + + + +
+
+
+ + Map<string, boolean | undefined> +
+
+
+
+ + + Map<string, boolean | undefined> + +
+
+ + + + + + +
+
+
+
+
+ staticPermitCacheAtom +
+
+
+
+
+
+ + staticPermitCacheAtom + +
+
+ + + + +
+
+
+ + Map<string, string> +
+
+
+
+ + + Map<string, string> + +
+
+ + + + + + +
+
+
+
+
+ userPermitCacheAtom +
+
+
+
+
+
+ + userPermitCacheAtom + +
+
+ + + + +
+
+
+ + Map<string, string> +
+
+
+
+ + + Map<string, string> + +
+
+ + + + + + +
+
+
+
+
+
+ + getPermitCacheAtom + +
+
+
+
+
+
+
+ + getPermitCacheAtom + +
+
+ + + + +
+
+
+ + Map<string, string> +
+
+
+
+ + + Map<string, string> + +
+
+ + + + + + +
+
+
+
+
+
+
+ + permittableTokensAtom + +
+
+
+
+
+
+
+
+ + permittableTokensAtom + +
+
+ + + + +
+
+
+ + - + + Map< + + + SupportedChainId, + + + Record + + + < + + + string + + + , + + + PermitInfo + + + > + + +
+
+
+ +
+
+
+
+
+
+ + - Map<SupportedChainId, Record<string, PermitInfo> + +
+
+ + + + + + +
+
+
+
+
+
+
+ + PermitInfo + +
+
+
+
+
+
+
+
+ + PermitInfo + +
+
+ + + + +
+
+
+ - + + chainId + + + : + + + + + SupportedChainId +
+
+
+
+
+
+
+ + - chainId: SupportedChainId... + +
+
+ + + + +
+
+
+ - + + tokenAddress + + + : + + + + + string + +
+
+
+
+ + - tokenAddress: string + +
+
+ + + + +
+
+
+ - + + permitInfo + + + : + + + + + PermitInfo + +
+
+
+
+ + - permitInfo: PermitInfo + +
+
+ + + + +
+
+
+ - + + chainId + + + : + + + + + SupportedChainId +
+
+
+
+
+
+
+ + - chainId: SupportedChainId... + +
+
+ + + + + + + + + + +
+
+
+ + Keeps track of open orders permit, in case they become invalid (i.e. nonce is bumped) + +
+
+
+
+ + Keeps track of open orders permit, in case th... + +
+
+ + + + +
+
+
+ + Permit cache for user signed permits + +
+
+
+
+ + Permit cache for user signed permits + +
+
+ + + + +
+
+
+ + Permit cache for MOCK account signed permits +
+ (relevant for getting the quote) +
+
+
+
+
+
+ + Permit cache for MOCK account signed permits... + +
+
+ + + + +
+
+
+ + Keeps track of the tokens that are permitable +
+
+
+
+
+
+ + Keeps track of the tokens that are permitable + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/apps/cowswap-frontend/src/modules/permit/utils/handlePermit.ts b/apps/cowswap-frontend/src/modules/permit/utils/handlePermit.ts index ce63421a0e..68ae1fd5f4 100644 --- a/apps/cowswap-frontend/src/modules/permit/utils/handlePermit.ts +++ b/apps/cowswap-frontend/src/modules/permit/utils/handlePermit.ts @@ -28,7 +28,9 @@ export async function handlePermit(params: HandlePermitParams): Promise | undefined, + CurrencyAmount | undefined +] { + const { v2Trade: trade, allowedSlippage } = useDerivedSwapInfo() + + const { INPUT, OUTPUT } = computeSlippageAdjustedAmounts(trade, allowedSlippage) + + return [INPUT, OUTPUT] +} + export function useBaseFlowContextSetup(): BaseFlowContextSetup { const { provider } = useWeb3React() const { account, chainId } = useWalletInfo() const { allowsOffchainSigning } = useWalletDetails() const gnosisSafeInfo = useGnosisSafeInfo() const { recipient } = useSwapState() - const { v2Trade: trade, allowedSlippage } = useDerivedSwapInfo() + const { v2Trade: trade } = useDerivedSwapInfo() const appData = useAppData() const closeModals = useCloseModals() @@ -104,10 +115,7 @@ export function useBaseFlowContextSetup(): BaseFlowContextSetup { const isEoaEthFlow = useIsEoaEthFlow() const isSafeEthFlow = useIsSafeEthFlow() - const { INPUT: inputAmountWithSlippage, OUTPUT: outputAmountWithSlippage } = computeSlippageAdjustedAmounts( - trade, - allowedSlippage - ) + const [inputAmountWithSlippage, outputAmountWithSlippage] = useSwapAmountsWithSlippage() const sellTokenContract = useTokenContract(getAddress(inputAmountWithSlippage?.currency) || undefined, true) const isSafeBundle = useIsSafeApprovalBundle(inputAmountWithSlippage) diff --git a/apps/cowswap-frontend/src/modules/swap/hooks/useSwapFlowContext.ts b/apps/cowswap-frontend/src/modules/swap/hooks/useSwapFlowContext.ts index 13bc3ef6b6..d8bcda4991 100644 --- a/apps/cowswap-frontend/src/modules/swap/hooks/useSwapFlowContext.ts +++ b/apps/cowswap-frontend/src/modules/swap/hooks/useSwapFlowContext.ts @@ -2,10 +2,16 @@ import { GP_VAULT_RELAYER } from '@cowprotocol/common-const' import { useGP2SettlementContract } from '@cowprotocol/common-hooks' import { getWrappedToken } from '@cowprotocol/common-utils' import { OrderKind, SupportedChainId } from '@cowprotocol/cow-sdk' +import { useWalletInfo } from '@cowprotocol/wallet' import { TradeType as UniTradeType } from '@uniswap/sdk-core' import { useGeneratePermitHook, useIsTokenPermittable } from 'modules/permit' -import { FlowType, getFlowContext, useBaseFlowContextSetup } from 'modules/swap/hooks/useFlowContext' +import { + FlowType, + getFlowContext, + useBaseFlowContextSetup, + useSwapAmountsWithSlippage, +} from 'modules/swap/hooks/useFlowContext' import { SwapFlowContext } from 'modules/swap/services/types' import { useEnoughBalanceAndAllowance } from 'modules/tokens' import { TradeType } from 'modules/trade' @@ -41,3 +47,17 @@ export function useSwapFlowContext(): SwapFlowContext | null { generatePermitHook, } } + +export function useSwapEnoughAllowance(): boolean | undefined { + const { chainId, account } = useWalletInfo() + const [inputAmountWithSlippage] = useSwapAmountsWithSlippage() + + const checkAllowanceAddress = GP_VAULT_RELAYER[chainId] + const { enoughAllowance } = useEnoughBalanceAndAllowance({ + account, + amount: inputAmountWithSlippage, + checkAllowanceAddress, + }) + + return enoughAllowance +} diff --git a/libs/permit-utils/src/index.ts b/libs/permit-utils/src/index.ts index 3edad2af5e..7c29fec237 100644 --- a/libs/permit-utils/src/index.ts +++ b/libs/permit-utils/src/index.ts @@ -1,3 +1,5 @@ +export { PERMIT_SIGNER } from './const' + export { checkIsCallDataAValidPermit } from './lib/checkIsCallDataAValidPermit' export { generatePermitHook } from './lib/generatePermitHook' export { getPermitUtilsInstance } from './lib/getPermitUtilsInstance'