Skip to content

feat: show consents before import modal#6770

Merged
limitofzero merged 34 commits intodevelopfrom
feat/implement-consent-for-token-importing
Jan 9, 2026
Merged

feat: show consents before import modal#6770
limitofzero merged 34 commits intodevelopfrom
feat/implement-consent-for-token-importing

Conversation

@limitofzero
Copy link
Copy Markdown
Contributor

@limitofzero limitofzero commented Dec 25, 2025

Summary

This PR extends the RWA (Real World Assets) consent system to cover token importing flows, in addition to the existing trading flows. It blocks users from importing restricted tokens or token lists when they are in a blocked country.

Resolves this issue.

Key Changes

1. Block Token List Imports for Blocked Countries

  • Blocked lists show "Not available in your region"

2. Block Individual Token Imports for Blocked Countries

  • When user is in a blocked country, import button is disabled

3. Hide Blocked Token Lists from UI

  • Blocked lists are hidden from the "Manage Lists" view

4. Consent Flow for Unknown Countries (Wallet Connected)

  • When country is unknown AND wallet is connected, consent modal appears before import

5. No Blocking for Unknown Countries (No Wallet)

  • Users without wallet can import restricted tokens/lists freely
  • Consent check is deferred to trade time (standard RWA flow)
image image image

modal with consents for token list
image

Testing table

Scenario Country Known & Blocked Country Known & Allowed Country Unknown + Wallet Country Unknown + No Wallet
Import Token ❌ Blocked ✅ Allowed Consent Modal ✅Allowed
Import List ❌ Blocked ✅ Allowed Consent Modal ✅ Allowed
Toggle List ❌ Not visible ✅ Allowed Consent Modal ✅ Allowed
See Tokens ❌ Hidden ✅ Visible ✅ Visible ✅ Visible

To Test

  1. Test restricted token import from blocked country:

    • Use VPN to mock being in a blocked country (e.g., US for Ondo tokens)
    • Try to import a restricted token by address
    • Import button should be disabled
    • Warning message should display: "This token is not available in your region"
  2. Test token list import from blocked country:

    • Mock being in a blocked country
    • Try to import a restricted token list URL
    • Should show: "This token list is not available in your region"
  3. Test Manage Lists filtering:

    • Mock being in a blocked country (e.g., US for Ondo tokens)
    • Go to token selector → Manage → Lists
    • Blocked token lists should not appear in the list
    • If searching for a blocked list URL, should show "Not available in your region"
  4. Test import with unknown country + wallet connected:

    • Use VPN or disable geo detection
    • Connect wallet
    • Try to import a restricted token/list
    • Consent modal should appear before import
    • After signing consent, import should proceed
  5. Test import with unknown country + no wallet:

    • Use VPN or disable geo detection
    • Disconnect wallet
    • Try to import a restricted token/list
    • Import should proceed without consent modal
    • Consent will be requested at trade time

Summary by CodeRabbit

  • New Features

    • Region-aware blocking and consent gating for token lists and token imports, with cached confirmations and wallet-aware consent prompts.
  • UI

    • Inline block banners, danger-styled warnings, and disabled import buttons showing block reasons; consent flow integrated into import modals and auto-import behavior.
    • Consent modal now refreshes when wallet/account changes.
  • Localization

    • Added user-facing translations for regional availability and consent messages.
  • Tests

    • Unit tests covering list filtering, block detection, and restricted-list info.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Copy Markdown

vercel Bot commented Dec 25, 2025

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

Project Deployment Review Updated (UTC)
cowfi Ready Ready Preview Jan 9, 2026 11:41am
explorer-dev Ready Ready Preview Jan 9, 2026 11:41am
swap-dev Ready Ready Preview Jan 9, 2026 11:41am
widget-configurator Ready Ready Preview Jan 9, 2026 11:41am
2 Skipped Deployments
Project Deployment Review Updated (UTC)
cosmos Ignored Ignored Jan 9, 2026 11:41am
sdk-tools Ignored Ignored Preview Jan 9, 2026 11:41am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 25, 2025

Walkthrough

Adds geo-based blocking and consent gating for token lists and imports: new restricted-lists state and persistence, list/token blocking hooks and updaters, RWA consent modal wiring, UI warnings/disabled imports, and new translation strings.

Changes

Cohort / File(s) Summary
Translations
apps/cowswap-frontend/src/locales/en-US.po
Adds msgids for region/blocking and consent messages used in import UIs.
App updaters
apps/cowswap-frontend/src/modules/application/containers/App/Updaters.tsx
Mounts BlockedListSourcesUpdater and GeoDataUpdater into Updaters render tree.
RWA consent & geo
apps/cowswap-frontend/src/modules/rwa/...
Exposes useGeoStatus, GeoDataUpdater, adds refetchGeoDataAtom, refactors consent modal state usage and onConfirm/onDismiss flows.
Restricted lists state & persistence
libs/tokens/src/state/restrictedTokens/*, libs/tokens/src/state/tokens/blockedListSourcesAtom.ts, libs/tokens/src/index.ts
Adds persisted restricted lists/tokens cache, last-update atom, blockedListSourcesAtom, and new public exports (RWA_CONSENT_HASH, restricted atoms).
Updaters & fetcher
libs/tokens/src/updaters/RestrictedTokensListUpdater/index.tsx, apps/cowswap-frontend/src/modules/tokensList/updaters/BlockedListSourcesUpdater.tsx
Populate per-list blockedCountries and consentHash, publish RWA_CONSENT_HASH, update cache/save logic and blocked sources atom.
List/blocking utilities (libs/tokens)
libs/tokens/src/hooks/lists/useFilterBlockedLists.ts, .../useIsListBlocked.ts, .../useRestrictedListInfo.ts
New hooks to normalize sources, filter lists by country, check list-blocked status, and read restricted-list metadata.
Restricted token helpers
libs/tokens/src/hooks/tokens/useRestrictedToken.ts
Exports findRestrictedToken publicly.
Tokens state filtering
libs/tokens/src/state/tokens/allTokensAtom.ts
Skip processing tokens from blocked/consent-required sources when building token state.
Front-end consent/list hooks
apps/cowswap-frontend/src/modules/tokensList/hooks/*
New hooks: useIsListRequiresConsent, useFilterListsWithConsent, useConsentAwareToggleList, useRestrictedTokensImportStatus to decide consent requirements and gating.
Import flow integration
apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, .../useAddTokenImportCallback.ts
Gate list/token imports on geo/restricted state and cached consent; open RWA consent modal when required and resume import on success.
Trade auto-import gating
apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/TradeWidgetModals.tsx
Uses useRestrictedTokensImportStatus to block or route auto-imports through RWA consent modal when needed.
Import UI components
apps/cowswap-frontend/src/modules/tokensList/pure/*
Add isBlocked/blockReason props, BlockedWarning/BlockedInfo UI, disable import actions and surface localized reasons.
List management & selection UI
apps/cowswap-frontend/src/modules/tokensList/containers/*, .../ManageLists/index.tsx, .../SelectTokenWidget/index.tsx
Compute/propagate isBlocked and blockReason, use consent-aware toggles and country filtering for list management and selection.
Geo updater / atom
apps/cowswap-frontend/src/modules/rwa/updaters/GeoDataUpdater.tsx, apps/cowswap-frontend/src/modules/rwa/state/geoDataAtom.ts
Add GeoDataUpdater to refetch on account change and refetchGeoDataAtom to centralize geo fetch logic.
Tests
libs/tokens/src/hooks/lists/*.{test.tsx}
Unit tests for list filtering, block detection, and restricted-list info (case-insensitivity, loading, edge cases).
Small UI/logic tweaks
apps/cowswap-frontend/src/modules/tokensList/pure/ListItem/index.tsx
Fix toggle state to compute and use newState consistently when toggling lists.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as SelectTokenWidget / ImportModal
    participant Geo as GeoStatus
    participant Lists as RestrictedLists / BlockedSources
    participant RWA as RwaConsentModal
    participant Importer as Import Hook

    User->>UI: attempt import (token or list)
    UI->>Geo: read geoStatus
    UI->>Lists: query restricted info & blocked sources
    Lists-->>UI: isBlocked / blockReason / requiresConsent
    alt blocked by geo
        UI->>User: show blocked warning (import disabled)
    else not blocked
        User->>UI: confirm import
        UI->>Importer: start import flow
        Importer->>Lists: check requiresConsent / consent cache
        alt consent required & not cached
            Importer->>RWA: open consent modal
            User->>RWA: accept consent
            RWA-->>Importer: onImportSuccess
            Importer->>UI: proceed with import
        else consent present or not required
            Importer->>UI: proceed with import
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

Tokens, Token selector

Suggested reviewers

  • shoom3301
  • fairlighteth

Poem

🐇 I hop through lists and country gates,
I sniff for consent on hidden plates.
If blocked I pause, if clear — I leap,
I nudge the modal, promises to keep.
Hooray — safe imports for every peep!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: show consents before import modal' accurately reflects a primary feature addition in this PR, which extends the consent system to token importing flows.
Description check ✅ Passed The description is comprehensive, covering summary, key changes, testing scenarios, and clear test instructions with checkboxes—it follows the required structure with summary, testing steps, and context.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

return true
}

return !blockedCountries.includes(countryKey)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

country list is not big, about 50 elements, don't need to use Set here

Copy link
Copy Markdown
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 (2)
apps/cowswap-frontend/src/modules/tokensList/pure/ImportTokenListItem/index.tsx (1)

27-45: LGTM! Conditional logic correctly handles all import states.

The three-way rendering logic properly covers all combinations of source and isBlocked:

  • Existing non-blocked lists show "Loaded"
  • Blocked lists (any source) show the block reason with AlertCircle
  • External non-blocked lists show the import button

The blockReason || <Trans> fallback ensures a sensible default message when no custom reason is provided.

Optional: Consider adding accessibility attributes

For screen reader users, consider adding an aria-label to the AlertCircle icon or wrapping the blocked state in an element with appropriate ARIA attributes:

 <styledEl.BlockedInfo>
-  <AlertCircle size={16} strokeWidth={2} />
+  <AlertCircle size={16} strokeWidth={2} aria-label="Blocked" />
   <span>{blockReason || <Trans>Not available in your region</Trans>}</span>
 </styledEl.BlockedInfo>
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx (1)

204-212: Consider making the consent message more actionable.

The current message "This list requires consent before importing." informs the user but doesn't guide them on how to proceed. When isListBlocked is true, the ImportListModal hides the import button, potentially leaving users unclear about next steps.

This partially addresses a past comment that flagged the message as confusing when it included "Please connect your wallet" (now removed). However, the message could still be enhanced to provide clearer guidance.

🔎 Possible enhancement

Consider adding more context about the consent requirement:

 const listBlockReason =
-  account && requiresConsent ? t`This list requires consent before importing.` : undefined
+  account && requiresConsent 
+    ? t`This list contains restricted tokens. Please review and accept the consent terms to proceed with importing.`
+    : undefined

Note: The actual consent flow appears to be handled in the import hooks (useAddListImport), so this message may be shown in edge cases. Verify the complete flow to ensure the message aligns with the user experience.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a752b85 and 7b7a567.

📒 Files selected for processing (5)
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useIsListRequiresConsent.ts
  • apps/cowswap-frontend/src/modules/tokensList/pure/ImportTokenListItem/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/updaters/BlockedListSourcesUpdater.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/cowswap-frontend/src/modules/tokensList/updaters/BlockedListSourcesUpdater.tsx
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:48:59.430Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.
📚 Learning: 2025-12-30T18:48:59.430Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:48:59.430Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/pure/ImportTokenListItem/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useIsListRequiresConsent.ts
📚 Learning: 2025-08-12T06:33:19.348Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 6137
File: libs/tokens/src/state/tokens/allTokensAtom.ts:34-65
Timestamp: 2025-08-12T06:33:19.348Z
Learning: In libs/tokens/src/utils/parseTokenInfo.ts, the parseTokenInfo() function returns a new instance of TokenInfo using object spread syntax ({ ...token, ... }), making it safe to mutate properties like lpTokenProvider on the returned object without side effects.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/pure/ImportTokenListItem/index.tsx
📚 Learning: 2025-10-10T20:28:16.565Z
Learnt from: fairlighteth
Repo: cowprotocol/cowswap PR: 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/tokensList/pure/ImportTokenListItem/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
📚 Learning: 2025-09-19T11:38:59.206Z
Learnt from: fairlighteth
Repo: cowprotocol/cowswap PR: 6232
File: apps/cowswap-frontend/src/modules/tokensList/pure/ChainsSelector/index.tsx:199-200
Timestamp: 2025-09-19T11:38:59.206Z
Learning: The makeBuildClickEvent function in apps/cowswap-frontend/src/modules/tokensList/pure/ChainsSelector/index.tsx takes five parameters: defaultChainId, contextLabel, mode, isSwapMode, and chainsCount. The chainsCount parameter is used to determine the CrossChain flag in analytics events.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/pure/ImportTokenListItem/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
📚 Learning: 2025-08-08T13:56:18.009Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 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/tokensList/pure/ImportTokenListItem/index.tsx
📚 Learning: 2025-08-08T13:55:17.528Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 6125
File: libs/tokens/src/state/tokens/allTokensAtom.ts:78-78
Timestamp: 2025-08-08T13:55:17.528Z
Learning: In libs/tokens/src/state/tokens/allTokensAtom.ts (TypeScript/Jotai), the team prefers to wait for token lists to initialize (listsStatesListAtom non-empty) before returning tokens. No fallback to favorites/user-added/native tokens should be used when listsStatesList is empty.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/pure/ImportTokenListItem/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useIsListRequiresConsent.ts
📚 Learning: 2025-10-13T19:41:31.440Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 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/tokensList/containers/SelectTokenWidget/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useIsListRequiresConsent.ts
📚 Learning: 2025-07-18T08:07:55.497Z
Learnt from: alfetopito
Repo: cowprotocol/cowswap PR: 5992
File: libs/tokens/src/const/tokensList.json:135-167
Timestamp: 2025-07-18T08:07:55.497Z
Learning: Token lists for CoW Swap are maintained in a separate repository at https://github.com/cowprotocol/token-lists, not in the main cowswap repository. Issues related to missing token lists should be tracked in the token-lists repository.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
📚 Learning: 2025-12-30T18:51:02.867Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts:38-78
Timestamp: 2025-12-30T18:51:02.867Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts and similar toggle functions, the `enabled` parameter represents the current state of the item (not the desired state). When `enabled === true`, the item is currently enabled and the user is disabling it; when `enabled === false`, the item is currently disabled and the user is enabling it.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
📚 Learning: 2025-12-30T18:51:02.867Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts:38-78
Timestamp: 2025-12-30T18:51:02.867Z
Learning: In useConsentAwareToggleList.ts and other similar toggle functions under apps/cowswap-frontend/src/modules/tokensList/hooks, clarify that the 'enabled' parameter represents the current state of the item (not the desired state). When enabled is true, the user is attempting to disable the currently-enabled item; when enabled is false, the user is attempting to enable a currently-disabled item. Ensure the toggle logic uses this current-state semantics (e.g., derive nextState as !enabled) and update comments/tests accordingly to reflect this behavior.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/hooks/useIsListRequiresConsent.ts
🧬 Code graph analysis (3)
apps/cowswap-frontend/src/modules/tokensList/pure/ImportTokenListItem/index.tsx (1)
libs/tokens/src/types.ts (1)
  • ListState (39-42)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx (4)
apps/cowswap-frontend/src/modules/tokensList/hooks/useRestrictedTokenImportStatus.ts (1)
  • useRestrictedTokenImportStatus (29-56)
apps/cowswap-frontend/src/modules/rwa/hooks/useGeoCountry.ts (1)
  • useGeoCountry (11-24)
libs/tokens/src/hooks/lists/useIsListBlocked.ts (1)
  • useIsListBlocked (22-43)
apps/cowswap-frontend/src/modules/tokensList/pure/ImportListModal/index.tsx (1)
  • ImportListModal (24-79)
apps/cowswap-frontend/src/modules/tokensList/hooks/useIsListRequiresConsent.ts (7)
libs/wallet/src/api/hooks.ts (1)
  • useWalletInfo (24-26)
apps/cowswap-frontend/src/modules/rwa/hooks/useGeoStatus.ts (1)
  • useGeoStatus (10-19)
libs/tokens/src/index.ts (2)
  • restrictedListsAtom (60-60)
  • getSourceAsKey (79-79)
libs/tokens/src/state/restrictedTokens/restrictedTokensAtom.ts (1)
  • restrictedListsAtom (62-62)
apps/cowswap-frontend/src/modules/rwa/state/rwaConsentAtom.ts (2)
  • rwaConsentCacheAtom (8-10)
  • getConsentFromCache (32-45)
libs/tokens/src/hooks/lists/useIsListBlocked.ts (1)
  • getSourceAsKey (6-8)
apps/cowswap-frontend/src/modules/rwa/types/rwaConsent.ts (1)
  • RwaConsentKey (11-14)
⏰ 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/tokensList/hooks/useIsListRequiresConsent.ts (1)

16-61: LGTM! Hook logic is sound and well-integrated.

The hook correctly implements the consent requirement logic with appropriate handling for:

  • Loading states (defers consent to trade time per project patterns)
  • Unknown country scenarios (requires consent when wallet connected)
  • Known country scenarios (delegates to blocking logic elsewhere)
  • Proper cache lookups using wallet and IPFS hash

Based on learnings, the behavior of allowing imports to proceed during loading states (requiresConsent: false, isLoading: true) is intentional.

apps/cowswap-frontend/src/modules/tokensList/pure/ImportTokenListItem/index.tsx (3)

1-1: LGTM! Imports support the new blocking UI.

The ReactElement type and AlertCircle icon imports are appropriate for the new blocked-state rendering.

Also applies to: 6-6


16-17: LGTM! Optional props maintain backwards compatibility.

The isBlocked and blockReason props are correctly typed as optional, allowing existing call sites to continue working without modification.


21-22: LGTM! Function signature improvement.

Adding the explicit ReactElement return type enhances type safety and documentation.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx (2)

126-133: LGTM: Blocking logic correctly implements geo-restriction and consent requirements.

The combined logic properly handles the different scenarios:

  • Country blocked: blocks import with region message
  • Country unknown + wallet connected: requires consent
  • Country unknown + no wallet: allows import (consent deferred to trade time)

Based on learnings and the hook implementations, isBlocked and requiresConsent are mutually exclusive, preventing conflicting messages.


157-173: LGTM: Clean refactoring extracts reusable logic.

The selectAndClose helper properly encapsulates the token selection and modal dismissal flow. The refactored importTokenAndClose correctly uses this helper for the first imported token.

@limitofzero
Copy link
Copy Markdown
Contributor Author

@elena-zh thank you for testing!

  1. fixed, now if the country is unknown - user will see the consent
  2. I see that there is no "country" api request in your devtools, so it means that you disabled it after loading, but country is already loaded before, need to refresh the page with opened devtools to see the consent
  3. I rewrote the flow, now I filter out lists from restricted country, so user can't see that it is loaded
  4. I have only one idea and implemented it with, if you have another - pls let me know how to improve it
image 5. We have a cache, it's why if you don't open a new incognito tab - you see these tokens I added filter for that, should work properly

@shoom3301 thank you for the review, added test cases

Copy link
Copy Markdown
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/tokensList/hooks/useIsListRequiresConsent.ts (1)

33-60: Core consent logic is correct and well-structured.

The consent determination flow properly implements all PR requirements:

  • Restricted lists are identified via consentHashPerList lookup
  • When country is known, consent checks are skipped (blocking handled by other hooks)
  • When country is unknown and no wallet is connected, consent is deferred to the modal
  • When country is unknown and wallet is connected, existing consent is checked via cache

The optional chaining on line 57 (!existingConsent?.acceptedAt) correctly handles all cases: undefined consent, null consent, or missing/falsy acceptedAt all result in requiresConsent: true.

💡 Optional: enhance comments for future maintainability

Consider clarifying two comments to improve code documentation:

-    // If country is known, no consent check needed (blocked check happens elsewhere)
+    // If country is known, no consent check needed (geo-blocking is enforced by useIsListBlocked and related hooks)
     if (geoStatus.country) {
       return { requiresConsent: false, consentHash, isLoading: false }
     }
 
     // Country is unknown - check if consent is given
-    // If no wallet connected, don't block - the consent modal will handle wallet connection
+    // If no wallet connected, don't block here - consent modal will prompt for wallet connection when user attempts import
     if (!account) {
       return { requiresConsent: false, consentHash, isLoading: false }
     }

These clarifications would help future maintainers understand:

  1. Where the geo-blocking logic resides
  2. The user flow when no wallet is connected
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b7a567 and 2c31147.

📒 Files selected for processing (1)
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useIsListRequiresConsent.ts
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:48:59.430Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.
📚 Learning: 2025-12-30T18:48:59.430Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:48:59.430Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/hooks/useIsListRequiresConsent.ts
📚 Learning: 2025-12-30T18:51:02.867Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts:38-78
Timestamp: 2025-12-30T18:51:02.867Z
Learning: In useConsentAwareToggleList.ts and other similar toggle functions under apps/cowswap-frontend/src/modules/tokensList/hooks, clarify that the 'enabled' parameter represents the current state of the item (not the desired state). When enabled is true, the user is attempting to disable the currently-enabled item; when enabled is false, the user is attempting to enable a currently-disabled item. Ensure the toggle logic uses this current-state semantics (e.g., derive nextState as !enabled) and update comments/tests accordingly to reflect this behavior.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/hooks/useIsListRequiresConsent.ts
📚 Learning: 2025-10-13T19:41:31.440Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 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/tokensList/hooks/useIsListRequiresConsent.ts
📚 Learning: 2025-08-08T13:56:18.009Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 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/tokensList/hooks/useIsListRequiresConsent.ts
📚 Learning: 2025-08-08T13:55:17.528Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 6125
File: libs/tokens/src/state/tokens/allTokensAtom.ts:78-78
Timestamp: 2025-08-08T13:55:17.528Z
Learning: In libs/tokens/src/state/tokens/allTokensAtom.ts (TypeScript/Jotai), the team prefers to wait for token lists to initialize (listsStatesListAtom non-empty) before returning tokens. No fallback to favorites/user-added/native tokens should be used when listsStatesList is empty.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/hooks/useIsListRequiresConsent.ts
🧬 Code graph analysis (1)
apps/cowswap-frontend/src/modules/tokensList/hooks/useIsListRequiresConsent.ts (7)
libs/wallet/src/api/hooks.ts (1)
  • useWalletInfo (24-26)
apps/cowswap-frontend/src/modules/rwa/hooks/useGeoStatus.ts (1)
  • useGeoStatus (10-19)
libs/tokens/src/index.ts (2)
  • restrictedListsAtom (60-60)
  • getSourceAsKey (79-79)
libs/tokens/src/state/restrictedTokens/restrictedTokensAtom.ts (1)
  • restrictedListsAtom (62-62)
apps/cowswap-frontend/src/modules/rwa/state/rwaConsentAtom.ts (2)
  • rwaConsentCacheAtom (8-10)
  • getConsentFromCache (32-45)
libs/tokens/src/hooks/lists/useIsListBlocked.ts (1)
  • getSourceAsKey (6-8)
apps/cowswap-frontend/src/modules/rwa/types/rwaConsent.ts (1)
  • RwaConsentKey (11-14)
⏰ 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: Setup
  • GitHub Check: Cypress
🔇 Additional comments (2)
apps/cowswap-frontend/src/modules/tokensList/hooks/useIsListRequiresConsent.ts (2)

1-13: LGTM! Clean imports and interface definition.

The imports are well-organized, and the ListConsentResult interface clearly defines the hook's return type. The previous type inconsistency (Boolean vs boolean) has already been addressed.


22-31: Loading-state logic correctly defers consent checks.

The hook correctly returns requiresConsent: false with isLoading: true when restricted lists or geo data are still loading. This intentional behavior allows imports to proceed during loading states, deferring consent checks until the necessary data is available (typically at trade time).

Based on learnings, this pattern is consistent with the design decision to avoid blocking user actions when loading states prevent a definitive consent determination.

Copy link
Copy Markdown
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.

Hey @limitofzero , cool, great job!

However, still some fixes are needed here:

  1. case 2 from this #6770 (review) is still reproducible to me:
  • no consent when location check is blocked
image
  • token is imported when I'm on the restricted location
image
  1. case 5 from this comment #6770 (review) is also reproducible to me even in the incognito mode. But this case has a lower priority. LMK please if this can be addressed later, so I will open a separate task for this.
incog incogn

Thanks

Copy link
Copy Markdown
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/rwa/updaters/GeoDataUpdater.tsx (1)

10-23: LGTM! Refetches geo data on wallet account changes.

The component correctly tracks account changes and triggers a refetch when the wallet address changes. The areAddressesEqual comparison prevents refetches on initial render and handles the connect/disconnect/switch scenarios appropriately.

One minor observation: disconnecting the wallet (account becomes undefined) also triggers a refetch. Since geo data isn't wallet-specific, this refetch is unnecessary but harmless due to the loading guard in refetchGeoDataAtom.

♻️ Optional optimization to skip refetch on disconnect
 useEffect(() => {
   // only refetch when wallet actually changes (not on initial render)
-  if (!areAddressesEqual(prevAccount, account)) {
+  if (prevAccount && account && !areAddressesEqual(prevAccount, account)) {
     refetchGeoData()
   }
 }, [account, prevAccount, refetchGeoData])

This ensures refetch only occurs when switching between two connected accounts, not when connecting initially or disconnecting.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c31147 and d0576d7.

📒 Files selected for processing (4)
  • apps/cowswap-frontend/src/modules/application/containers/App/Updaters.tsx
  • apps/cowswap-frontend/src/modules/rwa/index.ts
  • apps/cowswap-frontend/src/modules/rwa/state/geoDataAtom.ts
  • apps/cowswap-frontend/src/modules/rwa/updaters/GeoDataUpdater.tsx
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:49:03.377Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.
📚 Learning: 2025-12-30T18:49:03.377Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:49:03.377Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.

Applied to files:

  • apps/cowswap-frontend/src/modules/application/containers/App/Updaters.tsx
  • apps/cowswap-frontend/src/modules/rwa/index.ts
📚 Learning: 2025-08-08T13:56:18.009Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 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/application/containers/App/Updaters.tsx
  • apps/cowswap-frontend/src/modules/rwa/updaters/GeoDataUpdater.tsx
📚 Learning: 2025-10-10T20:28:16.565Z
Learnt from: fairlighteth
Repo: cowprotocol/cowswap PR: 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/application/containers/App/Updaters.tsx
  • apps/cowswap-frontend/src/modules/rwa/index.ts
📚 Learning: 2025-02-20T15:59:33.749Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 5443
File: apps/cowswap-frontend/src/modules/swap/containers/ConfirmSwapModalSetup/index.tsx:71-71
Timestamp: 2025-02-20T15:59:33.749Z
Learning: The swap module in apps/cowswap-frontend/src/modules/swap/ is marked for deletion in PR #5444 as part of the swap widget unification effort.

Applied to files:

  • apps/cowswap-frontend/src/modules/rwa/index.ts
🧬 Code graph analysis (2)
apps/cowswap-frontend/src/modules/application/containers/App/Updaters.tsx (3)
apps/cowswap-frontend/src/modules/tokensList/updaters/BlockedListSourcesUpdater.tsx (1)
  • BlockedListSourcesUpdater (13-41)
apps/cowswap-frontend/src/modules/tokensList/index.ts (1)
  • BlockedListSourcesUpdater (2-2)
apps/cowswap-frontend/src/modules/rwa/updaters/GeoDataUpdater.tsx (1)
  • GeoDataUpdater (10-23)
apps/cowswap-frontend/src/modules/rwa/updaters/GeoDataUpdater.tsx (3)
libs/wallet/src/api/hooks.ts (1)
  • useWalletInfo (24-26)
apps/cowswap-frontend/src/modules/rwa/state/geoDataAtom.ts (1)
  • refetchGeoDataAtom (49-57)
libs/common-utils/src/areAddressesEqual.ts (1)
  • areAddressesEqual (3-7)
⏰ 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/rwa/index.ts (1)

8-8: LGTM! Clean API extension.

The new exports extend the rwa module's public API to support geo-based consent and token list gating, aligning with the PR objectives.

Also applies to: 12-12

apps/cowswap-frontend/src/modules/application/containers/App/Updaters.tsx (1)

34-35: LGTM! Proper updater integration.

The new updaters are correctly integrated into the render tree with appropriate placement after RestrictedTokensListUpdater. The ordering ensures that:

  1. Restricted token/list data is loaded first
  2. Blocked list sources are computed based on that data and geo status
  3. Geo data refetch logic is active for wallet changes

Also applies to: 119-120

apps/cowswap-frontend/src/modules/rwa/state/geoDataAtom.ts (3)

17-36: LGTM! Clean extraction of fetch logic.

The doFetchGeoData helper properly encapsulates the fetch workflow with loading state management and error handling. The immediate isLoading flag update before the async fetch prevents race conditions.


38-46: LGTM! Preserved guard behavior for initial fetch.

The refactored fetchGeoDataAtom maintains the original guards (skip if country already loaded or currently loading) while delegating to the shared doFetchGeoData helper.


48-57: LGTM! Explicit refetch capability for wallet changes.

The new refetchGeoDataAtom correctly allows forced refetch (even when country is already set) while still preventing concurrent fetches via the isLoading guard. This is the appropriate behavior for wallet-change scenarios where fresh geo data is needed.

Copy link
Copy Markdown
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/trade/containers/TradeWidget/TradeWidgetModals.tsx (1)

66-66: Consolidate duplicate hook calls.

useRwaConsentModalState() is called twice—once on line 66 and again on line 73. This is inefficient and could lead to inconsistencies if the hook implementation changes.

♻️ Consolidate into a single call
- const { isModalOpen: isRwaConsentModalOpen, closeModal: closeRwaConsentModal } = useRwaConsentModalState()
+ const { isModalOpen: isRwaConsentModalOpen, closeModal: closeRwaConsentModal, openModal: openRwaConsentModal } = useRwaConsentModalState()
  // ... (lines 67-72)
- const { openModal: openRwaConsentModal } = useRwaConsentModalState()

Also applies to: 73-73

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0576d7 and 54e45f7.

📒 Files selected for processing (4)
  • apps/cowswap-frontend/src/locales/en-US.po
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useRestrictedTokensImportStatus.ts
  • apps/cowswap-frontend/src/modules/tokensList/index.ts
  • apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/TradeWidgetModals.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/cowswap-frontend/src/locales/en-US.po
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:49:03.377Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.
📚 Learning: 2025-12-30T18:49:03.377Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:49:03.377Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useRestrictedTokensImportStatus.ts
  • apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/TradeWidgetModals.tsx
📚 Learning: 2025-08-08T13:56:18.009Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 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/tokensList/index.ts
📚 Learning: 2025-08-08T13:55:17.528Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 6125
File: libs/tokens/src/state/tokens/allTokensAtom.ts:78-78
Timestamp: 2025-08-08T13:55:17.528Z
Learning: In libs/tokens/src/state/tokens/allTokensAtom.ts (TypeScript/Jotai), the team prefers to wait for token lists to initialize (listsStatesListAtom non-empty) before returning tokens. No fallback to favorites/user-added/native tokens should be used when listsStatesList is empty.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useRestrictedTokensImportStatus.ts
  • apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/TradeWidgetModals.tsx
📚 Learning: 2025-10-13T19:41:31.440Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 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/tokensList/hooks/useRestrictedTokensImportStatus.ts
📚 Learning: 2025-12-30T18:51:02.867Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts:38-78
Timestamp: 2025-12-30T18:51:02.867Z
Learning: In useConsentAwareToggleList.ts and other similar toggle functions under apps/cowswap-frontend/src/modules/tokensList/hooks, clarify that the 'enabled' parameter represents the current state of the item (not the desired state). When enabled is true, the user is attempting to disable the currently-enabled item; when enabled is false, the user is attempting to enable a currently-disabled item. Ensure the toggle logic uses this current-state semantics (e.g., derive nextState as !enabled) and update comments/tests accordingly to reflect this behavior.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/hooks/useRestrictedTokensImportStatus.ts
📚 Learning: 2025-10-10T20:28:16.565Z
Learnt from: fairlighteth
Repo: cowprotocol/cowswap PR: 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/trade/containers/TradeWidget/TradeWidgetModals.tsx
🧬 Code graph analysis (2)
apps/cowswap-frontend/src/modules/tokensList/hooks/useRestrictedTokensImportStatus.ts (2)
apps/cowswap-frontend/src/modules/rwa/hooks/useRwaTokenStatus.ts (2)
  • RwaTokenInfo (26-30)
  • useRwaTokenStatus (50-115)
libs/common-const/src/types.ts (1)
  • TokenWithLogo (6-36)
apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/TradeWidgetModals.tsx (3)
apps/cowswap-frontend/src/modules/tokensList/hooks/useRestrictedTokensImportStatus.ts (1)
  • useRestrictedTokensImportStatus (31-70)
apps/cowswap-frontend/src/modules/tokensList/index.ts (2)
  • useRestrictedTokensImportStatus (15-15)
  • ImportTokenModal (4-4)
apps/cowswap-frontend/src/modules/rwa/hooks/useRwaConsentModalState.ts (1)
  • useRwaConsentModalState (13-48)
⏰ 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: Setup
  • GitHub Check: Cypress
🔇 Additional comments (5)
apps/cowswap-frontend/src/modules/tokensList/index.ts (1)

2-2: LGTM! Clean API surface extension.

The new exports appropriately extend the tokensList module's public API to support the consent and geo-blocking features. Both BlockedListSourcesUpdater and useRestrictedTokensImportStatus are well-named and align with the PR's objectives.

Also applies to: 15-15

apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/TradeWidgetModals.tsx (2)

130-152: Verify consent modal reopening behavior.

If a user dismisses the consent modal without signing, the useEffect will fire again and reopen the modal (since isAutoImportModalOpen && requiresConsent remain true). While openModal has a guard against reopening an already-open modal, once the modal closes, it will immediately reopen.

This could create a poor user experience where the user cannot escape the consent flow without either:

  1. Signing the consent, or
  2. Closing the auto-import modal entirely

Is this the intended behavior, or should there be additional state to track that the user has explicitly dismissed the consent modal for this import attempt?


174-186: LGTM! Conditional rendering correctly gates import flow.

The conditional rendering correctly shows ImportTokenModal only when consent is not required, with proper pass-through of isImportDisabled and blockReason props. The flow defers to the consent modal when needed, aligning with the PR's objectives.

apps/cowswap-frontend/src/modules/tokensList/hooks/useRestrictedTokensImportStatus.ts (2)

31-38: LGTM! Clean integration with existing RWA status logic.

The hook correctly extracts the first two tokens as input/output pair and delegates to useRwaTokenStatus, ensuring consistent restriction logic across import and trade flows. The handling of undefined tokens (when tokens.length < 2) is safe since useRwaTokenStatus handles undefined currencies.


40-69: LGTM! Status mapping is comprehensive and correctly memoized.

The switch statement covers all RwaTokenStatus cases appropriately:

  • Restricted: Blocks import with region message
  • RequiredConsent: Allows import but flags consent requirement with token info
  • Allowed/ConsentIsSigned: No restrictions

The memoization dependencies (tokens.length, status, rwaTokenInfo) are correct—status and rwaTokenInfo will update when the input tokens change, triggering recomputation. Using tokens.length rather than the full array is an optimization to avoid unnecessary recalculations when the array reference changes without content change.

Copy link
Copy Markdown
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: 2

🤖 Fix all issues with AI agents
In
@apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts:
- Around line 38-41: The current branch in useConsentAwareToggleList uses if
(!enabled) to skip consent, but enabled represents the current state so skipping
should occur when enabled === true (user is disabling); change the condition to
if (enabled) { baseToggleList(list, enabled); return } so that disabling skips
consent and enabling (enabled === false) proceeds to run the consent checks for
restricted lists; ensure references to baseToggleList(list, enabled), the
enabled parameter, and any restricted-list checks remain consistent with this
inverted condition.
- Line 45: The condition in useConsentAwareToggleList checks for
!geoStatus.country && restrictedLists.isLoaded but misses geoStatus.isLoading,
causing consent logic to run while geo is still fetching; update that
conditional (the one referencing geoStatus.country and restrictedLists.isLoaded
inside useConsentAwareToggleList) to also require !geoStatus.isLoading so
consent checks are deferred until geoStatus has finished loading.
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54e45f7 and b2be8dd.

📒 Files selected for processing (2)
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts
  • apps/cowswap-frontend/src/modules/tokensList/pure/ListItem/index.tsx
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:49:03.377Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.
📚 Learning: 2025-12-30T18:49:03.377Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:49:03.377Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts
  • apps/cowswap-frontend/src/modules/tokensList/pure/ListItem/index.tsx
📚 Learning: 2025-12-30T18:51:02.867Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts:38-78
Timestamp: 2025-12-30T18:51:02.867Z
Learning: In useConsentAwareToggleList.ts and other similar toggle functions under apps/cowswap-frontend/src/modules/tokensList/hooks, clarify that the 'enabled' parameter represents the current state of the item (not the desired state). When enabled is true, the user is attempting to disable the currently-enabled item; when enabled is false, the user is attempting to enable a currently-disabled item. Ensure the toggle logic uses this current-state semantics (e.g., derive nextState as !enabled) and update comments/tests accordingly to reflect this behavior.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts
📚 Learning: 2025-10-13T19:41:31.440Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 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/tokensList/hooks/useConsentAwareToggleList.ts
📚 Learning: 2025-08-12T05:57:08.021Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 6138
File: libs/hook-dapp-lib/src/hookDappsRegistry.ts:1-1
Timestamp: 2025-08-12T05:57:08.021Z
Learning: The matchHooksToDapps function in libs/hook-dapp-lib/src/utils.ts provides backward compatibility for permit hooks through function selector detection (EIP_2612_PERMIT_SELECTOR and DAI_PERMIT_SELECTOR) rather than dappId matching, making it robust against dappId changes.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts
📚 Learning: 2025-10-10T20:28:16.565Z
Learnt from: fairlighteth
Repo: cowprotocol/cowswap PR: 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/tokensList/hooks/useConsentAwareToggleList.ts
📚 Learning: 2025-08-08T13:55:17.528Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 6125
File: libs/tokens/src/state/tokens/allTokensAtom.ts:78-78
Timestamp: 2025-08-08T13:55:17.528Z
Learning: In libs/tokens/src/state/tokens/allTokensAtom.ts (TypeScript/Jotai), the team prefers to wait for token lists to initialize (listsStatesListAtom non-empty) before returning tokens. No fallback to favorites/user-added/native tokens should be used when listsStatesList is empty.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts
📚 Learning: 2025-12-30T18:51:07.806Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts:38-78
Timestamp: 2025-12-30T18:51:07.806Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts and similar toggle functions, the `enabled` parameter represents the current state of the item (not the desired state). When `enabled === true`, the item is currently enabled and the user is disabling it; when `enabled === false`, the item is currently disabled and the user is enabling it.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/pure/ListItem/index.tsx
📚 Learning: 2025-08-08T13:56:18.009Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 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/tokensList/pure/ListItem/index.tsx
📚 Learning: 2025-09-19T11:38:59.206Z
Learnt from: fairlighteth
Repo: cowprotocol/cowswap PR: 6232
File: apps/cowswap-frontend/src/modules/tokensList/pure/ChainsSelector/index.tsx:199-200
Timestamp: 2025-09-19T11:38:59.206Z
Learning: The makeBuildClickEvent function in apps/cowswap-frontend/src/modules/tokensList/pure/ChainsSelector/index.tsx takes five parameters: defaultChainId, contextLabel, mode, isSwapMode, and chainsCount. The chainsCount parameter is used to determine the CrossChain flag in analytics events.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/pure/ListItem/index.tsx
🧬 Code graph analysis (1)
apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts (7)
libs/tokens/src/types.ts (1)
  • ListState (39-42)
apps/cowswap-frontend/src/modules/rwa/hooks/useGeoStatus.ts (1)
  • useGeoStatus (10-19)
libs/tokens/src/index.ts (2)
  • restrictedListsAtom (60-60)
  • getSourceAsKey (79-79)
apps/cowswap-frontend/src/modules/rwa/state/rwaConsentAtom.ts (1)
  • rwaConsentCacheAtom (8-10)
apps/cowswap-frontend/src/modules/rwa/hooks/useRwaConsentModalState.ts (1)
  • useRwaConsentModalState (13-48)
libs/tokens/src/hooks/lists/useIsListBlocked.ts (1)
  • getSourceAsKey (6-8)
apps/cowswap-frontend/src/modules/rwa/types/rwaConsent.ts (1)
  • RwaConsentKey (11-14)
⏰ 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 (2)
apps/cowswap-frontend/src/modules/tokensList/pure/ListItem/index.tsx (2)

36-39: LGTM: Good state synchronization pattern.

The useEffect ensures isActive stays in sync with the parent's enabled prop while allowing immediate UI feedback on toggle. The comment clearly documents this intent.


41-50: The code is correct and consistent.

The enabled parameter represents the desired state (not current state as suggested by the learning). ListItem correctly computes newState = !enabled and passes it to toggleList, where it's used for consent checks and analytics. The actual toggle is performed internally by toggleListAtom, which inverts the current state. All callers and the consent modal logic are consistent with this pattern.


// trying to enable - check if consent is required
// only require consent when country is unknown (blocked countries are handled by hiding the list)
if (!geoStatus.country && restrictedLists.isLoaded) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add isLoading check to defer consent during geo fetch.

The condition checks !geoStatus.country && restrictedLists.isLoaded but doesn't verify !geoStatus.isLoading. When geo data is still loading, country is typically undefined, so the code enters the consent-checking block prematurely. According to retrieved learnings, when geoStatus.isLoading is true, imports should proceed without blocking to defer consent checks to trade time.

🔧 Proposed fix
-      if (!geoStatus.country && restrictedLists.isLoaded) {
+      if (!geoStatus.country && !geoStatus.isLoading && restrictedLists.isLoaded) {
         const sourceKey = getSourceAsKey(list.source)
         const consentHash = restrictedLists.consentHashPerList[sourceKey]

Based on learnings, deferring consent checks when geo status is loading ensures that toggles proceed smoothly and consent verification occurs only when necessary data is available.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!geoStatus.country && restrictedLists.isLoaded) {
if (!geoStatus.country && !geoStatus.isLoading && restrictedLists.isLoaded) {
const sourceKey = getSourceAsKey(list.source)
const consentHash = restrictedLists.consentHashPerList[sourceKey]
🤖 Prompt for AI Agents
In
@apps/cowswap-frontend/src/modules/tokensList/hooks/useConsentAwareToggleList.ts
at line 45, The condition in useConsentAwareToggleList checks for
!geoStatus.country && restrictedLists.isLoaded but misses geoStatus.isLoading,
causing consent logic to run while geo is still fetching; update that
conditional (the one referencing geoStatus.country and restrictedLists.isLoaded
inside useConsentAwareToggleList) to also require !geoStatus.isLoading so
consent checks are deferred until geoStatus has finished loading.

@limitofzero
Copy link
Copy Markdown
Contributor Author

@elena-zh thank for testing, good catch, I've added check for this case, could u pls check it again?

Copy link
Copy Markdown
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/trade/containers/TradeWidget/TradeWidgetModals.tsx (1)

66-73: Combine duplicate hook calls into a single destructuring.

useRwaConsentModalState() is called twice (lines 66 and 73) to destructure different properties. This is inefficient and could potentially cause issues if the hook manages local state rather than context. Consolidate into a single call.

♻️ Combine the two calls into one
-  const { isModalOpen: isRwaConsentModalOpen, closeModal: closeRwaConsentModal } = useRwaConsentModalState()
   const {
     tokensToImport,
     modalState: { isModalOpen: isAutoImportModalOpen, closeModal: closeAutoImportModal },
   } = useAutoImportTokensState(rawState?.inputCurrencyId, rawState?.outputCurrencyId)
   const { isImportDisabled, blockReason, requiresConsent, restrictedTokenInfo, tokenNeedingConsent } =
     useRestrictedTokensImportStatus(tokensToImport)
-  const { openModal: openRwaConsentModal } = useRwaConsentModalState()
+  const { isModalOpen: isRwaConsentModalOpen, closeModal: closeRwaConsentModal, openModal: openRwaConsentModal } = 
+    useRwaConsentModalState()
🧹 Nitpick comments (1)
apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/TradeWidgetModals.tsx (1)

126-156: Consider adding a guard to prevent reopening the consent modal.

The effect correctly opens the consent modal when conditions are met. However, adding isRwaConsentModalOpen to the dependencies with a guard condition would make the intent more explicit and prevent potential race conditions if the modal state update is delayed.

♻️ Add guard condition
   useEffect(() => {
-    if (isAutoImportModalOpen && requiresConsent && restrictedTokenInfo && tokenNeedingConsent) {
+    if (!isRwaConsentModalOpen && isAutoImportModalOpen && requiresConsent && restrictedTokenInfo && tokenNeedingConsent) {
       openRwaConsentModal({
         consentHash: restrictedTokenInfo.consentHash,
         token: tokenNeedingConsent,
         pendingImportTokens: tokensToImport,
         onImportSuccess: () => {
           // After consent, import the tokens
           importTokenCallback(tokensToImport)
           closeAutoImportModal()
         },
         onDismiss: () => {
           // If consent is rejected, close the auto-import modal too
           closeAutoImportModal()
         },
       })
     }
   }, [
+    isRwaConsentModalOpen,
     isAutoImportModalOpen,
     requiresConsent,
     restrictedTokenInfo,
     tokenNeedingConsent,
     tokensToImport,
     openRwaConsentModal,
     importTokenCallback,
     closeAutoImportModal,
   ])
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b2be8dd and 06fe6e2.

📒 Files selected for processing (3)
  • apps/cowswap-frontend/src/modules/rwa/containers/RwaConsentModalContainer/index.tsx
  • apps/cowswap-frontend/src/modules/rwa/state/rwaConsentModalStateAtom.ts
  • apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/TradeWidgetModals.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/cowswap-frontend/src/modules/rwa/containers/RwaConsentModalContainer/index.tsx
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:49:03.377Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.
📚 Learning: 2025-12-30T18:49:03.377Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:49:03.377Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.

Applied to files:

  • apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/TradeWidgetModals.tsx
  • apps/cowswap-frontend/src/modules/rwa/state/rwaConsentModalStateAtom.ts
📚 Learning: 2025-10-10T20:28:16.565Z
Learnt from: fairlighteth
Repo: cowprotocol/cowswap PR: 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/trade/containers/TradeWidget/TradeWidgetModals.tsx
  • apps/cowswap-frontend/src/modules/rwa/state/rwaConsentModalStateAtom.ts
📚 Learning: 2025-07-24T16:42:53.154Z
Learnt from: cowdan
Repo: cowprotocol/cowswap PR: 6009
File: apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/HighFeeWarningTooltipContent.tsx:23-33
Timestamp: 2025-07-24T16:42:53.154Z
Learning: In apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/HighFeeWarningTooltipContent.tsx, the use of toFixed(2) for percentage formatting in tooltip content is intentional and differs from the banner message formatting that uses toSignificant(2, undefined, Rounding.ROUND_DOWN). This formatting difference serves different UX purposes and should not be flagged as inconsistent.

Applied to files:

  • apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/TradeWidgetModals.tsx
📚 Learning: 2025-08-08T13:55:17.528Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 6125
File: libs/tokens/src/state/tokens/allTokensAtom.ts:78-78
Timestamp: 2025-08-08T13:55:17.528Z
Learning: In libs/tokens/src/state/tokens/allTokensAtom.ts (TypeScript/Jotai), the team prefers to wait for token lists to initialize (listsStatesListAtom non-empty) before returning tokens. No fallback to favorites/user-added/native tokens should be used when listsStatesList is empty.

Applied to files:

  • apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/TradeWidgetModals.tsx
🧬 Code graph analysis (1)
apps/cowswap-frontend/src/modules/rwa/state/rwaConsentModalStateAtom.ts (1)
libs/common-const/src/types.ts (1)
  • TokenWithLogo (6-36)
⏰ 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). (4)
  • GitHub Check: Lint
  • GitHub Check: Build
  • GitHub Check: Test
  • GitHub Check: Cypress
🔇 Additional comments (2)
apps/cowswap-frontend/src/modules/rwa/state/rwaConsentModalStateAtom.ts (1)

6-21: LGTM! Well-structured modal state interface.

The RwaConsentModalContext interface cleanly supports both single-token (token?) and multi-token (pendingImportTokens?) import flows with appropriate lifecycle callbacks. Type safety is maintained, and the design aligns well with the PR's requirements for handling individual token and token list imports.

apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/TradeWidgetModals.tsx (1)

178-189: LGTM! Consent gating integrated correctly.

The conditional rendering correctly separates the consent flow from the standard import flow. When consent is required, the useEffect above handles opening the consent modal. When consent is not required but import is disabled (blocked country), the modal displays with a disabled state and block reason. This approach maintains clear separation of concerns.

Copy link
Copy Markdown
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

🤖 Fix all issues with AI agents
In @apps/cowswap-frontend/src/locales/en-US.po:
- Around line 3601-3602: The PO entry contains a leftover merge conflict marker
in the translation string for the "With hooks you can add specific actions
<0>before</0> and <1>after</1> your swap." message; remove the conflict marker
text ("<<<<<<< Updated upstream") from the msgstr so the translated string is a
valid PO value (no conflict markers, correct escaping/markup preserved) and save
the file.
🧹 Nitpick comments (1)
apps/cowswap-frontend/src/locales/en-US.po (1)

3603-3607: Potential duplication: “This token is not available in your region.” vs existing msgid without period.
This file already contains msgid "This token is not available in your region" (no trailing .). Consider standardizing to one msgid to avoid near-duplicate strings (and inconsistent punctuation across UI).

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06fe6e2 and 2c2fbf2.

📒 Files selected for processing (1)
  • apps/cowswap-frontend/src/locales/en-US.po
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:49:03.377Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.
📚 Learning: 2025-12-30T18:49:03.377Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 6770
File: apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts:27-31
Timestamp: 2025-12-30T18:49:03.377Z
Learning: In apps/cowswap-frontend/src/modules/tokensList/hooks/useAddListImport.ts, when restrictedLists.isLoaded is false or geoStatus.isLoading is true, the code intentionally proceeds with the import immediately without blocking. This allows imports to proceed during loading states, deferring consent checks to trade time when necessary data isn't yet available.

Applied to files:

  • apps/cowswap-frontend/src/locales/en-US.po
📚 Learning: 2025-07-18T08:07:55.497Z
Learnt from: alfetopito
Repo: cowprotocol/cowswap PR: 5992
File: libs/tokens/src/const/tokensList.json:135-167
Timestamp: 2025-07-18T08:07:55.497Z
Learning: Token lists for CoW Swap are maintained in a separate repository at https://github.com/cowprotocol/token-lists, not in the main cowswap repository. Issues related to missing token lists should be tracked in the token-lists repository.

Applied to files:

  • apps/cowswap-frontend/src/locales/en-US.po
📚 Learning: 2025-07-24T16:42:53.154Z
Learnt from: cowdan
Repo: cowprotocol/cowswap PR: 6009
File: apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/HighFeeWarningTooltipContent.tsx:23-33
Timestamp: 2025-07-24T16:42:53.154Z
Learning: In apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/HighFeeWarningTooltipContent.tsx, the use of toFixed(2) for percentage formatting in tooltip content is intentional and differs from the banner message formatting that uses toSignificant(2, undefined, Rounding.ROUND_DOWN). This formatting difference serves different UX purposes and should not be flagged as inconsistent.

Applied to files:

  • apps/cowswap-frontend/src/locales/en-US.po
📚 Learning: 2025-05-28T16:50:12.273Z
Learnt from: fairlighteth
Repo: cowprotocol/cowswap PR: 5768
File: apps/cow-fi/components/LearnPageComponent.tsx:184-185
Timestamp: 2025-05-28T16:50:12.273Z
Learning: In apps/cow-fi/components/LearnPageComponent.tsx, the user prefers to keep the inconsistent link behavior where featured articles always open in new tabs (target="_blank") while media coverage links conditionally open in new tabs based on the linkExternal flag. This inconsistency should not be flagged as an issue in future reviews.

Applied to files:

  • apps/cowswap-frontend/src/locales/en-US.po
📚 Learning: 2025-10-13T19:41:31.440Z
Learnt from: limitofzero
Repo: cowprotocol/cowswap PR: 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/locales/en-US.po
📚 Learning: 2025-10-10T20:28:16.565Z
Learnt from: fairlighteth
Repo: cowprotocol/cowswap PR: 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/locales/en-US.po
📚 Learning: 2025-07-24T16:43:47.639Z
Learnt from: cowdan
Repo: cowprotocol/cowswap PR: 6009
File: apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/highFeeWarningHelpers.ts:18-20
Timestamp: 2025-07-24T16:43:47.639Z
Learning: In apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/highFeeWarningHelpers.ts, the formatFeePercentage function uses ROUND_DOWN with toSignificant(2) for "at least X%" messaging. This ensures the displayed percentage is never higher than the actual fee, making the "at least" phrasing accurate. For example, if the actual fee is 25.4%, displaying "at least 25%" is correct, but "at least 26%" would be misleading.

Applied to files:

  • apps/cowswap-frontend/src/locales/en-US.po
⏰ 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: Setup
  • GitHub Check: Cypress
🔇 Additional comments (3)
apps/cowswap-frontend/src/locales/en-US.po (3)

2822-2825: Good addition: clear blocked-list message.
Translation entry looks fine and matches the intended UX.


3636-3639: Good: concise label for blocked lists/items.
Looks appropriate for compact UI surfaces.


4321-4324: Good: consent gating copy is clear.
String reads well for a pre-import warning/notice.

Comment on lines +3601 to +3602
msgstr "With hooks you can add specific actions <0>before</0> and <1>after</1> your swap.<<<<<<< Updated upstream"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Blocker: unresolved merge conflict marker in PO file.
<<<<<<< Updated upstream in msgstr will likely break localization extraction/compilation and must be removed.

Proposed fix
-msgstr "With hooks you can add specific actions <0>before</0> and <1>after</1> your swap.<<<<<<< Updated upstream"
+msgstr "With hooks you can add specific actions <0>before</0> and <1>after</1> your swap."
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
msgstr "With hooks you can add specific actions <0>before</0> and <1>after</1> your swap.<<<<<<< Updated upstream"
msgstr "With hooks you can add specific actions <0>before</0> and <1>after</1> your swap."
🤖 Prompt for AI Agents
In @apps/cowswap-frontend/src/locales/en-US.po around lines 3601 - 3602, The PO
entry contains a leftover merge conflict marker in the translation string for
the "With hooks you can add specific actions <0>before</0> and <1>after</1> your
swap." message; remove the conflict marker text ("<<<<<<< Updated upstream")
from the msgstr so the translated string is a valid PO value (no conflict
markers, correct escaping/markup preserved) and save the file.

@elena-zh
Copy link
Copy Markdown
Contributor

elena-zh commented Jan 9, 2026

Hey @limitofzero , great! The flow is working fine.
I've opened 2 issues to the board that would be nice to address later as they are not blocking the release.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants