Skip to content

Conversation

@NeOMakinG
Copy link
Collaborator

@NeOMakinG NeOMakinG commented Jan 18, 2026

Description

First attempt at having generic evm chains support, main things are:

  • Remove hdwallet needed update
  • Remove the need for a new chainadapter

Having a ChainAdapter that would handle any EVM chains magically

What this isn't doing:

  • Remove the need for coingecko assets gen
  • Remove the need for market data

It's not a "one line" to add a new chain, but it's closer to what we should do with EVMs: support is easily by just having to add chain names, native assets and a few scaffolded bits

This PR adds support for Generic EVM Chains - a new pattern for quickly adding EVM-compatible chains without the full overhead of a "first-class" chain integration. The first chains supported through this pattern are Celo and Linea.

Key Changes

New Generic Chain Infrastructure:

  • Added packages/caip/src/genericChains.ts - defines the GENERIC_EVM_CHAINS config array with chain metadata
  • Created GenericEvmChainAdapter that reuses existing EVM adapter logic
  • Added viem clients and ethers providers for Celo/Linea with fallback to public RPCs

Chain Support:

  • Celo (eip155:42220) and Linea (eip155:59144) base assets and tokens
  • CSP headers for Celo/Linea RPC endpoints
  • Relay swapper support for generic EVM chains
  • Asset generation scripts for Celo/Linea tokens via Coingecko

RPC Configuration:

  • VITE_CELO_NODE_URL / VITE_LINEA_NODE_URL env vars (optional)
  • Falls back to viem's default public RPCs when env vars not set

Issue (if applicable)

Nothing

Risk

High Risk PRs Require 2 approvals

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

Testing

  1. Enable Generic EVM Chains: Set VITE_FEATURE_GENERIC_EVM_CHAINS=true in .env
  2. View Celo/Linea Assets: Search for "CELO" or "Linea" in asset search - should appear
  3. Test Trade: Try a swap on Celo (e.g., CELO → USDC on Celo via Relay)
  4. Try multiple swaps: native to ERC20, ERC20 to ERC20, ERC20 to native
  5. Try to send Native and ERC20 tokens
  6. Verify all the derivation is working as expected for CELO and Linea
  7. Balances are properly fetched, and refetched after swap/sends

Engineering

Operations

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

Screenshots (if applicable)

https://jam.dev/c/f1a406e3-71c7-4d58-bb44-aa063f4d3d14

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for Celo and Linea blockchain networks.
    • Introduced generic EVM chain support framework for broader network compatibility.
    • Enhanced asset approval handling with improved asset resolution for token swaps.
    • Added per-chain popular assets categorization for better asset discovery.
  • Documentation

    • Published guide for integrating new EVM chains into the platform.

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

Minimoi and others added 24 commits January 18, 2026 02:13
…son to v1.62.40-evm-multi-chain.0

Update all 18 @shapeshiftoss/hdwallet-* packages from ^1.62.41 to
1.62.40-evm-multi-chain.0 to enable generic EVM chain support.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ters package.json

Updated @shapeshiftoss/hdwallet-core and @shapeshiftoss/hdwallet-ledger
from ^1.62.41 to 1.62.40-evm-multi-chain.0 to enable generic EVM chain support.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ckage.json

Updated @shapeshiftoss/hdwallet-core from ^1.62.41 to 1.62.40-evm-multi-chain.0
in the swapper package to enable generic EVM chain support.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…Id union

Add type definitions for generic EVM chain support:
- EvmGenericChainId: template literal type for any EVM chain ID in CAIP-2 format
- AnyEvmChainId: union of known and generic EVM chain IDs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…with EvmGenericChainConfig type and GENERIC_EVM_CHAINS array

- Add EvmGenericChainId template literal type for generic EVM chains (eip155:${number})
- Add EvmGenericChainConfig type with fields: chainId, name, displayName, nativeAssetId, rpcUrl, iconName, viemChainKey, multicallAddress, explorerUrl
- Add empty GENERIC_EVM_CHAINS array for future chain configurations
- Import viem types for Address and chains

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add helper functions:
- getGenericChainConfig: retrieves chain config by chain ID
- isGenericChainId: type guard to check if a chainId is a generic chain ID
- getGenericChainAssetId: returns native asset ID for a chain

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create packages/chain-adapters/src/evm/generic/GenericEvmChainAdapter.ts
extending SecondClassEvmAdapter for generic EVM chain support.

- Accepts EvmGenericChainConfig for dynamic chain configuration
- Uses coinType 60 (Ethereum) for BIP44 derivation
- Implements getName(), getDisplayName(), getType(), getFeeAssetId()
- Adds getExplorerUrl() helper for block explorer access
- Includes isGenericEvmChainAdapter type guard

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…eric/index.ts

Add index.ts to export all GenericEvmChainAdapter exports following the
same pattern used by other second-class EVM adapters like hyperevm and katana.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…pdate viemClientByChainId

- Add createGenericViemClient function that creates viem clients for generic EVM chains
- Add genericChainClientCache to cache created clients and avoid duplicates
- Add extractNetworkId helper to parse chain ID from CAIP-2 format
- Add getOrCreateViemClient function that supports both known and generic chains
- Use defineChain from viem for chains not in viem/chains
- Support viemChainKey config option to use built-in viem chain definitions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
… CSP for generic chain RPC URLs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…/index.ts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ables

Add placeholder sections for generic EVM chain RPC URLs to .env and
.env.development files. The pattern VITE_GENERIC_CHAIN_{CHAINID}_NODE_URL
allows adding RPC URLs for chains configured in packages/caip/src/genericChains.ts
without modifying code.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…tsx plugin

Create plugin that registers generic chain adapters from GENERIC_EVM_CHAINS configuration.
The plugin iterates over the config array and creates a GenericEvmChainAdapter for each chain.
Feature flag 'GenericEvmChains' controls visibility.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…reFlags

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…hains

- Import GENERIC_EVM_CHAINS from @shapeshiftoss/caip
- Add generic chain IDs to supportedChains when GenericEvmChains feature flag is enabled
- Generic chains are appended to KnownChainIds in the supportedChains array

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Revert hdwallet packages from 1.62.40-evm-multi-chain.0 to 1.62.41
  (verdaccio version doesn't exist on npm)
- Fix TS2589 in viemClient.ts by adding type assertions for chain types
- Update EvmBaseAdapter and SecondClassEvmAdapter to accept AnyEvmChainId
  (enables GenericEvmChainAdapter to extend SecondClassEvmAdapter)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The feature flag was referenced in preferencesSlice but not defined in
config.ts validators, which would cause the app to crash on startup.

Also added the flag to test mock store for test compatibility.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
… support

- Update ChainAdapterArgs to accept AnyEvmChainId for generic chains
- Fix targetNetworkByChainId to use Record<string, ...> with fallback
- Update ChainSpecificAccount, ChainSpecificFeeData, ChainSpecificBuildTxData,
  and ChainSpecificGetFeeDataInput types to handle EvmGenericChainId
- Update SignTx type to return ETHSignTx for generic EVM chains
- Update BuildSendApiTxInput and BuildDepositTxInput to accept any ChainId
- Add exclude patterns for dist directories and generated files in tsconfig

Note: Full type-check requires unchained-client generated types which need
Java (openapi-generator). The type fixes here address EVM generic chain support.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove unused env variable and loadEnv import from generic CSP
- Remove unused AnyEvmChainId import from chain-adapters types
- Apply auto-fix for import sorting, prettier formatting, and type-only imports

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes:
- Changed type-only import to value import for getGenericChainConfig in viemClient.ts
- Created genericChains.test.ts with tests for isGenericChainId, getGenericChainConfig, getGenericChainAssetId, and GENERIC_EVM_CHAINS
- Created GenericEvmChainAdapter.test.ts with tests for isGenericEvmChainAdapter and rootBip44Params

QA Fix Session: 1

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix import style in viemClient.ts (use top-level type-only import)
- Fix import sorting in test files
- Remove unused mockConfig variable

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

coderabbitai bot commented Jan 18, 2026

📝 Walkthrough

Walkthrough

Adds support for two new EVM-compatible blockchains (Celo and Linea) through a generic EVM chain system. Introduces new chain constants, adapter implementations, asset generation providers, CSP policies, configuration flags, and UI integrations to enable the application to support arbitrary EVM networks dynamically.

Changes

Cohort / File(s) Summary
Environment & Configuration
.env, .env.development, src/config.ts
Added documentation and environment variables for generic EVM chain RPC URLs, including feature flags for VITE_FEATURE_GENERIC_EVM_CHAINS and chain-specific node URL variables.
Type System & Chain Constants
packages/types/src/base.ts, packages/caip/src/constants.ts
Introduced EvmGenericChainId template literal type and AnyEvmChainId union. Added CeloMainnet and LineaMainnet to KnownChainIds, CHAIN_REFERENCE, and ASSET_REFERENCE enums with corresponding asset/chain IDs.
Generic Chain Infrastructure
packages/caip/src/genericChains.ts, packages/caip/src/genericChains.test.ts, packages/caip/src/index.ts
New module providing EvmGenericChainConfig type, GENERIC_EVM_CHAINS array with Celo/Linea configs, and utility functions (getGenericChainConfig, isGenericChainId, getGenericChainAssetId).
EVM Chain Adapters - Type Changes
packages/chain-adapters/src/evm/EvmBaseAdapter.ts, packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts, packages/chain-adapters/src/types.ts
Broadened generic parameter bounds from EvmChainId to AnyEvmChainId. Updated assertSupportsChain to derive chainReference from this.chainId with ETH fallback. Refactored chain-specific types (Account, FeeData, SignTx) to use mapping-based conditional resolution supporting eip155: pattern matching. Added CeloMainnet/LineaMainnet entries to ChainSignTx mapping.
Generic EVM Chain Adapter
packages/chain-adapters/src/evm/generic/GenericEvmChainAdapter.ts, packages/chain-adapters/src/evm/generic/GenericEvmChainAdapter.test.ts, packages/chain-adapters/src/evm/generic/index.ts, packages/chain-adapters/src/evm/index.ts
New GenericEvmChainAdapter class extending SecondClassEvmAdapter with generic RPC/token support. Exposes display name, asset ID, explorer URL getters and type guard. Added re-exports to EVM namespace.
CosmosSDK & UTXO Adapters
packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts, packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts
Minor type assertion changes: casting through unknown intermediate to bypass structural type checking for Account and FeeData returns.
VIEM & RPC Clients
packages/contracts/src/viemClient.ts, packages/contracts/src/ethersProviderSingleton.ts
Added viemCeloClient and viemLineaClient instances. Introduced createGenericViemClient factory with caching and getOrCreateViemClient resolver supporting dynamic RPC URL resolution. Extended viemClientByChainId, viemNetworkIdByChainId, and viemClientByNetworkId mappings for new chains.
CoinGecko Asset Integration
packages/caip/src/adapters/coingecko/index.ts, packages/caip/src/adapters/coingecko/index.test.ts, packages/caip/src/adapters/coingecko/utils.ts, packages/caip/src/adapters/coingecko/utils.test.ts
Extended CoingeckoAssetPlatform enum and chainId mappings to include Celo and Linea. Added error-tolerant ERC20 assetId construction and mapping for Celo/Linea platforms in parseData utility.
Asset Data Generation
scripts/generateAssetData/celo/index.ts, scripts/generateAssetData/linea/index.ts, scripts/generateAssetData/coingecko.ts, scripts/generateAssetData/generateAssetData.ts, scripts/generateAssetData/overrides.ts, scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts, scripts/generateAssetData/utils/index.ts
New Celo and Linea asset provider modules aggregating CoinGecko and Portal token data. Extended generateAssetData to import and merge new chain assets. Added Wrapped CELO override. Fixed asset.name nullish check in filtering. Updated related asset index with wrapped CELO mapping.
Base Assets
packages/utils/src/assetData/baseAssets.ts, packages/utils/src/assetData/getBaseAsset.ts, packages/utils/src/chainIdToFeeAssetId.ts, packages/utils/src/getAssetNamespaceFromChainId.ts, packages/utils/src/getChainShortName.ts, packages/utils/src/getNativeFeeAssetReference.ts
Added celo and linea asset constants. Extended getBaseAsset switch with CeloMainnet/LineaMainnet cases. Added new chains to fee asset ID, asset namespace, and chain short name mappings.
Content Security Policy
headers/csps/chains/celo.ts, headers/csps/chains/linea.ts, headers/csps/chains/generic.ts, headers/csps/index.ts
New CSP modules for Celo and Linea with environment-driven connect-src URLs. Generic chain CSP with empty connect-src. Updated index to import and export new chain CSPs in appropriate order.
Relay Swapper Refactoring
packages/swapper/src/swappers/RelaySwapper/constant.ts, packages/swapper/src/swappers/RelaySwapper/endpoints.ts, packages/swapper/src/swappers/RelaySwapper/getTradeQuote/getTradeQuote.ts, packages/swapper/src/swappers/RelaySwapper/getTradeRate/getTradeRate.ts, packages/swapper/src/swappers/RelaySwapper/utils/getTrade.ts, packages/swapper/src/swappers/RelaySwapper/utils/relayTokenToAssetId.ts, packages/swapper/src/swappers/RelaySwapper/utils/types.ts, packages/swapper/src/swappers/RelaySwapper/utils/relayTokenToAsset.ts
Removed relayChainMap parameter and replaced with dynamic getRelayChainId/getChainIdFromRelayChainId helpers. Added assetRequiringApproval field to RelayTransactionMetadata for approval tracking. Extended relayTokenToAssetId to handle generic EVM chains as Ethereum-derivation. Made orderId optional. Added currencyIn to QuoteDetails.
Swapper Helpers
packages/swapper/src/swappers/utils/helpers/helpers.ts
Extended isNativeEvmAsset to treat EVM chains with slip44/Ethereum references as native ETH-like assets based on namespace and reference.
Plugin System
src/plugins/genericEvmChains/index.tsx, src/plugins/activePlugins.ts
New genericEvmChains plugin registration module that creates adapters for each generic chain, resolving RPC URLs and populating known tokens from asset service. Added to activePlugins export.
Chain Configuration & Constants
src/constants/chains.ts, src/context/PluginProvider/PluginProvider.tsx
Added CeloMainnet/LineaMainnet to SECOND_CLASS_CHAINS. Updated PluginProvider to conditionally append generic EVM chain IDs when GenericEvmChains feature flag is enabled.
Trade UI & Asset Search
src/components/MultiHopTrade/MultiHopTrade.tsx, src/components/MultiHopTrade/components/TradeConfirm/hooks/useAllowanceApproval.tsx, src/components/MultiHopTrade/components/TradeConfirm/hooks/useIsApprovalInitiallyNeeded.tsx, src/components/TradeAssetSearch/components/GroupedAssetList/GroupedAssetList.tsx, src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx
Updated TradeRoutes to fetch rates for QuoteList path. Introduced assetForApproval in approval hooks to prefer relay-specified assetRequiringApproval over sellAsset. Refined approval initialization logic with error-state handling and per-hop dependencies. Added undefined asset guard in GroupedAssetList. Extended useGetPopularAssetsQuery to populate per-chain assets with Celo/Linea using market-cap sorting.
Feature Flags & State
src/state/slices/preferencesSlice/preferencesSlice.ts, src/test/mocks/store.ts
Added Celo, Linea, and GenericEvmChains feature flags to FeatureFlags interface and mock store with config-driven initialization.
Transaction Status & Utilities
src/hooks/useActionCenterSubscribers/useSendActionSubscriber.tsx, src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts, src/lib/address/utils.ts, src/lib/utils/evm/index.ts, src/state/slices/portfolioSlice/utils/index.ts
Added transaction status polling for CeloMainnet/LineaMainnet using getEvmTransactionStatus. Extended wallet support check for generic chain IDs via supportsETH. Updated bytecode fetch to use getOrCreateViemClient. New getEvmTransactionStatus function querying viem clients and mapping receipts to TxStatus. Extended accountIdToLabel to use middleEllipsis for EVM chains.
Opportunities & Migrations
src/state/slices/opportunitiesSlice/mappings.ts, src/state/migrations/index.ts
Added CeloMainnet/LineaMainnet to CHAIN_ID_TO_SUPPORTED_DEFI_OPPORTUNITIES with empty arrays. Added migration steps 278 and 279 for clearAssets.
Debugging & Configuration
src/state/apis/swapper/helpers/getInputOutputRatioFromQuote.ts, src/state/apis/swapper/helpers/swapperApiHelpers.ts
Added console.log debug statements for feeAsset selection and quote processing.
Build & Ignore Patterns
.gitignore, tsconfig.json, package.json
Added .auto-claude patterns to gitignore. Extended tsconfig exclude list for dist and generated code. Pinned @shapeshiftoss/hdwallet-\* dependencies to exact versions.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant PluginProvider
    participant GenericPlugin as Generic EVM<br/>Plugin
    participant ConfigService
    participant Adapter as GenericEvmChain<br/>Adapter
    participant RpcProvider as RPC Provider<br/>(Env/Default)
    participant ViemClient as Viem Client
    participant AssetService

    App->>PluginProvider: Initialize
    PluginProvider->>ConfigService: Check GenericEvmChains<br/>feature flag
    
    alt Feature Enabled
        PluginProvider->>GenericPlugin: Register Plugin
        GenericPlugin->>GenericPlugin: Iterate GENERIC_EVM_CHAINS<br/>(Celo, Linea)
        
        loop For Each Generic Chain
            GenericPlugin->>ConfigService: Resolve RPC URL from<br/>env or defaults
            ConfigService-->>GenericPlugin: RPC URL
            
            GenericPlugin->>AssetService: Query ERC20 tokens<br/>for chain
            AssetService-->>GenericPlugin: Token metadata
            
            GenericPlugin->>Adapter: Create Adapter<br/>with RPC + tokens
            Adapter->>ViemClient: Create PublicClient
            ViemClient-->>Adapter: Client ready
            
            Adapter-->>GenericPlugin: Adapter initialized
            GenericPlugin->>App: Register in plugin<br/>registry
        end
    else Feature Disabled
        PluginProvider->>App: Skip generic chains
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Rationale: This PR spans 60+ files across multiple packages with heterogeneous changes including: (1) complex type system refactoring (generic constraint broadening, mapping-based conditional type resolution in chain-adapters/types.ts), (2) new adapter implementation (GenericEvmChainAdapter), (3) RelaySwapper refactoring with new assetRequiringApproval field propagation across multiple branches, (4) extensive integration points across adapters/plugins/config/UI, and (5) asset generation pipeline changes. While many changes follow consistent patterns (adding chain references to switches/maps), the type system changes and RelaySwapper logic warrant careful senior-level review for type safety and data flow correctness.

Possibly related PRs

  • feat: implement monad #11241 — Adds support for additional EVM chains with overlapping changes to caip constants, viem client/ethers provider, chain-adapters, plugins, and config across the same core modules.
  • feat: near chain #11537 — Parallel EVM chain integration modifying identical core files (env, config, csps, caip, chain-adapters types, plugin registration, asset generation) with same architectural pattern.
  • feat: katana chain #11569 — Related new EVM chain support with code-level changes to env vars, viem/ethers providers, and EvmBaseAdapter constraint updates.

Suggested labels

high risk

Suggested reviewers

  • gomesalexandre

Poem

🐰 Hops with glee through chains anew,
Celo, Linea bold and true!
Generic adapters spring to life,
No more config chains and strife.
EVM lands now far and wide,
This hoppy bunny grins with pride! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: support generic evm routes' accurately summarizes the main change: introducing support for generic EVM chains with reduced integration overhead.

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

✨ 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 auto-claude/004-i-ve-been-doing-an-attempt-at-supporting-evms-chai

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.

- Commented out Sei imports and asset generation in generateAssetData.ts
- Commented out Sei chain config in genericChains.ts
- Kept Celo fully configured and active
- Black screen issue confirmed to be caused by Sei, not Celo
@NeOMakinG NeOMakinG marked this pull request as ready for review January 22, 2026 17:04
@NeOMakinG NeOMakinG requested a review from a team as a code owner January 22, 2026 17:04
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: 10

Caution

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

⚠️ Outside diff range comments (3)
packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts (1)

253-268: Remove double type assertion; fix the underlying type mismatch.

The as unknown as Account<T> at line 268 bypasses type safety and violates the "no type assertions without proper validation" guideline. Restructure the return using explicitly typed intermediate variables so TypeScript can verify the shape matches Account<T>.

♻️ Recommended fix
-      return {
-        balance: account.balance,
-        chainId: this.chainId,
-        assetId: this.assetId,
-        chain: this.getType(),
-        chainSpecific: {
-          accountNumber: account.accountNumber.toString(),
-          assets: account.assets,
-          sequence: account.sequence.toString(),
-          delegations: account.delegations,
-          redelegations: account.redelegations,
-          undelegations: account.undelegations,
-          rewards: account.rewards,
-        },
-        pubkey: account.pubkey,
-      } as unknown as Account<T>
+      const chainSpecific: cosmossdk.Account = {
+        accountNumber: account.accountNumber.toString(),
+        assets: account.assets,
+        sequence: account.sequence.toString(),
+        delegations: account.delegations,
+        redelegations: account.redelegations,
+        undelegations: account.undelegations,
+        rewards: account.rewards,
+      }
+
+      return {
+        balance: account.balance,
+        chainId: this.chainId,
+        assetId: this.assetId,
+        chain: this.getType(),
+        chainSpecific,
+        pubkey: account.pubkey,
+      } as Account<T>
packages/chain-adapters/src/types.ts (2)

1-19: Import EvmGenericChainId from @shapeshiftoss/caip for chain-ID consistency.

This file already sources ChainId from @shapeshiftoss/caip; the new EvmGenericChainId should come from the same package per coding guidelines that specify chain ID types must be imported from @shapeshiftoss/caip.

♻️ Proposed change
-import type { ChainId, Nominal } from '@shapeshiftoss/caip'
+import type { ChainId, EvmGenericChainId, Nominal } from '@shapeshiftoss/caip'
 import type {
   BTCSignTx,
   CosmosSignTx,
   ETHSignTx,
   HDWallet,
   MayachainSignTx,
   SolanaSignTx,
   StarknetSignTx,
   SuiSignTx,
   ThorchainSignTx,
 } from '@shapeshiftoss/hdwallet-core'
 import type {
   ChainSpecific,
-  EvmGenericChainId,
   KnownChainIds,
   UtxoAccountType,
   UtxoChainId,
 } from '@shapeshiftoss/types'

359-401: Add Cosmos/Thorchain/Mayachain to ChainSpecificGetFeeDataInputMapping or document the intentional omission.

Cosmos, Thorchain, and Mayachain are missing from the ChainSpecificGetFeeDataInputMapping. While their adapters have getFeeData implementations, they ignore the input parameter (marked with _) and return hardcoded fees—they don't accept dynamic chain-specific inputs like EVM or UTXO chains do. Either add placeholder entries for consistency (e.g., [KnownChainIds.CosmosMainnet]: cosmossdk.GetFeeDataInput), or add a comment explaining why these chains are intentionally omitted.

🤖 Fix all issues with AI agents
In @.env.development:
- Line 113: Move the environment variable VITE_FEATURE_GENERIC_EVM_CHAINS so it
appears before VITE_FEATURE_KATANA to satisfy the dotenv-linter UnorderedKey
rule; open the .env file, locate the lines defining
VITE_FEATURE_GENERIC_EVM_CHAINS and VITE_FEATURE_KATANA, swap their order
(ensure any related comments or grouping remain consistent), save, and re-run
the linter to confirm the warning is resolved.

In `@docs/adding-new-evm-chain.md`:
- Line 470: Replace the bold-as-heading line "**Total: ~15-17 files**" with a
real Markdown heading to satisfy MD036: convert that bold text into an actual
heading of the appropriate level (e.g., H2/H3) matching surrounding headings in
the document and remove the bold formatting so the line starts with the Markdown
heading marker instead; ensure spacing and hierarchy remain consistent with
nearby headings.
- Line 16: Replace the bare URL in the line containing "**CoinGecko Asset
Platform ID**" with a Markdown link to avoid MD034 failures; change the text so
it uses link syntax (e.g., include the URL as
[https://api.coingecko.com/api/v3/asset_platforms] or attach the URL to
descriptive text like [CoinGecko asset
platforms](https://api.coingecko.com/api/v3/asset_platforms)) so the line reads
as a proper markdown link rather than a bare URL.

In
`@scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts`:
- Around line 45-46: The ZERION_API_KEY constant is hardcoded in
generateRelatedAssetIndex.ts which leaks a secret; remove the literal and
instead read the key from an environment/secret store (e.g.
process.env.ZERION_API_KEY or your secrets manager) and keep the runtime guard
(if missing throw new Error(...)) so the process fails fast; also note to rotate
the exposed key in Zerion and update deployment secrets rather than committing
any new secret to source control.

In `@scripts/generateAssetData/overrides.ts`:
- Around line 30-33: The token metadata override for the Celo mainnet address
'eip155:42220/erc20:0x471ece3750da237f93b8e339c536989b8978a438' is
incorrect—it's the native CELO (GoldToken), not wrapped CELO; update the object
value for that key in overrides.ts to use the correct name "Celo" and symbol
"CELO" (replace the current name 'Wrapped CELO' and symbol 'WCELO' for the
referenced key).

In
`@src/components/MultiHopTrade/components/TradeConfirm/hooks/useAllowanceApproval.tsx`:
- Around line 38-52: The code computes assetForApproval optionally then accesses
its properties inconsistently (assetForApproval?.assetId passed to
useIsAllowanceApprovalRequired but assetForApproval.assetId passed to
useApprovalFees and later at another call), which can cause a runtime error if
assetForApproval is undefined; fix by making assetForApproval guaranteed (remove
the optional chaining from tradeQuoteStep when assigning assetForApproval so it
always resolves to tradeQuoteStep.sellAsset) or, if it can legitimately be
undefined, make all accesses consistent by using optional chaining
(assetForApproval?.assetId) and handling undefined results from useApprovalFees
and the later call; update the references to assetForApproval,
useIsAllowanceApprovalRequired, and useApprovalFees accordingly.

In `@src/config.ts`:
- Around line 69-70: Update the environment schema so VITE_CELO_NODE_URL and
VITE_LINEA_NODE_URL are optional and supply the public RPC fallbacks as
defaults; replace the current required url() validators for the symbols
VITE_CELO_NODE_URL and VITE_LINEA_NODE_URL in src/config.ts with optional URL
validators and set their default values to "https://forno.celo.org" (Celo) and
"https://rpc.linea.build" (Linea) respectively so the chains can operate without
custom node URLs.

In `@src/plugins/genericEvmChains/index.tsx`:
- Around line 11-24: The token discovery currently calls fromAssetId multiple
times without validation in createGetKnownTokens, which will throw on malformed
assetIds; update createGetKnownTokens to parse each asset once (e.g., const
parsed = safeParse via try/catch or a helper) before filtering/mapping, skip any
asset where fromAssetId throws or returns invalid data, and then use
parsed.chainId, parsed.assetNamespace, and parsed.assetReference when building
the returned object so malformed assetIds do not break adapter init.

In `@src/state/apis/swapper/helpers/getInputOutputRatioFromQuote.ts`:
- Around line 26-28: Remove the leftover debug console.log calls inside
getInputOutputRatioFromQuote: locate the console.log({ feeAsset }) and the other
console.log block around lines 70–72 and delete them so no debug output is
emitted during quote calculations; ensure no other stray console.log statements
remain in that helper to avoid noisy production logs.

In `@src/state/slices/portfolioSlice/utils/index.ts`:
- Around line 139-142: The default case only checks against the static
evmChainIds array so dynamically configured generic EVM chains return no label;
update the logic in the default branch to detect EVM chains by their namespace
instead of membership in evmChainIds — e.g., derive the chain namespace from
chainId (or use any existing helper like getChainNamespace) and if it indicates
an EVM chain, return middleEllipsis(pubkey) for EvmChainId-like addresses; keep
existing symbols evmChainIds, chainId, EvmChainId, middleEllipsis, and pubkey in
mind when locating and changing the conditional.
🧹 Nitpick comments (12)
src/state/apis/swapper/helpers/swapperApiHelpers.ts (1)

72-102: Debug logging statements should be removed or converted to structured logging.

These console.log statements appear to be debug artifacts from development. For production code:

  • If these logs are no longer needed, remove them before merging.
  • If logging is required for observability, use structured logging with appropriate log levels (e.g., debug or trace) so they can be filtered in production.

Based on coding guidelines, structured logging with relevant context should be used for error debugging.

packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts (1)

213-213: Type assertion via unknown is a workaround for complex generics.

The double assertion (as unknown as Account<T>) indicates TypeScript can't verify the structural compatibility directly. This is a common pattern when dealing with complex generic types with conditional chain-specific properties. While it works, it bypasses type checking at this point.

The object literal's structure is known to be correct at runtime, so this is acceptable, but worth noting for future maintainers that this assertion should be revisited if the Account<T> type definition changes.

packages/swapper/src/swappers/RelaySwapper/endpoints.ts (1)

210-222: Consider whether txIndexingMap should be set when notification is skipped.

The map is updated at line 222 regardless of whether notifyTransactionIndexing was called. This means for generic EVM chains where relayChainId might be undefined, the swap ID is marked as indexed even though no notification was sent.

If Relay later adds support for these chains, previously attempted swaps won't trigger notifications. This might be intentional (to avoid retrying), but worth confirming the desired behavior.

packages/swapper/src/swappers/RelaySwapper/constant.ts (1)

74-79: Return type is inconsistent with actual behavior.

getChainIdFromRelayChainId is typed to return ChainId | undefined, but it always returns a value - either from the known mapping or the constructed eip155:${relayChainId} string. The undefined return path is never reached.

Consider updating the return type to just ChainId to accurately reflect the behavior, or add explicit undefined handling if there are relay chain IDs that shouldn't be converted.

♻️ Suggested fix
-export const getChainIdFromRelayChainId = (relayChainId: number): ChainId | undefined => {
+export const getChainIdFromRelayChainId = (relayChainId: number): ChainId => {
   const knownChainId = relayChainIdToChainId[relayChainId]
   if (knownChainId) return knownChainId

   return `eip155:${relayChainId}` as ChainId
 }
packages/swapper/src/swappers/RelaySwapper/utils/relayTokenToAssetId.ts (1)

24-27: Guard may be unreachable based on current implementation.

getChainIdFromRelayChainId always returns a value (either from known mapping or constructs eip155:${relayChainId}), so the !chainId check at line 25 will never be true.

This guard provides good defensive coding against future changes, but if you want consistency, consider either:

  1. Keep it as-is for defensive programming
  2. Remove if getChainIdFromRelayChainId return type is updated to ChainId (non-nullable)
packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts (1)

275-290: Gas estimation fallback uses magic numbers that may need documentation.

The fallback gas limits (300000 for small data, 500000 for larger data) and the 1000-character threshold are heuristics. While practical for preventing transaction failures, consider:

  1. Adding a comment explaining the rationale for these specific values
  2. Extracting to named constants for clarity
  3. The dataLength > 1000 check operates on hex string length, not byte count (hex has 2 chars per byte + '0x' prefix)
♻️ Suggested improvement
+// Gas limit fallbacks for when estimation fails due to state-dependent checks
+// (e.g., allowance not yet granted, insufficient balance for simulation)
+const FALLBACK_GAS_LIMIT_SIMPLE = '300000'  // For transactions with small calldata
+const FALLBACK_GAS_LIMIT_COMPLEX = '500000' // For transactions with larger calldata
+const CALLDATA_COMPLEXITY_THRESHOLD = 1000  // Hex string length threshold

 if (isFallbackNeeded && estimateGasBody.data) {
   const dataLength = estimateGasBody.data.length
-  const fallbackGasLimit = dataLength > 1000 ? '500000' : '300000'
+  const fallbackGasLimit = dataLength > CALLDATA_COMPLEXITY_THRESHOLD 
+    ? FALLBACK_GAS_LIMIT_COMPLEX 
+    : FALLBACK_GAS_LIMIT_SIMPLE
   gasLimit = BigInt(fallbackGasLimit)
 }
headers/csps/chains/generic.ts (1)

1-5: Clarify the purpose of this empty CSP configuration.

This file exports a CSP with an empty connect-src array, which effectively contributes no additional CSP rules. If Celo and Linea have their own dedicated CSP files (as indicated in the PR summary), this generic placeholder may be unnecessary.

Consider either:

  1. Documenting why this empty placeholder exists (e.g., for future generic chain RPC endpoints)
  2. Adding a TODO comment explaining planned usage
  3. Removing if not needed
packages/caip/src/genericChains.test.ts (1)

10-37: Expand test coverage to include positive cases for Celo and Linea.

The current tests only cover negative cases (unknown chain IDs) and basic type checks. Since GENERIC_EVM_CHAINS includes Celo and Linea configurations, consider adding tests that verify:

  1. isGenericChainId returns true for Celo (eip155:42220) and Linea (eip155:59144)
  2. getGenericChainConfig returns the expected config object for these chains
  3. getGenericChainAssetId returns the correct asset ID for these chains
🧪 Suggested additional test cases
  describe('isGenericChainId', () => {
    it('should return false for unknown chain IDs when array is empty', () => {
      expect(isGenericChainId('eip155:12345')).toBe(false)
    })

    it('should return false for known chain IDs', () => {
      expect(isGenericChainId('eip155:1')).toBe(false)
    })
+
+   it('should return true for Celo chain ID', () => {
+     expect(isGenericChainId('eip155:42220')).toBe(true)
+   })
+
+   it('should return true for Linea chain ID', () => {
+     expect(isGenericChainId('eip155:59144')).toBe(true)
+   })
  })

  describe('getGenericChainConfig', () => {
    it('should return undefined for unknown chain IDs', () => {
      expect(getGenericChainConfig('eip155:99999')).toBeUndefined()
    })
+
+   it('should return config for Celo', () => {
+     const config = getGenericChainConfig('eip155:42220')
+     expect(config).toBeDefined()
+     expect(config?.chainId).toBe('eip155:42220')
+   })
+
+   it('should return config for Linea', () => {
+     const config = getGenericChainConfig('eip155:59144')
+     expect(config).toBeDefined()
+     expect(config?.chainId).toBe('eip155:59144')
+   })
  })

  describe('getGenericChainAssetId', () => {
    it('should return undefined for unknown chain IDs', () => {
      expect(getGenericChainAssetId('eip155:99999')).toBeUndefined()
    })
+
+   it('should return asset ID for Celo', () => {
+     expect(getGenericChainAssetId('eip155:42220')).toBeDefined()
+   })
+
+   it('should return asset ID for Linea', () => {
+     expect(getGenericChainAssetId('eip155:59144')).toBeDefined()
+   })
  })
packages/caip/src/genericChains.ts (2)

20-39: Consider importing asset IDs from constants to avoid duplication.

The nativeAssetId values are hardcoded strings that duplicate celoAssetId and lineaAssetId from constants.ts. If circular dependencies aren't a concern, importing them would ensure consistency.

♻️ Suggested refactor
+import { celoAssetId, lineaAssetId } from './constants'
+
 export const GENERIC_EVM_CHAINS: EvmGenericChainConfig[] = [
   {
     chainId: 'eip155:42220',
     name: 'celo',
     displayName: 'Celo',
-    nativeAssetId: 'eip155:42220/slip44:60',
+    nativeAssetId: celoAssetId,
     iconName: 'celo',
     viemChainKey: 'celo',
     explorerUrl: 'https://celoscan.io',
   },
   {
     chainId: 'eip155:59144',
     name: 'linea',
     displayName: 'Linea',
-    nativeAssetId: 'eip155:59144/slip44:60',
+    nativeAssetId: lineaAssetId,
     iconName: 'linea',
     viemChainKey: 'linea',
     explorerUrl: 'https://lineascan.build',
   },
 ]

45-48: Type guard naming may be misleading.

isGenericChainId checks if the chain is in GENERIC_EVM_CHAINS, not whether it matches the EvmGenericChainId pattern (eip155:${number}). A chain ID like 'eip155:999999' would return false even though it's a valid EvmGenericChainId pattern.

If this is the intended behavior (checking configured chains only), consider renaming to isConfiguredGenericChainId for clarity. Otherwise, adjust the implementation to validate the pattern.

packages/contracts/src/viemClient.ts (2)

126-129: Minor: No validation for malformed chainId.

extractNetworkId assumes the input is always a well-formed EvmGenericChainId. If chainId is malformed (e.g., missing colon or non-numeric reference), parseInt could return NaN. Since this is an internal function and the type constraint provides some safety, this is low risk but worth noting.


143-147: Native currency symbol derivation may be incorrect for some chains.

When viemChainKey is not provided, the symbol is derived as config.name.substring(0, 4).toUpperCase(). This could produce incorrect symbols (e.g., "LINE" for Linea, when it should be "ETH").

Since Celo and Linea both have viemChainKey set, this fallback won't be triggered for them. For future chains without viem support, consider adding a nativeCurrencySymbol field to EvmGenericChainConfig to avoid this issue.

VITE_FEATURE_YIELDS_PAGE=true
VITE_FEATURE_EARN_TAB=true
VITE_FEATURE_YIELD_MULTI_ACCOUNT=true
VITE_FEATURE_GENERIC_EVM_CHAINS=true
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix dotenv-linter UnorderedKey for feature flags.

Static analysis reports VITE_FEATURE_GENERIC_EVM_CHAINS should appear before VITE_FEATURE_KATANA. Please reorder to satisfy the linter.

🔧 Suggested reorder
 VITE_FEATURE_WC_DIRECT_CONNECTION=true
 VITE_FEATURE_CETUS_SWAP=true
 VITE_FEATURE_NEAR=true
+VITE_FEATURE_GENERIC_EVM_CHAINS=true
 VITE_FEATURE_KATANA=true
 VITE_FEATURE_YIELD_XYZ=true
 VITE_FEATURE_TON=true
 VITE_FEATURE_STONFI_SWAP=true
 VITE_FEATURE_YIELDS_PAGE=true
 VITE_FEATURE_EARN_TAB=true
 VITE_FEATURE_YIELD_MULTI_ACCOUNT=true
-VITE_FEATURE_GENERIC_EVM_CHAINS=true
📝 Committable suggestion

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

Suggested change
VITE_FEATURE_GENERIC_EVM_CHAINS=true
VITE_FEATURE_WC_DIRECT_CONNECTION=true
VITE_FEATURE_CETUS_SWAP=true
VITE_FEATURE_NEAR=true
VITE_FEATURE_GENERIC_EVM_CHAINS=true
VITE_FEATURE_KATANA=true
VITE_FEATURE_YIELD_XYZ=true
VITE_FEATURE_TON=true
VITE_FEATURE_STONFI_SWAP=true
VITE_FEATURE_YIELDS_PAGE=true
VITE_FEATURE_EARN_TAB=true
VITE_FEATURE_YIELD_MULTI_ACCOUNT=true
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 113-113: [UnorderedKey] The VITE_FEATURE_GENERIC_EVM_CHAINS key should go before the VITE_FEATURE_KATANA key

(UnorderedKey)

🤖 Prompt for AI Agents
In @.env.development at line 113, Move the environment variable
VITE_FEATURE_GENERIC_EVM_CHAINS so it appears before VITE_FEATURE_KATANA to
satisfy the dotenv-linter UnorderedKey rule; open the .env file, locate the
lines defining VITE_FEATURE_GENERIC_EVM_CHAINS and VITE_FEATURE_KATANA, swap
their order (ensure any related comments or grouping remain consistent), save,
and re-run the linter to confirm the warning is resolved.

- **Native Token Symbol** (e.g., `CELO` or `ETH`)
- **RPC URL** (public or private)
- **Block Explorer URL** (e.g., `https://celoscan.io`)
- **CoinGecko Asset Platform ID** (find at https://api.coingecko.com/api/v3/asset_platforms)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Replace the bare URL with a markdown link (MD034).

This avoids markdownlint failures for bare URLs.

🛠️ Suggested fix
- - **CoinGecko Asset Platform ID** (find at https://api.coingecko.com/api/v3/asset_platforms)
+ - **CoinGecko Asset Platform ID** (find at <https://api.coingecko.com/api/v3/asset_platforms>)
📝 Committable suggestion

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

Suggested change
- **CoinGecko Asset Platform ID** (find at https://api.coingecko.com/api/v3/asset_platforms)
- **CoinGecko Asset Platform ID** (find at <https://api.coingecko.com/api/v3/asset_platforms>)
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

16-16: Bare URL used

(MD034, no-bare-urls)

🤖 Prompt for AI Agents
In `@docs/adding-new-evm-chain.md` at line 16, Replace the bare URL in the line
containing "**CoinGecko Asset Platform ID**" with a Markdown link to avoid MD034
failures; change the text so it uses link syntax (e.g., include the URL as
[https://api.coingecko.com/api/v3/asset_platforms] or attach the URL to
descriptive text like [CoinGecko asset
platforms](https://api.coingecko.com/api/v3/asset_platforms)) so the line reads
as a proper markdown link rather than a bare URL.

| **Web App** | `src/constants/chains.ts`, `src/state/slices/opportunitiesSlice/mappings.ts` |
| **Environment** | `.env`, `.env.development` |

**Total: ~15-17 files**
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use a real heading instead of bold-as-heading (MD036).

This keeps the doc compliant with markdownlint rules.

🛠️ Suggested fix
- **Total: ~15-17 files**
+ ### Total: ~15-17 files
📝 Committable suggestion

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

Suggested change
**Total: ~15-17 files**
### Total: ~15-17 files
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

470-470: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🤖 Prompt for AI Agents
In `@docs/adding-new-evm-chain.md` at line 470, Replace the bold-as-heading line
"**Total: ~15-17 files**" with a real Markdown heading to satisfy MD036: convert
that bold text into an actual heading of the appropriate level (e.g., H2/H3)
matching surrounding headings in the document and remove the bold formatting so
the line starts with the Markdown heading marker instead; ensure spacing and
hierarchy remain consistent with nearby headings.

Comment on lines +45 to 46
const ZERION_API_KEY = 'zk_prod_bfcde7977df44972802bb1e372ea2ece'
if (!ZERION_API_KEY) throw new Error('Missing Zerion API key - see readme for instructions')
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

Remove hardcoded Zerion API key (secret leakage).
This exposes a production credential in source control. Please rotate the key and load it from environment/secrets instead.

🔐 Proposed fix
-const ZERION_API_KEY = 'zk_prod_bfcde7977df44972802bb1e372ea2ece'
+const ZERION_API_KEY = process.env.ZERION_API_KEY ?? ''
 if (!ZERION_API_KEY) throw new Error('Missing Zerion API key - see readme for instructions')
🧰 Tools
🪛 Gitleaks (8.30.0)

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

(generic-api-key)

🤖 Prompt for AI Agents
In
`@scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts`
around lines 45 - 46, The ZERION_API_KEY constant is hardcoded in
generateRelatedAssetIndex.ts which leaks a secret; remove the literal and
instead read the key from an environment/secret store (e.g.
process.env.ZERION_API_KEY or your secrets manager) and keep the runtime guard
(if missing throw new Error(...)) so the process fails fast; also note to rotate
the exposed key in Zerion and update deployment secrets rather than committing
any new secret to source control.

Comment on lines +30 to +33
'eip155:42220/erc20:0x471ece3750da237f93b8e339c536989b8978a438': {
name: 'Wrapped CELO',
symbol: 'WCELO',
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Celo WCELO token address 0x471ece3750da237f93b8e339c536989b8978a438 contract

💡 Result:

0x471EcE3750Da237f93b8E339c536989b8978A438 is the CELO (GoldToken) native token contract on Celo mainnet — not wCELO. [1][2]

Wrapped CELO (wCELO) is a separate ERC‑20 on Ethereum (example contract: 0xE452E6Ea2dDeB012e20dB73bf5d3863A3Ac8d77a). [3]

Sources:

  • Celo core contracts (CELO / GoldToken address). [1]
  • Celo block explorer / token page for 0x471e...a438. [2]
  • wCELO (Wrapped Celo) token on Ethereum (Etherscan). [3]

Update token metadata—address is the native CELO token, not wCELO.

The address 0x471ece3750da237f93b8e339c536989b8978a438 on Celo mainnet is the native CELO (GoldToken) contract. Update the override to reflect the correct name and symbol:

  'eip155:42220/erc20:0x471ece3750da237f93b8e339c536989b8978a438': {
    name: 'Celo',
    symbol: 'CELO',
  },
🤖 Prompt for AI Agents
In `@scripts/generateAssetData/overrides.ts` around lines 30 - 33, The token
metadata override for the Celo mainnet address
'eip155:42220/erc20:0x471ece3750da237f93b8e339c536989b8978a438' is
incorrect—it's the native CELO (GoldToken), not wrapped CELO; update the object
value for that key in overrides.ts to use the correct name "Celo" and symbol
"CELO" (replace the current name 'Wrapped CELO' and symbol 'WCELO' for the
referenced key).

Comment on lines +38 to +52
// Use wrapped asset if Relay specified one (e.g., native CELO → wrapped CELO)
const assetForApproval =
tradeQuoteStep?.relayTransactionMetadata?.assetRequiringApproval ?? tradeQuoteStep?.sellAsset

const { allowanceCryptoBaseUnitResult, isAllowanceApprovalRequired } =
useIsAllowanceApprovalRequired({
amountCryptoBaseUnit: tradeQuoteStep?.sellAmountIncludingProtocolFeesCryptoBaseUnit,
assetId: tradeQuoteStep?.sellAsset.assetId,
assetId: assetForApproval?.assetId,
from: sellAssetAccountId ? fromAccountId(sellAssetAccountId).account : undefined,
spender: tradeQuoteStep?.allowanceContract,
})

const { evmFeesResult } = useApprovalFees({
amountCryptoBaseUnit: tradeQuoteStep.sellAmountIncludingProtocolFeesCryptoBaseUnit,
assetId: tradeQuoteStep.sellAsset.assetId,
assetId: assetForApproval.assetId,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Inconsistent optional chaining creates potential runtime error.

assetForApproval is accessed with optional chaining on line 45 (assetForApproval?.assetId) but without it on line 52 (assetForApproval.assetId). If assetForApproval can be undefined (which the optional chaining implies), line 52 will throw.

Additionally, the same inconsistency exists on line 96.

Proposed fix

Either be consistent with optional chaining everywhere, or assert the value is defined if the context guarantees it:

  // Use wrapped asset if Relay specified one (e.g., native CELO → wrapped CELO)
- const assetForApproval =
-   tradeQuoteStep?.relayTransactionMetadata?.assetRequiringApproval ?? tradeQuoteStep?.sellAsset
+ const assetForApproval =
+   tradeQuoteStep.relayTransactionMetadata?.assetRequiringApproval ?? tradeQuoteStep.sellAsset

Since tradeQuoteStep is a required parameter and sellAsset is always present on a TradeQuoteStep, removing the optional chaining on tradeQuoteStep makes assetForApproval guaranteed to be defined, allowing direct access on lines 52 and 96 to be safe.

🤖 Prompt for AI Agents
In
`@src/components/MultiHopTrade/components/TradeConfirm/hooks/useAllowanceApproval.tsx`
around lines 38 - 52, The code computes assetForApproval optionally then
accesses its properties inconsistently (assetForApproval?.assetId passed to
useIsAllowanceApprovalRequired but assetForApproval.assetId passed to
useApprovalFees and later at another call), which can cause a runtime error if
assetForApproval is undefined; fix by making assetForApproval guaranteed (remove
the optional chaining from tradeQuoteStep when assigning assetForApproval so it
always resolves to tradeQuoteStep.sellAsset) or, if it can legitimately be
undefined, make all accesses consistent by using optional chaining
(assetForApproval?.assetId) and handling undefined results from useApprovalFees
and the later call; update the references to assetForApproval,
useIsAllowanceApprovalRequired, and useApprovalFees accordingly.

Comment on lines +69 to +70
VITE_CELO_NODE_URL: url(),
VITE_LINEA_NODE_URL: url(),
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check current src/config.ts around lines 69-70
head -n 80 src/config.ts | tail -n 20

Repository: shapeshift/web

Length of output: 727


🏁 Script executed:

# Search for CELO/LINEA env var definitions in .env files and docs
rg "VITE_(CELO|LINEA)_NODE_URL" --type-list | head -20

Repository: shapeshift/web

Length of output: 447


🏁 Script executed:

# Check if there are CELO/LINEA chain CSP files
fd -e ts "celo|linea" headers/csps/chains/

Repository: shapeshift/web

Length of output: 114


🏁 Script executed:

# Search for how node URLs are used and if there's fallback logic
rg -i "celo.*node|linea.*node" --type ts --type tsx -A 3

Repository: shapeshift/web

Length of output: 85


🏁 Script executed:

# Check what other chains use for optional node URLs in config.ts
rg "VITE_.*_NODE_URL.*url\(\)" src/config.ts

Repository: shapeshift/web

Length of output: 788


🏁 Script executed:

# Read CELO CSP chain file
cat -n headers/csps/chains/celo.ts

Repository: shapeshift/web

Length of output: 535


🏁 Script executed:

# Read LINEA CSP chain file
cat -n headers/csps/chains/linea.ts

Repository: shapeshift/web

Length of output: 539


🏁 Script executed:

# Search for fallback to public RPC logic
rg -i "fallback|public.*rpc|getRpcUrl|nodeUrl" --type ts -B 2 -A 3 | head -100

Repository: shapeshift/web

Length of output: 7169


🏁 Script executed:

# Check if CELO/LINEA are treated as optional/fallback chains anywhere
rg "celo|linea" src/ --type ts -i -B 2 -A 2 | head -80

Repository: shapeshift/web

Length of output: 7273


Make VITE_CELO_NODE_URL and VITE_LINEA_NODE_URL optional with defaults.
Their CSP chain files include hardcoded public RPC fallbacks (https://forno.celo.org and https://rpc.linea.build), meaning these chains can operate without custom node URLs. Both are feature-flagged (optional), following the NEAR fallback pattern. Using required url() validators contradicts the fallback design.

-  VITE_CELO_NODE_URL: url(),
-  VITE_LINEA_NODE_URL: url(),
+  VITE_CELO_NODE_URL: url({ default: '' }),
+  VITE_LINEA_NODE_URL: url({ default: '' }),
📝 Committable suggestion

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

Suggested change
VITE_CELO_NODE_URL: url(),
VITE_LINEA_NODE_URL: url(),
VITE_CELO_NODE_URL: url({ default: '' }),
VITE_LINEA_NODE_URL: url({ default: '' }),
🤖 Prompt for AI Agents
In `@src/config.ts` around lines 69 - 70, Update the environment schema so
VITE_CELO_NODE_URL and VITE_LINEA_NODE_URL are optional and supply the public
RPC fallbacks as defaults; replace the current required url() validators for the
symbols VITE_CELO_NODE_URL and VITE_LINEA_NODE_URL in src/config.ts with
optional URL validators and set their default values to "https://forno.celo.org"
(Celo) and "https://rpc.linea.build" (Linea) respectively so the chains can
operate without custom node URLs.

Comment on lines +11 to +24
const createGetKnownTokens = (config: EvmGenericChainConfig) => () => {
const assetService = getAssetService()
return assetService.assets
.filter(asset => {
const { chainId, assetNamespace } = fromAssetId(asset.assetId)
return chainId === config.chainId && assetNamespace === 'erc20'
})
.map(asset => ({
assetId: asset.assetId,
contractAddress: fromAssetId(asset.assetId).assetReference,
symbol: asset.symbol,
name: asset.name,
precision: asset.precision,
}))
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Guard token parsing to avoid hard failures on malformed assetIds.
Line 15 and Line 20 call fromAssetId without protection. If any assetId is malformed, token discovery throws and prevents adapter init. Consider parsing once and skipping invalid entries.

🔧 Suggested fix
-const createGetKnownTokens = (config: EvmGenericChainConfig) => () => {
-  const assetService = getAssetService()
-  return assetService.assets
-    .filter(asset => {
-      const { chainId, assetNamespace } = fromAssetId(asset.assetId)
-      return chainId === config.chainId && assetNamespace === 'erc20'
-    })
-    .map(asset => ({
-      assetId: asset.assetId,
-      contractAddress: fromAssetId(asset.assetId).assetReference,
-      symbol: asset.symbol,
-      name: asset.name,
-      precision: asset.precision,
-    }))
-}
+const createGetKnownTokens = (config: EvmGenericChainConfig) => () => {
+  const assetService = getAssetService()
+  return assetService.assets.flatMap(asset => {
+    try {
+      const { chainId, assetNamespace, assetReference } = fromAssetId(asset.assetId)
+      if (chainId !== config.chainId || assetNamespace !== 'erc20') return []
+      return [
+        {
+          assetId: asset.assetId,
+          contractAddress: assetReference,
+          symbol: asset.symbol,
+          name: asset.name,
+          precision: asset.precision,
+        },
+      ]
+    } catch {
+      return []
+    }
+  })
+}
📝 Committable suggestion

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

Suggested change
const createGetKnownTokens = (config: EvmGenericChainConfig) => () => {
const assetService = getAssetService()
return assetService.assets
.filter(asset => {
const { chainId, assetNamespace } = fromAssetId(asset.assetId)
return chainId === config.chainId && assetNamespace === 'erc20'
})
.map(asset => ({
assetId: asset.assetId,
contractAddress: fromAssetId(asset.assetId).assetReference,
symbol: asset.symbol,
name: asset.name,
precision: asset.precision,
}))
const createGetKnownTokens = (config: EvmGenericChainConfig) => () => {
const assetService = getAssetService()
return assetService.assets.flatMap(asset => {
try {
const { chainId, assetNamespace, assetReference } = fromAssetId(asset.assetId)
if (chainId !== config.chainId || assetNamespace !== 'erc20') return []
return [
{
assetId: asset.assetId,
contractAddress: assetReference,
symbol: asset.symbol,
name: asset.name,
precision: asset.precision,
},
]
} catch {
return []
}
})
}
🤖 Prompt for AI Agents
In `@src/plugins/genericEvmChains/index.tsx` around lines 11 - 24, The token
discovery currently calls fromAssetId multiple times without validation in
createGetKnownTokens, which will throw on malformed assetIds; update
createGetKnownTokens to parse each asset once (e.g., const parsed = safeParse
via try/catch or a helper) before filtering/mapping, skip any asset where
fromAssetId throws or returns invalid data, and then use parsed.chainId,
parsed.assetNamespace, and parsed.assetReference when building the returned
object so malformed assetIds do not break adapter init.

Comment on lines +26 to +28
console.log({
feeAsset,
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove debug console.log statements before merging.

These console.log statements appear to be leftover debug artifacts from development. They will execute on every trade quote calculation (potentially multiple times per quote with multi-hop trades), polluting the production console output.

🧹 Suggested removal
  const feeAsset = selectFeeAssetById(state, tradeQuoteStep?.sellAsset.assetId)

-  console.log({
-    feeAsset,
-  })
  if (feeAsset === undefined)
  const getFeeAssetUsdRate = (feeAssetId: AssetId) => {
    const feeAsset = selectFeeAssetById(state, feeAssetId)
-    console.log({
-      feeAsset,
-    })
    if (feeAsset === undefined) throw Error(`missing fee asset for assetId ${feeAssetId}`)

Also applies to: 70-72

🤖 Prompt for AI Agents
In `@src/state/apis/swapper/helpers/getInputOutputRatioFromQuote.ts` around lines
26 - 28, Remove the leftover debug console.log calls inside
getInputOutputRatioFromQuote: locate the console.log({ feeAsset }) and the other
console.log block around lines 70–72 and delete them so no debug output is
emitted during quote calculations; ensure no other stray console.log statements
remain in that helper to avoid noisy production logs.

Comment on lines 139 to +142
default: {
if (evmChainIds.includes(chainId as EvmChainId)) {
return middleEllipsis(pubkey)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Cover generic EVM chain IDs not in evmChainIds.

evmChainIds is static, so dynamically configured generic EVM chains won’t match and will still return an empty label. Consider using the chain namespace to cover all EVM chains.

🔧 Suggested fix
     default: {
-      if (evmChainIds.includes(chainId as EvmChainId)) {
+      if (fromChainId(chainId).chainNamespace === CHAIN_NAMESPACE.Evm) {
         return middleEllipsis(pubkey)
       }
       return ''
     }
🤖 Prompt for AI Agents
In `@src/state/slices/portfolioSlice/utils/index.ts` around lines 139 - 142, The
default case only checks against the static evmChainIds array so dynamically
configured generic EVM chains return no label; update the logic in the default
branch to detect EVM chains by their namespace instead of membership in
evmChainIds — e.g., derive the chain namespace from chainId (or use any existing
helper like getChainNamespace) and if it indicates an EVM chain, return
middleEllipsis(pubkey) for EvmChainId-like addresses; keep existing symbols
evmChainIds, chainId, EvmChainId, middleEllipsis, and pubkey in mind when
locating and changing the conditional.

@premiumjibles premiumjibles self-requested a review January 25, 2026 20:48
Copy link
Collaborator

@premiumjibles premiumjibles left a comment

Choose a reason for hiding this comment

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

@NeOMakinG

Swap from ETH -> CELO on native wallet https://jam.dev/c/542e30b0-3f8b-49e0-b44b-076ad43b5418

Swap from ETH -> CELO on phantom https://jam.dev/c/52818379-43f2-42a9-b839-3c1f47119a2a

Swap from CELO -> USDC.CELO worked 👍 https://jam.dev/c/a1d55050-852e-41de-a532-3acdeb3c7e99

In both cases it's showing me that I got both CELO and WCELO and essentially doubles my trade value. Is that expected? The actual swap on relay just shows the regular 1 for 1 asset swap.

Successfully swapped ETH -> LINEA on native 👍

Swap from LINEA -> USDC.LINEA also good, although it was kind of difficult to find ETH.LINEAR on our search. Might just be me 😆 https://jam.dev/c/4d3df3c4-024e-49dd-806c-9c881d444796 👍

So everything looks good just flagging the CELO thing. If there's an explanation or fix for that then happy to approve/merge


public static readonly rootBip44Params: RootBip44Params = {
purpose: 44,
coinType: 60,
Copy link
Collaborator

Choose a reason for hiding this comment

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

is this safe to assume for all generic evm chains?

)
} catch (estimateError) {
const errorMessage = String(estimateError)
const isFallbackNeeded =
Copy link
Collaborator

Choose a reason for hiding this comment

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

non-blocking: this logic is probably fine as a catch all. But worth asking the question if there's anything that's not that hard to do that's a bit more reliable and non-reliant on error string matching

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.

3 participants