Skip to content

Conversation

@shoom3301
Copy link
Collaborator

@shoom3301 shoom3301 commented Oct 16, 2025

Summary

Fixes https://www.notion.so/cownation/Handle-edge-case-for-Buy-orders-with-EOAs-2578da5f04ca8083a97de4eb393b4ac5
https://www.notion.so/cownation/Wrong-navigation-when-approval-TX-fails-2878da5f04ca801982fede0f21a79bc7
https://www.notion.so/cownation/Don-t-navigate-to-confirm-moda-when-approved-less-than-needed-2878da5f04ca80028f02e0ca16445316

  1. Added "Current allowance" displaying when approval is needed (commit)
  2. Added 1% on top of the amount to approve for buy orders (commit)
  3. Added "optimistic" allowance updating based on approval tx receipt. I added that to immediately update the allowance when tx is mined (commit)
  4. Fixed navigation to confirm screen after swap. Before we could go there when: a) tx was not successfully mined. b) user has lowered the allowance on the wallet side. Now, in those cases we don't go to confirm screen. See the demo video (commit)
Screenshot 2025-10-16 at 15 28 21 Screenshot 2025-10-16 at 15 50 18
demo-appr.mov

To Test

  1. https://www.notion.so/cownation/Handle-edge-case-for-Buy-orders-with-EOAs-2578da5f04ca8083a97de4eb393b4ac5
  2. https://www.notion.so/cownation/Wrong-navigation-when-approval-TX-fails-2878da5f04ca801982fede0f21a79bc7
  3. https://www.notion.so/cownation/Don-t-navigate-to-confirm-moda-when-approved-less-than-needed-2878da5f04ca80028f02e0ca16445316
  4. See the cases above

Summary by CodeRabbit

  • New Features

    • Global optimistic allowance tracking with automatic expiry and network reset.
    • Current token allowance shown near approval controls; consolidated approve-and-swap flow with clearer approved-amount results.
    • Permission tooltip only shown when no cached permit; confirm button text adapts for bridging.
  • Bug Fixes

    • UI uses actual on-chain approval amounts when wallet edits occur.
    • Reduced approval/swap race and error scenarios; standardized approval error handling and analytics.
  • Enhancements

    • Buy orders get a 1% send-amount buffer for better fills.
  • Tests

    • Extensive coverage added for approval, permit, and approval-result flows.

@vercel
Copy link

vercel bot commented Oct 16, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
cowfi Ready Ready Preview Oct 21, 2025 9:57am
explorer-dev Ready Ready Preview Oct 21, 2025 9:57am
swap-dev Ready Ready Preview Oct 21, 2025 9:57am
widget-configurator Canceled Canceled Oct 21, 2025 9:57am
2 Skipped Deployments
Project Deployment Preview Updated (UTC)
cosmos Ignored Ignored Oct 21, 2025 9:57am
sdk-tools Ignored Ignored Preview Oct 21, 2025 9:57am

💡 Enable Vercel Agent with $100 free credit for automated AI reviews

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 16, 2025

Walkthrough

Replaces per-token last-approve tracking with a global optimistic allowances store; adds utilities/hooks to set/derive optimistic allowances from approval tx logs; centralizes approve-and-swap orchestration; updates approval hooks, UI components, tests, and removes legacy last-approve plumbing.

Changes

