Skip to content

Conversation

@limitofzero
Copy link
Contributor

@limitofzero limitofzero commented Jan 5, 2026

Summary

<<if there's an issue>>Fixes #issueNumber

High-level description of what your changes are accomplishing

Add screenshots if applicable. Images are nice :)

To Test

  1. <> Open the page about
  • <<What to expect?>> Verify it contains about information...
  • Checkbox Style list of things a QA person could verify, i.e.
  • Should display Text Input our storybook
  • Input should not accept Numbers
  1. <> ...

Background

Optional: Give background information for changes you've made, that might be difficult to explain via comments

Summary by CodeRabbit

  • New Features

    • Cross-chain route pre-checks for destination availability
    • Fully revamped token/network selector with mobile-first selector, full-panel view, and composable modal
    • Per-network recent tokens persistence and management
    • New hooks/utilities to drive token list, chain selection, and widget state
  • Improvements

    • Prioritized token ordering and streamlined token list UX
    • Enhanced responsive layouts and clearer chain selection with loading/disabled indicators
    • Expanded localization strings across token/network flows
  • Tests

    • New unit tests for bridging enablement and chain-selection logic

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

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: 3

🤖 Fix all issues with AI Agents
In @apps/cowswap-frontend/src/entities/bridgeProvider/useRoutesAvailability.ts:
- Around line 55-60: The swrKey memo mutates the chainsToCheck array by calling
chainsToCheck.sort(), which can cause subtle bugs; change the code in the
useMemo that builds swrKey (symbol: swrKey in useRoutesAvailability) to sort a
copy instead of the original (e.g., use chainsToCheck.slice().sort(...) or
[...chainsToCheck].sort(...)) so the memoized chainsToCheck is not mutated.

