diff --git a/apps/cowswap-frontend/src/mocks/tradeStateMock.ts b/apps/cowswap-frontend/src/mocks/tradeStateMock.ts index 20d5b09ac3..bd59d7fe92 100644 --- a/apps/cowswap-frontend/src/mocks/tradeStateMock.ts +++ b/apps/cowswap-frontend/src/mocks/tradeStateMock.ts @@ -51,6 +51,7 @@ export const outputCurrencyInfoMock: CurrencyInfo = { export const tradeContextMock: TradeFlowContext = { permitInfo: undefined, + generatePermitHook: (() => void 0) as any, postOrderParams: { class: OrderClass.LIMIT, account: '0x000', diff --git a/apps/cowswap-frontend/src/modules/limitOrders/hooks/useTradeFlowContext.ts b/apps/cowswap-frontend/src/modules/limitOrders/hooks/useTradeFlowContext.ts index f049ef4600..488fa09af8 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/hooks/useTradeFlowContext.ts +++ b/apps/cowswap-frontend/src/modules/limitOrders/hooks/useTradeFlowContext.ts @@ -15,7 +15,7 @@ import { useAppData } from 'modules/appData' import { useRateImpact } from 'modules/limitOrders/hooks/useRateImpact' import { TradeFlowContext } from 'modules/limitOrders/services/types' import { limitOrdersSettingsAtom } from 'modules/limitOrders/state/limitOrdersSettingsAtom' -import { useIsTokenPermittable } from 'modules/permit' +import { useGeneratePermitHook, useIsTokenPermittable } from 'modules/permit' import { useEnoughBalanceAndAllowance } from 'modules/tokens' import { TradeType } from 'modules/trade' import { useTradeQuote } from 'modules/tradeQuote' @@ -42,6 +42,7 @@ export function useTradeFlowContext(): TradeFlowContext | null { amount: state.slippageAdjustedSellAmount || undefined, checkAllowanceAddress, }) + const generatePermitHook = useGeneratePermitHook() if ( !chainId || @@ -76,6 +77,7 @@ export function useTradeFlowContext(): TradeFlowContext | null { provider, rateImpact, permitInfo: !enoughAllowance ? permitInfo : undefined, + generatePermitHook, postOrderParams: { class: OrderClass.LIMIT, kind: state.orderKind, diff --git a/apps/cowswap-frontend/src/modules/limitOrders/pure/LimitOrdersDetails/index.cosmos.tsx b/apps/cowswap-frontend/src/modules/limitOrders/pure/LimitOrdersDetails/index.cosmos.tsx index 68a605eb1f..66d3058a37 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/pure/LimitOrdersDetails/index.cosmos.tsx +++ b/apps/cowswap-frontend/src/modules/limitOrders/pure/LimitOrdersDetails/index.cosmos.tsx @@ -17,6 +17,7 @@ const outputCurrency = GNO[SupportedChainId.MAINNET] const tradeContext: TradeFlowContext = { permitInfo: undefined, + generatePermitHook: () => Promise.resolve(undefined), postOrderParams: { class: OrderClass.LIMIT, account: '0x000', 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 b2ff0d36a4..e6c7baf500 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/services/tradeFlow/index.ts +++ b/apps/cowswap-frontend/src/modules/limitOrders/services/tradeFlow/index.ts @@ -34,6 +34,7 @@ export async function tradeFlow( settlementContract, dispatch, isGnosisSafeWallet, + generatePermitHook, } = params const { account, recipientAddressOrName, sellToken, buyToken, appData } = postOrderParams const marketLabel = [sellToken.symbol, buyToken.symbol].join(',') @@ -61,10 +62,9 @@ export async function tradeFlow( postOrderParams.appData = await handlePermit({ permitInfo, inputToken: sellToken, - provider, account, - chainId, appData, + generatePermitHook, }) logTradeFlow('LIMIT ORDER FLOW', 'STEP 3: send transaction') diff --git a/apps/cowswap-frontend/src/modules/limitOrders/services/types.ts b/apps/cowswap-frontend/src/modules/limitOrders/services/types.ts index 2c55f9e369..71d0f022e0 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/services/types.ts +++ b/apps/cowswap-frontend/src/modules/limitOrders/services/types.ts @@ -6,7 +6,7 @@ import SafeAppsSDK from '@safe-global/safe-apps-sdk' import { AppDispatch } from 'legacy/state' import { PostOrderParams } from 'legacy/utils/trade' -import { IsTokenPermittableResult } from 'modules/permit' +import { GeneratePermitHook, IsTokenPermittableResult } from 'modules/permit' export interface TradeFlowContext { // signer changes creates redundant re-renders @@ -20,6 +20,7 @@ export interface TradeFlowContext { allowsOffchainSigning: boolean isGnosisSafeWallet: boolean permitInfo: IsTokenPermittableResult + generatePermitHook: GeneratePermitHook } export interface SafeBundleFlowContext extends TradeFlowContext { diff --git a/apps/cowswap-frontend/src/modules/permit/hooks/useAccountAgnosticPermitHookData.ts b/apps/cowswap-frontend/src/modules/permit/hooks/useAccountAgnosticPermitHookData.ts index b4d95a2d79..458cbe3e21 100644 --- a/apps/cowswap-frontend/src/modules/permit/hooks/useAccountAgnosticPermitHookData.ts +++ b/apps/cowswap-frontend/src/modules/permit/hooks/useAccountAgnosticPermitHookData.ts @@ -1,17 +1,15 @@ import { useEffect, useState } from 'react' -import { useWalletInfo } from '@cowprotocol/wallet' import { Token } from '@uniswap/sdk-core' -import { useWeb3React } from '@web3-react/core' import { useDerivedTradeState } from 'modules/trade' import { useSafeMemo } from 'common/hooks/useSafeMemo' +import { useGeneratePermitHook } from './useGeneratePermitHook' import { useIsTokenPermittable } from './useIsTokenPermittable' -import { PermitHookData, PermitHookParams } from '../types' -import { generatePermitHook } from '../utils/generatePermitHook' +import { GeneratePermitHookParams, PermitHookData } from '../types' /** * Returns PermitHookData using an account agnostic signer if inputCurrency is permittable @@ -21,7 +19,8 @@ import { generatePermitHook } from '../utils/generatePermitHook' * If not permittable or not able to tell, returns undefined */ export function useAccountAgnosticPermitHookData(): PermitHookData | undefined { - const params = usePermitHookParams() + const params = useGeneratePermitHookParams() + const generatePermitHook = useGeneratePermitHook() const [data, setData] = useState(undefined) @@ -33,28 +32,23 @@ export function useAccountAgnosticPermitHookData(): PermitHookData | undefined { } generatePermitHook(params).then(setData) - }, [params]) + }, [generatePermitHook, params]) return data } -function usePermitHookParams(): PermitHookParams | undefined { - const { chainId } = useWalletInfo() - const { provider } = useWeb3React() - +function useGeneratePermitHookParams(): GeneratePermitHookParams | undefined { const { state } = useDerivedTradeState() const { inputCurrency, tradeType } = state || {} const permitInfo = useIsTokenPermittable(inputCurrency, tradeType) return useSafeMemo(() => { - if (!inputCurrency || !provider || !permitInfo) return undefined + if (!inputCurrency || !permitInfo) return undefined return { - chainId, - provider, inputToken: inputCurrency as Token, permitInfo, } - }, [inputCurrency, provider, permitInfo, chainId]) + }, [inputCurrency, permitInfo]) } diff --git a/apps/cowswap-frontend/src/modules/permit/hooks/useGeneratePermitHook.ts b/apps/cowswap-frontend/src/modules/permit/hooks/useGeneratePermitHook.ts new file mode 100644 index 0000000000..0505914230 --- /dev/null +++ b/apps/cowswap-frontend/src/modules/permit/hooks/useGeneratePermitHook.ts @@ -0,0 +1,59 @@ +import { useSetAtom } from 'jotai' +import { useCallback } from 'react' + +import { useWalletInfo } from '@cowprotocol/wallet' +import { useWeb3React } from '@web3-react/core' + +import { getPermitCacheAtom, storePermitCacheAtom } from '../state/permitCacheAtom' +import { GeneratePermitHook, GeneratePermitHookParams, PermitHookData } from '../types' +import { generatePermitHook } from '../utils/generatePermitHook' +import { getPermitUtilsInstance } from '../utils/getPermitUtilsInstance' + +/** + * Hook that returns callback to generate permit hook data + */ +export function useGeneratePermitHook(): GeneratePermitHook { + const getCachedPermit = useSetAtom(getPermitCacheAtom) + const storePermit = useSetAtom(storePermitCacheAtom) + const { chainId } = useWalletInfo() + const { provider } = useWeb3React() + + return useCallback( + async (params: GeneratePermitHookParams): Promise => { + const { inputToken, account, permitInfo } = params + + if (!provider) { + return + } + + const eip2162Utils = getPermitUtilsInstance(chainId, provider, account) + + // 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 } + + const cachedPermit = getCachedPermit(permitParams) + + if (cachedPermit) { + return cachedPermit + } + + const hookData = await generatePermitHook({ + chainId, + inputToken, + provider, + permitInfo, + eip2162Utils, + account, + nonce, + }) + + storePermit({ ...permitParams, hookData }) + + return hookData + }, + [storePermit, chainId, getCachedPermit, provider] + ) +} diff --git a/apps/cowswap-frontend/src/modules/permit/hooks/useIsTokenPermittable.ts b/apps/cowswap-frontend/src/modules/permit/hooks/useIsTokenPermittable.ts index 147db0402d..b804d3aaf5 100644 --- a/apps/cowswap-frontend/src/modules/permit/hooks/useIsTokenPermittable.ts +++ b/apps/cowswap-frontend/src/modules/permit/hooks/useIsTokenPermittable.ts @@ -13,7 +13,7 @@ import { TradeType } from 'modules/trade' import { useIsPermitEnabled } from 'common/hooks/featureFlags/useIsPermitEnabled' import { ORDER_TYPE_SUPPORTS_PERMIT } from '../const' -import { addPermitInfoForTokenAtom, permittableTokensAtom } from '../state/atoms' +import { addPermitInfoForTokenAtom, permittableTokensAtom } from '../state/permittableTokensAtom' import { IsTokenPermittableResult } from '../types' import { checkIsTokenPermittable } from '../utils/checkIsTokenPermittable' diff --git a/apps/cowswap-frontend/src/modules/permit/index.ts b/apps/cowswap-frontend/src/modules/permit/index.ts index 6906fa3831..5f70182cfc 100644 --- a/apps/cowswap-frontend/src/modules/permit/index.ts +++ b/apps/cowswap-frontend/src/modules/permit/index.ts @@ -1,5 +1,5 @@ -export { useAccountAgnosticPermitHookData } from './hooks/useAccountAgnosticPermitHookData' -export { generatePermitHook } from './utils/generatePermitHook' -export { useIsTokenPermittable } from './hooks/useIsTokenPermittable' +export * from './hooks/useAccountAgnosticPermitHookData' +export * from './hooks/useIsTokenPermittable' +export * from './hooks/useGeneratePermitHook' export * from './utils/handlePermit' export * from './types' diff --git a/apps/cowswap-frontend/src/modules/permit/state/permitCacheAtom.ts b/apps/cowswap-frontend/src/modules/permit/state/permitCacheAtom.ts new file mode 100644 index 0000000000..aac142e2a1 --- /dev/null +++ b/apps/cowswap-frontend/src/modules/permit/state/permitCacheAtom.ts @@ -0,0 +1,106 @@ +import { atom } from 'jotai' +import { atomWithStorage } from 'jotai/utils' + +import { + CachedPermitData, + GetPermitCacheParams, + PermitCache, + PermitCacheKeyParams, + StorePermitCacheParams, +} from '../types' + +/** + * Atom that stores permit data for static permit requests. + * Should never change once it has been created. + * Used exclusively for quote requests + */ +export const staticPermitCacheAtom = atomWithStorage('staticPermitCache:v0', {}) + +/** + * 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:v0', {}) + +/** + * Atom to add/update permit cache data + * + * Input depends on the target type of cache: static or user + */ +export const storePermitCacheAtom = atom(null, (get, set, params: StorePermitCacheParams) => { + const atomToUpdate = params.account ? userPermitCacheAtom : staticPermitCacheAtom + + const key = buildKey(params) + + const dataToCache: CachedPermitData = { + hookData: params.hookData, + nonce: params.nonce, + } + + set(atomToUpdate, (permitCache) => ({ ...permitCache, [key]: JSON.stringify(dataToCache) })) +}) + +/** + * Atom to get the cached permit data. + * + * Returns either undefined when no cache or cache is outdated, or the cached permit hook data. + * + * When cache is outdated, it will remove the cache key from the target cache. + * For this reason it's a writable atom. + */ +export const getPermitCacheAtom = atom(null, (get, set, params: GetPermitCacheParams) => { + const atomToUpdate = params.account ? userPermitCacheAtom : staticPermitCacheAtom + + const permitCache = get(atomToUpdate) + const key = buildKey(params) + const cachedData = permitCache[key] + + if (!cachedData) { + return undefined + } + + try { + const { hookData, nonce: storedNonce }: CachedPermitData = JSON.parse(cachedData) + + if (params.account !== undefined) { + // User type permit cache, check the nonce + + const inputNonce = params.nonce + + if (storedNonce !== undefined && inputNonce !== undefined && storedNonce < inputNonce) { + // When both nonces exist and storedNonce < inputNonce, data is outdated + + // Remove cache key + set(atomToUpdate, removePermitCacheBuilder(key)) + + return undefined + } + } + + // Cache hit for both static and user permit types + return hookData + } catch (e) { + // Failed to parse stored data, clear cache and return nothing + + set(atomToUpdate, removePermitCacheBuilder(key)) + + console.info(`[getPermitCacheAtom] failed to parse stored data`, cachedData, e) + + return undefined + } +}) + +function buildKey({ chainId, tokenAddress, account }: PermitCacheKeyParams) { + const base = `${chainId}-${tokenAddress.toLowerCase()}` + + return account ? `${base}-${account.toLowerCase()}` : base +} + +const removePermitCacheBuilder = (key: string) => (permitCache: PermitCache) => { + const newPermitCache = { ...permitCache } + + delete newPermitCache[key] + + return newPermitCache +} diff --git a/apps/cowswap-frontend/src/modules/permit/state/atoms.ts b/apps/cowswap-frontend/src/modules/permit/state/permittableTokensAtom.ts similarity index 100% rename from apps/cowswap-frontend/src/modules/permit/state/atoms.ts rename to apps/cowswap-frontend/src/modules/permit/state/permittableTokensAtom.ts diff --git a/apps/cowswap-frontend/src/modules/permit/types.ts b/apps/cowswap-frontend/src/modules/permit/types.ts index 9017f2bfb2..5993a6fa67 100644 --- a/apps/cowswap-frontend/src/modules/permit/types.ts +++ b/apps/cowswap-frontend/src/modules/permit/types.ts @@ -31,12 +31,19 @@ export type PermitHookParams = { chainId: SupportedChainId permitInfo: SupportedPermitInfo provider: Web3Provider - account?: string + eip2162Utils: Eip2612PermitUtils + account?: string | undefined + nonce?: number | undefined } -export type HandlePermitParams = Omit & { +export type GeneratePermitHookParams = Pick + +export type GeneratePermitHook = (params: GeneratePermitHookParams) => Promise + +export type HandlePermitParams = Omit & { permitInfo: IsTokenPermittableResult appData: AppDataInfo + generatePermitHook: GeneratePermitHook } export type PermitHookData = latest.CoWHook @@ -67,3 +74,21 @@ export type CheckIsTokenPermittableParams = { chainId: SupportedChainId provider: Web3Provider } + +export type PermitCache = Record + +export type CachedPermitData = { + hookData: PermitHookData + nonce: number | undefined +} + +export type PermitCacheKeyParams = { + chainId: SupportedChainId + tokenAddress: string + account: string | undefined + nonce: number | undefined +} + +export type StorePermitCacheParams = PermitCacheKeyParams & { hookData: PermitHookData } + +export type GetPermitCacheParams = PermitCacheKeyParams diff --git a/apps/cowswap-frontend/src/modules/permit/utils/generatePermitHook.ts b/apps/cowswap-frontend/src/modules/permit/utils/generatePermitHook.ts index 1f2e295bab..7d781342aa 100644 --- a/apps/cowswap-frontend/src/modules/permit/utils/generatePermitHook.ts +++ b/apps/cowswap-frontend/src/modules/permit/utils/generatePermitHook.ts @@ -1,34 +1,17 @@ import { GP_VAULT_RELAYER } from '@cowprotocol/common-const' import { Web3Provider } from '@ethersproject/providers' -import { Eip2612PermitUtils } from '@1inch/permit-signed-approvals-utils' - import { buildDaiLikePermitCallData, buildEip2162PermitCallData } from './buildPermitCallData' import { getPermitDeadline } from './getPermitDeadline' -import { getPermitUtilsInstance } from './getPermitUtilsInstance' import { DEFAULT_PERMIT_GAS_LIMIT, DEFAULT_PERMIT_VALUE, PERMIT_SIGNER } from '../const' import { PermitHookData, PermitHookParams } from '../types' -const CACHE_PREFIX = 'permitCache:v0-' const REQUESTS_CACHE: { [permitKey: string]: Promise } = {} export async function generatePermitHook(params: PermitHookParams): Promise { const permitKey = getCacheKey(params) - const { chainId, provider, account, inputToken } = params - - const eip2162Utils = getPermitUtilsInstance(chainId, provider, account) - - // 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 cachedResult = load(permitKey, nonce) - if (cachedResult) { - return cachedResult - } - const cachedRequest = REQUESTS_CACHE[permitKey] if (cachedRequest) { @@ -40,10 +23,7 @@ export async function generatePermitHook(params: PermitHookParams): Promise { - // Store permit in the cache - save(permitKey, nonce, permitHookData) - + const request = generatePermitHookRaw(params).then((permitHookData) => { // Remove consumed request to avoid stale data delete REQUESTS_CACHE[permitKey] @@ -55,10 +35,8 @@ export async function generatePermitHook(params: PermitHookParams): Promise { - const { inputToken, chainId, permitInfo, provider, account, eip2162Utils, preFetchedNonce } = params +async function generatePermitHookRaw(params: PermitHookParams): Promise { + const { inputToken, chainId, permitInfo, provider, account, eip2162Utils, nonce: preFetchedNonce } = params const tokenAddress = inputToken.address const tokenName = inputToken.name || tokenAddress @@ -142,55 +120,5 @@ async function calculateGasLimit( function getCacheKey(params: PermitHookParams): string { const { inputToken, chainId, account } = params - return `${CACHE_PREFIX}${inputToken.address.toLowerCase()}-${chainId}${account ? `-${account.toLowerCase()}` : ''}` -} - -type CachedData = PermitHookData & { - nonce: number | undefined -} - -/** - * Stores data based on `key` and `nonce` - */ -function save(key: string, nonce: number | undefined, data: PermitHookData): void { - const cachedData: CachedData = { nonce, ...data } - - localStorage.setItem(key, JSON.stringify(cachedData)) -} - -/** - * Loads stored data if still valid - * - * Validity is based on `nonce` and only applies to real accounts - * Static data never gets invalidated - */ -function load(key: string, nonce: number | undefined): PermitHookData | undefined { - const cachedData = localStorage.getItem(key) - - if (!cachedData) { - return undefined - } - - try { - const parsedData: CachedData = JSON.parse(cachedData) - - // Extract nonce out of cached data - const { nonce: storedNonce, ...permitHookData } = parsedData - - // TODO: if one is defined while the other is not, should also ignore the cache - if (nonce !== undefined && storedNonce !== undefined && storedNonce < nonce) { - // When both nonces exist and stored is < than new, data is outdated - - // Remove cache key - localStorage.removeItem(key) - - return undefined - } - - // Cache is valid, return it - return permitHookData - } catch { - // Failed to parse stored data, return nothing - return undefined - } + return `${inputToken.address.toLowerCase()}-${chainId}${account ? `-${account.toLowerCase()}` : ''}` } diff --git a/apps/cowswap-frontend/src/modules/permit/utils/handlePermit.ts b/apps/cowswap-frontend/src/modules/permit/utils/handlePermit.ts index e623fd8ec3..38bc18b9c3 100644 --- a/apps/cowswap-frontend/src/modules/permit/utils/handlePermit.ts +++ b/apps/cowswap-frontend/src/modules/permit/utils/handlePermit.ts @@ -1,7 +1,5 @@ import { AppDataInfo, buildAppDataHooks, updateHooksOnAppData } from 'modules/appData' -import { generatePermitHook } from './generatePermitHook' - import { HandlePermitParams } from '../types' /** @@ -15,19 +13,21 @@ import { HandlePermitParams } from '../types' * Returns the updated appData */ export async function handlePermit(params: HandlePermitParams): Promise { - const { permitInfo, inputToken, provider, account, chainId, appData } = params + const { permitInfo, inputToken, account, appData, generatePermitHook } = params if (permitInfo) { - // permitInfo will only be set if there's enough allowance + // permitInfo will only be set if there's NOT enough allowance const permitData = await generatePermitHook({ inputToken, - provider, account, - chainId, permitInfo, }) + if (!permitData) { + throw new Error(`Unable to generate permit data`) + } + const hooks = buildAppDataHooks([permitData]) return updateHooksOnAppData(appData, hooks) diff --git a/apps/cowswap-frontend/src/modules/swap/hooks/useSwapFlowContext.ts b/apps/cowswap-frontend/src/modules/swap/hooks/useSwapFlowContext.ts index f2c1329a80..086e4329ba 100644 --- a/apps/cowswap-frontend/src/modules/swap/hooks/useSwapFlowContext.ts +++ b/apps/cowswap-frontend/src/modules/swap/hooks/useSwapFlowContext.ts @@ -3,7 +3,7 @@ import { useGP2SettlementContract } from '@cowprotocol/common-hooks' import { OrderKind, SupportedChainId } from '@cowprotocol/cow-sdk' import { TradeType as UniTradeType } from '@uniswap/sdk-core' -import { useIsTokenPermittable } from 'modules/permit' +import { useGeneratePermitHook, useIsTokenPermittable } from 'modules/permit' import { FlowType, getFlowContext, useBaseFlowContextSetup } from 'modules/swap/hooks/useFlowContext' import { SwapFlowContext } from 'modules/swap/services/types' import { useEnoughBalanceAndAllowance } from 'modules/tokens' @@ -14,6 +14,7 @@ export function useSwapFlowContext(): SwapFlowContext | null { const baseProps = useBaseFlowContextSetup() const sellCurrency = baseProps.trade?.inputAmount?.currency const permitInfo = useIsTokenPermittable(sellCurrency, TradeType.SWAP) + const generatePermitHook = useGeneratePermitHook() const checkAllowanceAddress = GP_VAULT_RELAYER[baseProps.chainId || SupportedChainId.MAINNET] const { enoughAllowance } = useEnoughBalanceAndAllowance({ @@ -36,5 +37,6 @@ export function useSwapFlowContext(): SwapFlowContext | null { ...baseContext, contract, permitInfo: !enoughAllowance ? permitInfo : undefined, + generatePermitHook, } } diff --git a/apps/cowswap-frontend/src/modules/swap/services/swapFlow/index.ts b/apps/cowswap-frontend/src/modules/swap/services/swapFlow/index.ts index 326f3889ed..ec49a936b3 100644 --- a/apps/cowswap-frontend/src/modules/swap/services/swapFlow/index.ts +++ b/apps/cowswap-frontend/src/modules/swap/services/swapFlow/index.ts @@ -1,4 +1,3 @@ -import { Web3Provider } from '@ethersproject/providers' import { Percent, Token } from '@uniswap/sdk-core' import { PriceImpact } from 'legacy/hooks/usePriceImpact' @@ -31,12 +30,10 @@ export async function swapFlow( input.orderParams.appData = await handlePermit({ appData: input.orderParams.appData, - inputToken: input.context.trade.inputAmount.currency as Token, - provider: input.orderParams.signer.provider as Web3Provider, account: input.orderParams.account, - chainId: input.orderParams.chainId, permitInfo: input.permitInfo, + generatePermitHook: input.generatePermitHook, }) input.swapConfirmManager.permitSigned() diff --git a/apps/cowswap-frontend/src/modules/swap/services/types.ts b/apps/cowswap-frontend/src/modules/swap/services/types.ts index 6417e077aa..52d3f55a30 100644 --- a/apps/cowswap-frontend/src/modules/swap/services/types.ts +++ b/apps/cowswap-frontend/src/modules/swap/services/types.ts @@ -10,7 +10,7 @@ import TradeGp from 'legacy/state/swap/TradeGp' import { PostOrderParams } from 'legacy/utils/trade' import { AppDataInfo, UploadAppDataParams } from 'modules/appData' -import { IsTokenPermittableResult } from 'modules/permit' +import { GeneratePermitHook, IsTokenPermittableResult } from 'modules/permit' import { SwapConfirmManager } from 'modules/swap/hooks/useSwapConfirmManager' import { SwapFlowAnalyticsContext } from 'modules/trade/utils/analytics' @@ -45,6 +45,7 @@ export interface BaseFlowContext { export type SwapFlowContext = BaseFlowContext & { contract: GPv2Settlement permitInfo: IsTokenPermittableResult + generatePermitHook: GeneratePermitHook } export type EthFlowContext = BaseFlowContext & {