Cohort / File(s) Summary
Optimistic allowance core
apps/cowswap-frontend/src/entities/optimisticAllowance/types.ts, apps/cowswap-frontend/src/entities/optimisticAllowance/optimisticAllowancesAtom.ts, apps/cowswap-frontend/src/entities/optimisticAllowance/getOptimisticAllowanceKey.ts, apps/cowswap-frontend/src/entities/optimisticAllowance/useSetOptimisticAllowance.ts
New OptimisticAllowance type, jotai atom store, composite key generator, and setter hook to record optimistic allowances (amount, blockNumber, timestamp).
Token allowance hook
apps/cowswap-frontend/src/common/hooks/useTokenAllowance.ts
Replaces per-token last-approve block tracking with optimisticAllowances; SWR key deps simplified; optimistic override applied to SWR data; TTL cleanup and network-reset added; removed last-approve atom and updater.
Approval processing utils & tests
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.ts, apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.test.ts
New processApprovalTransaction to validate receipts and extract Approval events/amounts; comprehensive tests covering success, revocation, multi-log resolution, and error paths.
Trade approve callback & tests
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts, .../useTradeApproveCallback.test.ts
Callback integrates processApprovalTransaction and useSetOptimisticAllowance, introduces typed TradeApproveResult, new analytics/error hooks, and expanded tests for optimistic allowance and lifecycle behaviors.
Approve-and-swap orchestration
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts, .../useApproveAndSwap.test.ts
New useApproveAndSwap hook coordinating permit or approval flows with optional onApproveConfirm/confirmSwap, validating approved amounts; tests added.
Approve UI & helpers
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx, apps/cowswap-frontend/src/modules/erc20Approve/pure/TradeAllowanceDisplay/*, apps/cowswap-frontend/src/modules/erc20Approve/utils/getIsTradeApproveResult.ts
TradeApproveButton switched to useApproveAndSwap, label prop optional/removed in some places; added TradeAllowanceDisplay component + styles; added type guard getIsTradeApproveResult.
Approval-related hooks & signatures
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts, apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveCurrency.ts, tests
useIsApprovalOrPermitRequired now returns { reason, currentAllowance }; useApproveCurrency return types updated to include generic GenerecTradeApproveResult; tests adapted.
Finalize tx / pending tx cleanup
apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/types.ts, .../hooks/usePendingTransactionsContext.ts, .../services/finalizeEthereumTransaction.ts
Removed updateLastApproveTxBlockNumber from interface and usages; finalizer no longer updates per-token last-approve block number.
Trade/order helpers & misc
apps/cowswap-frontend/src/modules/trade/hooks/useAmountsToSignFromQuote.ts, apps/cowswap-frontend/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts, libs/common-utils/src/fractionUtils.ts, apps/cowswap-frontend/src/modules/ethFlow/containers/EthFlow/hooks/useEthFlowActions.ts
Added FractionUtils.addPercent and 1% buy-order buffer; trade-form validation now reads .reason; ethFlow resolves tx hash from res?.txResponse.hash.
Permit caching & exposure
apps/cowswap-frontend/src/modules/permit/hooks/useGetCachedPermit.ts, apps/cowswap-frontend/src/modules/permit/hooks/useHasCachedPermit.ts, apps/cowswap-frontend/src/modules/permit/index.ts, apps/cowswap-frontend/src/modules/permit/state/permitCacheAtom.ts
Added useHasCachedPermit hook and re-export; minor TODO and atom init option changes to permit cache atoms.
UI label/flow removals
apps/cowswap-frontend/src/modules/swap/hooks/useGetConfirmButtonLabel.ts, apps/cowswap-frontend/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx
Removed useGetConfirmButtonLabel and updated callsites (label removed from some TradeApproveButton invocations).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as TradeApproveButton
    participant Hook as useApproveAndSwap
    participant Permit as PermitFlow
    participant Approver as ApprovalFlow
    participant Chain as Blockchain
    participant Store as OptimisticAllowances

    User->>UI: click "Approve"
    UI->>Hook: invoke(amount, params)

    alt permit path (supported & used)
        Hook->>Permit: generate/sign permit
        Permit->>Chain: submit signature
        Chain-->>Permit: result
        Hook->>UI: continue with permit (confirmSwap)
    else standard approval
        Hook->>Approver: send approval tx
        Approver->>Chain: broadcast tx
        Chain-->>Approver: tx receipt
        Approver->>Approver: processApprovalTransaction(receipt)
        Approver->>Store: setOptimisticAllowance(key, { amount, blockNumber, timestamp })
        Approver-->>Hook: { txResponse, approvedAmount }
        Hook->>UI: validate approvedAmount → confirmSwap()
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

Tokens

Suggested reviewers

  • elena-zh

Poem

🐇
I hop with keys and tiny logs,
I stash allowances, tick the clocks,
Optimistic leaps before the chain,
Then settle safe when receipts remain,
A rabbit cheers — the UX hums along.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "feat(approve): adjust partial approves for buy orders" is related to the changeset and describes a real aspect of the changes—specifically, the 1% buffer addition for buy orders in useAmountsToSignFromQuote.ts. However, this is one of several significant modifications in the PR. The changeset also introduces "Current allowance" display, optimistic allowance tracking with TTL-based cleanup, and importantly, fixes navigation and error handling for edge cases when approvals fail or result in insufficient allowance. The title focuses on the tactical buy-order adjustment rather than capturing the broader intent of addressing edge cases and preventing incorrect navigation during approval failures.
Description Check ✅ Passed The PR description is substantive and mostly complete. It includes a clear Summary section that references three linked Notion issues and outlines four specific changes with associated commits. The description is accompanied by visual evidence (two screenshots and a demo video), providing context for the modifications. The "To Test" section references the three Notion pages that contain detailed test cases. While the Background section (marked as optional in the template) is absent, and the "To Test" section provides links rather than inline step-by-step instructions, the description conveys the key information needed for understanding and testing the changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/partial-approve-buy-order

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@shoom3301 shoom3301 self-assigned this Oct 16, 2025
@shoom3301 shoom3301 requested review from a team October 16, 2025 10:35
@shoom3301 shoom3301 changed the title Fix/partial approve buy order feat(approve): adjust partial approves for buy orders Oct 16, 2025
@shoom3301 shoom3301 marked this pull request as ready for review October 16, 2025 10:36
return { maximumSendSellAmount, minimumReceiveBuyAmount }
}, [isQuoteBasedOrder, inputCurrencyAmount, outputCurrencyAmount, afterSlippage])
return {
// Add 1% threshold for buy orders to level out price/gas fluctuations
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1% addition to approving amount for buy orders

ONCHAIN_TRANSACTIONS_EVENTS.emit(OnchainTxEvents.BEFORE_TX_FINALIZE, { transaction, receipt })

// Once approval tx is mined, we add the priority allowance to immediately allow the user to place orders
if (transaction.approval) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was removed because now we have optimistic allowance

const approvedAmount = tx.approvedAmount
const isApprovedAmountSufficient = Boolean(approvedAmount && approvedAmount >= amountToApproveBig)

if (isApprovedAmountSufficient) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prevent opening swap confirm if there is not enough allowance after tx mined

amountToApprove={partialAmountToApprove}
/>
{typeof currentAllowance === 'bigint' && currencyToApprove && (
<TradeAllowanceDisplay currentAllowance={currentAllowance} currencyToApprove={currencyToApprove} />
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Display current allowance amount

txResponse: TransactionReceipt,
): SetOptimisticAllowanceParams | null {
if (txResponse.status !== 1) {
throw new Error('Approval transaction failed')
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prevent successful approval path if tx was not successfuly mined

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (9)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveToggle/TradeApproveToggle.tsx (1)

18-23: Unwrapping the fragment is fine

Directly returning Toggle is clean; no behavior change.

Consider returning JSX.Element instead of ReactNode for component return type to be more specific (and drop the ReactNode import).

apps/cowswap-frontend/src/entities/optimisticAllowance/getOptimisticAllowanceKey.ts (1)

1-7: Deterministic composite key looks good

Lowercasing token/owner/spender with chainId provides a stable key for the store. Works well with the setter.

If inputs may arrive in mixed formats, consider getAddress normalization before lowercasing to guard against malformed strings.

apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.ts (1)

69-81: More robust log parsing (guard topics, pick last match) and safer status check

  • Prefer the last matching Approval log to reflect the final state if multiple approvals occur in one tx.
  • Guard topics length to avoid exceptions in malformed logs (reduces console noise).
  • Treat explicit status === 0 as failure; some providers may return null/undefined.

Apply these diffs:

@@
-  if (txResponse.status !== 1) {
+  if (txResponse.status === 0) {
     throw new Error('Approval transaction failed')
   }
@@
-    // Find the Approval event log
-    const approvalLog = txReceipt.logs.find((log) => {
-      // Check if it's from the token contract and has the Approval event signature
-      if (log.address.toLowerCase() !== tokenAddress.toLowerCase()) return false
-      if (log.topics[0] !== APPROVAL_EVENT_TOPIC) return false
-
-      // Verify owner and spender match (topics[1] = owner, topics[2] = spender)
-      const logOwner = getAddress('0x' + log.topics[1].slice(26))
-      const logSpender = getAddress('0x' + log.topics[2].slice(26))
-
-      return logOwner.toLowerCase() === owner.toLowerCase() && logSpender.toLowerCase() === spender.toLowerCase()
-    })
+    // Find the last matching Approval event log (in case multiple occur in one tx)
+    let approvalLog: typeof txReceipt.logs[number] | undefined
+    for (let i = txReceipt.logs.length - 1; i >= 0; i--) {
+      const log = txReceipt.logs[i]
+      if (log.address.toLowerCase() !== tokenAddress.toLowerCase()) continue
+      if (!log.topics || log.topics.length < 3) continue
+      if (log.topics[0] !== APPROVAL_EVENT_TOPIC) continue
+      try {
+        const logOwner = getAddress('0x' + log.topics[1].slice(26))
+        const logSpender = getAddress('0x' + log.topics[2].slice(26))
+        if (logOwner.toLowerCase() === owner.toLowerCase() && logSpender.toLowerCase() === spender.toLowerCase()) {
+          approvalLog = log
+          break
+        }
+      } catch {
+        // skip invalid topics
+        continue
+      }
+    }

Replace console.error with your app logger for consistency.

Also applies to: 25-27

apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.test.ts (1)

244-259: Tighten the “returns receipt” test with an explicit assertion

The test name promises a return; currently it only checks state cleanup. Consider asserting the returned object shape to lock the callback’s API.

Example:

const resultValue = await result.current(mockAmount)
expect(resultValue).toEqual(
  expect.objectContaining({
    txResponse: expect.objectContaining({ transactionHash: '0xtxhash' }),
    approvedAmount: undefined,
  }),
)
```<!-- review_comment_end -->

</blockquote></details>
<details>
<summary>apps/cowswap-frontend/src/entities/optimisticAllowance/useSetOptimisticAllowance.ts (1)</summary><blockquote>

`19-33`: **Guard against stale writes (older blockNumber) when updating optimistic allowances**

Without an ordering check, an older tx can overwrite a newer allowance. Compare blockNumbers and skip stale updates.


```diff
   return useCallback(
     (params: SetOptimisticAllowanceParams) => {
       const key = getOptimisticAllowanceKey(params)

       // Set optimistic allowance immediately
-      setOptimisticAllowances((state) => ({
-        ...state,
-        [key]: {
-          amount: params.amount,
-          blockNumber: params.blockNumber,
-          timestamp: Date.now(),
-        },
-      }))
+      setOptimisticAllowances((state) => {
+        const prev = state[key]
+        if (prev && params.blockNumber < prev.blockNumber) {
+          // Ignore stale update
+          return state
+        }
+        return {
+          ...state,
+          [key]: {
+            amount: params.amount,
+            blockNumber: params.blockNumber,
+            timestamp: Date.now(),
+          },
+        }
+      })
     },
     [setOptimisticAllowances],
   )
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveWithAffectedOrderList/TradeApproveWithAffectedOrderList.tsx (1)

26-30: Confirm intent: Dai-like permit case excluded from UI block

You gate the toggle/allowance display for Required and EIP‑2612, but not Dai‑like. If Dai‑like should also show the same UI, include ApproveRequiredReason.DaiLikePermitRequired.

-  const isApproveOrPartialPermitRequired =
-    isApproveRequired === ApproveRequiredReason.Required ||
-    isApproveRequired === ApproveRequiredReason.Eip2612PermitRequired
+  const isApproveOrPartialPermitRequired =
+    isApproveRequired === ApproveRequiredReason.Required ||
+    isApproveRequired === ApproveRequiredReason.Eip2612PermitRequired ||
+    isApproveRequired === ApproveRequiredReason.DaiLikePermitRequired

Minor: rename isApproveRequired to approveReason for clarity (it holds an enum reason).

Also applies to: 36-45

apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.test.ts (1)

170-219: Add a test for the Safe wallet path (non-TradeApproveResult branch)

Currently untested: when handleApprove returns SafeMultisigTransactionResponse, confirmSwap should still be called (else-branch of getIsTradeApproveResult).

@@
   describe('approval flow with TradeApproveResult', () => {
@@
   })
 
+  it('should call confirmSwap when handleApprove returns SafeMultisigTransactionResponse', async () => {
+    // Emulate a Safe response (any non-TradeApproveResult object)
+    const safeTx = { safeTxHash: '0xabc', isExecuted: false } as unknown as SafeMultisigTransactionResponse
+    mockHandleApprove.mockResolvedValue(safeTx)
+
+    const { result } = renderHook(() =>
+      useApproveAndSwap({
+        amountToApprove: mockAmountToApprove,
+        confirmSwap: mockConfirmSwap,
+        ignorePermit: false,
+        useModals: true,
+      }),
+    )
+
+    await result.current()
+
+    await waitFor(() => {
+      expect(mockHandleApprove).toHaveBeenCalledWith(MAX_APPROVE_AMOUNT)
+      expect(mockConfirmSwap).toHaveBeenCalled()
+    })
+  })

Also applies to: 335-359

apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx (1)

74-79: Tooltip text mismatches partial-approve behavior

In the partial-approve branch, the tooltip still references “approve the default amount… once per token.” Adjust copy to reflect partial approvals.

-            <Trans>
-              You must give the CoW Protocol smart contracts permission to use your{' '}
-              <TokenSymbol token={amountToApprove.currency} />. If you approve the default amount, you will only have to
-              do this once per token.
-            </Trans>
+            <Trans>
+              You must give the CoW Protocol smart contracts permission to use your{' '}
+              <TokenSymbol token={amountToApprove.currency} />. If you approve only the trade amount, you may need to
+              approve again for future trades.
+            </Trans>
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts (1)

66-84: Clarify variable naming to avoid confusion

approvedAmount holds SetOptimisticAllowanceParams, not a bigint. Rename for clarity and avoid shadowing meaning.

-          return response?.wait().then((txResponse) => {
+          return response?.wait().then((txResponse) => {
             // Set optimistic allowance immediately after transaction is mined
             // Extract the actual approved amount from transaction logs
-            const approvedAmount = processApprovalTransaction(
+            const optimisticParams = processApprovalTransaction(
               {
                 currency,
                 account,
                 spender,
                 chainId,
               },
               txResponse,
             )

-            if (approvedAmount) {
-              setOptimisticAllowance(approvedAmount)
+            if (optimisticParams) {
+              setOptimisticAllowance(optimisticParams)
             }

-            return { txResponse, approvedAmount: approvedAmount?.amount }
+            return { txResponse, approvedAmount: optimisticParams?.amount }
           })
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16aaa17 and fd9ad88.

📒 Files selected for processing (27)
  • apps/cowswap-frontend/src/common/hooks/useTokenAllowance.ts (2 hunks)
  • apps/cowswap-frontend/src/entities/optimisticAllowance/getOptimisticAllowanceKey.ts (1 hunks)
  • apps/cowswap-frontend/src/entities/optimisticAllowance/optimisticAllowancesAtom.ts (1 hunks)
  • apps/cowswap-frontend/src/entities/optimisticAllowance/types.ts (1 hunks)
  • apps/cowswap-frontend/src/entities/optimisticAllowance/useSetOptimisticAllowance.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx (2 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.test.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.test.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts (4 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveToggle/TradeApproveToggle.tsx (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveWithAffectedOrderList/TradeApproveWithAffectedOrderList.tsx (2 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.test.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveCurrency.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts (21 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts (2 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/pure/TradeAllowanceDisplay/index.tsx (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/pure/TradeAllowanceDisplay/styled.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/utils/getIsTradeApproveResult.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/ethFlow/containers/EthFlow/hooks/useEthFlowActions.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/hooks/usePendingTransactionsContext.ts (0 hunks)
  • apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/services/finalizeEthereumTransaction.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/types.ts (0 hunks)
  • apps/cowswap-frontend/src/modules/trade/hooks/useAmountsToSignFromQuote.ts (2 hunks)
  • apps/cowswap-frontend/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts (1 hunks)
  • libs/common-utils/src/fractionUtils.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/types.ts
  • apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/hooks/usePendingTransactionsContext.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-10T20:28:16.565Z
Learnt from: fairlighteth
PR: cowprotocol/cowswap#6347
File: apps/cowswap-frontend/src/modules/trade/pure/TradeConfirmation/index.tsx:49-49
Timestamp: 2025-10-10T20:28:16.565Z
Learning: In apps/cowswap-frontend/src/modules/trade, TradeConfirmation follows a two-layer architecture: TradeConfirmationView (pure/stateless) in pure/TradeConfirmation/index.tsx renders the UI, while TradeConfirmation (container) in containers/TradeConfirmation/index.tsx wraps it to freeze props during pending trades (via useStableTradeConfirmationProps), wire in signing state (useSigningStep), and inject trade confirmation state (useTradeConfirmState). Consuming modules should import the container TradeConfirmation from 'modules/trade' to preserve this stateful behavior.
<!-- [add_learning]
When reviewing component refactoring in apps/cowswap-frontend/src/modules/trade, recognize the pattern where a pure view component (e.g., TradeConfirmationView) is separated from a stateful container (e.g., TradeConfirmation) that wraps it. The container adds runtime state management (prop stabilization, signing state, etc.) while the view remains testable and composable. Do not flag usages that import th...

Applied to files:

  • apps/cowswap-frontend/src/modules/erc20Approve/pure/TradeAllowanceDisplay/index.tsx
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveToggle/TradeApproveToggle.tsx
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveWithAffectedOrderList/TradeApproveWithAffectedOrderList.tsx
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx
📚 Learning: 2025-10-13T19:41:31.440Z
Learnt from: limitofzero
PR: cowprotocol/cowswap#6351
File: apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts:87-121
Timestamp: 2025-10-13T19:41:31.440Z
Learning: In apps/cowswap-frontend/src/modules/erc20Approve, useApproveCallback returns Promise<TransactionResponse | undefined> and is distinct from useApproveCurrency, which can return Promise<TransactionReceipt | SafeMultisigTransactionResponse>. When reviewing approval flows, verify which hook is actually being used before flagging Safe wallet concerns.

Applied to files:

  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.test.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.ts
  • apps/cowswap-frontend/src/modules/ethFlow/containers/EthFlow/hooks/useEthFlowActions.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveWithAffectedOrderList/TradeApproveWithAffectedOrderList.tsx
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.test.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveCurrency.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.test.ts
📚 Learning: 2025-08-08T13:56:18.009Z
Learnt from: shoom3301
PR: cowprotocol/cowswap#6125
File: libs/tokens/src/updaters/TokensListsUpdater/index.tsx:29-31
Timestamp: 2025-08-08T13:56:18.009Z
Learning: In libs/tokens/src/updaters/TokensListsUpdater/index.tsx, the project’s current Jotai version requires using `unstable_getOnInit` (not `getOnInit`) in atomWithStorage options; keep `{ unstable_getOnInit: true }` until Jotai is upgraded.

Applied to files:

  • apps/cowswap-frontend/src/common/hooks/useTokenAllowance.ts
🧬 Code graph analysis (18)
apps/cowswap-frontend/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts (1)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts (1)
  • useIsApprovalOrPermitRequired (22-48)
apps/cowswap-frontend/src/entities/optimisticAllowance/getOptimisticAllowanceKey.ts (1)
apps/cowswap-frontend/src/entities/optimisticAllowance/useSetOptimisticAllowance.ts (1)
  • SetOptimisticAllowanceParams (7-14)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.test.ts (6)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveCurrency.ts (1)
  • useApproveCurrency (10-30)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useGeneratePermitInAdvanceToTrade.ts (1)
  • useGeneratePermitInAdvanceToTrade (12-41)
apps/cowswap-frontend/src/modules/erc20Approve/state/useIsPartialApproveSelectedByUser.ts (1)
  • useIsPartialApproveSelectedByUser (5-7)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts (1)
  • useApproveAndSwap (21-69)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts (1)
  • TradeApproveResult (24-24)
apps/cowswap-frontend/src/modules/erc20Approve/constants/maxApproveAmount.ts (1)
  • MAX_APPROVE_AMOUNT (3-3)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.ts (1)
apps/cowswap-frontend/src/entities/optimisticAllowance/useSetOptimisticAllowance.ts (1)
  • SetOptimisticAllowanceParams (7-14)
apps/cowswap-frontend/src/modules/erc20Approve/utils/getIsTradeApproveResult.ts (1)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts (1)
  • TradeApproveResult (24-24)
apps/cowswap-frontend/src/entities/optimisticAllowance/useSetOptimisticAllowance.ts (2)
apps/cowswap-frontend/src/entities/optimisticAllowance/optimisticAllowancesAtom.ts (1)
  • optimisticAllowancesAtom (5-5)
apps/cowswap-frontend/src/entities/optimisticAllowance/getOptimisticAllowanceKey.ts (1)
  • getOptimisticAllowanceKey (3-7)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveWithAffectedOrderList/TradeApproveWithAffectedOrderList.tsx (4)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts (1)
  • useIsApprovalOrPermitRequired (22-48)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveToggle/TradeApproveToggle.tsx (1)
  • TradeApproveToggle (13-25)
apps/cowswap-frontend/src/modules/erc20Approve/pure/TradeAllowanceDisplay/index.tsx (1)
  • TradeAllowanceDisplay (13-24)
apps/cowswap-frontend/src/modules/erc20Approve/containers/ActiveOrdersWithAffectedPermit/ActiveOrdersWithAffectedPermit.tsx (1)
  • ActiveOrdersWithAffectedPermit (16-45)
apps/cowswap-frontend/src/entities/optimisticAllowance/optimisticAllowancesAtom.ts (1)
apps/cowswap-frontend/src/entities/optimisticAllowance/types.ts (1)
  • OptimisticAllowance (1-5)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts (5)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useGetAmountToSignApprove.tsx (1)
  • useGetAmountToSignApprove (21-45)
libs/common-hooks/src/useFeatureFlags.ts (1)
  • useFeatureFlags (5-7)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveState.ts (1)
  • useApproveState (22-38)
apps/cowswap-frontend/src/modules/trade/hooks/useDerivedTradeState.ts (1)
  • useDerivedTradeState (7-9)
apps/cowswap-frontend/src/modules/permit/hooks/usePermitInfo.ts (1)
  • usePermitInfo (46-119)
apps/cowswap-frontend/src/modules/trade/hooks/useAmountsToSignFromQuote.ts (2)
apps/cowswap-frontend/src/modules/trade/hooks/useGetReceiveAmountInfo.ts (1)
  • useGetReceiveAmountInfo (13-60)
libs/common-utils/src/fractionUtils.ts (1)
  • FractionUtils (11-186)
apps/cowswap-frontend/src/common/hooks/useTokenAllowance.ts (2)
apps/cowswap-frontend/src/entities/optimisticAllowance/optimisticAllowancesAtom.ts (1)
  • optimisticAllowancesAtom (5-5)
apps/cowswap-frontend/src/entities/optimisticAllowance/getOptimisticAllowanceKey.ts (1)
  • getOptimisticAllowanceKey (3-7)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts (3)
libs/wallet/src/api/hooks.ts (1)
  • useWalletInfo (24-26)
apps/cowswap-frontend/src/entities/optimisticAllowance/useSetOptimisticAllowance.ts (1)
  • useSetOptimisticAllowance (16-35)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.ts (1)
  • processApprovalTransaction (21-51)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.test.ts (1)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.ts (1)
  • processApprovalTransaction (21-51)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx (1)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts (1)
  • useApproveAndSwap (21-69)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveCurrency.ts (1)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts (2)
  • TradeApproveResult (24-24)
  • useTradeApproveCallback (30-117)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts (1)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts (1)
  • useIsApprovalOrPermitRequired (22-48)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts (5)
apps/cowswap-frontend/src/modules/erc20Approve/state/useIsPartialApproveSelectedByUser.ts (1)
  • useIsPartialApproveSelectedByUser (5-7)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveCurrency.ts (1)
  • useApproveCurrency (10-30)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useGeneratePermitInAdvanceToTrade.ts (1)
  • useGeneratePermitInAdvanceToTrade (12-41)
apps/cowswap-frontend/src/modules/erc20Approve/constants/maxApproveAmount.ts (1)
  • MAX_APPROVE_AMOUNT (3-3)
apps/cowswap-frontend/src/modules/erc20Approve/utils/getIsTradeApproveResult.ts (1)
  • getIsTradeApproveResult (6-13)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.test.ts (7)
libs/common-hooks/src/useFeatureFlags.ts (1)
  • useFeatureFlags (5-7)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveCallback.tsx (1)
  • useApproveCallback (43-79)
apps/cowswap-frontend/src/modules/erc20Approve/state/useUpdateTradeApproveState.ts (1)
  • useUpdateTradeApproveState (7-9)
libs/wallet/src/api/hooks.ts (1)
  • useWalletInfo (24-26)
apps/cowswap-frontend/src/entities/optimisticAllowance/useSetOptimisticAllowance.ts (1)
  • useSetOptimisticAllowance (16-35)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.ts (1)
  • processApprovalTransaction (21-51)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts (1)
  • useTradeApproveCallback (30-117)
🪛 Gitleaks (8.28.0)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.test.ts

[high] 13-13: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cypress
  • GitHub Check: Setup
🔇 Additional comments (18)
apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/services/finalizeEthereumTransaction.ts (1)

19-19: Ensure optimistic allowance update is dispatched for approvals
All references to updateLastApproveTxBlockNumber have been removed. Verify that finalizeEthereumTransaction now dispatches the new optimistic allowance update when processing approval transactions.

apps/cowswap-frontend/src/modules/ethFlow/containers/EthFlow/hooks/useEthFlowActions.ts (1)

76-79: Correctly sourcing tx hash from receipt

Using res?.txResponse?.transactionHash aligns with TradeApproveCallback’s returned shape and ethers types. Fixes prior hash lookup mismatch.

apps/cowswap-frontend/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts (1)

44-44: LGTM! Correctly extracts the reason property.

The change correctly adapts to the new return shape of useIsApprovalOrPermitRequired, which now returns { reason: ApproveRequiredReason; currentAllowance: Nullish<bigint> } instead of just ApproveRequiredReason.

libs/common-utils/src/fractionUtils.ts (1)

183-185: LGTM! Clean utility for percentage addition.

The implementation correctly calculates the percentage increase using amount.add(amount.multiply(percent)). The type signature properly preserves the generic currency type.

apps/cowswap-frontend/src/entities/optimisticAllowance/types.ts (1)

1-5: LGTM! Well-defined interface for optimistic allowances.

The interface provides a clean structure for tracking optimistic allowances with amount, block number, and timestamp. The timestamp field is particularly useful for cache invalidation of stale optimistic data.

apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts (1)

90-90: LGTM! Tests correctly updated for new hook return shape.

All test cases have been consistently updated to access the reason property from the hook's return value, aligning with the change to return { reason, currentAllowance } instead of just ApproveRequiredReason.

Also applies to: 101-101, 112-112, 126-126, 138-138, 150-150, 160-160, 170-170, 178-178, 186-186, 196-196, 204-204, 216-216, 228-228, 240-240, 252-252, 266-266, 278-278, 290-290, 299-299, 303-303, 307-307, 311-311, 327-327, 342-342, 357-357, 369-369

apps/cowswap-frontend/src/entities/optimisticAllowance/optimisticAllowancesAtom.ts (1)

1-5: LGTM! Simple and effective global state store.

The atom provides a clean centralized store for optimistic allowances, replacing the previous per-token block-number tracking approach.

apps/cowswap-frontend/src/modules/erc20Approve/pure/TradeAllowanceDisplay/index.tsx (1)

13-24: LGTM! Clean presentational component.

The component correctly converts the bigint allowance to a CurrencyAmount for display. The implementation is straightforward and focused on rendering the current allowance information.

apps/cowswap-frontend/src/modules/trade/hooks/useAmountsToSignFromQuote.ts (3)

9-9: LGTM! Well-defined threshold constant.

The 1% threshold is clearly defined and aligns with the PR objective to handle price/gas fluctuations for buy orders.


37-37: Correct dependency array updates.

The addition of isSell and afterSlippage to the dependency array is correct, as both are used in the memoized computation.


30-36: Confirm 1% buffer covers all buy orders
The 1% threshold is correctly applied to all buy orders (both quote-based and non-quote-based). No additional buy-order code paths require adjustment.

apps/cowswap-frontend/src/modules/erc20Approve/utils/getIsTradeApproveResult.ts (1)

6-13: Confirm discriminator property
Verify that SafeMultisigTransactionResponse does not include a txResponse field—this type guard assumes only TradeApproveResult has txResponse, so it’s sound only if no safe response defines that property.

apps/cowswap-frontend/src/modules/erc20Approve/pure/TradeAllowanceDisplay/styled.ts (1)

5-30: LGTM – styling is clear and consistent with UI tokens

No issues found; components are well-scoped and reusable.

apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveCurrency.ts (1)

7-14: Return type broadened to include Safe transactions—confirm upstream hook aligns

The union makes sense with Safe flows. Please confirm useTradeApproveCallback actually returns SafeMultisigTransactionResponse in Safe contexts, or adjust the union if not.

Based on learnings

Also applies to: 26-29

apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts (1)

34-38: Verify the 'unsupported' sentinel aligns with usePermitInfo/PermitType

const isPermitSupported = type !== 'unsupported' assumes a specific sentinel. Confirm usePermitInfo yields 'unsupported' for non-permittable tokens; otherwise this check may misclassify support.

apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.test.ts (2)

1-97: Solid coverage of approval log extraction and failure paths

Tests exercise success, zero-amount (revoke), mixed logs, and failure statuses. Looks good.


13-13: Gitleaks false positive

The hex at Line 13 is an Ethereum address (USDC), not a secret/API key. Safe to ignore.

Based on static analysis hints

apps/cowswap-frontend/src/common/hooks/useTokenAllowance.ts (1)

40-58: LGTM: Optimistic allowance overlay with safe SWR keying

  • Correct composite keying and TTL cleanup.
  • Avoiding erc20Contract in SWR key prevents extra RPC churn.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (8)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts (1)

49-374: Consider adding test coverage for the new currentAllowance property.

The hook now exposes currentAllowance in its return value, but no tests verify this property. Consider adding assertions to ensure currentAllowance is correctly propagated from useApproveState.

Example test additions:

it('should return currentAllowance from useApproveState', () => {
  const expectedAllowance = BigInt(1000000000000000000)
  mockUseApproveState.mockReturnValue({
    state: ApprovalState.APPROVED,
    currentAllowance: expectedAllowance,
  })

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

  expect(result.current.currentAllowance).toBe(expectedAllowance)
})

it('should handle null currentAllowance', () => {
  mockUseApproveState.mockReturnValue({
    state: ApprovalState.UNKNOWN,
    currentAllowance: null,
  })

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

  expect(result.current.currentAllowance).toBeNull()
})
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx (2)

51-56: Avoid unhandled promise rejections on LegacyApproveButton click

Wrap the async call to prevent unhandledrejection in the browser.

-          onClick={() => handleApprove(MAX_APPROVE_AMOUNT)}
+          onClick={() =>
+            handleApprove(MAX_APPROVE_AMOUNT).catch(() => {
+              /* handled via modal state elsewhere */
+            })
+          }

64-69: Avoid unhandled promise rejections on partial-approve button click

Same here—wrap the async callback to swallow errors already surfaced via state.

-      onClick={approveWithPreventedDoubleExecution}
+      onClick={() =>
+        approveWithPreventedDoubleExecution().catch(() => {
+          /* handled via modal state elsewhere */
+        })
+      }
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useHandleApprovalError.ts (1)

15-24: Guard console logging in production; include token symbol

Reduce noisy/PII logs in prod and add context for debugging.

-      console.error('Error setting the allowance for token', error)
+      if (process.env.NODE_ENV !== 'production') {
+        // Include token symbol for context in dev logs
+        console.error('Allowance error', { symbol, error })
+      }
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.test.ts (1)

138-167: Assert approveUtils receives correct params (chainId/owner/spender/token)

Strengthen this success-path test by verifying the payload into processApprovalTransaction.

       await waitFor(() => {
-        expect(mockProcessApprovalTransaction).toHaveBeenCalled()
+        expect(mockProcessApprovalTransaction).toHaveBeenCalledWith(
+          expect.objectContaining({
+            chainId: mockChainId,
+            account: mockAccount,
+            spender: mockSpenderAddress,
+            currency: mockToken,
+          }),
+          expect.any(Object)
+        )
         expect(mockSetOptimisticAllowance).toHaveBeenCalledWith({
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts (1)

59-75: Always surface “insufficient approved amount” even when onApproveConfirm is absent

Currently the error state is only set when onApproveConfirm exists. Surface the error regardless to avoid silent failures.

-    if (tx && onApproveConfirm) {
-      if (getIsTradeApproveResult(tx)) {
+    if (!tx) return
+
+    if (getIsTradeApproveResult(tx)) {
         const approvedAmount = tx.approvedAmount
         const isApprovedAmountSufficient = Boolean(approvedAmount && approvedAmount >= amountToApproveBig)
 
-        if (isApprovedAmountSufficient) {
-          const hash =
-            (tx.txResponse as TransactionReceipt).transactionHash || (tx.txResponse as TransactionResponse).hash
-
-          onApproveConfirm(hash)
-        } else {
-          updateTradeApproveState({ error: 'Approved amount is not sufficient!' })
-        }
-      } else {
-        onApproveConfirm(tx.transactionHash)
-      }
-    }
+        if (!isApprovedAmountSufficient) {
+          updateTradeApproveState({ error: 'Approved amount is not sufficient!' })
+          return
+        }
+        const hash =
+          (tx.txResponse as TransactionReceipt).transactionHash || (tx.txResponse as TransactionResponse).hash
+        onApproveConfirm?.(hash)
+    } else {
+      onApproveConfirm?.(tx.transactionHash)
+    }
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.test.ts (1)

305-369: Add coverage for Safe/multisig (non-TradeApproveResult) path

Include a case where handleApprove resolves to a SafeMultisigTransactionResponse-like object (no txResponse), and assert onApproveConfirm is called with transactionHash.

   describe('no transaction or confirmSwap', () => {
+    it('should call onApproveConfirm with Safe tx hash for multisig submissions', async () => {
+      const safeTx = { transactionHash: '0xsafehash123' } as any
+      mockHandleApprove.mockResolvedValue(safeTx)
+
+      const { result } = renderHook(() =>
+        useApproveAndSwap({
+          amountToApprove: mockAmountToApprove,
+          onApproveConfirm: mockOnApproveConfirm,
+          ignorePermit: false,
+          useModals: true,
+        }),
+      )
+
+      await result.current()
+
+      await waitFor(() => {
+        expect(mockOnApproveConfirm).toHaveBeenCalledWith('0xsafehash123')
+      })
+    })
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts (1)

114-120: Consider clarifying the comment to cover both conditions.

The comment on line 114 only mentions the feature flag case, but the condition on line 115 also handles the case where response is undefined (which can happen if approveCallback returns undefined). While the logic is correct, the comment could be more explicit.

Consider updating the comment:

-        // if ff is disabled - use old flow, hide modal when tx is sent
+        // If approval failed/cancelled (no response) or feature flag disabled - use old flow, hide modal
         if (!response || !isPartialApproveEnabled) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54ad06d and 87ca67a.

📒 Files selected for processing (7)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx (2 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useHandleApprovalError.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.test.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts (3 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.test.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts (20 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-13T19:41:31.440Z
Learnt from: limitofzero
PR: cowprotocol/cowswap#6351
File: apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts:87-121
Timestamp: 2025-10-13T19:41:31.440Z
Learning: In apps/cowswap-frontend/src/modules/erc20Approve, useApproveCallback returns Promise<TransactionResponse | undefined> and is distinct from useApproveCurrency, which can return Promise<TransactionReceipt | SafeMultisigTransactionResponse>. When reviewing approval flows, verify which hook is actually being used before flagging Safe wallet concerns.

Applied to files:

  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useHandleApprovalError.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.test.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.test.ts
📚 Learning: 2025-10-10T20:28:16.565Z
Learnt from: fairlighteth
PR: cowprotocol/cowswap#6347
File: apps/cowswap-frontend/src/modules/trade/pure/TradeConfirmation/index.tsx:49-49
Timestamp: 2025-10-10T20:28:16.565Z
Learning: In apps/cowswap-frontend/src/modules/trade, TradeConfirmation follows a two-layer architecture: TradeConfirmationView (pure/stateless) in pure/TradeConfirmation/index.tsx renders the UI, while TradeConfirmation (container) in containers/TradeConfirmation/index.tsx wraps it to freeze props during pending trades (via useStableTradeConfirmationProps), wire in signing state (useSigningStep), and inject trade confirmation state (useTradeConfirmState). Consuming modules should import the container TradeConfirmation from 'modules/trade' to preserve this stateful behavior.
<!-- [add_learning]
When reviewing component refactoring in apps/cowswap-frontend/src/modules/trade, recognize the pattern where a pure view component (e.g., TradeConfirmationView) is separated from a stateful container (e.g., TradeConfirmation) that wraps it. The container adds runtime state management (prop stabilization, signing state, etc.) while the view remains testable and composable. Do not flag usages that import th...

Applied to files:

  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts
🧬 Code graph analysis (7)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts (1)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts (1)
  • useIsApprovalOrPermitRequired (22-48)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useHandleApprovalError.ts (4)
apps/cowswap-frontend/src/modules/erc20Approve/state/useUpdateApproveProgressModalState.ts (1)
  • useUpdateApproveProgressModalState (5-9)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useApprovalAnalytics.ts (1)
  • useApprovalAnalytics (7-21)
libs/common-utils/src/misc.ts (1)
  • isRejectRequestProviderError (168-187)
libs/common-utils/src/errorToString.ts (1)
  • errorToString (3-7)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.test.ts (7)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveCurrency.ts (1)
  • useApproveCurrency (10-30)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useGeneratePermitInAdvanceToTrade.ts (1)
  • useGeneratePermitInAdvanceToTrade (12-51)
apps/cowswap-frontend/src/modules/erc20Approve/state/useIsPartialApproveSelectedByUser.ts (1)
  • useIsPartialApproveSelectedByUser (5-7)
apps/cowswap-frontend/src/modules/erc20Approve/state/useUpdateApproveProgressModalState.ts (1)
  • useUpdateApproveProgressModalState (5-9)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts (1)
  • useApproveAndSwap (22-84)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts (1)
  • TradeApproveResult (68-68)
apps/cowswap-frontend/src/modules/erc20Approve/constants/maxApproveAmount.ts (1)
  • MAX_APPROVE_AMOUNT (3-3)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx (1)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts (1)
  • useApproveAndSwap (22-84)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts (6)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.ts (1)
  • processApprovalTransaction (21-51)
libs/wallet/src/api/hooks.ts (1)
  • useWalletInfo (24-26)
apps/cowswap-frontend/src/entities/optimisticAllowance/useSetOptimisticAllowance.ts (1)
  • useSetOptimisticAllowance (16-35)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveCallback.tsx (1)
  • useApproveCallback (43-79)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useApprovalAnalytics.ts (1)
  • useApprovalAnalytics (7-21)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useHandleApprovalError.ts (1)
  • useHandleApprovalError (9-28)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts (6)
apps/cowswap-frontend/src/modules/erc20Approve/state/useIsPartialApproveSelectedByUser.ts (1)
  • useIsPartialApproveSelectedByUser (5-7)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveCurrency.ts (1)
  • useApproveCurrency (10-30)
apps/cowswap-frontend/src/modules/erc20Approve/state/useUpdateApproveProgressModalState.ts (1)
  • useUpdateApproveProgressModalState (5-9)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useGeneratePermitInAdvanceToTrade.ts (1)
  • useGeneratePermitInAdvanceToTrade (12-51)
apps/cowswap-frontend/src/modules/erc20Approve/constants/maxApproveAmount.ts (1)
  • MAX_APPROVE_AMOUNT (3-3)
apps/cowswap-frontend/src/modules/erc20Approve/utils/getIsTradeApproveResult.ts (1)
  • getIsTradeApproveResult (6-13)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.test.ts (8)
libs/common-hooks/src/useFeatureFlags.ts (1)
  • useFeatureFlags (5-7)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveCallback.tsx (1)
  • useApproveCallback (43-79)
apps/cowswap-frontend/src/modules/erc20Approve/state/useUpdateApproveProgressModalState.ts (1)
  • useUpdateApproveProgressModalState (5-9)
apps/cowswap-frontend/src/modules/erc20Approve/state/useResetApproveProgressModalState.ts (1)
  • useResetApproveProgressModalState (5-7)
libs/wallet/src/api/hooks.ts (1)
  • useWalletInfo (24-26)
apps/cowswap-frontend/src/entities/optimisticAllowance/useSetOptimisticAllowance.ts (1)
  • useSetOptimisticAllowance (16-35)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/approveUtils.ts (1)
  • processApprovalTransaction (21-51)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts (1)
  • useTradeApproveCallback (88-163)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cypress
  • GitHub Check: Setup
🔇 Additional comments (6)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts (1)

92-371: LGTM! Test assertions updated consistently.

All test assertions have been correctly updated to access the nested reason property following the hook's API change from returning ApproveRequiredReason directly to returning { reason, currentAllowance }.

apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts (1)

72-74: Safe/multisig: confirm navigation before execution? Verify intended UX

For non-TradeApproveResult (Safe) you invoke onApproveConfirm with the submission hash immediately. Confirm this won’t navigate to confirm/swap screens before the Safe tx is executed/mined. If execution confirmation is required, consider deferring the callback until executed or surfacing a “pending in Safe” state. Based on learnings

apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts (4)

1-16: LGTM!

The new imports are correctly added to support the optimistic allowance flow and refactored error handling.


63-66: LGTM!

The default parameters are well-defined and used correctly throughout the implementation (line 103).


148-162: LGTM!

The dependencies array is complete and correctly includes all external values used within the callback. The type assertion to TradeApproveCallback is necessary to preserve the overloaded signature.


37-39: No issues found – this is intentional defensive programming.

The early return when chainId is undefined is correct. After successfully waiting for transaction confirmation (line 35), if chainId becomes unavailable, the function gracefully returns with approvedAmount: undefined instead of attempting to process the approval details. This mirrors the same behavior when waitForTxConfirmation is false (line 134). While this scenario is unusual—since chainId should remain available if the transaction was initiated—it represents proper handling of a rare wallet or network state change. The code doesn't mask an error; it simply skips the optimistic allowance optimization when the required context is missing.

@limitofzero
Copy link
Contributor

@elena-zh could you pls check again?

… fix/partial-approve-buy-order

# Conflicts:
#	apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveWithAffectedOrderList/TradeApproveWithAffectedOrderList.tsx
#	apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts
#	apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts
#	apps/cowswap-frontend/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts (1)

86-97: Consider adding test coverage for the new currentAllowance property.

All test assertions verify only the reason property of the returned object. Since the hook now also returns currentAllowance (which is part of the API change described in the PR objectives), consider adding at least one test case that verifies this property is present and has the expected value.

Example test addition:

it('should return currentAllowance along with reason', () => {
  const expectedAllowance = BigInt(1000000000000000000)
  mockUseApproveState.mockReturnValue({
    state: ApprovalState.APPROVED,
    currentAllowance: expectedAllowance,
  })

  const { result } = renderHook(() =>
    useIsApprovalOrPermitRequired({ isBundlingSupportedOrEnabledForContext: null }),
  )

  expect(result.current.reason).toBe(ApproveRequiredReason.NotRequired)
  expect(result.current.currentAllowance).toBe(expectedAllowance)
})
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87ca67a and 88466bb.

📒 Files selected for processing (4)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveWithAffectedOrderList/TradeApproveWithAffectedOrderList.tsx (2 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts (32 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts (2 hunks)
  • apps/cowswap-frontend/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveWithAffectedOrderList/TradeApproveWithAffectedOrderList.tsx
  • apps/cowswap-frontend/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-13T19:41:31.440Z
Learnt from: limitofzero
PR: cowprotocol/cowswap#6351
File: apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts:87-121
Timestamp: 2025-10-13T19:41:31.440Z
Learning: In apps/cowswap-frontend/src/modules/erc20Approve, useApproveCallback returns Promise<TransactionResponse | undefined> and is distinct from useApproveCurrency, which can return Promise<TransactionReceipt | SafeMultisigTransactionResponse>. When reviewing approval flows, verify which hook is actually being used before flagging Safe wallet concerns.

Applied to files:

  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts
🧬 Code graph analysis (2)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts (5)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useGetAmountToSignApprove.tsx (1)
  • useGetAmountToSignApprove (21-45)
libs/common-hooks/src/useFeatureFlags.ts (1)
  • useFeatureFlags (5-7)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveState.ts (1)
  • useApproveState (22-38)
apps/cowswap-frontend/src/modules/trade/hooks/useDerivedTradeState.ts (1)
  • useDerivedTradeState (7-9)
apps/cowswap-frontend/src/modules/permit/hooks/usePermitInfo.ts (1)
  • usePermitInfo (46-119)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.test.ts (1)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts (1)
  • useIsApprovalOrPermitRequired (31-64)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cypress
  • GitHub Check: Setup
🔇 Additional comments (5)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useIsApprovalOrPermitRequired.ts (5)

1-1: LGTM: React import added for memoization.

The useMemo import is correctly added to support the new memoized return value.


31-34: LGTM: API enriched to include current allowance.

The return type change from a bare ApproveRequiredReason to an object containing both reason and currentAllowance aligns with the PR objective to display current allowance in the UI. This is a breaking change, but all consumers have been updated accordingly.


37-37: LGTM: Current allowance extracted from approval state.

The currentAllowance is correctly extracted from useApproveState for inclusion in the hook's return value.


41-61: LGTM: Reason computation logic preserved.

The approval/permit requirement logic has been correctly wrapped in an IIFE to compute the reason value. All decision branches are preserved and the logic remains unchanged from the previous implementation.


63-63: LGTM: Return value properly memoized.

The useMemo hook correctly memoizes the return object based on reason and currentAllowance. Since both dependencies are primitive or primitive-like values (enum and bigint), the memoization will work effectively to prevent unnecessary re-renders in consuming components.

@elena-zh
Copy link
Contributor

Hey @shoom3301 , still there are some issues:

  1. eth-flow: it cannot have the "Approve and Swap' button
image It should be only Swap for EOAs image
  1. When permittable token is approved, the button says 'Approve and Swap" instead of Swap
image

Could you please fix it?

@shoom3301
Copy link
Collaborator Author

@elena-zh fixed both, thanks!

@shoom3301
Copy link
Collaborator Author

@elena-zh I could affect this fix, please make sure that it's still fixed

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx (1)

65-95: Fix label/tooltip gating: show “Swap” for ETH and already‑approved tokens.

Current logic keys off permit cache only, which causes:

  • ETH flow for EOAs to show “Approve and Swap” (should be “Swap”).
  • Already‑approved permittable tokens to still show “Approve and Swap”.

Gate on on‑chain approval need instead: native tokens never need approval; approved tokens don’t need another approval. Tooltip should also appear only when an on‑chain approval is required.

 import { MAX_APPROVE_AMOUNT } from '../../constants'
-import { useApprovalStateForSpender, useApproveCurrency } from '../../hooks'
+import { useApprovalStateForSpender, useApproveCurrency } from '../../hooks'
 import { useApproveAndSwap } from '../../hooks/useApproveAndSwap'
 import { LegacyApproveButton } from '../../pure/LegacyApproveButton'
 import { ApprovalState } from '../../types'
+import { getIsNativeToken } from '@cowprotocol/common-utils'
@@
   const isPending = approvalState === ApprovalState.PENDING
-  const noCachedPermit = !cachedPermitLoading && !cachedPermit
-
-  const label =
-    props.label || (noCachedPermit ? (isCurrentTradeBridging ? 'Approve, Swap & Bridge' : 'Approve and Swap') : 'Swap')
+  // On‑chain approval is needed only for non‑native tokens that are not already approved
+  const isNative = getIsNativeToken(amountToApprove.currency)
+  const needsOnchainApproval = !isNative && approvalState !== ApprovalState.APPROVED
+  const label =
+    props.label ||
+    (needsOnchainApproval
+      ? isCurrentTradeBridging
+        ? 'Approve, Swap & Bridge'
+        : 'Approve and Swap'
+      : isCurrentTradeBridging
+        ? 'Swap & Bridge'
+        : 'Swap')
@@
-        {noCachedPermit ? (
+        {needsOnchainApproval ? (
           <HoverTooltip
             wrapInContainer
             content={
               <Trans>
                 You must give the CoW Protocol smart contracts permission to use your{' '}
                 <TokenSymbol token={amountToApprove.currency} />. If you approve the default amount, you will only have
                 to do this once per token.
               </Trans>
             }
           >
             {isPending ? <styledEl.StyledLoader /> : <styledEl.StyledAlert size={24} />}
           </HoverTooltip>
         ) : null}

Please also remove any now‑unused variables (e.g., noCachedPermit) if present after the change.

🧹 Nitpick comments (3)
apps/cowswap-frontend/src/modules/swap/containers/TradeButtons/index.tsx (1)

61-61: LGTM: Bridging-aware button text improves UX clarity.

The conditional text logic correctly displays "Swap and Bridge" for cross-chain operations and "Swap" otherwise, making the operation type explicit to users. This aligns with the broader approval and bridging flow improvements in the PR.

Optional: Consider i18n support for user-facing text.

The hardcoded strings "Swap and Bridge" and "Swap" could benefit from internationalization. However, this is consistent with the existing pattern in the codebase, so it can be addressed separately if/when i18n is implemented more broadly.

apps/cowswap-frontend/src/modules/permit/hooks/useHasCachedPermit.ts (1)

9-18: Skip fetching for native currency and zero amounts to reduce noise.

Guard the SWR key so we don’t query cache for ETH/native or 0 approvals; it also prevents misleading consumers that equate “no cached permit” with “needs approval”.

 import { getWrappedToken } from '@cowprotocol/common-utils'
 import { PermitHookData } from '@cowprotocol/permit-utils'
 import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
 
 import useSWR, { SWRResponse } from 'swr'
 
 import { useGetCachedPermit } from './useGetCachedPermit'
 
 export function useHasCachedPermit(amountToApprove: CurrencyAmount<Currency>): SWRResponse<PermitHookData | undefined> {
   const getCachedPermit = useGetCachedPermit()
-  const token = getWrappedToken(amountToApprove.currency)
+  const token = getWrappedToken(amountToApprove.currency)
   const tokenAddress = token.address
   const amount = BigInt(amountToApprove.quotient.toString())
 
-  return useSWR([tokenAddress, amount, token.chainId, 'useHasCachedPermit'], ([tokenAddress, amount]) => {
-    return getCachedPermit(tokenAddress, amount)
-  })
+  // Skip fetching for native currency (no permit/approve) or zero amounts
+  const isNative = amountToApprove.currency.isNative === true
+  const key = isNative || amount === 0n ? null : [tokenAddress, amount, token.chainId, 'useHasCachedPermit'] as const
+
+  return useSWR(key, ([tokenAddress, amount]) => getCachedPermit(tokenAddress, amount))
 }
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx (1)

71-77: Optional: disable while permit state is loading to prevent label flicker.

When cachedPermitLoading is true, consider disabling the button or freezing the label until resolved to avoid transient “Approve…” text.

-    <ButtonWrapper
-      disabled={isPending || isDisabled}
+    <ButtonWrapper
+      disabled={isPending || isDisabled || cachedPermitLoading}
       buttonSize={buttonSize}
       onClick={approveWithPreventedDoubleExecution}
       altDisabledStyle={isPending}
     >
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 88466bb and 3113335.

📒 Files selected for processing (9)
  • apps/cowswap-frontend/src/modules/account/containers/OrderPartialApprove/OrderPartialApprove.tsx (0 hunks)
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx (5 hunks)
  • apps/cowswap-frontend/src/modules/permit/hooks/useGetCachedPermit.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/permit/hooks/useHasCachedPermit.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/permit/index.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/permit/state/permitCacheAtom.ts (1 hunks)
  • apps/cowswap-frontend/src/modules/swap/containers/TradeButtons/index.tsx (2 hunks)
  • apps/cowswap-frontend/src/modules/swap/hooks/useGetConfirmButtonLabel.ts (0 hunks)
  • apps/cowswap-frontend/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx (0 hunks)
💤 Files with no reviewable changes (3)
  • apps/cowswap-frontend/src/modules/account/containers/OrderPartialApprove/OrderPartialApprove.tsx
  • apps/cowswap-frontend/src/modules/swap/hooks/useGetConfirmButtonLabel.ts
  • apps/cowswap-frontend/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/cowswap-frontend/src/modules/permit/hooks/useGetCachedPermit.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-10T20:28:16.565Z
Learnt from: fairlighteth
PR: cowprotocol/cowswap#6347
File: apps/cowswap-frontend/src/modules/trade/pure/TradeConfirmation/index.tsx:49-49
Timestamp: 2025-10-10T20:28:16.565Z
Learning: In apps/cowswap-frontend/src/modules/trade, TradeConfirmation follows a two-layer architecture: TradeConfirmationView (pure/stateless) in pure/TradeConfirmation/index.tsx renders the UI, while TradeConfirmation (container) in containers/TradeConfirmation/index.tsx wraps it to freeze props during pending trades (via useStableTradeConfirmationProps), wire in signing state (useSigningStep), and inject trade confirmation state (useTradeConfirmState). Consuming modules should import the container TradeConfirmation from 'modules/trade' to preserve this stateful behavior.
<!-- [add_learning]
When reviewing component refactoring in apps/cowswap-frontend/src/modules/trade, recognize the pattern where a pure view component (e.g., TradeConfirmationView) is separated from a stateful container (e.g., TradeConfirmation) that wraps it. The container adds runtime state management (prop stabilization, signing state, etc.) while the view remains testable and composable. Do not flag usages that import th...

Applied to files:

  • apps/cowswap-frontend/src/modules/swap/containers/TradeButtons/index.tsx
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx
📚 Learning: 2025-10-13T19:41:31.440Z
Learnt from: limitofzero
PR: cowprotocol/cowswap#6351
File: apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveModal/useTradeApproveCallback.ts:87-121
Timestamp: 2025-10-13T19:41:31.440Z
Learning: In apps/cowswap-frontend/src/modules/erc20Approve, useApproveCallback returns Promise<TransactionResponse | undefined> and is distinct from useApproveCurrency, which can return Promise<TransactionReceipt | SafeMultisigTransactionResponse>. When reviewing approval flows, verify which hook is actually being used before flagging Safe wallet concerns.

Applied to files:

  • apps/cowswap-frontend/src/modules/permit/hooks/useHasCachedPermit.ts
  • apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx
📚 Learning: 2025-08-08T13:56:18.009Z
Learnt from: shoom3301
PR: cowprotocol/cowswap#6125
File: libs/tokens/src/updaters/TokensListsUpdater/index.tsx:29-31
Timestamp: 2025-08-08T13:56:18.009Z
Learning: In libs/tokens/src/updaters/TokensListsUpdater/index.tsx, the project’s current Jotai version requires using `unstable_getOnInit` (not `getOnInit`) in atomWithStorage options; keep `{ unstable_getOnInit: true }` until Jotai is upgraded.

Applied to files:

  • apps/cowswap-frontend/src/modules/permit/state/permitCacheAtom.ts
🧬 Code graph analysis (4)
apps/cowswap-frontend/src/modules/swap/containers/TradeButtons/index.tsx (1)
apps/cowswap-frontend/src/modules/trade/hooks/useIsCurrentTradeBridging.ts (1)
  • useIsCurrentTradeBridging (5-9)
apps/cowswap-frontend/src/modules/permit/hooks/useHasCachedPermit.ts (2)
apps/cowswap-frontend/src/modules/permit/hooks/useGetCachedPermit.ts (1)
  • useGetCachedPermit (13-48)
apps/cowswap-frontend/src/modules/permit/index.ts (1)
  • useGetCachedPermit (7-7)
apps/cowswap-frontend/src/modules/erc20Approve/containers/TradeApproveButton/TradeApproveButton.tsx (7)
apps/cowswap-frontend/src/modules/trade/hooks/useIsCurrentTradeBridging.ts (1)
  • useIsCurrentTradeBridging (5-9)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproval.ts (1)
  • useApprovalStateForSpender (18-32)
apps/cowswap-frontend/src/modules/erc20Approve/hooks/useApproveAndSwap.ts (1)
  • useApproveAndSwap (22-84)
libs/common-hooks/src/usePreventDoubleExecution.ts (1)
  • usePreventDoubleExecution (3-17)
apps/cowswap-frontend/src/modules/permit/hooks/useHasCachedPermit.ts (1)
  • useHasCachedPermit (9-18)
libs/ui/src/pure/Tooltip/index.tsx (1)
  • HoverTooltip (61-177)
libs/ui/src/pure/TokenSymbol/index.tsx (1)
  • TokenSymbol (33-44)
apps/cowswap-frontend/src/modules/permit/state/permitCacheAtom.ts (1)
apps/cowswap-frontend/src/modules/permit/types.ts (1)
  • PermitCache (31-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cypress
  • GitHub Check: Setup
🔇 Additional comments (4)
apps/cowswap-frontend/src/modules/swap/containers/TradeButtons/index.tsx (2)

7-12: LGTM: Import addition is appropriate.

The useIsCurrentTradeBridging import is correctly added to support the bridging-aware UI text update.


57-57: LGTM: Hook usage is correct.

The useIsCurrentTradeBridging hook is properly invoked to detect cross-chain operations.

apps/cowswap-frontend/src/modules/permit/state/permitCacheAtom.ts (1)

17-19: Correct use of unstable_getOnInit; please confirm SSR safety.

Adding unstable_getOnInit avoids hydration flicker and matches our Jotai usage elsewhere. Confirm these atoms aren’t read during SSR or that storage access is safely guarded to avoid “localStorage is not defined” in server builds.

Based on learnings.

Also applies to: 26-28

apps/cowswap-frontend/src/modules/permit/index.ts (1)

6-6: Public re‑export looks good.

No conflicts detected; aligns the permit API surface.

Copy link
Contributor

@elena-zh elena-zh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, works great now!

@shoom3301 shoom3301 merged commit 5646eae into develop Oct 21, 2025
15 checks passed
@shoom3301 shoom3301 deleted the fix/partial-approve-buy-order branch October 21, 2025 11:03
@github-actions github-actions bot locked and limited conversation to collaborators Oct 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants