feat: integrate Mode (eip155:34443) as second-class citizen#11926
feat: integrate Mode (eip155:34443) as second-class citizen#11926gomesalexandre merged 61 commits intodevelopfrom
Conversation
…Relay bridge support Add support for Mantle (MNT native gas) including CAIP constants, chain adapter, plugin, feature flag, Relay swapper mapping, HDWallet support flags, CSP headers, asset generation script, and all required shared-file entries. Part of #11902
…lay bridge support Add support for Cronos (CRO native gas) including CAIP constants, chain adapter, plugin, feature flag, Relay swapper mapping, HDWallet support flags, CSP headers, asset generation script, and all required shared-file entries. Part of #11902
Add src/lib/utils/mantle.ts with getMantleTransactionStatus using eth_getTransactionReceipt via the Mantle RPC. Add KnownChainIds.MantleMainnet case to useSendActionSubscriber.tsx so Mantle transactions resolve in the action center.
Add src/lib/utils/cronos.ts with getCronosTransactionStatus using eth_getTransactionReceipt via the Cronos RPC. Add KnownChainIds.CronosMainnet case to useSendActionSubscriber.tsx so Cronos transactions resolve in the action center. Add CHAIN_REFERENCE.CronosMainnet case to relayTokenToAssetId.ts to prevent runtime crash on Relay swaps involving Cronos.
Address PR review feedback: - Add mantleChainId to getCoingeckoSupportedChainIds (feature-flagged) - Add mantle to ZERION_CHAINS array and ZERION_CHAINS_MAP - Across does not support Mantle, skipped
Address PR review feedback: - Add cronosChainId to getCoingeckoSupportedChainIds (feature-flagged) - Add cronos to ZERION_CHAINS array and ZERION_CHAINS_MAP - Across does not support Cronos, skipped
…ay swapper support
Adds full Unichain support including: - CAIP constants, types, and chain adapter - HDWallet support flags across all wallet implementations - Relay and Across swapper mappings - CoinGecko adapter with unichain platform - Zerion chain mapping - Feature flag, plugin, CSP headers - Portfolio, account, asset service, and market integrations - Asset generation scripts and related asset index (ETH-native) Part of #11902
📝 WalkthroughWalkthroughComprehensive integration of Mode blockchain network across the codebase, including environment configuration, CAIP constants, EVM chain adapters, wallet support capabilities, asset data generation pipelines, viem/ethers clients, swapper integrations, plugin registration, and state management—all gated behind a feature flag for conditional enablement. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
Mode (eip155:34443) sorts before MegaEth (eip155:4326) in coingeckoToAssetIds. Add missing Mode entry to market-service test destructuring and expected array.
The BOB adapter.json (eip155_60808) was created but not imported/exported from the generated index.ts, causing coingeckoToAssetIds to not return BOB. Also fix ethereum assetId ordering and add BOB to market-service test.
The Unichain adapter.json (eip155_130) was created but not imported/exported from the generated index.ts, causing coingeckoToAssetIds to not return Unichain.
The Sonic adapter.json (eip155_146) was created but not imported/exported from the generated index.ts, preventing CoinGecko mapping for Sonic tokens.
…tle-relay # Conflicts: # .env # .env.development # packages/caip/src/adapters/coingecko/generated/index.ts # packages/caip/src/adapters/coingecko/index.ts # packages/caip/src/adapters/coingecko/utils.test.ts # packages/caip/src/adapters/coingecko/utils.ts # packages/caip/src/constants.ts # packages/chain-adapters/src/evm/EvmBaseAdapter.ts # packages/chain-adapters/src/types.ts # packages/contracts/src/ethersProviderSingleton.ts # packages/contracts/src/viemClient.ts # packages/hdwallet-coinbase/src/coinbase.ts # packages/hdwallet-core/src/ethereum.ts # packages/hdwallet-core/src/wallet.ts # packages/hdwallet-gridplus/src/gridplus.ts # packages/hdwallet-keepkey/src/keepkey.ts # packages/hdwallet-ledger/src/ledger.ts # packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts # packages/hdwallet-native/src/ethereum.ts # packages/hdwallet-phantom/src/phantom.ts # packages/hdwallet-trezor/src/trezor.ts # packages/hdwallet-vultisig/src/vultisig.ts # packages/hdwallet-walletconnectv2/src/walletconnectV2.ts # packages/swapper/src/swappers/RelaySwapper/constant.ts # packages/swapper/src/swappers/RelaySwapper/utils/relayTokenToAssetId.ts # packages/types/src/base.ts # packages/types/src/zerion.ts # 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 # scripts/generateAssetData/coingecko.ts # scripts/generateAssetData/generateAssetData.ts # src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx # src/config.ts # src/constants/chains.ts # src/hooks/useActionCenterSubscribers/useSendActionSubscriber.tsx # src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts # src/lib/account/evm.ts # src/lib/asset-service/service/AssetService.ts # src/lib/coingecko/utils.ts # src/pages/Markets/components/MarketsRow.tsx # src/state/slices/portfolioSlice/utils/index.ts # src/state/slices/preferencesSlice/preferencesSlice.ts # src/test/mocks/store.ts # src/vite-env.d.ts
Missing closing braces in 7 files where auto-resolve stripped them at mantle/next-entry boundaries. Also made generateChainRelatedAssetIndex self-contained (no import from generateRelatedAssetIndex to avoid module-scope ZERION_API_KEY check). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
77 Mantle assets added with relatedAssetKey cross-chain linking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e-relay # Conflicts: # .beads/pr-context.jsonl # .beads/ss-dx5.8.json # .beads/ss-dx5.9.json # .claude/contracts/second-class-evm-chain.md # .env # .env.development # headers/csps/index.ts # packages/caip/src/adapters/coingecko/generated/index.ts # packages/caip/src/adapters/coingecko/index.test.ts # packages/caip/src/adapters/coingecko/index.ts # packages/caip/src/adapters/coingecko/utils.test.ts # packages/caip/src/adapters/coingecko/utils.ts # packages/caip/src/constants.ts # packages/chain-adapters/src/evm/EvmBaseAdapter.ts # packages/chain-adapters/src/evm/SecondClassEvmAdapter.ts # packages/chain-adapters/src/evm/index.ts # packages/chain-adapters/src/types.ts # packages/contracts/src/ethersProviderSingleton.ts # packages/contracts/src/viemClient.ts # packages/hdwallet-coinbase/src/coinbase.ts # packages/hdwallet-core/src/ethereum.ts # packages/hdwallet-core/src/wallet.ts # packages/hdwallet-gridplus/src/gridplus.ts # packages/hdwallet-keepkey/src/keepkey.ts # packages/hdwallet-ledger/src/ledger.ts # packages/hdwallet-metamask-multichain/src/shapeshift-multichain.ts # packages/hdwallet-native/src/ethereum.ts # packages/hdwallet-phantom/src/phantom.ts # packages/hdwallet-trezor/src/trezor.ts # packages/hdwallet-vultisig/src/vultisig.ts # packages/hdwallet-walletconnectv2/src/walletconnectV2.ts # packages/swapper/src/swappers/RelaySwapper/constant.ts # packages/swapper/src/swappers/RelaySwapper/utils/relayTokenToAssetId.ts # packages/types/src/base.ts # packages/types/src/zerion.ts # 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 # public/generated/asset-manifest.json # public/generated/asset-manifest.json.br # public/generated/asset-manifest.json.gz # public/generated/generatedAssetData.json # public/generated/generatedAssetData.json.br # public/generated/generatedAssetData.json.gz # public/generated/relatedAssetIndex.json # public/generated/relatedAssetIndex.json.br # public/generated/relatedAssetIndex.json.gz # scripts/generateAssetData/coingecko.ts # scripts/generateAssetData/generateRelatedAssetIndex/generateChainRelatedAssetIndex.ts # scripts/generateAssetData/generateRelatedAssetIndex/generateRelatedAssetIndex.ts # src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx # src/config.ts # src/constants/chains.ts # src/context/PluginProvider/PluginProvider.tsx # src/hooks/useActionCenterSubscribers/useSendActionSubscriber.tsx # src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts # src/lib/account/evm.ts # src/lib/asset-service/service/AssetService.ts # src/lib/coingecko/utils.ts # src/lib/market-service/coingecko/coingecko.test.ts # src/pages/Markets/components/MarketsRow.tsx # src/plugins/activePlugins.ts # src/state/slices/opportunitiesSlice/mappings.ts # src/state/slices/portfolioSlice/utils/index.ts # src/state/slices/preferencesSlice/preferencesSlice.ts # src/test/mocks/store.ts # src/vite-env.d.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/caip/src/constants.ts (1)
298-331:⚠️ Potential issue | 🟡 Minor
modeAssetIdis missing fromFEE_ASSET_IDS.
modeAssetIdis introduced in this PR but not appended toFEE_ASSET_IDS. Most other second-class EVM chains added in this series (monadAssetId,mantleAssetId,inkAssetId,megaethAssetId,lineaAssetId,berachainAssetId,scrollAssetId,sonicAssetId,unichainAssetId) are included. Any consumer that checksFEE_ASSET_IDSfor Mode ETH (fee asset detection, trade/quote fee checks, portfolio display) will silently miss it.🐛 Proposed fix
unichainAssetId, + modeAssetId, solAssetId,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/caip/src/constants.ts` around lines 298 - 331, FEE_ASSET_IDS is missing modeAssetId so Mode ETH won't be recognized for fee detection; update the array FEE_ASSET_IDS to include modeAssetId alongside the other asset ids (e.g., add modeAssetId near the other EVM-like entries such as monadAssetId, mantleAssetId, inkAssetId) so consumers checking FEE_ASSET_IDS will correctly detect Mode as a fee asset.
🧹 Nitpick comments (6)
.env (2)
168-168: Minor:VITE_MODE_NODE_URLkey ordering flagged by dotenv-linter.
MODEsorts beforeMONADalphabetically;VITE_MODE_NODE_URLshould be placed beforeVITE_MONAD_NODE_URL(currently at line 156).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.env at line 168, Reorder the environment keys to satisfy dotenv-linter: move the VITE_MODE_NODE_URL entry so it appears alphabetically before VITE_MONAD_NODE_URL (i.e., place VITE_MODE_NODE_URL above VITE_MONAD_NODE_URL) to correct the ordering issue flagged by dotenv-linter.
326-326: Minor:VITE_FEATURE_MODEkey ordering flagged by dotenv-linter.
VITE_FEATURE_MODEshould precedeVITE_FEATURE_MONAD(currently at line 309).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.env at line 326, Move the environment variable line so that VITE_FEATURE_MODE appears before VITE_FEATURE_MONAD in the .env file; update the ordering by relocating the VITE_FEATURE_MODE entry (currently "VITE_FEATURE_MODE=false") to a position above the existing VITE_FEATURE_MONAD key to satisfy dotenv-linter ordering rules..env.development (2)
67-67: Optional:VITE_MODE_NODE_URLorderingThe linter flags this key as unordered — alphabetically
MODEprecedesMONAD. A quick reorder keeps the file consistent.♻️ Proposed fix
+VITE_MODE_NODE_URL=https://mainnet.mode.network VITE_MONAD_NODE_URL=https://rpc.monad.xyz VITE_PLASMA_NODE_URL=https://rpc.plasma.to ... VITE_BOB_NODE_URL=https://rpc.gobob.xyz -VITE_MODE_NODE_URL=https://mainnet.mode.network VITE_HYPEREVM_NODE_URL=https://rpc.hyperliquid.xyz/evm🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.env.development at line 67, The .env.development file has an unordered environment key: VITE_MODE_NODE_URL is out of alphabetical order relative to other keys (e.g., keys containing MONAD), causing the linter warning; reorder the environment variables so that VITE_MODE_NODE_URL appears in its correct alphabetical position among the VITE_* keys (i.e., place VITE_MODE_NODE_URL before any VITE_MONAD_* entries) to satisfy the linter and keep the file consistent.
118-118: Optional:VITE_FEATURE_MODEorderingSame linter finding —
MODE(M) should precedeSCROLL(S).♻️ Proposed fix
+VITE_FEATURE_MODE=true VITE_FEATURE_KATANA=true VITE_FEATURE_SONIC=true VITE_FEATURE_UNICHAIN=true VITE_FEATURE_BOB=true -VITE_FEATURE_MODE=true VITE_FEATURE_TON=true🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.env.development at line 118, The env variable VITE_FEATURE_MODE is out of alphabetical order (M should come before S); reorder the .env.development entries so the line defining VITE_FEATURE_MODE appears before any variables starting with "VITE_FEATURE_SCROLL" (or other "S" keys) to satisfy the linter and maintain consistent alphabetical ordering.src/lib/utils/mode.ts (1)
15-43:getModeTransactionStatuscreates a newJsonRpcProvideron every poll invocation.This function is called in a polling loop (
useSendActionSubscriber.tsx). Constructing a freshJsonRpcProviderper call allocates a new HTTP connection pool each time, even withstaticNetwork: true. Other parts of the project use provider singletons (seepackages/contracts/src/ethersProviderSingleton.ts). Consider a module-level singleton keyed onnodeUrl:♻️ Suggested refactor
+const modeProviders = new Map<string, JsonRpcProvider>() + +const getModeProvider = (nodeUrl: string): JsonRpcProvider => { + const existing = modeProviders.get(nodeUrl) + if (existing) return existing + const provider = new JsonRpcProvider(nodeUrl, undefined, { staticNetwork: true }) + modeProviders.set(nodeUrl, provider) + return provider +} + export const getModeTransactionStatus = async ( txHash: string, nodeUrl: string, ): Promise<TxStatus> => { try { - const provider = new JsonRpcProvider(nodeUrl, undefined, { - staticNetwork: true, - }) - + const provider = getModeProvider(nodeUrl) const receipt = await provider.getTransactionReceipt(txHash)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/utils/mode.ts` around lines 15 - 43, getModeTransactionStatus currently constructs a new JsonRpcProvider on every call which creates repeated connection pools; change it to reuse a module-level provider cache keyed by nodeUrl (e.g., add a getProvider(nodeUrl) that returns an existing JsonRpcProvider or constructs-and-caches one with staticNetwork: true) and update getModeTransactionStatus to call getProvider(nodeUrl) instead of new JsonRpcProvider(...). Ensure the cached provider is shared across calls and retain existing behavior for receipt handling and error fallback to TxStatus.Unknown; you can reference existing patterns like ethersProviderSingleton for implementation guidance.src/plugins/mode/index.tsx (1)
22-36: Minor:fromAssetIdcalled twice per asset ingetKnownTokens.The
filterstep callsfromAssetId(asset.assetId)and themapstep calls it again. A combinedreduce(or destructuring the result insidefilterinto a variable) would avoid the double parse.♻️ Proposed refactor
- const getKnownTokens = () => { - const assetService = getAssetService() - return assetService.assets - .filter(asset => { - const { chainId, assetNamespace } = fromAssetId(asset.assetId) - return chainId === modeChainId && assetNamespace === 'erc20' - }) - .map(asset => ({ - assetId: asset.assetId, - contractAddress: fromAssetId(asset.assetId).assetReference, - symbol: asset.symbol, - name: asset.name, - precision: asset.precision, - })) - } + const getKnownTokens = () => { + const assetService = getAssetService() + return assetService.assets.reduce<ReturnType<typeof getKnownTokens>>((acc, asset) => { + const { chainId, assetNamespace, assetReference } = fromAssetId(asset.assetId) + if (chainId === modeChainId && assetNamespace === 'erc20') { + acc.push({ + assetId: asset.assetId, + contractAddress: assetReference, + symbol: asset.symbol, + name: asset.name, + precision: asset.precision, + }) + } + return acc + }, []) + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/plugins/mode/index.tsx` around lines 22 - 36, getKnownTokens currently calls fromAssetId(asset.assetId) twice per asset (once in the filter and again in the map); change the implementation so fromAssetId is invoked only once per asset by extracting its result into a variable and reusing it (e.g., use a single reduce or map that checks chainId === modeChainId and assetNamespace === 'erc20' while building the returned object), updating getKnownTokens to reference the parsed result rather than calling fromAssetId again; keep references to assetService.assets and modeChainId intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/swapper/src/swappers/RelaySwapper/constant.ts`:
- Line 46: The import of mode from viem/chains will fail against the pinned viem
2.43.5; either update the viem dependency to >=2.46.0 so mode is exported, or
stop importing mode and instead define a local chain object using defineChain
(mirroring the hardcoded approach used for megaethChainId) and replace uses of
mode with that new chain symbol; update the import list in constant.ts (remove
mode) and add the locally defined chain constant before it is referenced.
---
Outside diff comments:
In `@packages/caip/src/constants.ts`:
- Around line 298-331: FEE_ASSET_IDS is missing modeAssetId so Mode ETH won't be
recognized for fee detection; update the array FEE_ASSET_IDS to include
modeAssetId alongside the other asset ids (e.g., add modeAssetId near the other
EVM-like entries such as monadAssetId, mantleAssetId, inkAssetId) so consumers
checking FEE_ASSET_IDS will correctly detect Mode as a fee asset.
---
Nitpick comments:
In @.env:
- Line 168: Reorder the environment keys to satisfy dotenv-linter: move the
VITE_MODE_NODE_URL entry so it appears alphabetically before VITE_MONAD_NODE_URL
(i.e., place VITE_MODE_NODE_URL above VITE_MONAD_NODE_URL) to correct the
ordering issue flagged by dotenv-linter.
- Line 326: Move the environment variable line so that VITE_FEATURE_MODE appears
before VITE_FEATURE_MONAD in the .env file; update the ordering by relocating
the VITE_FEATURE_MODE entry (currently "VITE_FEATURE_MODE=false") to a position
above the existing VITE_FEATURE_MONAD key to satisfy dotenv-linter ordering
rules.
In @.env.development:
- Line 67: The .env.development file has an unordered environment key:
VITE_MODE_NODE_URL is out of alphabetical order relative to other keys (e.g.,
keys containing MONAD), causing the linter warning; reorder the environment
variables so that VITE_MODE_NODE_URL appears in its correct alphabetical
position among the VITE_* keys (i.e., place VITE_MODE_NODE_URL before any
VITE_MONAD_* entries) to satisfy the linter and keep the file consistent.
- Line 118: The env variable VITE_FEATURE_MODE is out of alphabetical order (M
should come before S); reorder the .env.development entries so the line defining
VITE_FEATURE_MODE appears before any variables starting with
"VITE_FEATURE_SCROLL" (or other "S" keys) to satisfy the linter and maintain
consistent alphabetical ordering.
In `@src/lib/utils/mode.ts`:
- Around line 15-43: getModeTransactionStatus currently constructs a new
JsonRpcProvider on every call which creates repeated connection pools; change it
to reuse a module-level provider cache keyed by nodeUrl (e.g., add a
getProvider(nodeUrl) that returns an existing JsonRpcProvider or
constructs-and-caches one with staticNetwork: true) and update
getModeTransactionStatus to call getProvider(nodeUrl) instead of new
JsonRpcProvider(...). Ensure the cached provider is shared across calls and
retain existing behavior for receipt handling and error fallback to
TxStatus.Unknown; you can reference existing patterns like
ethersProviderSingleton for implementation guidance.
In `@src/plugins/mode/index.tsx`:
- Around line 22-36: getKnownTokens currently calls fromAssetId(asset.assetId)
twice per asset (once in the filter and again in the map); change the
implementation so fromAssetId is invoked only once per asset by extracting its
result into a variable and reusing it (e.g., use a single reduce or map that
checks chainId === modeChainId and assetNamespace === 'erc20' while building the
returned object), updating getKnownTokens to reference the parsed result rather
than calling fromAssetId again; keep references to assetService.assets and
modeChainId intact.
gomesalexandre
left a comment
There was a problem hiding this comment.
lgtm - all CI green, contract review done.
prior testing confirmed on Mode (native ETH balance, relay bridge quotes, chain selector). will re-verify on Soneium as final chain in the pipeline.
merge conflicts with develop (cronos+sonic+unichain+bob) resolved cleanly - migration 299 added, related asset index updated with modeAssetId in ethAssetId group, market service test counts bumped (flatten=12, rate limited=11).
re: coderabbit viem concern - mode export confirmed in viem/chains (id: 34443, name: Mode Mainnet).
Description
Integrates Mode (eip155:34443) as a second-class citizen chain in ShapeShift Web via Relay.link.
This is PR 6 of 17 in a sequential chain integration series. These PRs must be reviewed and merged in order, as each builds on the previous one (stacked branches).
PR merge order:
Mode is an EVM L2 with ETH as its native gas token. This integration adds full support including:
mode, maps toethereum)VITE_FEATURE_MODE) gated behind second-class citizen configIssue (if applicable)
Part of #11902
Risk
Low risk - feature-flagged second-class chain integration following established patterns (identical to Scroll, Ink, Mantle, Cronos, Berachain, Linea, Sonic, Unichain, BOB integrations).
No existing protocols affected. New chain only accessible when feature flag is enabled.
Testing
Engineering
VITE_FEATURE_MODE=truein.env.developmentyarn type-check- passesyarn lint- passesOperations
Screenshots (if applicable)
N/A - chain integration following established patterns
Summary by CodeRabbit
Release Notes