Skip to content

Conversation

@gomesalexandre
Copy link
Contributor

@gomesalexandre gomesalexandre commented Dec 22, 2025

Description

This PR:

Issue (if applicable)

Risk

High Risk PRs Require 2 approvals

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

Medium - risk of borked asset data

Testing

⚠️ This does not handle WETH/ETH

  • Ensure WBTC, WETH, WSTETH show no regression against develop re: primary (i.e group naming should look sane)
  • Ensure USDT/0 are one group, but this time around, with Plasma USDT0 being part of it
  • Ensure bridged USDC/USDT/WBTC asset variants (like USDC.e) are part of group - match with coingecko lists for USDT, USDC, WBTC, DAI
  • Do a sanity check with other assets with related like LINK, FOX
  • Do another smoke test with other assets

Engineering

  • rerun generation script once beforehand (without REGEN_ALL env var) and confirm it works fine

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

Screenshots (if applicable)

https://jam.dev/c/51bdb4b6-2f4e-4fa2-a50c-0732f5be9c39

Summary by CodeRabbit

  • New Features

    • Added a regeneration switch to control full vs incremental related-asset rebuilds.
    • Pre-fetch and use bridged-token mappings for improved canonical-token resolution and more accurate asset grouping.
  • Bug Fixes

    • Fixed duplicate network icon display and stacking order for grouped assets.
    • Resolved cross-group conflicts so related-asset keys are assigned consistently and deduplicated.
  • Chores

    • Added a state migration to ensure data consistency.

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

gomesalexandre and others added 7 commits December 19, 2025 19:47
Adds symbol filtering and conflict detection to prevent USDT (Tether) and
USDT0 (Plasma Tether variant) from being incorrectly grouped together.

Root cause: Zerion API returns corrupt data - includes Arbitrum/Polygon
USDT0 contracts when querying Ethereum USDT. With cached relatedAssetIndex,
the symbol filter was bypassed via early return before it could reject
mismatched symbols.

Changes:
- Add FULL_REGEN flag to clear relatedAssetIndex cache when regenerating
- Add symbol filtering: USDT variants (symbol: "USDT", "BSC-USD") can only
  group with other USDT variants, USDT0 (symbol: "USDT0") can only group
  with other USDT0 variants
- Add first-come-first-served conflict detection to prevent assets from
  being stolen between groups
- Remove Plasma USDT0-specific workarounds (now handled by generic filter)

Result:
- USDT group: ETH USDT, Avalanche USDT, BSC-USD, Base USDT, etc. (no USDT0)
- USDT0 group: Plasma USDT0, Arbitrum USDT0, Polygon USDT0, etc. (no USDT)
- Plasma USDT0 no longer orphaned

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

📝 Walkthrough

Walkthrough

Adds REGEN_ALL env flag to control full vs incremental asset regeneration, prefetches bridged-category → coinId mappings from CoinGecko, changes related-asset grouping to prefer canonical resolution and first-come-first-served conflict detection (removing Plasma USDT0 special-casing), and deduplicates chain icons in the UI.

Changes

Cohort / File(s) Summary
Related Asset Generation Core
scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
Adds REGEN_ALL usage, BRIDGED_CATEGORY_MAPPINGS, fetchBridgedCategoryMappings(), expands getCoingeckoRelatedAssetIds(..., categoryToCoinIds), updates processRelatedAssetIds(..., categoryToCoinIds, ...) to use bridged mappings, prefer canonical assets, dedupe/unshift canonical, enforce first-come-first-served conflict detection, and remove Plasma USDT0 special-casing.
Asset Data Regeneration Control
scripts/generateAssetData/generateAssetData.ts
Reads REGEN_ALL env flag and changes preservation/reset behavior for existing relatedAssetKey values (full vs incremental regen).
Asset Generator Outputs / Index Workflow
scripts/generateAssetData/generateRelatedAssetIndex/*
Main workflow now conditionally decodes prior index based on REGEN_ALL, precomputes categoryToCoinIds, passes it through processing, and cleans up stale relatedAssetKey entries without Plasma-specific removal.
UI Related Assets Rendering
src/components/AssetSearch/components/GroupedAssetRow.tsx
Deduplicates relatedAssetIds by chainId for network icon rendering, uses selectFeeAssetByChainId(...) directly, updates keys and z-index calculations to use deduplicated chains.
State Migration Index
src/state/migrations/index.ts
Adds migration entry 255: clearAssets to clearAssetsMigrations.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Gen as Asset Generator
    participant CG as CoinGecko API
    participant Map as Bridged Mappings
    participant Proc as Related-Asset Processor
    participant Index as Related-Asset Index Store

    Gen->>CG: Request bridged-category market data
    CG-->>Map: Return category → coinId mappings

    Gen->>Proc: Start processing assets (REGEN_ALL flag, categoryToCoinIds)
    Note over Proc: For each asset
    Proc->>Proc: getCoingeckoRelatedAssetIds(assetId, assetData, categoryToCoinIds)
    Proc->>Proc: Resolve canonical via:\n1) direct canonical match\n2) category→coinId bridged mapping\n3) platform-derived canonical
    Proc->>Proc: Unshift canonical into related list & dedupe
    Proc->>Proc: Check existing group assignments (first-come-first-served)
    alt No conflict
        Proc->>Index: Assign relatedAssetKey to group members
    else Conflict detected
        Proc->>Proc: Preserve existing assignment / skip reassignment
    end
    Index-->>Gen: Persist updated related-asset index
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • NeOMakinG
  • 0xApotheosis

Poem

🐰 I hopped through CoinGecko streams,
Mapped bridged tokens to canonical dreams.
No Plasma snares to slow our race,
Groups find homes and icons embrace.
A tiny hop, a tidy fix — hooray! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: related bridge assets' accurately summarizes the main feature—detecting and grouping bridged asset variants using CoinGecko bridged-category mappings to fix canonical asset relationships.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat_bridged_grouping

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

❤️ Share

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

Comment on lines +91 to +97
// Deduplicate by chainId to show each chain only once
const uniqueChainIds = Array.from(
new Set(relatedAssetIds.map(assetId => fromAssetId(assetId).chainId)),
)

return uniqueChainIds.map((chainId, index) => {
const feeAsset = selectFeeAssetByChainId(store.getState(), chainId)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We're starting to have quite a few

Image

**Bridged Asset Canonical Detection:**
- Add BRIDGED_CATEGORY_MAPPINGS for 6 Ethereum canonicals (WBTC, WETH, WSTETH, USDC, USDT, DAI)
- Implement three-way canonical detection via CoinGecko:
  1. Check if asset IS canonical (processes ETH WBTC/USDC/USDT directly)
  2. Check if coinId in bridged category (catches Arbitrum WBTC, Polygon WBTC.E variants)
  3. Check if platforms contains canonical (catches shared coin IDs like USDC/USDT)
- Prioritize CoinGecko over Zerion when Ethereum canonical detected
- Fix length <= 1 edge case to return canonical instead of undefined
- Merge USDT0 into USDT group via bridged-usdt category

**UI Improvements:**
- Deduplicate chain icons in grouped asset rows
- Each chain now appears only once regardless of asset variant count

**Results:**
- ✅ ETH WBTC/WETH/WSTETH/USDC/USDT are primaries (clean "Wrapped Bitcoin" titles)
- ✅ All L2 bridged variants point to Ethereum canonicals
- ✅ USDT0 (Plasma) merged into USDT group as intended
- ✅ No more duplicate chain icons in search results
- ✅ No more ugly "Arbitrum Bridged WBTC" titles

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
@gomesalexandre gomesalexandre changed the title wip: related bridge assets feat: related bridge assets Dec 23, 2025
@gomesalexandre gomesalexandre changed the base branch from feat_usdt0_happy_again_plasma to develop December 23, 2025 13:34
// Category → Canonical Asset mapping for bridged tokens
// Maps CoinGecko bridged categories to their Ethereum canonical tokens
// Note: bridged-usdt includes USDT0 variants - they will be grouped together with ETH USDT as primary
const BRIDGED_CATEGORY_MAPPINGS: Record<string, AssetId> = {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could add more here and automagically handle all bridged variants for a given asset. That's the main ones I could think of but we could handle more if want to

Image

@gomesalexandre gomesalexandre marked this pull request as ready for review December 23, 2025 13:58
@gomesalexandre gomesalexandre requested a review from a team as a code owner December 23, 2025 13:58
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/components/AssetSearch/components/GroupedAssetRow.tsx (1)

90-118: Excellent improvement—deduplication addresses the previous UI concern!

The deduplication logic correctly eliminates duplicate chain icons by grouping relatedAssetIds by chainId. Using chainId as the React key (instead of feeAsset?.chainId) provides better stability, and the z-index calculation properly maintains icon stacking order.

This directly addresses the previous review feedback about having too many chain icons displayed.

Optional: Remove explanatory comment

Per coding guidelines, comments should only describe logic when explicitly requested. The variable name uniqueChainIds and the Set deduplication pattern are self-documenting.

-    // Deduplicate by chainId to show each chain only once
     const uniqueChainIds = Array.from(
       new Set(relatedAssetIds.map(assetId => fromAssetId(assetId).chainId)),
     )
scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts (1)

100-122: Consider pagination for categories with >250 coins.

The function fetches only the first page with 250 items. While this is likely sufficient for bridged token categories, consider whether any category could exceed this limit in the future. Additionally, individual category fetch failures will propagate and fail the entire generation.

🔎 Optional: Add error handling for resilience
 const fetchBridgedCategoryMappings = async (): Promise<Record<string, string[]>> => {
   const categoryToCoinIds: Record<string, string[]> = {}

   for (const category of Object.keys(BRIDGED_CATEGORY_MAPPINGS)) {
-    const { data } = await axiosInstance.get<CoinGeckoMarketCap[]>(
-      `${coingeckoBaseUrl}/coins/markets`,
-      {
-        params: {
-          category,
-          vs_currency: 'usd',
-          per_page: 250,
-          page: 1,
-        },
-      },
-    )
-
-    categoryToCoinIds[category] = data.map(coin => coin.id)
+    try {
+      const { data } = await axiosInstance.get<CoinGeckoMarketCap[]>(
+        `${coingeckoBaseUrl}/coins/markets`,
+        {
+          params: {
+            category,
+            vs_currency: 'usd',
+            per_page: 250,
+            page: 1,
+          },
+        },
+      )
+      categoryToCoinIds[category] = data.map(coin => coin.id)
+    } catch (e) {
+      console.warn(`Failed to fetch bridged category ${category}, skipping:`, e)
+      categoryToCoinIds[category] = []
+    }
   }

   return categoryToCoinIds
 }
scripts/generateAssetData/generateAssetData.ts (1)

112-118: Minor: Redundant null check on line 115.

The condition currentGeneratedAssetId?.relatedAssetKey already evaluates to false for null values, making the !== null check redundant. However, it does serve as documentation of intent per the comment on lines 109-111.

🔎 Optional: Simplify the condition
     if (
       !REGEN_ALL &&
-      currentGeneratedAssetId?.relatedAssetKey &&
-      currentGeneratedAssetId.relatedAssetKey !== null
+      currentGeneratedAssetId?.relatedAssetKey
     ) {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d970ba9 and bc5b2e5.

📒 Files selected for processing (6)
  • scripts/generateAssetData/generateAssetData.ts
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
  • src/components/AssetSearch/components/GroupedAssetRow.tsx
  • src/lib/asset-service/service/encodedAssetData.json
  • src/lib/asset-service/service/encodedRelatedAssetIndex.json
  • src/state/migrations/index.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Never assume a library is available - always check imports/package.json first
Prefer composition over inheritance
Write self-documenting code with clear variable and function names
Keep functions small and focused on a single responsibility
Avoid deep nesting - use early returns instead
Prefer procedural and easy to understand code
Never expose, log, or commit secrets, API keys, or credentials
Validate all inputs, especially user inputs
Handle errors gracefully with meaningful messages
Don't silently catch and ignore exceptions
Log errors appropriately for debugging
Provide fallback behavior when possible
Use appropriate data structures for the task
Never add code comments unless explicitly requested
When modifying code, do not add comments that reference previous implementations or explain what changed. Comments should only describe the current logic and functionality.
Use meaningful names for branches, variables, and functions
Always run yarn lint --fix and yarn type-check after making changes
Avoid let variable assignments - prefer const with inline IIFE switch statements or extract to functions for conditional logic

Files:

  • scripts/generateAssetData/generateAssetData.ts
  • src/components/AssetSearch/components/GroupedAssetRow.tsx
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
  • src/state/migrations/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Avoid useEffect where practical - use it only when necessary and following best practices
Avoid 'any' types - use specific type annotations instead
For default values with user overrides, use computed values (useMemo) instead of useEffect - pattern: userSelected ?? smartDefault ?? fallback
When function parameters are unused due to interface requirements, refactor the interface or implementation to remove them rather than prefixing with underscore
Sanitize data before displaying to prevent XSS
Memoize aggressively - wrap component variables in useMemo and callbacks in useCallback where possible
For static JSX icon elements (e.g., <TbCopy />) that don't depend on state/props, define them as constants outside the component to avoid re-renders instead of using useMemo
Account for light/dark mode using useColorModeValue hook
Account for responsive mobile designs in all UI components
When applying styles, use the existing standards and conventions of the codebase
Use Chakra UI components and conventions
All copy/text must use translation keys - never hardcode strings
Use the translation hook: useTranslate() from react-polyglot
Use useFeatureFlag('FlagName') hook to access feature flag values in components
Prefer type over interface for type definitions
Use strict typing - avoid any
Use Nominal types for domain identifiers (e.g., WalletId, AccountId)
Import types from @shapeshiftoss/caip for chain/account/asset IDs
Use useAppSelector for Redux state
Use useAppDispatch for Redux actions
Memoize expensive computations with useMemo
Memoize callbacks with useCallback

**/*.{ts,tsx}: Use Result<T, E> pattern for error handling in swappers and APIs; ALWAYS use Ok() and Err() from @sniptt/monads; AVOID throwing within swapper API implementations
ALWAYS use custom error classes from @shapeshiftoss/errors with meaningful error codes for internationalization and relevant details in error objects
ALWAYS wrap async op...

Files:

  • scripts/generateAssetData/generateAssetData.ts
  • src/components/AssetSearch/components/GroupedAssetRow.tsx
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
  • src/state/migrations/index.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/*.{js,jsx,ts,tsx}: Use camelCase for variables, functions, and methods with descriptive names that explain the purpose
Use verb prefixes for functions that perform actions (e.g., fetch, validate, execute, update, calculate)
Use UPPER_SNAKE_CASE for constants and configuration values with descriptive names
Use handle prefix for event handlers with descriptive names in camelCase
Use descriptive boolean variable names with is, has, can, should prefixes
Use named exports for components, functions, and utilities instead of default exports
Use descriptive import names and avoid renaming imports unless necessary
Avoid non-descriptive variable names like data, item, obj, and single-letter variable names except in loops
Avoid abbreviations in names unless they are widely understood
Avoid generic function names like fn, func, or callback

Files:

  • scripts/generateAssetData/generateAssetData.ts
  • src/components/AssetSearch/components/GroupedAssetRow.tsx
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
  • src/state/migrations/index.ts
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.{tsx,jsx}: ALWAYS wrap React components in error boundaries and provide user-friendly fallback components with error logging
ALWAYS use useErrorToast hook for displaying errors with translated error messages and handle different error types appropriately

Use PascalCase for React component names and match the component name to the file name

Files:

  • src/components/AssetSearch/components/GroupedAssetRow.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

**/*.{jsx,tsx}: ALWAYS use useMemo for expensive computations, object/array creations, and filtered data
ALWAYS use useMemo for derived values and computed properties
ALWAYS use useMemo for conditional values and simple transformations
ALWAYS use useCallback for event handlers and functions passed as props
ALWAYS use useCallback for any function that could be passed as a prop or dependency
ALWAYS include all dependencies in useEffect, useMemo, useCallback dependency arrays
NEVER use // eslint-disable-next-line react-hooks/exhaustive-deps unless absolutely necessary, and ALWAYS explain why dependencies are excluded if using eslint disable
ALWAYS use named exports for components; NEVER use default exports for components
KEEP component files under 200 lines when possible; BREAK DOWN large components into smaller, reusable pieces
EXTRACT complex logic into custom hooks
ALWAYS wrap components in error boundaries for production
ALWAYS handle async errors properly in async operations
ALWAYS provide user-friendly error messages in error handling
ALWAYS use virtualization for lists with 100+ items
ALWAYS implement proper key props for list items
ALWAYS lazy load heavy components using React.lazy for code splitting
ALWAYS use Suspense wrapper for lazy loaded components
USE local state for component-level state; LIFT state up when needed across multiple components; USE Context for avoiding prop drilling; USE Redux only for global state shared across multiple places
Wrap components receiving props with memo for performance optimization

Files:

  • src/components/AssetSearch/components/GroupedAssetRow.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react-best-practices.mdc)

Ensure TypeScript types are explicit and proper; avoid use of any type

Files:

  • src/components/AssetSearch/components/GroupedAssetRow.tsx
src/state/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/state/**/*.{ts,tsx}: Use createDeepEqualOutputSelector from @/state/selector-utils for deep equality checks
Use createCachedSelector from re-reselect for parameterized selectors

Files:

  • src/state/migrations/index.ts
🧠 Learnings (19)
📓 Common learnings
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10136
File: src/lib/asset-service/service/encodedRelatedAssetIndex.json:1-1
Timestamp: 2025-07-29T10:22:27.037Z
Learning: PRs with titles starting with "feat: regenerate asset data" are routine daily asset updates that don't need detailed code analysis. Users prefer to skip automated reviews for these maintenance PRs using coderabbitai ignore.
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/lib/utils/asset.ts:3-5
Timestamp: 2025-09-04T13:00:47.748Z
Learning: In shapeshift/web, primary assets are assets that represent a group of related assets across multiple chains. An asset with undefined relatedAssetKey doesn't participate in grouping and is not considered primary. Only assets with null relatedAssetKey or where relatedAssetKey === assetId are considered primary assets.
Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Branch naming: Use descriptive names (e.g., `feat_gridplus`, `fix_wallet_connect`)
Learnt from: 0xApotheosis
Repo: shapeshift/web PR: 10290
File: scripts/generateAssetData/color-map.json:41-47
Timestamp: 2025-08-17T21:53:03.806Z
Learning: In the ShapeShift web codebase, native assets (using CAIP-19 slip44 namespace like eip155:1/slip44:60, bip122:.../slip44:..., cosmos:.../slip44:...) are manually hardcoded and not generated via the automated asset generation script. Only ERC20/BEP20 tokens go through the asset generation process. The validation scripts should only validate generated assets, not manually added native assets.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/config.ts:127-128
Timestamp: 2025-08-07T11:20:44.614Z
Learning: gomesalexandre prefers required environment variables without default values in the config file (src/config.ts). They want explicit configuration and fail-fast behavior when environment variables are missing, rather than having fallback defaults.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/ContractInteractionBreakdown.tsx:0-0
Timestamp: 2025-09-13T16:45:18.813Z
Learning: gomesalexandre prefers aggressively deleting unused/obsolete code files ("ramboing") rather than fixing technical issues in code that won't be used, demonstrating his preference for keeping codebases clean and PR scope focused.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/types.ts:7-7
Timestamp: 2025-09-10T15:34:29.604Z
Learning: gomesalexandre is comfortable relying on transitive dependencies (like abitype through ethers/viem) rather than explicitly declaring them in package.json, preferring to avoid package.json bloat when the transitive dependency approach works reliably in practice.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10503
File: .env:56-56
Timestamp: 2025-09-16T13:17:02.938Z
Learning: gomesalexandre prefers to enable feature flags globally in the base .env file when the intent is to activate features everywhere, even when there are known issues like crashes, demonstrating his preference for intentional global feature rollouts over cautious per-environment enablement.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10249
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:447-503
Timestamp: 2025-08-13T17:07:10.763Z
Learning: gomesalexandre prefers relying on TypeScript's type system for validation rather than adding defensive runtime null checks when types are properly defined. They favor a TypeScript-first approach over defensive programming with runtime validations.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/hooks/useActionCenterSubscribers/useThorchainLpDepositActionSubscriber.tsx:61-66
Timestamp: 2025-08-14T17:51:47.556Z
Learning: gomesalexandre is not concerned about structured logging and prefers to keep console.error usage as-is rather than implementing structured logging patterns, even when project guidelines suggest otherwise.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10413
File: src/components/Modals/FiatRamps/fiatRampProviders/onramper/utils.ts:29-55
Timestamp: 2025-09-02T14:26:19.028Z
Learning: gomesalexandre prefers to keep preparatory/reference code simple until it's actively consumed, rather than implementing comprehensive error handling, validation, and robustness improvements upfront. They prefer to add these improvements when the code is actually being used in production.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:396-402
Timestamp: 2025-08-14T17:55:57.490Z
Learning: gomesalexandre is comfortable with functions/variables that return undefined or true (tri-state) when only the truthy case matters, preferring to rely on JavaScript's truthy/falsy behavior rather than explicitly returning boolean values.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10783
File: src/context/ModalStackProvider/useModalRegistration.ts:30-41
Timestamp: 2025-10-16T11:14:40.657Z
Learning: gomesalexandre prefers to add lint rules (like typescript-eslint/strict-boolean-expressions for truthiness checks on numbers) to catch common issues project-wide rather than relying on code review to catch them.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/lib/moralis.ts:47-85
Timestamp: 2025-08-07T11:22:16.983Z
Learning: gomesalexandre prefers console.error over structured logging for Moralis API integration debugging, as they find it more conventional and prefer to examine XHR requests directly rather than rely on structured logs for troubleshooting.
📚 Learning: 2025-09-04T17:35:57.584Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/state/slices/common-selectors.ts:164-178
Timestamp: 2025-09-04T17:35:57.584Z
Learning: In shapeshift/web, while the Asset type allows relatedAssetKey to be AssetId | null | undefined, the actual asset generation and processing system ensures relatedAssetKey is never undefined in practice. All asset creation utilities explicitly set it to either a specific AssetId (for grouped assets) or null (for primary/unique assets). The only undefined assignments occur during intermediate processing phases and get resolved during the final asset generation step.

Applied to files:

  • scripts/generateAssetData/generateAssetData.ts
  • src/components/AssetSearch/components/GroupedAssetRow.tsx
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-04T17:35:57.584Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/state/slices/common-selectors.ts:164-178
Timestamp: 2025-09-04T17:35:57.584Z
Learning: In shapeshift/web, while the Asset type allows relatedAssetKey to be AssetId | null | undefined, the actual asset generation and processing system ensures relatedAssetKey is never undefined in practice. All asset creation utilities explicitly set it to either a specific AssetId (for grouped assets) or null (for primary/unique assets). The only exception is temporary undefined assignment during portal asset processing, which gets resolved later in the pipeline.

Applied to files:

  • scripts/generateAssetData/generateAssetData.ts
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-04T13:22:35.399Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/state/slices/assetsSlice/selectors.ts:45-51
Timestamp: 2025-09-04T13:22:35.399Z
Learning: In shapeshift/web, the isPrimaryAsset function in src/lib/utils/asset.ts is intentionally designed to only treat null relatedAssetKey as primary (not undefined). The asset generation process specifically sets relatedAssetKey to null for primary assets, while undefined means the asset doesn't participate in grouping and should not be considered primary. Only assets with null relatedAssetKey or where relatedAssetKey === assetId are primary assets.

Applied to files:

  • scripts/generateAssetData/generateAssetData.ts
  • src/components/AssetSearch/components/GroupedAssetRow.tsx
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-07-29T10:22:27.037Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10136
File: src/lib/asset-service/service/encodedRelatedAssetIndex.json:1-1
Timestamp: 2025-07-29T10:22:27.037Z
Learning: PRs with titles starting with "feat: regenerate asset data" are routine daily asset updates that don't need detailed code analysis. Users prefer to skip automated reviews for these maintenance PRs using coderabbitai ignore.

Applied to files:

  • scripts/generateAssetData/generateAssetData.ts
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-08T15:53:09.362Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10442
File: src/components/TradeAssetSearch/components/GroupedAssetList/GroupedAssetList.tsx:34-35
Timestamp: 2025-09-08T15:53:09.362Z
Learning: In DefaultAssetList.tsx, the GroupedAssetList component already receives the activeChainId prop correctly on line ~58, contrary to automated analysis that may flag it as missing.

Applied to files:

  • src/components/AssetSearch/components/GroupedAssetRow.tsx
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-04T17:29:59.479Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx:28-33
Timestamp: 2025-09-04T17:29:59.479Z
Learning: In shapeshift/web, the useGetPopularAssetsQuery function in src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx intentionally uses primaryAssets[assetId] instead of falling back to assets[assetId]. The design distributes primary assets across chains by iterating through their related assets and adding the primary asset to each related asset's chain. This ensures primary assets appear in all chains where they have related assets, supporting the grouped asset system.

Applied to files:

  • src/components/AssetSearch/components/GroupedAssetRow.tsx
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/*.ts : Implement filterAssetIdsBySellable method to filter assets by supported chain IDs in the sell property

Applied to files:

  • src/components/AssetSearch/components/GroupedAssetRow.tsx
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-08-08T15:00:49.887Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.

Applied to files:

  • src/components/AssetSearch/components/GroupedAssetRow.tsx
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/*.ts : Implement filterBuyAssetsBySellAssetId method to filter assets by supported chain IDs in the buy property

Applied to files:

  • src/components/AssetSearch/components/GroupedAssetRow.tsx
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-02T12:37:37.608Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/components/AssetSearch/components/AssetList.tsx:0-0
Timestamp: 2025-09-02T12:37:37.608Z
Learning: NeOMakinG in project shapeshift/web prefers solutions that preserve sorting order when implementing asset grouping/deduplication functionality, avoiding approaches like lodash groupBy that would reorganize the array structure.

Applied to files:

  • src/components/AssetSearch/components/GroupedAssetRow.tsx
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-11-24T21:20:04.979Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to **/*.{ts,tsx} : Import types from `shapeshiftoss/caip` for chain/account/asset IDs

Applied to files:

  • src/components/AssetSearch/components/GroupedAssetRow.tsx
📚 Learning: 2025-08-14T17:54:32.563Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx:97-108
Timestamp: 2025-08-14T17:54:32.563Z
Learning: In ReusableLpStatus component (src/pages/ThorChainLP/components/ReusableLpStatus/ReusableLpStatus.tsx), the txAssets dependency is stable from first render because poolAsset, baseAsset, actionSide, and action are all defined first render, making the current txAssetsStatuses initialization pattern safe without needing useEffect synchronization.

Applied to files:

  • src/components/AssetSearch/components/GroupedAssetRow.tsx
📚 Learning: 2025-08-17T21:53:03.806Z
Learnt from: 0xApotheosis
Repo: shapeshift/web PR: 10290
File: scripts/generateAssetData/color-map.json:41-47
Timestamp: 2025-08-17T21:53:03.806Z
Learning: In the ShapeShift web codebase, native assets (using CAIP-19 slip44 namespace like eip155:1/slip44:60, bip122:.../slip44:..., cosmos:.../slip44:...) are manually hardcoded and not generated via the automated asset generation script. Only ERC20/BEP20 tokens go through the asset generation process. The validation scripts should only validate generated assets, not manually added native assets.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-10-15T15:57:39.956Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10810
File: src/plugins/walletConnectToDapps/utils/tenderly/index.ts:212-0
Timestamp: 2025-10-15T15:57:39.956Z
Learning: gomesalexandre uses discriminated union patterns (e.g., `isEIP1559 ? { max_fee_per_gas, max_priority_fee_per_gas } : { gas_price }`) in WalletConnect flows without additional validation guards, trusting that the runtime data structure ensures mutual exclusivity between EIP-1559 and legacy gas pricing fields.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-04T17:27:33.328Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/components/TradeAssetSearch/components/SearchTermAssetList.tsx:88-94
Timestamp: 2025-09-04T17:27:33.328Z
Learning: In shapeshift/web, NFTs never have related assets in the grouping system. The related assets feature is designed for fungible tokens that exist across multiple chains (like USDC on different chains), not for NFTs which are unique. Therefore, checking isNft() for related asset IDs is unnecessary since NFT asset IDs won't be present in the relatedAssetIdsById mapping.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-04T13:00:47.748Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/lib/utils/asset.ts:3-5
Timestamp: 2025-09-04T13:00:47.748Z
Learning: In shapeshift/web, primary assets are assets that represent a group of related assets across multiple chains. An asset with undefined relatedAssetKey doesn't participate in grouping and is not considered primary. Only assets with null relatedAssetKey or where relatedAssetKey === assetId are considered primary assets.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-08-27T13:49:48.668Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10375
File: src/state/migrations/index.ts:214-215
Timestamp: 2025-08-27T13:49:48.668Z
Learning: The clearAssetsMigrations system in the ShapeShift web app works independently of the root persistConfig version. Migration numbers (like 171) don't need to match the root persistConfig version field, and the system has been functioning correctly for months/years without manual version bumps.

Applied to files:

  • src/state/migrations/index.ts
📚 Learning: 2025-11-24T21:20:04.979Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to src/state/slices/**/*.ts : Migrations are required when changing persisted state structure (see `src/state/migrations/`)

Applied to files:

  • src/state/migrations/index.ts
🧬 Code graph analysis (3)
src/components/AssetSearch/components/GroupedAssetRow.tsx (3)
packages/caip/src/assetId/assetId.ts (1)
  • fromAssetId (140-172)
src/state/slices/assetsSlice/selectors.ts (1)
  • selectFeeAssetByChainId (79-83)
src/state/store.ts (1)
  • store (138-138)
scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts (6)
packages/caip/src/constants.ts (7)
  • ethAssetId (10-10)
  • optimismAssetId (12-12)
  • arbitrumAssetId (16-16)
  • arbitrumNovaAssetId (17-17)
  • baseAssetId (18-18)
  • foxAssetId (38-38)
  • foxOnArbitrumOneAssetId (36-37)
packages/caip/src/adapters/coingecko/index.ts (1)
  • coingeckoBaseUrl (57-57)
scripts/generateAssetData/generateRelatedAssetIndex/mapping.ts (1)
  • coingeckoPlatformDetailsToMaybeAssetId (19-29)
src/lib/utils/index.ts (1)
  • isSome (110-111)
packages/utils/src/index.ts (1)
  • isSome (25-26)
packages/utils/src/assetData/decodeRelatedAssetIndex.ts (1)
  • decodeRelatedAssetIndex (5-22)
src/state/migrations/index.ts (1)
src/state/migrations/clearAssets.ts (1)
  • clearAssets (6-8)
🔇 Additional comments (10)
src/state/migrations/index.ts (1)

302-303: Clarify the need for two consecutive clearAssets migrations.

Both migrations 255 and 256 perform identical operations (clearing assets state). While clearing assets makes sense given the structural changes to asset grouping and bridged detection in this PR, having two consecutive identical migrations is unusual.

Is there a specific reason for adding two migrations instead of one (e.g., separate deployment phases, addressing distinct schema changes from PR #11510 vs. this PR's bridged detection, or ensuring multiple test environments are cleared)?

scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts (8)

52-53: LGTM - REGEN_ALL flag for full rebuild control.

The environment variable approach provides a clean mechanism to switch between incremental updates and full regeneration, aligning with the PR objectives for controlled asset data regeneration.


59-69: LGTM - Bridged category mappings provide clear canonical sources.

The mapping structure correctly associates CoinGecko bridged categories with their Ethereum canonical tokens. The inline comment on line 61 helpfully documents that USDT0 variants will be grouped with ETH USDT.


212-249: Well-structured three-way canonical detection.

The canonical resolution logic is clear and well-documented:

  1. Direct match for Ethereum canonicals
  2. Category membership check via pre-fetched mappings
  3. Platform list check for shared coin IDs

The unshift/deduplicate pattern correctly ensures the canonical becomes the primary.


250-256: Good edge case handling for lone canonicals.

Returning the canonical even when allRelatedAssetIds.length <= 1 correctly fixes the Zerion override issue mentioned in the comment, ensuring canonical assets maintain their primary status regardless of Zerion data.


371-389: Solid first-come-first-served conflict detection.

The conflict detection correctly prevents cross-group contamination by filtering out assets already claimed by a different group. The detailed warning message with context about data quality issues will aid debugging.


337-350: CoinGecko canonical prioritization logic is correct.

The conditional structure properly prioritizes CoinGecko's canonical detection over Zerion when an Ethereum canonical is detected, while falling back to Zerion otherwise. This addresses the PR objective of treating Ethereum as canonical for bridged assets.


433-436: LGTM - Conditional index initialization for full rebuild.

Starting with an empty object when REGEN_ALL is true correctly enables a clean slate for full regeneration while preserving the existing index for incremental updates.


460-461: Correct integration of bridged category pre-fetch.

The categoryToCoinIds is fetched before the processing loop and passed through correctly, ensuring all assets have access to the same canonical mapping data.

Also applies to: 474-480

scripts/generateAssetData/generateAssetData.ts (1)

48-50: LGTM - Clear REGEN_ALL documentation.

The inline comment helpfully documents the usage pattern for regenerating all related asset keys.

Implements robust Ethereum canonical detection for bridged assets (WBTC, WETH, WSTETH, USDC, USDT, DAI).

- Add BRIDGED_CATEGORY_MAPPINGS for 6 Ethereum canonicals
- Implement three-way canonical detection via CoinGecko
- Prioritize CoinGecko over Zerion when Ethereum canonical detected
- Fix length <= 1 edge case to return canonical instead of undefined
- Merge USDT0 into USDT group via bridged-usdt category
- Deduplicate chain icons in grouped asset rows

Results:
- ETH WBTC/WETH/WSTETH/USDC/USDT are primaries (clean titles)
- All L2 bridged variants point to Ethereum canonicals
- USDT0 merged into USDT group
- No duplicate chain icons

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
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

♻️ Duplicate comments (1)
scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts (1)

59-69: BRIDGED_CATEGORY_MAPPINGS covers the major bridged tokens.

The mapping is clear and covers the main bridged assets. Extension to additional bridged tokens can be done as needed.

🧹 Nitpick comments (1)
scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts (1)

212-248: Consider extracting canonical detection to a helper function.

The three-way canonical detection logic (checking if asset is Ethereum canonical, in bridged category, or has canonical in platforms) spans ~37 lines and has multiple concerns. Extracting it would improve readability and make the logic easier to test independently.

🔎 Proposed refactor

Add a helper function before getCoingeckoRelatedAssetIds:

const detectBridgedCanonical = (
  assetId: AssetId,
  coinId: string,
  platformAssetIds: AssetId[],
  categoryToCoinIds: Record<string, string[]>,
): AssetId | undefined => {
  const ethereumCanonicals = Object.values(BRIDGED_CATEGORY_MAPPINGS)
  
  // 1. Check if THIS asset is an Ethereum canonical
  if (ethereumCanonicals.includes(assetId)) {
    return assetId
  }
  
  // 2. Check if this coin is in a bridged category
  for (const [category, coinIds] of Object.entries(categoryToCoinIds)) {
    if (coinIds.includes(coinId)) {
      return BRIDGED_CATEGORY_MAPPINGS[category]
    }
  }
  
  // 3. Check if platforms list contains an Ethereum canonical
  for (const canonical of ethereumCanonicals) {
    if (platformAssetIds.includes(canonical)) {
      return canonical
    }
  }
  
  return undefined
}

Then simplify the main function:

   let allRelatedAssetIds = Object.entries(platforms)
     ?.map(coingeckoPlatformDetailsToMaybeAssetId)
     .filter(isSome)
     .filter(relatedAssetId => assetData[relatedAssetId] !== undefined)
 
-  // Determine canonical asset in THREE ways:
-  let bridgedCanonical: AssetId | undefined
-  
-  // 1. Check if THIS asset is an Ethereum canonical (e.g., processing ETH USDT itself)
-  const ethereumCanonicals = Object.values(BRIDGED_CATEGORY_MAPPINGS)
-  if (ethereumCanonicals.includes(assetId)) {
-    bridgedCanonical = assetId
-  }
-  
-  // 2. Check if this coin is in a bridged category (catches bridged variants with unique coin IDs)
-  if (!bridgedCanonical) {
-    for (const [category, coinIds] of Object.entries(categoryToCoinIds)) {
-      if (coinIds.includes(coinId)) {
-        bridgedCanonical = BRIDGED_CATEGORY_MAPPINGS[category]
-        break
-      }
-    }
-  }
-  
-  // 3. Check if platforms list contains an Ethereum canonical (catches shared coin IDs like USDC/USDT)
-  // CoinGecko uses the same coin ID for native USDC/USDT across multiple chains
-  if (!bridgedCanonical) {
-    for (const canonical of ethereumCanonicals) {
-      if (allRelatedAssetIds.includes(canonical)) {
-        bridgedCanonical = canonical
-        break
-      }
-    }
-  }
+  const bridgedCanonical = detectBridgedCanonical(
+    assetId,
+    coinId,
+    allRelatedAssetIds,
+    categoryToCoinIds,
+  )
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9d55baf and ab4fab0.

📒 Files selected for processing (4)
  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
  • src/lib/asset-service/service/encodedAssetData.json
  • src/lib/asset-service/service/encodedRelatedAssetIndex.json
  • src/state/migrations/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/state/migrations/index.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Never assume a library is available - always check imports/package.json first
Prefer composition over inheritance
Write self-documenting code with clear variable and function names
Keep functions small and focused on a single responsibility
Avoid deep nesting - use early returns instead
Prefer procedural and easy to understand code
Never expose, log, or commit secrets, API keys, or credentials
Validate all inputs, especially user inputs
Handle errors gracefully with meaningful messages
Don't silently catch and ignore exceptions
Log errors appropriately for debugging
Provide fallback behavior when possible
Use appropriate data structures for the task
Never add code comments unless explicitly requested
When modifying code, do not add comments that reference previous implementations or explain what changed. Comments should only describe the current logic and functionality.
Use meaningful names for branches, variables, and functions
Always run yarn lint --fix and yarn type-check after making changes
Avoid let variable assignments - prefer const with inline IIFE switch statements or extract to functions for conditional logic

Files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Avoid useEffect where practical - use it only when necessary and following best practices
Avoid 'any' types - use specific type annotations instead
For default values with user overrides, use computed values (useMemo) instead of useEffect - pattern: userSelected ?? smartDefault ?? fallback
When function parameters are unused due to interface requirements, refactor the interface or implementation to remove them rather than prefixing with underscore
Sanitize data before displaying to prevent XSS
Memoize aggressively - wrap component variables in useMemo and callbacks in useCallback where possible
For static JSX icon elements (e.g., <TbCopy />) that don't depend on state/props, define them as constants outside the component to avoid re-renders instead of using useMemo
Account for light/dark mode using useColorModeValue hook
Account for responsive mobile designs in all UI components
When applying styles, use the existing standards and conventions of the codebase
Use Chakra UI components and conventions
All copy/text must use translation keys - never hardcode strings
Use the translation hook: useTranslate() from react-polyglot
Use useFeatureFlag('FlagName') hook to access feature flag values in components
Prefer type over interface for type definitions
Use strict typing - avoid any
Use Nominal types for domain identifiers (e.g., WalletId, AccountId)
Import types from @shapeshiftoss/caip for chain/account/asset IDs
Use useAppSelector for Redux state
Use useAppDispatch for Redux actions
Memoize expensive computations with useMemo
Memoize callbacks with useCallback

**/*.{ts,tsx}: Use Result<T, E> pattern for error handling in swappers and APIs; ALWAYS use Ok() and Err() from @sniptt/monads; AVOID throwing within swapper API implementations
ALWAYS use custom error classes from @shapeshiftoss/errors with meaningful error codes for internationalization and relevant details in error objects
ALWAYS wrap async op...

Files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/*.{js,jsx,ts,tsx}: Use camelCase for variables, functions, and methods with descriptive names that explain the purpose
Use verb prefixes for functions that perform actions (e.g., fetch, validate, execute, update, calculate)
Use UPPER_SNAKE_CASE for constants and configuration values with descriptive names
Use handle prefix for event handlers with descriptive names in camelCase
Use descriptive boolean variable names with is, has, can, should prefixes
Use named exports for components, functions, and utilities instead of default exports
Use descriptive import names and avoid renaming imports unless necessary
Avoid non-descriptive variable names like data, item, obj, and single-letter variable names except in loops
Avoid abbreviations in names unless they are widely understood
Avoid generic function names like fn, func, or callback

Files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
🧠 Learnings (13)
📓 Common learnings
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10136
File: src/lib/asset-service/service/encodedRelatedAssetIndex.json:1-1
Timestamp: 2025-07-29T10:22:27.037Z
Learning: PRs with titles starting with "feat: regenerate asset data" are routine daily asset updates that don't need detailed code analysis. Users prefer to skip automated reviews for these maintenance PRs using coderabbitai ignore.
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/lib/utils/asset.ts:3-5
Timestamp: 2025-09-04T13:00:47.748Z
Learning: In shapeshift/web, primary assets are assets that represent a group of related assets across multiple chains. An asset with undefined relatedAssetKey doesn't participate in grouping and is not considered primary. Only assets with null relatedAssetKey or where relatedAssetKey === assetId are considered primary assets.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/config.ts:127-128
Timestamp: 2025-08-07T11:20:44.614Z
Learning: gomesalexandre prefers required environment variables without default values in the config file (src/config.ts). They want explicit configuration and fail-fast behavior when environment variables are missing, rather than having fallback defaults.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/ContractInteractionBreakdown.tsx:0-0
Timestamp: 2025-09-13T16:45:18.813Z
Learning: gomesalexandre prefers aggressively deleting unused/obsolete code files ("ramboing") rather than fixing technical issues in code that won't be used, demonstrating his preference for keeping codebases clean and PR scope focused.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/types.ts:7-7
Timestamp: 2025-09-10T15:34:29.604Z
Learning: gomesalexandre is comfortable relying on transitive dependencies (like abitype through ethers/viem) rather than explicitly declaring them in package.json, preferring to avoid package.json bloat when the transitive dependency approach works reliably in practice.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10503
File: .env:56-56
Timestamp: 2025-09-16T13:17:02.938Z
Learning: gomesalexandre prefers to enable feature flags globally in the base .env file when the intent is to activate features everywhere, even when there are known issues like crashes, demonstrating his preference for intentional global feature rollouts over cautious per-environment enablement.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10249
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:447-503
Timestamp: 2025-08-13T17:07:10.763Z
Learning: gomesalexandre prefers relying on TypeScript's type system for validation rather than adding defensive runtime null checks when types are properly defined. They favor a TypeScript-first approach over defensive programming with runtime validations.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/hooks/useActionCenterSubscribers/useThorchainLpDepositActionSubscriber.tsx:61-66
Timestamp: 2025-08-14T17:51:47.556Z
Learning: gomesalexandre is not concerned about structured logging and prefers to keep console.error usage as-is rather than implementing structured logging patterns, even when project guidelines suggest otherwise.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10413
File: src/components/Modals/FiatRamps/fiatRampProviders/onramper/utils.ts:29-55
Timestamp: 2025-09-02T14:26:19.028Z
Learning: gomesalexandre prefers to keep preparatory/reference code simple until it's actively consumed, rather than implementing comprehensive error handling, validation, and robustness improvements upfront. They prefer to add these improvements when the code is actually being used in production.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10276
File: src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx:396-402
Timestamp: 2025-08-14T17:55:57.490Z
Learning: gomesalexandre is comfortable with functions/variables that return undefined or true (tri-state) when only the truthy case matters, preferring to rely on JavaScript's truthy/falsy behavior rather than explicitly returning boolean values.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10783
File: src/context/ModalStackProvider/useModalRegistration.ts:30-41
Timestamp: 2025-10-16T11:14:40.657Z
Learning: gomesalexandre prefers to add lint rules (like typescript-eslint/strict-boolean-expressions for truthiness checks on numbers) to catch common issues project-wide rather than relying on code review to catch them.
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10206
File: src/lib/moralis.ts:47-85
Timestamp: 2025-08-07T11:22:16.983Z
Learning: gomesalexandre prefers console.error over structured logging for Moralis API integration debugging, as they find it more conventional and prefer to examine XHR requests directly rather than rely on structured logs for troubleshooting.
📚 Learning: 2025-08-17T21:53:03.806Z
Learnt from: 0xApotheosis
Repo: shapeshift/web PR: 10290
File: scripts/generateAssetData/color-map.json:41-47
Timestamp: 2025-08-17T21:53:03.806Z
Learning: In the ShapeShift web codebase, native assets (using CAIP-19 slip44 namespace like eip155:1/slip44:60, bip122:.../slip44:..., cosmos:.../slip44:...) are manually hardcoded and not generated via the automated asset generation script. Only ERC20/BEP20 tokens go through the asset generation process. The validation scripts should only validate generated assets, not manually added native assets.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-04T17:35:57.584Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/state/slices/common-selectors.ts:164-178
Timestamp: 2025-09-04T17:35:57.584Z
Learning: In shapeshift/web, while the Asset type allows relatedAssetKey to be AssetId | null | undefined, the actual asset generation and processing system ensures relatedAssetKey is never undefined in practice. All asset creation utilities explicitly set it to either a specific AssetId (for grouped assets) or null (for primary/unique assets). The only undefined assignments occur during intermediate processing phases and get resolved during the final asset generation step.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-04T13:22:35.399Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/state/slices/assetsSlice/selectors.ts:45-51
Timestamp: 2025-09-04T13:22:35.399Z
Learning: In shapeshift/web, the isPrimaryAsset function in src/lib/utils/asset.ts is intentionally designed to only treat null relatedAssetKey as primary (not undefined). The asset generation process specifically sets relatedAssetKey to null for primary assets, while undefined means the asset doesn't participate in grouping and should not be considered primary. Only assets with null relatedAssetKey or where relatedAssetKey === assetId are primary assets.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-04T17:35:57.584Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/state/slices/common-selectors.ts:164-178
Timestamp: 2025-09-04T17:35:57.584Z
Learning: In shapeshift/web, while the Asset type allows relatedAssetKey to be AssetId | null | undefined, the actual asset generation and processing system ensures relatedAssetKey is never undefined in practice. All asset creation utilities explicitly set it to either a specific AssetId (for grouped assets) or null (for primary/unique assets). The only exception is temporary undefined assignment during portal asset processing, which gets resolved later in the pipeline.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-07-29T10:22:27.037Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10136
File: src/lib/asset-service/service/encodedRelatedAssetIndex.json:1-1
Timestamp: 2025-07-29T10:22:27.037Z
Learning: PRs with titles starting with "feat: regenerate asset data" are routine daily asset updates that don't need detailed code analysis. Users prefer to skip automated reviews for these maintenance PRs using coderabbitai ignore.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-08T15:53:09.362Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10442
File: src/components/TradeAssetSearch/components/GroupedAssetList/GroupedAssetList.tsx:34-35
Timestamp: 2025-09-08T15:53:09.362Z
Learning: In DefaultAssetList.tsx, the GroupedAssetList component already receives the activeChainId prop correctly on line ~58, contrary to automated analysis that may flag it as missing.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-04T17:29:59.479Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx:28-33
Timestamp: 2025-09-04T17:29:59.479Z
Learning: In shapeshift/web, the useGetPopularAssetsQuery function in src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx intentionally uses primaryAssets[assetId] instead of falling back to assets[assetId]. The design distributes primary assets across chains by iterating through their related assets and adding the primary asset to each related asset's chain. This ensures primary assets appear in all chains where they have related assets, supporting the grouped asset system.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-10-15T15:57:39.956Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10810
File: src/plugins/walletConnectToDapps/utils/tenderly/index.ts:212-0
Timestamp: 2025-10-15T15:57:39.956Z
Learning: gomesalexandre uses discriminated union patterns (e.g., `isEIP1559 ? { max_fee_per_gas, max_priority_fee_per_gas } : { gas_price }`) in WalletConnect flows without additional validation guards, trusting that the runtime data structure ensures mutual exclusivity between EIP-1559 and legacy gas pricing fields.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-04T17:27:33.328Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/components/TradeAssetSearch/components/SearchTermAssetList.tsx:88-94
Timestamp: 2025-09-04T17:27:33.328Z
Learning: In shapeshift/web, NFTs never have related assets in the grouping system. The related assets feature is designed for fungible tokens that exist across multiple chains (like USDC on different chains), not for NFTs which are unique. Therefore, checking isNft() for related asset IDs is unnecessary since NFT asset IDs won't be present in the relatedAssetIdsById mapping.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/*.ts : Implement filterAssetIdsBySellable method to filter assets by supported chain IDs in the sell property

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-04T13:00:47.748Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/lib/utils/asset.ts:3-5
Timestamp: 2025-09-04T13:00:47.748Z
Learning: In shapeshift/web, primary assets are assets that represent a group of related assets across multiple chains. An asset with undefined relatedAssetKey doesn't participate in grouping and is not considered primary. Only assets with null relatedAssetKey or where relatedAssetKey === assetId are considered primary assets.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
📚 Learning: 2025-09-02T12:37:37.608Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/components/AssetSearch/components/AssetList.tsx:0-0
Timestamp: 2025-09-02T12:37:37.608Z
Learning: NeOMakinG in project shapeshift/web prefers solutions that preserve sorting order when implementing asset grouping/deduplication functionality, avoiding approaches like lodash groupBy that would reorganize the array structure.

Applied to files:

  • scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
⏰ 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). (1)
  • GitHub Check: Call / Static
🔇 Additional comments (7)
scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts (7)

52-52: LGTM!

The REGEN_ALL flag is properly sourced from the environment and defaults to false when not set, providing safe incremental behavior by default.


242-248: Good fix for canonical prioritization.

Unshifting the canonical asset to the front and deduplicating ensures it becomes the primary (relatedAssetKey), fixing the issue where non-canonical assets could become primaries based on processing order.


250-256: Good fix for solo canonical assets.

Returning the canonical even when no other related assets are found prevents Zerion from overriding the canonical assignment for assets like WBTC/WETH/WSTETH that are detected via bridged categories.


337-349: Good prioritization of CoinGecko-detected canonicals.

The logic correctly prioritizes CoinGecko when it detects an Ethereum canonical via the three-way check, preventing Zerion from overriding the canonical assignment. This aligns with the PR objective to ensure CoinGecko data is not overridden by Zerion.


433-435: REGEN_ALL integration looks good.

The conditional logic correctly starts with an empty relatedAssetIndex when REGEN_ALL is true, enabling full rebuilds, while preserving existing data for incremental updates.


460-460: Good pre-fetching of bridged category mappings.

Pre-fetching the category mappings once before processing all assets is efficient and avoids redundant API calls during asset processing.


371-389: Conflict detection logic is sound, but test verification is optional.

The first-come-first-served conflict detection correctly prevents assets from being reassigned to different groups. The logic properly handles edge cases: assets with no existing group are included, assets already in the current group remain (idempotent), and conflicts are rejected with clear warnings. The implementation follows proper filtering semantics and explains the data quality issue clearly.

The suggested test command (yarn generate-asset-data) is reasonable for observing real conflicts in practice, but it's optional—the code logic itself validates correctly through static analysis. If you want to verify warnings are produced, ensure test data includes known multi-group assets from CoinGecko/Zerion or add unit tests with mock asset data.

Comment on lines +100 to +122
// Pre-fetch bridged category mappings
// Returns mapping of category → array of coin IDs in that category
const fetchBridgedCategoryMappings = async (): Promise<Record<string, string[]>> => {
const categoryToCoinIds: Record<string, string[]> = {}

for (const category of Object.keys(BRIDGED_CATEGORY_MAPPINGS)) {
const { data } = await axiosInstance.get<CoinGeckoMarketCap[]>(
`${coingeckoBaseUrl}/coins/markets`,
{
params: {
category,
vs_currency: 'usd',
per_page: 250,
page: 1,
},
},
)

categoryToCoinIds[category] = data.map(coin => coin.id)
}

return categoryToCoinIds
}
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 | 🔴 Critical

Add error handling for API calls.

The function lacks error handling for CoinGecko API requests. If any category fetch fails, the entire asset generation script will crash. While axiosRetry handles transient failures, permanent errors (e.g., rate limits, invalid category names) will still propagate.

🔎 Proposed fix with error handling
 const fetchBridgedCategoryMappings = async (): Promise<Record<string, string[]>> => {
   const categoryToCoinIds: Record<string, string[]> = {}
 
   for (const category of Object.keys(BRIDGED_CATEGORY_MAPPINGS)) {
-    const { data } = await axiosInstance.get<CoinGeckoMarketCap[]>(
-      `${coingeckoBaseUrl}/coins/markets`,
-      {
-        params: {
-          category,
-          vs_currency: 'usd',
-          per_page: 250,
-          page: 1,
-        },
-      },
-    )
-
-    categoryToCoinIds[category] = data.map(coin => coin.id)
+    try {
+      const { data } = await axiosInstance.get<CoinGeckoMarketCap[]>(
+        `${coingeckoBaseUrl}/coins/markets`,
+        {
+          params: {
+            category,
+            vs_currency: 'usd',
+            per_page: 250,
+            page: 1,
+          },
+        },
+      )
+      categoryToCoinIds[category] = data.map(coin => coin.id)
+    } catch (error) {
+      console.error(`Failed to fetch bridged category ${category}:`, error)
+      categoryToCoinIds[category] = []
+    }
   }
 
   return categoryToCoinIds
 }
🤖 Prompt for AI Agents
In
scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts
around lines 100 to 122, the CoinGecko API calls have no error handling so a
single failed category request will crash the whole script; wrap the
axiosInstance.get call in a try/catch, and on error log a clear warning
including the category and error message, assign an empty array (or skip
assigning) for that category in categoryToCoinIds, and continue processing
remaining categories so permanent failures for one category do not abort the
entire run.

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.

Link bridged assets to canonical Spike: Can we add Plasma USDT0 under USDT0 related assets

2 participants