-
Notifications
You must be signed in to change notification settings - Fork 197
feat: related bridge assets #11515
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
feat: related bridge assets #11515
Conversation
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>
📝 WalkthroughWalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
16f281a to
c11ff3f
Compare
| // 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
c11ff3f to
70fb301
Compare
**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>
70fb301 to
bc5b2e5
Compare
| // 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> = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this 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
chainIdas the React key (instead offeeAsset?.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
uniqueChainIdsand 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?.relatedAssetKeyalready evaluates tofalsefornullvalues, making the!== nullcheck 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.
📒 Files selected for processing (6)
scripts/generateAssetData/generateAssetData.tsscripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.tssrc/components/AssetSearch/components/GroupedAssetRow.tsxsrc/lib/asset-service/service/encodedAssetData.jsonsrc/lib/asset-service/service/encodedRelatedAssetIndex.jsonsrc/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 runyarn lint --fixandyarn type-checkafter making changes
Avoidletvariable assignments - preferconstwith inline IIFE switch statements or extract to functions for conditional logic
Files:
scripts/generateAssetData/generateAssetData.tssrc/components/AssetSearch/components/GroupedAssetRow.tsxscripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.tssrc/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 inuseMemoand callbacks inuseCallbackwhere 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 usinguseColorModeValuehook
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()fromreact-polyglot
UseuseFeatureFlag('FlagName')hook to access feature flag values in components
Prefertypeoverinterfacefor type definitions
Use strict typing - avoidany
UseNominaltypes for domain identifiers (e.g.,WalletId,AccountId)
Import types from@shapeshiftoss/caipfor chain/account/asset IDs
UseuseAppSelectorfor Redux state
UseuseAppDispatchfor Redux actions
Memoize expensive computations withuseMemo
Memoize callbacks withuseCallback
**/*.{ts,tsx}: UseResult<T, E>pattern for error handling in swappers and APIs; ALWAYS useOk()andErr()from@sniptt/monads; AVOID throwing within swapper API implementations
ALWAYS use custom error classes from@shapeshiftoss/errorswith meaningful error codes for internationalization and relevant details in error objects
ALWAYS wrap async op...
Files:
scripts/generateAssetData/generateAssetData.tssrc/components/AssetSearch/components/GroupedAssetRow.tsxscripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.tssrc/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
Usehandleprefix for event handlers with descriptive names in camelCase
Use descriptive boolean variable names withis,has,can,shouldprefixes
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 likedata,item,obj, and single-letter variable names except in loops
Avoid abbreviations in names unless they are widely understood
Avoid generic function names likefn,func, orcallback
Files:
scripts/generateAssetData/generateAssetData.tssrc/components/AssetSearch/components/GroupedAssetRow.tsxscripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.tssrc/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 useuseErrorToasthook for displaying errors with translated error messages and handle different error types appropriatelyUse 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 useuseMemofor expensive computations, object/array creations, and filtered data
ALWAYS useuseMemofor derived values and computed properties
ALWAYS useuseMemofor conditional values and simple transformations
ALWAYS useuseCallbackfor event handlers and functions passed as props
ALWAYS useuseCallbackfor any function that could be passed as a prop or dependency
ALWAYS include all dependencies inuseEffect,useMemo,useCallbackdependency arrays
NEVER use// eslint-disable-next-line react-hooks/exhaustive-depsunless 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 withmemofor 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
anytype
Files:
src/components/AssetSearch/components/GroupedAssetRow.tsx
src/state/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
src/state/**/*.{ts,tsx}: UsecreateDeepEqualOutputSelectorfrom@/state/selector-utilsfor deep equality checks
UsecreateCachedSelectorfromre-reselectfor 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.tssrc/components/AssetSearch/components/GroupedAssetRow.tsxscripts/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.tsscripts/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.tssrc/components/AssetSearch/components/GroupedAssetRow.tsxscripts/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.tsscripts/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.tsxscripts/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.tsxscripts/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.tsxscripts/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.tsxscripts/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.tsxscripts/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:
- Direct match for Ethereum canonicals
- Category membership check via pre-fetched mappings
- 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 <= 1correctly 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_ALLis 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
categoryToCoinIdsis 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.
9d55baf to
e25d27d
Compare
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>
e25d27d to
0ad6b67
Compare
There was a problem hiding this 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.
📒 Files selected for processing (4)
scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.tssrc/lib/asset-service/service/encodedAssetData.jsonsrc/lib/asset-service/service/encodedRelatedAssetIndex.jsonsrc/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 runyarn lint --fixandyarn type-checkafter making changes
Avoidletvariable assignments - preferconstwith 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 inuseMemoand callbacks inuseCallbackwhere 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 usinguseColorModeValuehook
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()fromreact-polyglot
UseuseFeatureFlag('FlagName')hook to access feature flag values in components
Prefertypeoverinterfacefor type definitions
Use strict typing - avoidany
UseNominaltypes for domain identifiers (e.g.,WalletId,AccountId)
Import types from@shapeshiftoss/caipfor chain/account/asset IDs
UseuseAppSelectorfor Redux state
UseuseAppDispatchfor Redux actions
Memoize expensive computations withuseMemo
Memoize callbacks withuseCallback
**/*.{ts,tsx}: UseResult<T, E>pattern for error handling in swappers and APIs; ALWAYS useOk()andErr()from@sniptt/monads; AVOID throwing within swapper API implementations
ALWAYS use custom error classes from@shapeshiftoss/errorswith 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
Usehandleprefix for event handlers with descriptive names in camelCase
Use descriptive boolean variable names withis,has,can,shouldprefixes
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 likedata,item,obj, and single-letter variable names except in loops
Avoid abbreviations in names unless they are widely understood
Avoid generic function names likefn,func, orcallback
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.
| // 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 | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.


Description
This PR:
Issue (if applicable)
Risk
Medium - risk of borked asset data
Testing
Engineering
REGEN_ALLenv var) and confirm it works fineOperations
Screenshots (if applicable)
https://jam.dev/c/51bdb4b6-2f4e-4fa2-a50c-0732f5be9c39
Summary by CodeRabbit
New Features
Bug Fixes
Chores
✏️ Tip: You can customize this high-level summary in your review settings.