In @apps/cowswap-frontend/src/locales/en-US.po:
- Around line 3253-3257: The msgid "This destination is not supported for this
source chain" (used in ChainsSelector and MobileChainSelector) and related
strings mix "chain" and "network"; update the msgstr to consistently use
"network" (e.g., "This destination network is not supported for this source
network") and similarly revise the other occurrences ("This route is not yet
supported.", "No networks match {chainQuery}.", "No networks available for this
trade.") to use consistent "network" terminology and phrasing (e.g., "This route
is not yet supported for this network combination", "No networks match
{chainQuery}.", "No networks available for this trade.") so all translations and
placeholders remain intact and consistent across the referenced message entries.
- Around line 2384-2387: Update the translation entry for the SelectTokenModal
component: replace the msgid/msgstr "Cross chain swap" with the hyphenated
singular form "Cross-chain swap" to match the existing style (also update the
other occurrence noted at lines 5426-5428). Locate the string in the
SelectTokenModal translation entry (msgid "Cross chain swap") and change both
msgid and msgstr to "Cross-chain swap" so wording and hyphenation are consistent
across the locale file.
🧹 Nitpick comments (6)
apps/cowswap-frontend/src/modules/tokensList/pure/TokensContent/index.tsx (2)

32-32: Optional: Simplify redundant condition.

At line 32, shouldShowRecentsInline is guaranteed to be true because of the early return at line 26. Only the recentTokens null check is needed here.

🔎 Proposed simplification
-    if (shouldShowRecentsInline && recentTokens) {
+    if (recentTokens) {
       recentTokens.forEach((token) => pinned.add(getTokenUniqueKey(token)))
     }

12-68: Consider: Potential duplicate hook invocation.

Both TokensContent (line 17) and TokensVirtualList (from relevant snippets) call useTokenListData(). While this is acceptable if the hook is atom-based and memoized, consider whether all needed data could be passed as props to TokensVirtualList to avoid conceptual duplication.

apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/index.cosmos.tsx (2)

68-68: Consider replacing void 0 with explicit return or empty function body.

Using void 0 is a minification pattern that's uncommon in source code. For consistency with other callbacks in this file that use explicit behavior, consider simplifying.

🔎 Suggested change
-  tokenListCategoryState: [null, () => void 0],
+  tokenListCategoryState: [null, () => {}],

121-147: Extract duplicated search values to constants to prevent desync.

The searchInput in atomState and defaultInputValue prop contain identical values but are specified separately. If one is updated without the other, fixtures could behave unexpectedly.

🔎 Suggested approach
+const IMPORT_ADDRESS = '0x252d98fab648203aa33310721bbbddfa8f1b6587'
+const NOT_FOUND_ADDRESS = '0x543ff227f64aa17ea132bf9886cab5db55dcaddd'
+const INACTIVE_LIST_SEARCH = 'cDAI'
+const EXTERNAL_SEARCH = 'Coo'
+
 importByAddress: () => (
-  <CosmosAtomProvider atomState={{ searchInput: '0x252d98fab648203aa33310721bbbddfa8f1b6587' }}>
+  <CosmosAtomProvider atomState={{ searchInput: IMPORT_ADDRESS }}>
     <Wrapper>
-      <SelectTokenModal defaultInputValue={'0x252d98fab648203aa33310721bbbddfa8f1b6587'} {...defaultModalProps} />
+      <SelectTokenModal defaultInputValue={IMPORT_ADDRESS} {...defaultModalProps} />
     </Wrapper>
   </CosmosAtomProvider>
 ),

Apply similar pattern for NoTokenFound, searchFromInactiveLists, and searchFromExternalSources fixtures.

apps/cowswap-frontend/src/locales/en-US.po (2)

885-891: Casing change for “Manage token lists” vs obsolete “Manage Token Lists”

You’re moving from title case (#~ “Manage Token Lists”) to sentence case (“Manage token lists”) for the active key, which is fine if it matches your updated UI copy guidelines. Just double‑check other navigation/actions around tokens to ensure this casing is intentional and consistent.

Also applies to: 1296-1297


305-308: Network selection labels are consistent; optional polish on wording

“From network”, “To network”, “View all {totalChains} networks”, “View all ({totalChains})”, “Select network”, and “Selected network {activeChainLabel}” all read well and use placeholders correctly. Deprecating “Less” in favor of explicit “View all …” controls also makes sense.

If you want to tighten copy further, you could consider:

  • “Selected network: {activeChainLabel}” (with a colon) for clarity, especially for screen readers.
  • Reusing either “Select a network” or “Select network” consistently across the app, but this is purely stylistic.

Also applies to: 667-670, 873-876, 1959-1962, 5095-5098, 5580-5583, 6243-6245

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f03cb5f and ffb8735.

📒 Files selected for processing (10)
  • apps/cowswap-frontend/src/entities/bridgeProvider/useRoutesAvailability.ts
  • apps/cowswap-frontend/src/locales/en-US.po
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/controller.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts
  • apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/index.cosmos.tsx
  • apps/cowswap-frontend/src/modules/tokensList/pure/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/state/tokenListViewAtom.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/controller.ts
  • apps/cowswap-frontend/src/modules/tokensList/state/tokenListViewAtom.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/index.tsx
🧰 Additional context used
🧠 Learnings (15)
📓 Common learnings
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.
📚 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/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/index.cosmos.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/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/pure/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts
  • apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/index.cosmos.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/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/tokensList/pure/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts
  • apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/index.cosmos.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/index.ts
📚 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/pure/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts
  • apps/cowswap-frontend/src/entities/bridgeProvider/useRoutesAvailability.ts
  • apps/cowswap-frontend/src/locales/en-US.po
📚 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/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts
📚 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/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts
  • apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx
  • apps/cowswap-frontend/src/entities/bridgeProvider/useRoutesAvailability.ts
  • apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/index.cosmos.tsx
  • apps/cowswap-frontend/src/locales/en-US.po
📚 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/state/tokens/allTokensAtom.ts, the parseTokenInfo() function returns a new instance of TokenInfo each time, making it safe to mutate properties like lpTokenProvider on the returned object without side effects.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts
  • apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/index.cosmos.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/index.ts
📚 Learning: 2025-08-05T14:27:05.023Z
Learnt from: alfetopito
Repo: cowprotocol/cowswap PR: 5992
File: libs/wallet/src/web3-react/utils/switchChain.ts:36-38
Timestamp: 2025-08-05T14:27:05.023Z
Learning: In libs/wallet/src/web3-react/utils/switchChain.ts, the team prefers using Record<SupportedChainId, string | null> over Partial<Record<SupportedChainId, string>> for WALLET_RPC_SUGGESTION to enforce that all supported chain IDs have explicit values set, even if some might be null. This ensures compile-time completeness checking.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts
  • apps/cowswap-frontend/src/entities/bridgeProvider/useRoutesAvailability.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/useTokenListData.ts
📚 Learning: 2025-09-25T08:49:32.256Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 6299
File: apps/cowswap-frontend/src/entities/bridgeProvider/useBridgeSupportedNetworks.test.tsx:62-67
Timestamp: 2025-09-25T08:49:32.256Z
Learning: In the cowswap-frontend codebase, when testing hooks that use multiple bridge providers, both providers are always properly mocked as complete jest.Mocked<BridgeProvider<BridgeQuoteResult>> objects with all required methods stubbed, ensuring no undefined returns that could break the hook logic.

Applied to files:

  • apps/cowswap-frontend/src/entities/bridgeProvider/useRoutesAvailability.ts
  • apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/index.cosmos.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/entities/bridgeProvider/useRoutesAvailability.ts
📚 Learning: 2025-09-25T08:48:53.495Z
Learnt from: shoom3301
Repo: cowprotocol/cowswap PR: 6299
File: apps/cowswap-frontend/src/entities/bridgeProvider/useBridgeSupportedNetworks.test.tsx:58-60
Timestamp: 2025-09-25T08:48:53.495Z
Learning: In the cowswap-frontend codebase, when writing SWR tests, the team prefers maximum test isolation by using `provider: () => new Map()` in SWRConfig wrappers, even if it recreates cache on every render, to ensure tests don't share any state.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/index.cosmos.tsx
📚 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-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/locales/en-US.po
🧬 Code graph analysis (2)
apps/cowswap-frontend/src/modules/tokensList/pure/TokensContent/index.tsx (6)
apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListViewState.ts (1)
  • useTokenListViewState (5-7)
apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts (1)
  • useTokenListData (45-103)
apps/cowswap-frontend/src/modules/tokensList/utils/tokenKey.ts (1)
  • getTokenUniqueKey (6-8)
libs/ui/src/pure/Loader/index.tsx (1)
  • Loader (32-43)
apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/index.tsx (1)
  • TokenSearchResults (13-78)
apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx (1)
  • TokensVirtualList (31-118)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/index.ts (1)
apps/cowswap-frontend/src/modules/tokensList/state/tokenListViewAtom.ts (1)
  • atom (19-21)
⏰ 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 (28)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/index.ts (1)

1-22: LGTM! Clean state management following established patterns.

The implementation correctly follows the atomWithPartialUpdate pattern used elsewhere in the codebase (e.g., tokenListViewAtom.ts). The interface is well-documented, the naming is clear and descriptive, and the separation of modal UI state into its own atom promotes maintainability.

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

12-12: LGTM: Clean refactor to hook-driven component.

The transition from prop-based to hook-driven state management makes the component more self-contained and aligns with modern React patterns.


22-37: LGTM: Intentional design to keep favorites in the main list.

The logic correctly excludes only recent tokens from the main list while keeping favorites visible in "All tokens" for balance-based sorting. The comment on lines 23-25 clearly documents this design decision.


50-68: LGTM: Clean conditional rendering flow.

The rendering branches are well-structured: loading state shows the loader, search delegates to TokenSearchResults, and the default path renders the filtered token list with optional inline favorites/recents.

apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/index.cosmos.tsx (2)

46-56: LGTM!

The CosmosAtomProvider correctly uses useHydrateAtoms from jotai/utils to hydrate the atom state for Cosmos fixtures. This is an appropriate pattern for test fixture isolation.


94-120: LGTM!

The fixture structure provides good coverage of different modal states: default with chain panel, loading sidebar, and standalone modal without sidebar. The separation of concerns between SelectTokenModal and ChainPanel is clean.

apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts (3)

1-17: LGTM! Clear documentation and appropriate imports.

The comment block effectively explains the architectural shift from atom hydration to direct hook composition, making the design intent clear.


19-43: LGTM! Well-structured interface design.

The interfaces are comprehensive and provide clear contracts for the hook's inputs and outputs.


45-102: LGTM! Solid hook implementation with proper memoization.

The hook correctly composes data from multiple sources, properly manages the activeChainId fallback, and includes all necessary dependencies in the useMemo. The standalone mode handling and context construction are well-implemented.

apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx (12)

5-5: LGTM! Appropriate import for native token detection.


7-7: LGTM! Lingui macro for internationalization.


13-13: LGTM! Imports the new token data hook.


16-17: LGTM! Imports for UI composition.


20-24: LGTM! Clean props interface with clear responsibilities.

The comment clarifying that tokensToDisplay is pre-filtered by the parent is helpful.


26-29: LGTM! Well-designed discriminated union for row types.

The type definition cleanly represents the three rendering modes with appropriate properties for each.


31-37: LGTM! Clean component signature and data hook integration.


43-63: LGTM! Efficient token prioritization and sorting.

The implementation correctly prioritizes tokens with balances or native tokens, sorts only the prioritized subset, and preserves the original order for the remainder. This is an efficient approach.


65-92: LGTM! Well-structured row composition logic.

The rows are built in a logical order (favorites → recent → all tokens) with appropriate section headers and actions. The useMemo dependencies are complete.


108-116: LGTM! Proper virtual list configuration with scroll reset.

The key and scrollResetKey props correctly handle component remounting and scroll position reset when the chain changes.


120-145: LGTM! Clean row renderer implementation.

The switch statement correctly handles all three row types with appropriate rendering for each. The default case properly handles token rows.


103-105: No action required. The showCoWAmmBanner = isYieldEnabled logic is correct and consistent with the application's overall banner display strategy. The banner is gated by the same isYieldEnabled flag both globally (in AppContainer) and within the token selector (TokensVirtualList). Since TokensVirtualList only renders when displaying regular tokens (never when showing LP token lists, which use a separate LpTokenLists component), defaulting the banner visibility to isYieldEnabled is the appropriate behavior. The comment's acknowledgment of missing atom context is valid but does not indicate a logic error.

apps/cowswap-frontend/src/entities/bridgeProvider/useRoutesAvailability.ts (3)

1-27: LGTM! Clean imports and type definitions.

The imports, interfaces, and constants are well-structured and appropriate for the hook's functionality.


88-111: LGTM! Return logic correctly handles all states.

The return memoization properly handles the inactive case (returning EMPTY_RESULT), loading state (marking all chains as loading), and completed state (building the unavailable set from results). Dependencies are correct.


72-74: No action needed on line 73.

The double condition check (result.tokens.length > 0 && result.isRouteAvailable) is necessary and correct. Comparison with useBridgeSupportedTokens.ts (which uses tokens.length > 0 ? result.isRouteAvailable : false) reveals that the SDK can independently return isRouteAvailable=true while providing no tokens, or vice versa. Both conditions must be true for a route to be considered available.

apps/cowswap-frontend/src/locales/en-US.po (4)

282-288: Token action labels (“Select/Sell/Buy/Swap …”) look coherent

The new labels for “Select token”, “Sell token”, “Buy token”, “Swap from”, and “Swap to” are concise, consistent with existing terminology, and preserve all placeholders correctly. No i18n issues from my side.

Also applies to: 2141-2144, 2608-2611, 5255-5258, 5388-5391


1009-1010: Search, tabs, and empty/loading states for token lists look good

The shared copy for:

  • “Search name or paste address…”
  • “Recent”
  • “All tokens”
  • “Loading tokens…”
  • “No tokens found”
  • “Search network”
  • “Clear”

is consistent, idiomatic, and appropriately reused across desktop/mobile and list/virtual‑list components. No placeholder or pluralization issues here.

Also applies to: 1445-1449, 2181-2184, 3912-3915, 4393-4396, 5002-5005, 6164-6168


667-670: “View all …” and selected-network indicators: confirm UX behavior only

The two toggles “View all {totalChains} networks” and “View all ({totalChains})”, plus “Selected network {activeChainLabel}”, are grammatically fine and keep placeholders aligned. The only thing to watch is that the two “View all …” variants are used in clearly distinct UI contexts so users aren’t confused by the different formats. Functionally nothing to fix in the .po itself.

Also applies to: 1959-1962, 5095-5098


4455-4457: Context-only change for swap expiry tooltip

This hunk only adds/updates the source reference for an existing (now obsolete) tooltip string in tradeSettingsTooltips.tsx; the msgid/msgstr pair is unchanged and still correctly formed. Nothing to do here.

Comment on lines +2384 to +2387
#: apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/index.tsx
msgid "Cross chain swap"
msgstr "Cross chain swap"

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent hyphenation: “Cross chain swap” vs “Cross-chain swaps are here”

You now have:

  • “Cross chain swap” (no hyphen, singular)
  • “Cross-chain swaps are here” (hyphenated, plural, existing key)

For consistency and more standard English, consider changing the new one to “Cross-chain swap”.

Proposed diff for msgid/msgstr (if you decide to adjust)
- msgid "Cross chain swap"
- msgstr "Cross chain swap"
+ msgid "Cross-chain swap"
+ msgstr "Cross-chain swap"

Also applies to: 5426-5428

🤖 Prompt for AI Agents
In @apps/cowswap-frontend/src/locales/en-US.po around lines 2384 - 2387, Update
the translation entry for the SelectTokenModal component: replace the
msgid/msgstr "Cross chain swap" with the hyphenated singular form "Cross-chain
swap" to match the existing style (also update the other occurrence noted at
lines 5426-5428). Locate the string in the SelectTokenModal translation entry
(msgid "Cross chain swap") and change both msgid and msgstr to "Cross-chain
swap" so wording and hyphenation are consistent across the locale file.

Comment on lines +3253 to +3257
#: apps/cowswap-frontend/src/modules/tokensList/pure/ChainsSelector/index.tsx
#: apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/MobileChainSelector.tsx
msgid "This destination is not supported for this source chain"
msgstr "This destination is not supported for this source chain"

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Route/network availability messages: consider aligning “network” vs “chain” terminology

The new status/empty strings are clear overall, but wording mixes “destination”/“source chain” and “network”:

  • “This destination is not supported for this source chain”
  • “This route is not yet supported.”
  • “No networks match {chainQuery}.”
  • “No networks available for this trade.”

To keep copy consistent with the surrounding UI (which generally says “network”), you might want to revise the first one to something like “This destination network is not supported for this source network” or similar, and optionally have “This route is not yet supported for this network combination” for extra clarity.

Placeholders are all preserved correctly; this is just UX wording polish.

Also applies to: 4554-4558, 4603-4606, 5007-5010, 5505-5508

🤖 Prompt for AI Agents
In @apps/cowswap-frontend/src/locales/en-US.po around lines 3253 - 3257, The
msgid "This destination is not supported for this source chain" (used in
ChainsSelector and MobileChainSelector) and related strings mix "chain" and
"network"; update the msgstr to consistently use "network" (e.g., "This
destination network is not supported for this source network") and similarly
revise the other occurrences ("This route is not yet supported.", "No networks
match {chainQuery}.", "No networks available for this trade.") to use consistent
"network" terminology and phrasing (e.g., "This route is not yet supported for
this network combination", "No networks match {chainQuery}.", "No networks
available for this trade.") so all translations and placeholders remain intact
and consistent across the referenced message entries.

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

🤖 Fix all issues with AI Agents
In @apps/cowswap-frontend/src/modules/yield/containers/YieldWidget/index.tsx:
- Line 186: The Yield widget's token selector lost LP visibility because the
SelectTokenWidget instance assigned to selectTokenWidget was rendered without
the displayLpTokenLists prop; restore LP token visibility by passing
displayLpTokenLists={true} to the SelectTokenWidget used in the YieldWidget (the
selectTokenWidget entry where <SelectTokenWidget /> is returned) so LP tokens
remain selectable for LpToken handling, pool APY logic, and LP-specific
operations.
🧹 Nitpick comments (5)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx (1)

68-70: Consider memoizing the overlay click handler.

The handleOverlayClick function is recreated on every render. While this is not a major concern since it's used directly on the overlay element, wrapping it with useCallback would be more consistent with React best practices.

🔎 Suggested refactor
+import { MouseEvent, ReactNode, useCallback, useEffect } from 'react'
-import { MouseEvent, ReactNode, useEffect } from 'react'

Then replace the inline function:

-  const handleOverlayClick = (event: MouseEvent<HTMLDivElement>): void => {
-    if (event.target === event.currentTarget) onDismiss()
-  }
+  const handleOverlayClick = useCallback(
+    (event: MouseEvent<HTMLDivElement>): void => {
+      if (event.target === event.currentTarget) onDismiss()
+    },
+    [onDismiss],
+  )
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts (1)

97-113: Consider memoizing the return object to prevent unnecessary re-renders.

The useWidgetMetadata hook returns a new object on every render, which could cause downstream re-renders even when the values haven't changed. Consider wrapping the return in useMemo.

🔎 Proposed improvement
+import { Dispatch, SetStateAction, useMemo, useState } from 'react'
-import { Dispatch, SetStateAction, useState } from 'react'
...
 export function useWidgetMetadata(
   field: Field,
   tradeType: TradeType | undefined,
   displayLpTokenLists: boolean | undefined,
   oppositeToken: Parameters<typeof getDefaultTokenListCategories>[1],
   lpTokensWithBalancesCount: number,
 ): WidgetMetadata {
   const disableErc20 = field === Field.OUTPUT && !!displayLpTokenLists
   const tokenListCategoryState: TokenListCategoryState = useState<TokenListCategory[] | null>(
     getDefaultTokenListCategories(field, oppositeToken, lpTokensWithBalancesCount),
   )
   const modalTitle = resolveModalTitle(field, tradeType)
   const chainsPanelTitle =
     field === Field.INPUT ? t`From network` : field === Field.OUTPUT ? t`To network` : t`Select network`
 
-  return { disableErc20, tokenListCategoryState, modalTitle, chainsPanelTitle }
+  return useMemo(
+    () => ({ disableErc20, tokenListCategoryState, modalTitle, chainsPanelTitle }),
+    [disableErc20, tokenListCategoryState, modalTitle, chainsPanelTitle],
+  )
 }
apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts (1)

87-99: Consider memoizing the return object and caching isInjectedWidget() result.

The return object is recreated on every render, and isInjectedWidget() performs DOM access (reads window.location.href) on each call. Consider memoizing both.

🔎 Proposed improvement
 export function useTokenListData(): TokenListData {
   const { account, chainId: walletChainId } = useWalletInfo()
   const widgetState = useSelectTokenWidgetState()
   const tokenData = useTokenDataSources()
   const standalone = widgetState.standalone ?? false
+  const hideFavoriteTokensTooltip = useMemo(() => isInjectedWidget(), [])

   // ... existing code ...

-  return {
-    allTokens: tokenData.allTokens,
-    favoriteTokens,
-    recentTokens,
-    areTokensLoading: tokenData.areTokensLoading,
-    areTokensFromBridge: tokenData.areTokensFromBridge,
-    hideFavoriteTokensTooltip: isInjectedWidget(),
-    selectedTargetChainId: widgetState.selectedTargetChainId,
-    onClearRecentTokens: clearRecentTokens,
-    onTokenListItemClick: handleTokenListItemClick,
-    selectTokenContext,
-  }
+  return useMemo(
+    () => ({
+      allTokens: tokenData.allTokens,
+      favoriteTokens,
+      recentTokens,
+      areTokensLoading: tokenData.areTokensLoading,
+      areTokensFromBridge: tokenData.areTokensFromBridge,
+      hideFavoriteTokensTooltip,
+      selectedTargetChainId: widgetState.selectedTargetChainId,
+      onClearRecentTokens: clearRecentTokens,
+      onTokenListItemClick: handleTokenListItemClick,
+      selectTokenContext,
+    }),
+    [
+      tokenData.allTokens,
+      favoriteTokens,
+      recentTokens,
+      tokenData.areTokensLoading,
+      tokenData.areTokensFromBridge,
+      hideFavoriteTokensTooltip,
+      widgetState.selectedTargetChainId,
+      clearRecentTokens,
+      handleTokenListItemClick,
+      selectTokenContext,
+    ],
+  )
 }
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useBlockingViewState.ts (1)

66-83: Consider memoizing the return object.

The hook returns a new object on every render. While some of the values are stable references, the object itself causes downstream components to see new props. Consider wrapping in useMemo.

🔎 Proposed improvement
+import { useMemo } from 'react'
...
-  return {
+  return useMemo(() => ({
     hasBlockingView,
     standalone: isStandalone,
     tokenToImport: widgetState.tokenToImport,
     listToImport: widgetState.listToImport,
     isManageWidgetOpen: manageWidget.isManageWidgetOpen,
     selectedPoolAddress: widgetState.selectedPoolAddress,
     allTokenLists: tokenData.allTokenLists,
     userAddedTokens: tokenData.userAddedTokens,
     onDismiss,
     onBackFromImport: importFlows.resetTokenImport,
     onImportTokens: importFlows.importTokenAndClose,
     onImportList: importFlows.importListAndBack,
     onCloseManageWidget: manageWidget.closeManageWidget,
     onClosePoolPage: closePoolPage,
     onSelectToken: handleSelectToken,
-  }
+  }), [
+    hasBlockingView,
+    isStandalone,
+    widgetState.tokenToImport,
+    widgetState.listToImport,
+    manageWidget.isManageWidgetOpen,
+    widgetState.selectedPoolAddress,
+    tokenData.allTokenLists,
+    tokenData.userAddedTokens,
+    onDismiss,
+    importFlows,
+    manageWidget.closeManageWidget,
+    closePoolPage,
+    handleSelectToken,
+  ])
 }
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useHeaderState.ts (1)

35-40: The modalTitle fallback is redundant.

resolveModalTitle in tokenDataHooks.ts already returns t\Select token` as its fallback (line 126). The nullish coalescing here (modalTitle ?? t`Select token`) will never trigger since resolveModalTitlealways returns a string. Consider removing the fallback or confirminguseWidgetMetadatacan returnundefinedformodalTitle`.

🔎 Proposed simplification
   return {
-    title: modalTitle ?? t`Select token`,
+    title: modalTitle,
     showManageButton: !standalone,
     onOpenManageWidget: openManageWidget,
   }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ffb8735 and ee146ad.

📒 Files selected for processing (13)
  • apps/cowswap-frontend/src/locales/en-US.po
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/MobileChainPanelPortal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useBlockingViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useHeaderState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts
  • apps/cowswap-frontend/src/modules/tokensList/state/selectTokenWidgetAtom.ts
  • apps/cowswap-frontend/src/modules/yield/containers/YieldWidget/index.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/state/selectTokenWidgetAtom.ts
🧰 Additional context used
🧠 Learnings (13)
📚 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/yield/containers/YieldWidget/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useBlockingViewState.ts
  • apps/cowswap-frontend/src/locales/en-US.po
  • 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/yield/containers/YieldWidget/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/MobileChainPanelPortal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts
  • apps/cowswap-frontend/src/locales/en-US.po
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/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/yield/containers/YieldWidget/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/MobileChainPanelPortal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useBlockingViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useHeaderState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
📚 Learning: 2025-08-05T14:27:05.023Z
Learnt from: alfetopito
Repo: cowprotocol/cowswap PR: 5992
File: libs/wallet/src/web3-react/utils/switchChain.ts:36-38
Timestamp: 2025-08-05T14:27:05.023Z
Learning: In libs/wallet/src/web3-react/utils/switchChain.ts, the team prefers using Record<SupportedChainId, string | null> over Partial<Record<SupportedChainId, string>> for WALLET_RPC_SUGGESTION to enforce that all supported chain IDs have explicit values set, even if some might be null. This ensures compile-time completeness checking.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.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/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
📚 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/containers/SelectTokenWidget/tokenDataHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useBlockingViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useHeaderState.ts
  • apps/cowswap-frontend/src/locales/en-US.po
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/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/containers/SelectTokenWidget/tokenDataHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
📚 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/containers/SelectTokenWidget/tokenDataHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.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/containers/SelectTokenWidget/tokenDataHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.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/state/tokens/allTokensAtom.ts, the parseTokenInfo() function returns a new instance of TokenInfo each time, making it safe to mutate properties like lpTokenProvider on the returned object without side effects.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.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/useTokenListData.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/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/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.ts
🧬 Code graph analysis (7)
apps/cowswap-frontend/src/modules/yield/containers/YieldWidget/index.tsx (1)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx (1)
  • SelectTokenWidget (19-32)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/MobileChainPanelPortal.tsx (3)
apps/cowswap-frontend/src/modules/tokensList/types.ts (1)
  • ChainsToSelectState (26-32)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/styled.ts (2)
  • MobileChainPanelOverlay (50-58)
  • MobileChainPanelCard (60-64)
apps/cowswap-frontend/src/modules/tokensList/pure/ChainPanel/index.tsx (1)
  • ChainPanel (24-76)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx (14)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/index.ts (8)
  • SelectTokenModal (2-2)
  • Header (6-6)
  • Search (6-6)
  • ChainSelector (6-6)
  • DesktopChainPanel (6-6)
  • TokenList (6-6)
  • BlockingView (6-6)
  • useHasBlockingView (2-2)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/index.ts (1)
  • DEFAULT_MODAL_UI_STATE (15-18)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useWidgetOpenState.ts (1)
  • useWidgetOpenState (14-30)
libs/common-hooks/src/useMediaQuery.ts (1)
  • useMediaQuery (3-19)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/widgetUIState.ts (2)
  • useManageWidgetVisibility (21-29)
  • useDismissHandler (31-39)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useChainPanelState.ts (1)
  • useChainPanelState (23-36)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/styled.ts (1)
  • InnerWrapper (11-41)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useHeaderState.ts (1)
  • useHeaderState (20-40)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/Header.tsx (1)
  • Header (23-49)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/Search.tsx (1)
  • Search (23-54)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ChainSelector.tsx (2)
  • ChainSelector (24-63)
  • DesktopChainPanel (71-84)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/TokenList.tsx (1)
  • TokenList (17-27)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/BlockingView.tsx (1)
  • BlockingView (34-85)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/NetworkPanel.tsx (1)
  • NetworkPanel (19-25)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts (5)
libs/tokens/src/types.ts (1)
  • ListState (39-42)
libs/common-const/src/types.ts (1)
  • TokenWithLogo (6-36)
apps/cowswap-frontend/src/modules/combinedBalances/hooks/useTokensBalancesCombined.ts (1)
  • useTokensBalancesCombined (7-9)
libs/tokens/src/index.ts (3)
  • useAddUserToken (35-35)
  • useUserAddedTokens (34-34)
  • useAllListsList (28-28)
apps/cowswap-frontend/src/modules/tokensList/hooks/useTokensToSelect.ts (1)
  • useTokensToSelect (24-64)
apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts (6)
libs/common-const/src/types.ts (1)
  • TokenWithLogo (6-36)
apps/cowswap-frontend/src/modules/tokensList/types.ts (1)
  • SelectTokenContext (13-24)
libs/wallet/src/api/hooks.ts (1)
  • useWalletInfo (24-26)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts (1)
  • useTokenDataSources (73-95)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.ts (2)
  • useTokenSelectionHandler (100-138)
  • useRecentTokenSection (79-98)
libs/common-utils/src/isInjectedWidget.ts (1)
  • isInjectedWidget (5-14)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.ts (8)
libs/common-const/src/types.ts (1)
  • TokenWithLogo (6-36)
libs/tokens/src/index.ts (1)
  • useAddUserToken (35-35)
apps/cowswap-frontend/src/modules/tokensList/types.ts (2)
  • TokenSelectionHandler (11-11)
  • ChainsToSelectState (26-32)
apps/cowswap-frontend/src/modules/tokensList/hooks/recentTokensStorage.ts (1)
  • persistRecentTokenSelection (123-138)
libs/wallet/src/api/hooks.ts (1)
  • useWalletInfo (24-26)
apps/cowswap-frontend/src/common/hooks/useOnSelectNetwork.tsx (1)
  • useOnSelectNetwork (19-64)
apps/cowswap-frontend/src/modules/trade/types/index.ts (1)
  • TradeType (5-5)
libs/widget-lib/src/types.ts (1)
  • SupportedChainId (4-4)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx (3)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx (2)
  • SelectTokenModal (52-89)
  • useHasBlockingView (143-146)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/index.ts (2)
  • SelectTokenModal (2-2)
  • useHasBlockingView (2-2)
apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/index.tsx (1)
  • SelectTokenModal (26-120)
⏰ 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 (27)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx (3)

91-140: LGTM - Connected slot components follow a consistent pattern.

Each connected component reads from domain hooks directly without prop drilling, aligning with the documented slot-based architecture. The slight redundancy in hook calls (e.g., useChainPanelState in multiple places) is an acceptable tradeoff for slot independence.


142-146: LGTM - Clean helper hook.

Simple extraction of hasBlockingView for external consumption. This enables conditional rendering logic outside the modal without accessing the full blocking view state.


157-158: LGTM - Raw slot exports provide flexibility.

Exporting raw slot components alongside connected versions allows consumers to use the unconnected versions with custom props when needed.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/MobileChainPanelPortal.tsx (4)

1-17: LGTM! Clean imports and well-defined interface.

The imports are properly organized and the MobileChainPanelPortalProps interface clearly defines the component's contract.


25-27: Good SSR safety check.

The document check ensures this portal component won't break during server-side rendering in Next.js.


29-45: Excellent portal implementation with proper event handling.

The component correctly implements the React portal pattern with:

  • Overlay backdrop that closes on click (standard modal UX)
  • Event propagation stopped on the card to prevent backdrop clicks from bubbling
  • Auto-close after chain selection (lines 35-38) for smooth UX
  • Multiple close mechanisms (backdrop, close button, after selection)

This follows established modal/dialog patterns effectively.


39-39: The variant="fullscreen" prop is correctly used. ChainPanel defines the variant type as 'default' | 'fullscreen', and 'fullscreen' is a valid option for the mobile use case.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts (5)

1-30: LGTM - Clean type definitions and imports.

The type alias TokenListCategoryState and interface definitions provide clear contracts for the hooks. The imports are well-organized.


58-71: LGTM - Analytics integration is appropriate.

The hook correctly wraps useAddList with analytics tracking and composes useAddUserToken for token import functionality.


73-95: LGTM - Clean data aggregation pattern.

The hook effectively consolidates multiple token-related data sources into a single return object, improving ergonomics for consumers.


105-107: Verify useState initializer behavior with derived values.

The useState is initialized with getDefaultTokenListCategories(...) which depends on field, oppositeToken, and lpTokensWithBalancesCount. However, useState's initial value is only used on the first render - subsequent changes to these dependencies won't update the state. If this is intentional (state should only initialize once), this is fine. Otherwise, consider using useEffect to sync state when dependencies change.


115-127: LGTM - Helper function is pure and handles all Field cases.

The resolveModalTitle function correctly handles Field.INPUT, Field.OUTPUT, and provides a sensible fallback for any other cases.

apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts (3)

1-18: LGTM - Clear documentation and well-organized imports.

The docstring clearly explains the hook's purpose and its replacement of the atom hydration pattern.


19-39: LGTM - Comprehensive interface definition.

The TokenListData interface provides good documentation through its comments grouping related properties.


64-85: LGTM - Proper memoization of context object.

The selectTokenContext is correctly memoized with all its dependencies, preventing unnecessary re-renders of child components.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useBlockingViewState.ts (3)

1-33: LGTM - Well-defined interface with clear separation of data and handlers.

The BlockingViewState interface clearly documents all the state and callbacks needed for blocking view management.


35-57: LGTM - Clean hook composition.

The hook correctly composes multiple specialized hooks to build the blocking view state. The import flow callbacks are properly wired with all necessary dependencies.


59-64: Verify: selectedPoolAddress bypasses the isStandalone check.

The hasBlockingView condition includes widgetState.selectedPoolAddress without the !isStandalone guard that's applied to the other conditions. This means pool views will show as blocking even in standalone mode. Confirm this is the intended behavior.

const hasBlockingView = Boolean(
  (widgetState.tokenToImport && !isStandalone) ||
    (widgetState.listToImport && !isStandalone) ||
    (manageWidget.isManageWidgetOpen && !isStandalone) ||
    widgetState.selectedPoolAddress, // <-- no !isStandalone check
)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useHeaderState.ts (2)

14-18: LGTM - Simple and clear interface.

The HeaderState interface provides exactly what the header slot needs.


27-33: Clarify: displayLpTokenLists is hardcoded to false.

The third parameter to useWidgetMetadata is hardcoded as false. This may be intentional for header-specific behavior, but if widgetState.displayLpTokenLists should be used instead, this could lead to inconsistent UI state. Consider adding a comment explaining the rationale or using the actual widget state value.

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

1-12: LGTM - Clean imports and interface definition.

The SelectTokenWidgetProps interface is appropriately simple, exposing only the configuration needed.


19-32: LGTM - Proper atom synchronization pattern.

The useEffect correctly syncs the props to the widget atom, ensuring the slot components can read from a single source of truth.


58-59: LGTM - Clean re-export for external use.

Re-exporting SelectTokenModal allows external modules to use the modal directly if needed.

apps/cowswap-frontend/src/locales/en-US.po (1)

282-288: Token/network selector copy looks consistent and clear

The new labels and helper texts for token/network selection and recents (“Select token”, “From/To network”, “Swap from/Swap to”, “Sell/Buy token”, “All tokens”, “Recent”, “Clear”, “View all…”, “Selected network {activeChainLabel}”, etc.) are concise, consistent with existing terminology, and preserve placeholders correctly. No changes needed here.

Also applies to: 305-307, 667-670, 873-876, 885-891, 1445-1448, 1958-1961, 2141-2142, 2393-2395, 2608-2610, 3911-3913, 4393-4395, 5005-5007, 5093-5096, 5253-5255, 5578-5580, 5836-5837, 6163-6165

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.ts (3)

119-127: Consider surfacing network switch failures to the user.

When network switching fails for limit/TWAP orders, the error is logged but swallowed, and token selection proceeds normally. This could lead to user confusion if they expect the wallet to switch networks automatically but it doesn't happen.

Verify that this silent failure is the intended UX. Consider whether users should be notified when the network switch fails, especially since the condition on lines 112-116 suggests the network switch is important for these trade types.


44-57: This behavior is intentional and not a correctness issue. All actual call sites in the codebase pass single-element arrays to importTokenAndClose:

  • BlockingView.tsx passes tokens={[tokenToImport]}
  • AddIntermediateTokenModal.tsx passes tokens={[tokenToImport]}

The function is designed to import all tokens via importTokenCallback(tokens) but select and persist only the first one. This is a deliberate UX choice and works correctly with the current codebase patterns.

Likely an incorrect or invalid review comment.


118-120: No action needed — the code on line 118 is correct and follows the established validation pattern across the codebase.

SupportedChainId is a numeric enum from the @cowprotocol/cow-sdk library, not a type-only alias. The in operator correctly validates numeric chain IDs against the enum's runtime object. This pattern is used consistently throughout the codebase (8+ locations) without issues and is the standard approach for validating enum membership in TypeScript.

Likely an incorrect or invalid review comment.

<CoWAmmInlineBanner token={undefined} apyDiff={undefined} />
) : null,
selectTokenWidget: <SelectTokenWidget displayLpTokenLists />,
selectTokenWidget: <SelectTokenWidget />,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find files related to selectTokenWidgetAtom
find apps/cowswap-frontend -type f -name "*.ts" -o -name "*.tsx" | xargs rg -l "selectTokenWidgetAtom" | head -20

Repository: cowprotocol/cowswap

Length of output: 707


🏁 Script executed:

# Search for the atom definition
rg -n "selectTokenWidgetAtom" apps/cowswap-frontend --type ts --type tsx -B 3 -A 10 | head -100

Repository: cowprotocol/cowswap

Length of output: 90


🏁 Script executed:

# Also check the SelectTokenWidget component to understand how it uses the prop
rg -n "displayLpTokenLists" apps/cowswap-frontend --type ts --type tsx -B 2 -A 2 | head -150

Repository: cowprotocol/cowswap

Length of output: 90


🏁 Script executed:

cat -n apps/cowswap-frontend/src/modules/tokensList/state/selectTokenWidgetAtom.ts

Repository: cowprotocol/cowswap

Length of output: 1884


🏁 Script executed:

# Search for displayLpTokenLists usage without the tsx flag
rg -n "displayLpTokenLists" apps/cowswap-frontend -A 2 -B 2 | head -200

Repository: cowprotocol/cowswap

Length of output: 9497


🏁 Script executed:

# Check the context around YieldWidget to understand if LP tokens should be displayed
rg -n "YieldWidget\|displayLpTokenLists" apps/cowswap-frontend/src/modules/yield --type-list -B 3 -A 3 | head -100

Repository: cowprotocol/cowswap

Length of output: 452


🏁 Script executed:

# Also check git diff or PR context to understand the intent of the change
git log --oneline -20 -- apps/cowswap-frontend/src/modules/yield/containers/YieldWidget/index.tsx

Repository: cowprotocol/cowswap

Length of output: 83


🏁 Script executed:

# Read the YieldWidget file to understand the context
cat -n apps/cowswap-frontend/src/modules/yield/containers/YieldWidget/index.tsx | head -200

Repository: cowprotocol/cowswap

Length of output: 9190


🏁 Script executed:

# Compare with TradeWidget which keeps the prop
cat -n apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/index.tsx | grep -A 5 -B 5 "selectTokenWidget"

Repository: cowprotocol/cowswap

Length of output: 864


Restore the displayLpTokenLists prop to maintain LP token visibility in the Yield widget's token selector.

Removing displayLpTokenLists changes the behavior from explicitly enabling LP token lists (true) to relying on the atom's default value (false), which disables LP token selection. Since the YieldWidget explicitly works with LP tokens (handling LpToken types, pool APY logic, and LP-specific operations), LP tokens must remain visible in the token selector. The TradeWidget retains this prop, indicating this removal is likely unintended.

🤖 Prompt for AI Agents
In @apps/cowswap-frontend/src/modules/yield/containers/YieldWidget/index.tsx at
line 186, The Yield widget's token selector lost LP visibility because the
SelectTokenWidget instance assigned to selectTokenWidget was rendered without
the displayLpTokenLists prop; restore LP token visibility by passing
displayLpTokenLists={true} to the SelectTokenWidget used in the YieldWidget (the
selectTokenWidget entry where <SelectTokenWidget /> is returned) so LP tokens
remain selectable for LpToken handling, pool APY logic, and LP-specific
operations.

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

🤖 Fix all issues with AI agents
In
@apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx:
- Around line 55-92: SelectTokenModal currently dismisses on overlay clicks but
lacks ESC key handling; add a useEffect inside SelectTokenModal that, when
isOpen is true, registers a keydown listener on window (or document) that calls
the existing onDismiss when event.key === 'Escape' (or keyCode 27) and removes
the listener on cleanup; ensure the effect depends on isOpen and onDismiss, only
attaches in the browser (avoid server-side by checking typeof window !==
'undefined' or isOpen), and cleanly removes the handler to prevent leaks.
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee146ad and f4a4629.

📒 Files selected for processing (15)
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useActiveBlockingView.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useImportListViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useImportTokenViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useLpTokenViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useManageViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ImportListView.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ImportTokenView.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/LpTokenView.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ManageView.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/index.tsx
🧰 Additional context used
🧠 Learnings (12)
📚 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/containers/SelectTokenWidget/internal/slots/ImportListView.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ImportTokenView.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useManageViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useActiveBlockingView.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useImportListViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useLpTokenViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/LpTokenView.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useImportTokenViewState.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/containers/SelectTokenWidget/internal/slots/ImportListView.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ImportTokenView.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useManageViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useActiveBlockingView.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useImportListViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useLpTokenViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/LpTokenView.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ManageView.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useImportTokenViewState.ts
📚 Learning: 2025-06-16T15:58:00.268Z
Learnt from: alfetopito
Repo: cowprotocol/cowswap PR: 5830
File: apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/index.tsx:1-2
Timestamp: 2025-06-16T15:58:00.268Z
Learning: JSX can be imported as a named export from React in modern React versions (React 17+). The import `import { JSX } from 'react'` is valid and does not cause compilation errors.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ImportListView.tsx
📚 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/containers/SelectTokenWidget/hooks/useManageViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/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/containers/SelectTokenWidget/hooks/useManageViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useImportListViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useLpTokenViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useImportTokenViewState.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/containers/SelectTokenWidget/hooks/useActiveBlockingView.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useLpTokenViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.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/containers/SelectTokenWidget/internal/slots/LpTokenView.tsx
📚 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/state/tokens/allTokensAtom.ts, the parseTokenInfo() function returns a new instance of TokenInfo each time, making it safe to mutate properties like lpTokenProvider on the returned object without side effects.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/LpTokenView.tsx
📚 Learning: 2025-08-05T14:27:05.023Z
Learnt from: alfetopito
Repo: cowprotocol/cowswap PR: 5992
File: libs/wallet/src/web3-react/utils/switchChain.ts:36-38
Timestamp: 2025-08-05T14:27:05.023Z
Learning: In libs/wallet/src/web3-react/utils/switchChain.ts, the team prefers using Record<SupportedChainId, string | null> over Partial<Record<SupportedChainId, string>> for WALLET_RPC_SUGGESTION to enforce that all supported chain IDs have explicit values set, even if some might be null. This ensures compile-time completeness checking.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.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/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/index.ts
  • 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/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx
📚 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/tokensList/containers/SelectTokenWidget/index.tsx
🧬 Code graph analysis (6)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ImportTokenView.tsx (5)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx (1)
  • ImportTokenView (159-159)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/index.ts (1)
  • ImportTokenView (9-9)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/index.ts (1)
  • ImportTokenView (8-8)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts (1)
  • useImportTokenViewState (8-8)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useImportTokenViewState.ts (1)
  • useImportTokenViewState (20-49)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useManageViewState.ts (4)
libs/tokens/src/types.ts (1)
  • ListState (39-42)
libs/common-const/src/types.ts (1)
  • TokenWithLogo (6-36)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/widgetUIState.ts (2)
  • useManageWidgetVisibility (21-29)
  • useDismissHandler (31-39)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts (1)
  • useTokenDataSources (73-95)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useLpTokenViewState.ts (4)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts (2)
  • LpTokenViewState (11-11)
  • useLpTokenViewState (11-11)
libs/common-const/src/types.ts (1)
  • TokenWithLogo (6-36)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/widgetUIState.ts (3)
  • useManageWidgetVisibility (21-29)
  • useDismissHandler (31-39)
  • usePoolPageHandlers (41-54)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.ts (1)
  • useTokenSelectionHandler (100-138)
apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/index.tsx (2)
apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListViewState.ts (1)
  • useTokenListViewState (5-7)
apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts (1)
  • useTokenListData (41-99)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ManageView.tsx (6)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx (1)
  • ManageView (159-159)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/index.ts (1)
  • ManageView (9-9)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/index.ts (1)
  • ManageView (10-10)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts (1)
  • useManageViewState (10-10)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useManageViewState.ts (1)
  • useManageViewState (18-32)
apps/cowswap-frontend/src/modules/tokensList/containers/ManageListsAndTokens/index.tsx (1)
  • ManageListsAndTokens (26-99)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx (2)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx (2)
  • SelectTokenModal (55-92)
  • useActiveBlockingView (141-141)
apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/index.tsx (1)
  • SelectTokenModal (26-120)
⏰ 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 (22)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useActiveBlockingView.ts (1)

14-17: Verify standalone mode handling for lpToken view.

Lines 14-16 guard against standalone mode before returning 'importToken', 'importList', or 'manage', but line 17 returns 'lpToken' without checking isStandalone.

Is this intentional? Should LP token details always display regardless of standalone mode, or should line 17 also include the !isStandalone check?

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useImportListViewState.ts (1)

21-51: LGTM! Well-structured hook composition.

The hook cleanly composes multiple data sources and callbacks into a cohesive state object for the ImportListModal. The early return pattern (line 43) avoids unnecessary computation when no list is queued for import, and all callbacks are properly wired through importFlows.

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

19-32: LGTM! Clean slot-based architecture.

The refactored component properly syncs configuration props to the widget state atom (lines 23-25) and delegates rendering to SelectTokenModal with slot-based content. The separation between the outer SelectTokenWidget (which handles prop-to-atom synchronization) and the inner SelectTokenWidgetContent (which handles view switching) is well-structured.


34-59: Conditional slot rendering is well-organized.

The useActiveBlockingView hook cleanly determines which view to render, and the default token list view (lines 44-58) composes slot components from SelectTokenModal in a clear layout. The structure is easy to follow and maintain.


61-62: Note: Past review comment may no longer apply.

The previous review flagged missing TypeScript types for compound component properties. However, after this refactor, SelectTokenWidget no longer has compound properties attached—instead, it imports and re-exports SelectTokenModal, which has the compound properties (.ImportTokenView, .Header, .Search, etc.).

If TypeScript typing is still missing for SelectTokenModal's compound properties, that issue would now be in apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx rather than in this file.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/LpTokenView.tsx (1)

9-22: LGTM! Consistent slot component pattern.

The component follows the same clean pattern as other slot components in the PR: fetch state from a dedicated hook, return null if unavailable, otherwise render the view with mapped props. This consistency makes the codebase easier to maintain.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ImportTokenView.tsx (1)

9-22: LGTM! Properly wraps single token in array.

The component correctly wraps state.token in an array (line 16) to match ImportTokenModal's expected tokens prop type, and follows the consistent slot pattern used throughout the refactor.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ImportListView.tsx (1)

1-17: LGTM! Clean slot component following the established pattern.

The ImportListView follows the container/view separation pattern consistently used across the codebase. The component correctly delegates state management to useImportListViewState, guards against null state, and cleanly passes through props to ImportListModal.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/index.ts (1)

1-11: LGTM! Well-organized barrel exports.

The barrel file provides a clean public API surface for the slot components. The comment on line 7 helpfully distinguishes the blocking views (each with dedicated state hooks) from the standard slot components.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ManageView.tsx (1)

1-22: LGTM! Consistent with the slot component pattern.

ManageView correctly follows the same architecture as ImportListView: dedicated state hook (useManageViewState), null guard, and clean prop forwarding to ManageListsAndTokens. The explicit prop listing enhances readability.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useLpTokenViewState.ts (1)

1-37: LGTM! Well-structured state composition hook.

The hook cleanly composes multiple existing hooks to produce a cohesive LpTokenViewState. The conditional null return (line 29) correctly guards against rendering when no pool is selected, and all handlers are properly wired through existing utility hooks.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useImportTokenViewState.ts (1)

1-49: LGTM! Comprehensive import flow state management.

The hook effectively composes token data sources, admin actions, and import flow callbacks into a cohesive ImportTokenViewState. The empty function at line 36 is appropriately documented—token list error handling isn't needed for single token imports.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useManageViewState.ts (1)

18-32: LGTM! Clean hook composition.

The hook follows a clear pattern: composes multiple domain hooks, returns null when the manage widget is closed, and assembles the state object when open. The onDismiss handler properly coordinates closing both the manage widget and the token select widget.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts (1)

1-11: LGTM! Consistent export surface.

The barrel file cleanly re-exports hooks and their corresponding types, maintaining a clear public API for the SelectTokenWidget hook ecosystem.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/index.ts (1)

1-15: LGTM! Well-organized exports.

The barrel file cleanly separates and re-exports the modal component, slot components, blocking views, and types with helpful comments for each section.

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

38-92: Solid architecture for the modal component.

The implementation follows clean patterns:

  • Portal rendering with proper SSR check
  • Effect-based body scroll management with correct cleanup
  • Overlay click dismissal with event target validation
  • Early return for closed state

The slot-based architecture with hook composition eliminates prop drilling and maintains clear separation of concerns.


144-155: TypeScript types for static properties still missing.

The static property assignments (Header, Search, TokenList, etc.) on SelectTokenModal lack TypeScript type definitions. This was previously flagged and remains unresolved. Consider defining a compound component type that extends the modal's function signature and includes typed static members, then declaring SelectTokenModal with that type.

⛔ Skipped due to learnings
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.
apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/index.tsx (5)

7-8: LGTM: Hook imports support the refactored architecture.

These hook imports enable the component to derive all necessary state internally, consistent with the hook-driven refactor described in the PR summary.


13-18: Well-structured hook-driven refactor.

The transition from prop-based to hook-based state management improves modularity and aligns with the comprehensive refactor described in the PR summary. Deriving searchInput, selectTokenContext, areTokensFromBridge, and allTokens from centralized hooks makes the data flow more maintainable.


21-34: Appropriate handling of bridge token search.

The conditional logic correctly distinguishes between regular tokens (using useSearchToken) and bridge tokens (filtering locally). Passing null to disable the search hook for bridge tokens and manually applying the filter ensures consistent behavior across both token sources.


60-64: LGTM: Effect correctly synchronizes Enter key handler.

The effect properly updates the widget state with the latest onInputPressEnter callback, and the dependency array is accurate.


51-58: No action needed. The onTokenListItemClick call is error-resilient: persistStoredTokens() in the recent tokens tracking already has a try-catch block that silently handles localStorage failures with "best effort persistence," preventing any errors from propagating to the Enter key handler.

Likely an incorrect or invalid review comment.

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: 3

🤖 Fix all issues with AI agents
In
@apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx:
- Around line 55-92: SelectTokenModal lacks ESC-key dismissal: add a useEffect
inside the SelectTokenModal component that, when isOpen is true, registers a
keydown listener (window.addEventListener) which calls the existing onDismiss
handler when event.key === 'Escape' (or keyCode 27), and removes the listener in
cleanup; ensure the effect depends on isOpen and onDismiss so it
registers/unregisters correctly and avoid memory leaks.
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f4a4629 and 6d8e2f3.

📒 Files selected for processing (6)
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useLpTokenViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useWidgetOpenState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useSelectTokenWidgetState.ts
  • apps/cowswap-frontend/src/modules/tokensList/state/selectTokenWidgetAtom.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useLpTokenViewState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/state/selectTokenWidgetAtom.ts
🧰 Additional context used
🧠 Learnings (9)
📚 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/useSelectTokenWidgetState.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/useSelectTokenWidgetState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
📚 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/useSelectTokenWidgetState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useWidgetOpenState.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/state/tokens/allTokensAtom.ts, the parseTokenInfo() function returns a new instance of TokenInfo each time, making it safe to mutate properties like lpTokenProvider on the returned object without side effects.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/hooks/useSelectTokenWidgetState.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/useSelectTokenWidgetState.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/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useWidgetOpenState.ts
📚 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/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
📚 Learning: 2025-08-05T14:27:05.023Z
Learnt from: alfetopito
Repo: cowprotocol/cowswap PR: 5992
File: libs/wallet/src/web3-react/utils/switchChain.ts:36-38
Timestamp: 2025-08-05T14:27:05.023Z
Learning: In libs/wallet/src/web3-react/utils/switchChain.ts, the team prefers using Record<SupportedChainId, string | null> over Partial<Record<SupportedChainId, string>> for WALLET_RPC_SUGGESTION to enforce that all supported chain IDs have explicit values set, even if some might be null. This ensures compile-time completeness checking.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
📚 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/containers/SelectTokenWidget/hooks/useWidgetOpenState.ts
🧬 Code graph analysis (1)
apps/cowswap-frontend/src/modules/tokensList/hooks/useSelectTokenWidgetState.ts (1)
apps/cowswap-frontend/src/modules/tokensList/state/selectTokenWidgetAtom.ts (1)
  • SelectTokenWidgetState (15-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: Setup
  • GitHub Check: Cypress
🔇 Additional comments (3)
apps/cowswap-frontend/src/modules/tokensList/hooks/useSelectTokenWidgetState.ts (1)

3-7: LGTM! Type annotation improves API clarity.

Adding the explicit return type makes the hook's contract clear and ensures type safety for consumers. The return type correctly matches the selectTokenWidgetAtom state shape.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useWidgetOpenState.ts (1)

9-25: Implementation follows React best practices.

The hook correctly:

  • Derives isOpen state from widget configuration
  • Uses useRef to track previous state for transition detection
  • Triggers cleanup (reset) only on open → closed transitions
  • Has proper effect dependencies

The logic requiring both onSelectToken callback and an open flag (line 11) ensures the widget only displays when it can handle token selection, which is reasonable.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx (1)

94-138: LGTM: Clean slot-based architecture.

The connected slot components properly encapsulate their state management using hooks/atoms, eliminating prop drilling. Each component reads only what it needs, making the architecture modular and maintainable.

Comment on lines +45 to +52
useEffect(() => {
if (!isOpen) {
removeBodyClass('noScroll')
return
}
addBodyClass('noScroll')
return () => removeBodyClass('noScroll')
}, [isOpen])
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for other usages of addBodyClass/removeBodyClass with 'noScroll'
rg -n "addBodyClass.*noScroll|removeBodyClass.*noScroll" apps/cowswap-frontend/src/

Repository: cowprotocol/cowswap

Length of output: 716


🏁 Script executed:

# Search for other modal/overlay components that might be open simultaneously
rg -n "createPortal.*document\.body" apps/cowswap-frontend/src/modules/

Repository: cowprotocol/cowswap

Length of output: 400


🏁 Script executed:

# Also check for the body utility functions to understand implementation
fd -n "body" apps/cowswap-frontend/src/ -type f | head -20

Repository: cowprotocol/cowswap

Length of output: 293


🏁 Script executed:

# Search for definitions of addBodyClass and removeBodyClass
rg -n "export.*addBodyClass|export.*removeBodyClass|function addBodyClass|function removeBodyClass" apps/cowswap-frontend/src/

Repository: cowprotocol/cowswap

Length of output: 45


🏁 Script executed:

# Check FortuneWidget to understand if it could be open at the same time as SelectTokenModal
rg -B 5 -A 10 "addBodyClass.*noScroll" apps/cowswap-frontend/src/modules/fortune/containers/FortuneWidget/index.tsx | head -30

Repository: cowprotocol/cowswap

Length of output: 606


🏁 Script executed:

# Check the page/widget structure to understand if both modals can coexist
rg -n "SelectTokenModal|FortuneWidget" apps/cowswap-frontend/src/modules/ | grep -E "(import|export)" | head -20

Repository: cowprotocol/cowswap

Length of output: 3126


🏁 Script executed:

# Find where these components are used
rg -l "SelectTokenModal" apps/cowswap-frontend/src/ --type tsx

Repository: cowprotocol/cowswap

Length of output: 90


🏁 Script executed:

# Check if there's a scroll lock utility already
rg -n "ScrollLock|disableBodyScroll|enableBodyScroll" apps/cowswap-frontend/src/ | head -10

Repository: cowprotocol/cowswap

Length of output: 45


🏁 Script executed:

# Check SelectTokenWidget index to see how SelectTokenModal is used
cat -n apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/index.tsx | head -50

Repository: cowprotocol/cowswap

Length of output: 2012


🏁 Script executed:

# Check if FortuneWidget and SelectTokenWidget could be on same page
rg -n "SelectTokenWidget|FortuneWidget" apps/cowswap-frontend/src/modules/ --type-list | grep -v "pure/SelectTokenModal" | head -15

Repository: cowprotocol/cowswap

Length of output: 452


🏁 Script executed:

# Look for parent pages that might render both widgets
fd -type f "page" apps/cowswap-frontend/src/ | head -10

Repository: cowprotocol/cowswap

Length of output: 233


🏁 Script executed:

# Search for where SelectTokenWidget is imported and used
rg -l "SelectTokenWidget" apps/cowswap-frontend/src/ | head -10

Repository: cowprotocol/cowswap

Length of output: 817


🏁 Script executed:

# Search for where FortuneWidget is imported and used
rg -l "FortuneWidget" apps/cowswap-frontend/src/ | head -10

Repository: cowprotocol/cowswap

Length of output: 516


🏁 Script executed:

# Check if they could both be on the same page by looking at main app structure
find apps/cowswap-frontend/src -name "*.tsx" -path "*pages*" -o -name "app.tsx" -o -name "App.tsx" | head -10

Repository: cowprotocol/cowswap

Length of output: 621


🏁 Script executed:

# Check TradeWidget to see if it uses SelectTokenWidget
rg -A 20 "SelectTokenWidget" apps/cowswap-frontend/src/modules/trade/containers/TradeWidget/index.tsx

Repository: cowprotocol/cowswap

Length of output: 1910


🏁 Script executed:

# Check where FortuneWidget is rendered in the app
rg -B 5 -A 5 "FortuneWidget" apps/cowswap-frontend/src/modules/application/containers/App/ 2>/dev/null || rg -B 5 -A 5 "FortuneWidget" apps/cowswap-frontend/src/ | grep -v ".po:" | head -30

Repository: cowprotocol/cowswap

Length of output: 2032


🏁 Script executed:

# Check the main App component structure
cat -n apps/cowswap-frontend/src/modules/application/containers/App/index.tsx 2>/dev/null | head -80 || find apps/cowswap-frontend/src -name "App.tsx" -o -name "app.tsx" | xargs ls -la 2>/dev/null

Repository: cowprotocol/cowswap

Length of output: 916


🏁 Script executed:

# Verify AppContainer includes the footer with FortuneWidget
cat -n apps/cowswap-frontend/src/modules/application/containers/AppContainer/index.tsx | head -60

Repository: cowprotocol/cowswap

Length of output: 2921


Coordinate noScroll class management across multiple modals to prevent race conditions.

The noScroll class is managed independently by both SelectTokenModal and FortuneWidget, which can be open simultaneously (token selector in main area, fortune widget in footer). When one modal closes, it unconditionally removes noScroll without checking if another modal is still active, causing body scrolling to re-enable prematurely.

Consider implementing a reference-counted scroll lock mechanism or centralizing scroll state management to ensure the body remains unscrollable only when at least one modal is active.

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: 3

🤖 Fix all issues with AI agents
In
@apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx:
- Around line 144-155: SelectTokenModal is being augmented with static
subcomponents (Header, Search, TokenList, NetworkPanel, ChainSelector,
DesktopChainPanel, ImportTokenView, ImportListView, ManageView, LpTokenView) but
lacks a compound component type; create a TypeScript type (e.g.,
SelectTokenModalType) that extends the base component type (React.FC or
appropriate component type for SelectTokenModal) and declares each static member
with the correct component types (use typeof ConnectedHeader, typeof
ConnectedSearch, etc.), then annotate or cast the SelectTokenModal export to
that compound type before assigning the static properties so the assignments are
properly typed and IDE/typecheck friendly.
- Around line 55-92: The modal lacks ESC key handling; inside SelectTokenModal
add a useEffect that, when isOpen is true, attaches a keydown listener which
calls onDismiss if the pressed key is Escape (or keyCode 27), and cleans up the
listener on unmount or when isOpen/onDismiss change; ensure the effect
dependency array includes isOpen and onDismiss so the handler is registered only
while the modal is open and removed afterward.

In
@apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx:
- Around line 104-107: The comment is misleading: the code sets showCoWAmmBanner
= isYieldEnabled and lpLists = isYieldEnabled ? LP_TOKEN_LISTS : [], so the
banner is shown when yield (LP tokens) is enabled; update the comment above the
showCoWAmmBanner assignment to reflect that the CoWAmmBanner is displayed when
isYieldEnabled is true (i.e., when LP token lists are available) or, if intended
behavior was to hide it for LP lists, change the boolean logic in
showCoWAmmBanner to match that intent (refer to showCoWAmmBanner,
isYieldEnabled, lpLists, and LP_TOKEN_LISTS).
🧹 Nitpick comments (2)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useTokenListState.ts (1)

1-9: Simple passthrough works correctly.

The hook correctly returns the isRouteAvailable flag. Note that it calls useTokenDataSources(), which aggregates multiple data sources even though only one field is used. If this hook is used in contexts where the other data sources aren't needed nearby, consider extracting isRouteAvailable directly from useTokensToSelect() for efficiency. However, if consumers typically need multiple fields from TokenDataSources, the current approach is fine.

apps/cowswap-frontend/src/modules/tokensList/pure/TokensContent/index.tsx (1)

22-37: Optional: Remove redundant check at line 32.

The shouldShowRecentsInline check on line 32 is redundant since the function already returns undefined early (lines 26-28) when shouldShowRecentsInline is false. You can simplify line 32 to just check recentTokens.

♻️ Proposed simplification
-    if (shouldShowRecentsInline && recentTokens) {
+    if (recentTokens) {
       recentTokens.forEach((token) => pinned.add(getTokenUniqueKey(token)))
     }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d8e2f3 and 4945efd.

📒 Files selected for processing (12)
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useChainPanelState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useHeaderState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useSearchState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useTokenListState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useRecentTokens.ts
  • apps/cowswap-frontend/src/modules/tokensList/pure/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useHeaderState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useSearchState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useChainPanelState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenSelectionHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts
  • apps/cowswap-frontend/src/modules/tokensList/hooks/useRecentTokens.ts
🧰 Additional context used
🧠 Learnings (10)
📚 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/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/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/pure/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useTokenListState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/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/tokensList/pure/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useTokenListState.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/index.ts
📚 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/pure/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useTokenListState.ts
📚 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/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
  • apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx
📚 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/TokensContent/index.tsx
  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useTokenListState.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/containers/SelectTokenWidget/tokenDataHooks.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/containers/SelectTokenWidget/tokenDataHooks.ts
  • apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx
📚 Learning: 2025-08-05T14:27:05.023Z
Learnt from: alfetopito
Repo: cowprotocol/cowswap PR: 5992
File: libs/wallet/src/web3-react/utils/switchChain.ts:36-38
Timestamp: 2025-08-05T14:27:05.023Z
Learning: In libs/wallet/src/web3-react/utils/switchChain.ts, the team prefers using Record<SupportedChainId, string | null> over Partial<Record<SupportedChainId, string>> for WALLET_RPC_SUGGESTION to enforce that all supported chain IDs have explicit values set, even if some might be null. This ensures compile-time completeness checking.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
📚 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/state/tokens/allTokensAtom.ts, the parseTokenInfo() function returns a new instance of TokenInfo each time, making it safe to mutate properties like lpTokenProvider on the returned object without side effects.

Applied to files:

  • apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/index.ts
🧬 Code graph analysis (5)
apps/cowswap-frontend/src/modules/tokensList/pure/TokensContent/index.tsx (4)
apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListViewState.ts (1)
  • useTokenListViewState (5-7)
apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts (1)
  • useTokenListData (41-99)
apps/cowswap-frontend/src/modules/tokensList/containers/TokenSearchResults/index.tsx (1)
  • TokenSearchResults (13-76)
apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx (1)
  • TokensVirtualList (32-119)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx (14)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/index.ts (7)
  • SelectTokenModal (2-2)
  • Header (6-6)
  • Search (6-6)
  • ChainSelector (6-6)
  • DesktopChainPanel (6-6)
  • TokenList (6-6)
  • LpTokenView (9-9)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/index.ts (1)
  • DEFAULT_MODAL_UI_STATE (13-15)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useWidgetOpenState.ts (1)
  • useWidgetOpenState (9-25)
libs/common-hooks/src/useMediaQuery.ts (1)
  • useMediaQuery (3-19)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/widgetUIState.ts (2)
  • useManageWidgetVisibility (21-29)
  • useDismissHandler (31-39)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useChainPanelState.ts (1)
  • useChainPanelState (24-40)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useHeaderState.ts (1)
  • useHeaderState (18-31)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/Header.tsx (1)
  • Header (23-49)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useSearchState.ts (1)
  • useSearchState (6-9)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/ChainSelector.tsx (2)
  • ChainSelector (24-63)
  • DesktopChainPanel (71-84)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useTokenListState.ts (1)
  • useTokenListState (6-9)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/TokenList.tsx (1)
  • TokenList (17-27)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/NetworkPanel.tsx (1)
  • NetworkPanel (19-25)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/slots/LpTokenView.tsx (1)
  • LpTokenView (9-22)
apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx (8)
libs/common-const/src/types.ts (1)
  • TokenWithLogo (6-36)
apps/cowswap-frontend/src/modules/tokensList/hooks/useTokenListData.ts (1)
  • useTokenListData (41-99)
libs/common-utils/src/getIsNativeToken.ts (1)
  • getIsNativeToken (13-33)
apps/cowswap-frontend/src/modules/tokensList/utils/tokensListSorter.ts (1)
  • tokensListSorter (5-27)
apps/cowswap-frontend/src/common/pure/VirtualList/index.tsx (1)
  • VirtualList (87-158)
apps/cowswap-frontend/src/common/containers/CoWAmmBanner/index.tsx (1)
  • CoWAmmBanner (34-102)
apps/cowswap-frontend/src/modules/tokensList/pure/FavoriteTokensList/index.tsx (1)
  • FavoriteTokensList (21-41)
apps/cowswap-frontend/src/modules/tokensList/pure/TokenListItemContainer/index.tsx (1)
  • TokenListItemContainer (13-46)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/useTokenListState.ts (2)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/hooks/index.ts (1)
  • useTokenListState (5-5)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts (1)
  • useTokenDataSources (73-110)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/index.ts (1)
apps/cowswap-frontend/src/modules/tokensList/state/tokenListViewAtom.ts (1)
  • atom (19-21)
⏰ 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 (10)
apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/tokenDataHooks.ts (5)

1-56: LGTM: Well-structured type definitions.

The imports and type definitions are clean and properly organized. The interfaces clearly define the contracts for the hooks, and using ReturnType for dependent hook types maintains type safety.


58-71: LGTM: Analytics integration is correct.

The hook properly integrates analytics tracking when custom token lists are added, and cleanly exposes both admin actions.


73-110: LGTM: Proper data aggregation with correct memoization.

The hook correctly aggregates multiple token data sources with proper memoization. All dependencies are accurately listed, ensuring the memoized value updates when any source changes.


112-128: LGTM: Widget metadata computation is well-structured.

The hook correctly computes UI metadata including the ERC20 disable flag for LP token mode, maintains token list category state, and provides contextually appropriate titles for different widget configurations.


130-142: LGTM: Modal title resolution logic is correct.

The function properly resolves modal titles based on field type and trade type, providing appropriate context-specific labels for different user flows.

apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/atoms/index.ts (1)

1-19: LGTM! Clean atom definition following established patterns.

The modal UI state atom follows the same pattern as tokenListViewAtom and uses atomWithPartialUpdate appropriately. The interface is well-documented and the default state is properly typed.

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

75-89: Unresolved: Missing focus management and ARIA attributes for accessibility.

This accessibility issue was previously flagged but remains unresolved. The modal lacks essential WCAG-compliant features:

  1. Focus trap: Focus should be constrained within the modal while open
  2. Focus restoration: Focus should return to the trigger element on close
  3. ARIA attributes: Missing role="dialog", aria-modal="true", and aria-labelledby

Consider using an accessibility-focused modal library (e.g., @radix-ui/react-dialog or @headlessui/react) or manually implement focus management with a focus trap utility and proper ARIA attributes on the ModalContainer or WidgetCard (lines 78, 85).

Likely an incorrect or invalid review comment.


45-52: Unresolved: Body scroll lock race condition with multiple modals.

This issue was previously flagged but remains unresolved. The noScroll class is managed independently by both SelectTokenModal and FortuneWidget. When one modal closes, it unconditionally removes noScroll (line 47 and line 51) without checking if another modal is still active, causing the body to become scrollable prematurely.

Consider implementing a reference-counted scroll lock mechanism or centralizing scroll state management to ensure the body remains unscrollable while any modal is active.

⛔ Skipped due to learnings
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 the container as outdated when the pure view is renamed or refactored.
apps/cowswap-frontend/src/modules/tokensList/pure/TokensContent/index.tsx (1)

12-69: LGTM: Clean hook-driven refactor.

The refactor successfully decouples TokensContent from props and uses hooks (useTokenListViewState, useTokenListData) to source all data. The filtering logic for pinned/recent tokens is clear, and the conditional rendering for loading/search/main states is well-structured.

apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx (1)

20-146: LGTM: Well-structured multi-row refactor.

The refactor introduces a clean type-safe row model (TokensVirtualRow union type) and separates rendering concerns via TokensVirtualRowRenderer. The token prioritization logic (lines 44-64) correctly elevates native tokens and tokens with balances, and the composed rows pattern (lines 66-93) enables flexible section rendering (favorites, recents, all tokens).

Comment on lines +55 to +92
export function SelectTokenModal({ children }: SelectTokenModalProps): ReactNode {
const isOpen = useWidgetOpenState()
const isCompactLayout = useMediaQuery(Media.upToMedium(false))
const widgetState = useSelectTokenWidgetState()
const { closeManageWidget } = useManageWidgetVisibility()
const closeTokenSelectWidget = useCloseTokenSelectWidget()
const onDismiss = useDismissHandler(closeManageWidget, closeTokenSelectWidget)

// Chain panel state
const chainPanel = useChainPanelState(widgetState.tradeType)
const isChainPanelVisible = chainPanel.isEnabled && !isCompactLayout

useWidgetEffects(isOpen)

if (!isOpen) return null

const handleOverlayClick = (event: MouseEvent<HTMLDivElement>): void => {
if (event.target === event.currentTarget) onDismiss()
}

const content = (
<Wrapper>
<InnerWrapper $hasSidebar={isChainPanelVisible} $isMobileOverlay={isCompactLayout}>
<ModalContainer>{children}</ModalContainer>
</InnerWrapper>
</Wrapper>
)

const overlay = (
<WidgetOverlay onClick={handleOverlayClick}>
<WidgetCard $isCompactLayout={isCompactLayout} $hasChainPanel={chainPanel.isEnabled}>
{content}
</WidgetCard>
</WidgetOverlay>
)

return typeof document === 'undefined' ? overlay : createPortal(overlay, document.body)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Unresolved: Missing ESC key handler for modal dismissal.

This accessibility issue was previously flagged but remains unresolved. The modal dismisses on overlay clicks (line 72) but lacks keyboard support for ESC key dismissal, which is a standard accessibility requirement.

Add a useEffect with a keyboard event listener that calls onDismiss when ESC is pressed:

⌨️ Suggested ESC key handler implementation
  useWidgetEffects(isOpen)

+ useEffect(() => {
+   if (!isOpen) return
+
+   const handleEscKey = (event: KeyboardEvent): void => {
+     if (event.key === 'Escape') {
+       onDismiss()
+     }
+   }
+
+   window.addEventListener('keydown', handleEscKey)
+   return () => window.removeEventListener('keydown', handleEscKey)
+ }, [isOpen, onDismiss])
+
  if (!isOpen) return null
🤖 Prompt for AI Agents
In
@apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
around lines 55 - 92, The modal lacks ESC key handling; inside SelectTokenModal
add a useEffect that, when isOpen is true, attaches a keydown listener which
calls onDismiss if the pressed key is Escape (or keyCode 27), and cleans up the
listener on unmount or when isOpen/onDismiss change; ensure the effect
dependency array includes isOpen and onDismiss so the handler is registered only
while the modal is open and removed afterward.

Comment on lines +144 to +155
SelectTokenModal.Header = ConnectedHeader
SelectTokenModal.Search = ConnectedSearch
SelectTokenModal.TokenList = ConnectedTokenList
SelectTokenModal.NetworkPanel = NetworkPanel
SelectTokenModal.ChainSelector = ConnectedChainSelector
SelectTokenModal.DesktopChainPanel = ConnectedDesktopChainPanel

// Blocking view slots (each with its own focused hook)
SelectTokenModal.ImportTokenView = ImportTokenView
SelectTokenModal.ImportListView = ImportListView
SelectTokenModal.ManageView = ManageView
SelectTokenModal.LpTokenView = LpTokenView
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Unresolved: Static property assignments lack TypeScript type definitions.

This TypeScript issue was previously flagged but remains unresolved. The static subcomponent assignments (lines 144-155) on SelectTokenModal lack proper type definitions, reducing type safety and IDE support.

Define a compound component type that extends the base component and includes typed static members:

🔧 Suggested TypeScript compound component pattern
+type SelectTokenModalComponent = typeof SelectTokenModal & {
+  Header: typeof ConnectedHeader
+  Search: typeof ConnectedSearch
+  TokenList: typeof ConnectedTokenList
+  NetworkPanel: typeof NetworkPanel
+  ChainSelector: typeof ConnectedChainSelector
+  DesktopChainPanel: typeof ConnectedDesktopChainPanel
+  ImportTokenView: typeof ImportTokenView
+  ImportListView: typeof ImportListView
+  ManageView: typeof ManageView
+  LpTokenView: typeof LpTokenView
+}
+
-export function SelectTokenModal({ children }: SelectTokenModalProps): ReactNode {
+export const SelectTokenModal = (({ children }: SelectTokenModalProps): ReactNode => {
   // ... existing implementation
-}
+}) as SelectTokenModalComponent
 
 // Attach slots
 SelectTokenModal.Header = ConnectedHeader

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
@apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenWidget/internal/SelectTokenModal.tsx
around lines 144 - 155, SelectTokenModal is being augmented with static
subcomponents (Header, Search, TokenList, NetworkPanel, ChainSelector,
DesktopChainPanel, ImportTokenView, ImportListView, ManageView, LpTokenView) but
lacks a compound component type; create a TypeScript type (e.g.,
SelectTokenModalType) that extends the base component type (React.FC or
appropriate component type for SelectTokenModal) and declares each static member
with the correct component types (use typeof ConnectedHeader, typeof
ConnectedSearch, etc.), then annotate or cast the SelectTokenModal export to
that compound type before assigning the static properties so the assignments are
properly typed and IDE/typecheck friendly.

Comment on lines +104 to +107
// Check if LP token lists are displayed (no CoWAmmBanner in that case)
// This was previously read from atom, now we don't have it - default to not showing banner for LP
const showCoWAmmBanner = isYieldEnabled

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's read the target file to see the full context
cat -n apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx | sed -n '90,120p'

Repository: cowprotocol/cowswap

Length of output: 1243


🏁 Script executed:

# Read the start of the file to see imports and props
cat -n apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx | sed -n '1,50p'

Repository: cowprotocol/cowswap

Length of output: 2212


🏁 Script executed:

# Search for more context about isYieldEnabled and CoWAmmBanner usage
rg -i "isYieldEnabled|coWAmmBanner|LP.*token" --type tsx --type ts apps/cowswap-frontend/src/modules/tokensList/ -B 2 -A 2 | head -100

Repository: cowprotocol/cowswap

Length of output: 90


🏁 Script executed:

# Search for context about isYieldEnabled and CoWAmmBanner
rg -i "isYieldEnabled|coWAmmBanner" apps/cowswap-frontend/src/modules/tokensList/ -B 2 -A 2

Repository: cowprotocol/cowswap

Length of output: 2490


🏁 Script executed:

# Search for history or comments about yield feature and LP tokens
rg -i "yield|LP.*token|CoWAmm" apps/cowswap-frontend/src/modules/tokensList/ -B 1 -A 1 | grep -i "yield\|LP\|CoWAmm" | head -50

Repository: cowprotocol/cowswap

Length of output: 6032


🏁 Script executed:

# Look at the selectTokenWidgetAtom to understand the displayLpTokenLists logic
cat -n apps/cowswap-frontend/src/modules/tokensList/state/selectTokenWidgetAtom.ts | head -50

Repository: cowprotocol/cowswap

Length of output: 1891


🏁 Script executed:

# Look at TokenColumnContent to see the relationship between displayLpTokenLists and the token list
cat -n apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/TokenColumnContent.tsx

Repository: cowprotocol/cowswap

Length of output: 3017


🏁 Script executed:

# Check if TokensVirtualList receives displayLpTokenLists as a prop
grep -n "displayLpTokenLists" apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx

Repository: cowprotocol/cowswap

Length of output: 45


🏁 Script executed:

# Search for uses of displayLpTokenLists in SelectTokenModal context
rg "displayLpTokenLists" apps/cowswap-frontend/src/modules/tokensList/ -B 3 -A 3

Repository: cowprotocol/cowswap

Length of output: 11679


🏁 Script executed:

# Search for any git history or PRs related to this change
git log --oneline --all -S "showCoWAmmBanner" | head -10

Repository: cowprotocol/cowswap

Length of output: 90


🏁 Script executed:

# Look at the specific commit that introduced showCoWAmmBanner
git show 4945efd11 --stat | head -30

Repository: cowprotocol/cowswap

Length of output: 1704


🏁 Script executed:

# Look at the actual diff for the TokensVirtualList file in that commit
git show 4945efd11 -- apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx | head -100

Repository: cowprotocol/cowswap

Length of output: 3621


🏁 Script executed:

# Look at the rest of the diff for that file
git show 4945efd11 -- apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx | tail -80

Repository: cowprotocol/cowswap

Length of output: 2732


🏁 Script executed:

# Let's check if there are previous versions of this file that might clarify the history
git log --all --follow -- apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx | head -20

Repository: cowprotocol/cowswap

Length of output: 579


🏁 Script executed:

# Search for isYieldEnabled definition and what it controls
rg "isYieldEnabled" --type-list | head -5

Repository: cowprotocol/cowswap

Length of output: 452


🏁 Script executed:

# Search for isYieldEnabled definition and usage
rg "isYieldEnabled" -B 3 -A 3 | head -80

Repository: cowprotocol/cowswap

Length of output: 6286


Clarify the misleading comment about CoWAmmBanner visibility.

The comment states "no CoWAmmBanner in that case" when LP token lists are displayed, but the implementation shows the banner when isYieldEnabled is true. The codebase confirms that isYieldEnabled enables LP token lists (const lpLists = isYieldEnabled ? LP_TOKEN_LISTS : []). When LP tokens are available (isYieldEnabled = true), the banner is shown, which contradicts the comment's claim that no banner appears in that case.

Update the comment to accurately describe the behavior: either the banner should NOT show when LP tokens are displayed (and the logic needs fixing), or the comment should reflect that the banner DOES show when the yield feature is enabled.

🤖 Prompt for AI Agents
In
@apps/cowswap-frontend/src/modules/tokensList/pure/TokensVirtualList/index.tsx
around lines 104 - 107, The comment is misleading: the code sets
showCoWAmmBanner = isYieldEnabled and lpLists = isYieldEnabled ? LP_TOKEN_LISTS
: [], so the banner is shown when yield (LP tokens) is enabled; update the
comment above the showCoWAmmBanner assignment to reflect that the CoWAmmBanner
is displayed when isYieldEnabled is true (i.e., when LP token lists are
available) or, if intended behavior was to hide it for LP lists, change the
boolean logic in showCoWAmmBanner to match that intent (refer to
showCoWAmmBanner, isYieldEnabled, lpLists, and LP_TOKEN_LISTS).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants