Skip to content

Comments

PRO-3645/feature-flag-gnosis#392

Merged
RanaBug merged 3 commits intostagingfrom
PRO-3645/feature-flag-gnosis
Sep 8, 2025
Merged

PRO-3645/feature-flag-gnosis#392
RanaBug merged 3 commits intostagingfrom
PRO-3645/feature-flag-gnosis

Conversation

@RanaBug
Copy link
Collaborator

@RanaBug RanaBug commented Sep 8, 2025

Description

  • Implementation of a feature flag to turn on and off the support of Gnosis

How Has This Been Tested?

  • Existing unit tests and manual testing

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Summary by CodeRabbit

  • New Features

    • Gnosis (xDAI) support is now toggleable via a feature flag: when enabled, Gnosis networks, tokens, native symbols, explorers, and stable assets appear across Deposit, PillarX, Pulse, PillarX-app and Exchange; when disabled, they are omitted and lookups fall back gracefully.
  • Bug Fixes

    • Native asset logos now default to an empty value when missing to prevent broken images.
  • Tests

    • Tests updated to mock the Gnosis flag and conditionally assert presence/naming of XDAI.

@RanaBug RanaBug requested a review from IAmKio September 8, 2025 13:01
@RanaBug RanaBug self-assigned this Sep 8, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 8, 2025

Walkthrough

Adds a VITE_FEATURE_FLAG_GNOSIS flag and gates inclusion of the Gnosis (chainId 100 / XDAI) entries across multiple modules (blockchain lists, networks, token lists, native symbols, stablecoins, explorers, and tests); also adds safe fallbacks for missing native token logos.

Changes

Cohort / File(s) Summary of Changes
Core blockchain flag & gated exports
src/utils/blockchain.ts, src/utils/__tests__/blockchain.test.ts
Introduces isGnosisEnabled; creates all* base lists and exposes filtered supportedChains, CompatibleChains, STABLECOIN_ADDRESSES, etc., excluding chainId 100 when flag is false; tests updated to be flag-aware and resilient.
Deposit app networks & token lists
src/apps/deposit/index.tsx, src/apps/deposit/components/AssetsList/AssetsList.tsx, src/apps/deposit/utils/blockchain.tsx
Adds isGnosisEnabled; builds allNetworks/allTokenLists and conditionally filters out Gnosis (100). getNetworkViem guarded to fallback to mainnet if Gnosis disabled.
Exchange native symbols & tests
src/apps/the-exchange/utils/blockchain.ts, src/apps/the-exchange/components/DropdownTokensList/test/DropdownTokensList.test.tsx, src/apps/the-exchange/components/SelectDropdown/test/SelectDropdown.test.tsx
Replaces hard-coded NATIVE_SYMBOLS with allNativeSymbols → filtered export (exclude chain 100 when flag off); tests remove the XDAI→'Gnosis' special-case mapping.
Pulse constants
src/apps/pulse/constants/tokens.ts, src/apps/pulse/utils/constants.ts
Introduces allStableCurrencies/allMobulaChainNames and exports STABLE_CURRENCIES/MOBULA_CHAIN_NAMES filtered by isGnosisEnabled (XDAI excluded when flag off).
PillarX assets & tests
src/apps/pillarx-app/utils/constants.ts, src/apps/pillarx-app/components/TokensWithMarketDataTile/test/TokensWithMarketDataTile.test.tsx
Adds isGnosisEnabled; PRIME_ASSETS_MOBULA derived from allPrimeAssetsMobula and filters out XDAI when flag is off; tests mock import.meta.env and conditionally assert XDAI presence.
Token service logo fallback
src/services/tokensData.ts
In convertAPIResponseToTokens, native asset logo now falls back to '' when logoURI is missing (two places).
Small test/mapping adjustments
src/apps/the-exchange/components/DropdownTokensList/test/DropdownTokensList.test.tsx, src/apps/the-exchange/components/SelectDropdown/test/SelectDropdown.test.tsx
Removed explicit XDAI→'Gnosis' mapping in mocks so XDAI falls back to input name unless feature flag logic supplies otherwise.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Env as import.meta.env
  participant Core as utils/blockchain
  participant Builder as all* builders
  participant Consumer as UI/Services

  Env->>Core: read VITE_FEATURE_FLAG_GNOSIS
  Core->>Core: isGnosisEnabled = (flag === 'true')
  Core->>Builder: build allSupported/allNative/allStable lists
  alt isGnosisEnabled == true
    Builder-->>Consumer: include chainId 100 entries
  else
    Builder-->>Consumer: filter out chainId 100 entries
  end
  Consumer->>Consumer: render networks, tokens, symbols, explorers
Loading
sequenceDiagram
  autonumber
  participant Caller as getBlockScan(100, isAddress)
  participant Core as utils/blockchain

  Caller->>Core: request explorer base for chainId 100
  alt isGnosisEnabled == true
    Core-->>Caller: "https://gnosisscan.io/{address|tx}/"
  else
    Core-->>Caller: "" (empty string)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • vignesha22
  • IAmKio

Poem

I flipped a tiny Gnosis switch—tap!
Chains hop in or quietly nap.
XDAI peeks when flags say “go,”
Hops back when the lights are low.
Missing logos? I’ll hum and patch—rabbit cheer, one perfect batch. 🐰✨


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b9b773 and 9e9e66f.

📒 Files selected for processing (1)
  • src/utils/__tests__/blockchain.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/utils/tests/blockchain.test.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). (3)
  • GitHub Check: unit-tests
  • GitHub Check: lint
  • GitHub Check: build
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch PRO-3645/feature-flag-gnosis

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.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Sep 8, 2025

Deploying x with  Cloudflare Pages  Cloudflare Pages

Latest commit: 9e9e66f
Status: ✅  Deploy successful!
Preview URL: https://5db9b139.x-e62.pages.dev
Branch Preview URL: https://pro-3645-feature-flag-gnosis.x-e62.pages.dev

View logs

@github-actions github-actions bot temporarily deployed to Preview (PRO-3645/feature-flag-gnosis) September 8, 2025 13:07 Inactive
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: 4

Caution

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

⚠️ Outside diff range comments (2)
src/apps/deposit/components/AssetsList/AssetsList.tsx (1)

210-216: getDecimal may return a string on error; ensure numeric fallback to avoid NaN/typing issues.

Both call sites assume a number; when getDecimal fails today it returns a string, causing parse/format failures.

-  const tokenDecimals = await getDecimal(asset.tokenAddress, chainId);
-  const readableBalance = formatUnits(
-    BigInt(tokenBalance[0].balance),
-    Number(tokenDecimals || 18)
-  );
+  const tokenDecimals = await getDecimal(asset.tokenAddress, chainId);
+  const decimalsToUse =
+    typeof tokenDecimals === 'number' && Number.isFinite(tokenDecimals)
+      ? tokenDecimals
+      : 18;
+  const readableBalance = formatUnits(
+    BigInt(tokenBalance[0].balance),
+    decimalsToUse
+  );
-const readableBalance = formatUnits(
-  BigInt(newAssetBalance[0].balance),
-  Number(newAssetDecimals || 18)
-);
+const decimalsToUse =
+  typeof newAssetDecimals === 'number' && Number.isFinite(newAssetDecimals)
+    ? newAssetDecimals
+    : 18;
+const readableBalance = formatUnits(
+  BigInt(newAssetBalance[0].balance),
+  decimalsToUse
+);

Also applies to: 294-297

src/apps/deposit/utils/blockchain.tsx (1)

199-227: getDecimal should return a number; current string fallback can break callers.

Today on error it returns a string, propagating to parseUnits/formatUnits and causing runtime errors. Return a numeric fallback (18) and narrow the return type.

-export const getDecimal = async (
+export const getDecimal = async (
   tokenAddress: string,
   chainId: number
-): Promise<string | number> => {
+): Promise<number> => {
   const chain = getNetworkViem(chainId);
   const chainUrl = chainMapping[chain.name.toLowerCase() as Network] || null;

   try {
     if (!chainUrl) {
       throw new Error(`Unsupported chain: ${chain.name}`);
     }

     const provider = createPublicClient({
       chain,
       transport: http(chainUrl),
     });

     const contract = getContract({
       address: tokenAddress as `0x${string}`,
       abi: ERC20_ABI.abi,
       client: provider,
     });

     const result = await contract.read.decimals();

-    return result as number;
+    return Number(result);
   } catch (error) {
-    return `Error to get the decimal for token: ${tokenAddress}, ${error}`;
+    console.warn(`Falling back to 18 decimals for ${tokenAddress}:`, error);
+    return 18;
   }
 };

And simplify the caller accordingly:

-const tokenDecimals =
-  selectedAsset && 'name' in selectedAsset
-    ? selectedAsset.decimals
-    : ((await getDecimal(
-        selectedAsset.tokenAddress,
-        Number(chainId)
-      )) as number) || 18;
+const tokenDecimals =
+  selectedAsset && 'name' in selectedAsset
+    ? selectedAsset.decimals
+    : await getDecimal(selectedAsset.tokenAddress, Number(chainId));

Also applies to: 360-366

🧹 Nitpick comments (15)
src/services/tokensData.ts (1)

351-355: Prefer undefined over empty-string fallback for missing token logos
Replace logo: nativeAsset.logoURI || '' with logo: nativeAsset.logoURI (or logo: nativeAsset.logoURI ?? undefined) so that absent logos are undefined rather than ''. this prevents inadvertent <img src="" /> in components that render unconditionally and lets existing guards (logo && <img>…) and ImageWithFallback handle missing images correctly. Applies to lines 351–355 and 398–402 in src/services/tokensData.ts.

src/utils/blockchain.ts (3)

257-258: Fix brand casing: “GnosisScan”

Use the official casing.

-      return isGnosisEnabled ? 'Gnosisscan' : '';
+      return isGnosisEnabled ? 'GnosisScan' : '';

278-279: Prefer a neutral name when disabled

Returning the numeric id as a string can look odd in UI. Consider an empty string or “Unsupported”.

-      return isGnosisEnabled ? 'Gnosis' : `${chain}`;
+      return isGnosisEnabled ? 'Gnosis' : '';

225-245: Nit: use HTTPS for Arbiscan

Minor hardening and consistency with other explorers.

-    case 42161:
-      return `http://arbiscan.io/${isAddress ? 'address' : 'tx'}/`;
+    case 42161:
+      return `https://arbiscan.io/${isAddress ? 'address' : 'tx'}/`;
src/utils/__tests__/blockchain.test.ts (1)

20-24: Consider also asserting logoURI per chain.

Small addition to strengthen coverage for native token metadata (name, symbol, logoURI).

src/apps/the-exchange/utils/blockchain.ts (2)

27-35: Use consistent symbol casing for XDAI.

Elsewhere symbols use 'XDAI' (uppercase). Here it’s 'xDAI'. Standardize to avoid UI inconsistencies.

 const allNativeSymbols: Record<number, string> = {
-  100: 'xDAI',
+  100: 'XDAI',
 };

37-41: Prefer satisfies over as for safer typing.

Avoids promising keys that may be filtered out at runtime.

-export const NATIVE_SYMBOLS = Object.fromEntries(
+export const NATIVE_SYMBOLS = Object.fromEntries(
   Object.entries(allNativeSymbols).filter(
     ([chainId]) => isGnosisEnabled || chainId !== '100'
   )
-) as Record<number, string>;
+) satisfies Record<number, string>;
src/apps/pulse/constants/tokens.ts (1)

3-11: Freeze addresses to prevent accidental mutation.

Mark the list readonly for safety; no behavior change.

-const allStableCurrencies = [
+const allStableCurrencies = [
   { chainId: 1, address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' },
   { chainId: 10, address: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85' }, // USDC on Optimism
   { chainId: 137, address: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359' }, // USDC on Polygon
   { chainId: 8453, address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' }, // USDC on Base
   { chainId: 42161, address: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831' }, // USDC on Arbitrum
   { chainId: 56, address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d' }, // USDC on BNB Smart Chain
   { chainId: 100, address: '0x2a22f9c3b484c3629090FeED35F17Ff8F88f76F0' }, // USDC on Gnosis
-];
+] as const;
src/apps/deposit/index.tsx (2)

42-42: DRY the feature flag source.

Import isGnosisEnabled from a single utility (src/utils/blockchain.ts) to avoid divergence across files.

-const isGnosisEnabled = import.meta.env.VITE_FEATURE_FLAG_GNOSIS === 'true';
+import { isGnosisEnabled } from '../../utils/blockchain';

56-61: Type the networks without a cast.

Infer a readonly tuple to drop the need for the explicit cast.

-  networks: (isGnosisEnabled
-    ? allNetworks
-    : allNetworks.filter((n) => n.id !== gnosis.id)) as [
-    typeof mainnet,
-    ...(typeof mainnet)[],
-  ],
+  networks: (isGnosisEnabled ? allNetworks : allNetworks.filter((n) => n.id !== gnosis.id)) as const,
src/apps/deposit/components/AssetsList/AssetsList.tsx (1)

41-41: DRY the feature flag source.

Reuse isGnosisEnabled from utils to keep behavior centralized.

-const isGnosisEnabled = import.meta.env.VITE_FEATURE_FLAG_GNOSIS === 'true';
+import { isGnosisEnabled } from '../../../utils/blockchain';
src/apps/pillarx-app/components/TokensWithMarketDataTile/test/TokensWithMarketDataTile.test.tsx (2)

92-99: Use vi.stubEnv/vi.unstubAllEnvs and remove redundant conditional.

You always stub GNOSIS to 'true', so the conditional is dead code. Prefer stubbing helpers and, if needed, split contexts for true/false.

-  beforeEach(() => {
-    // Reset environment
-    Object.defineProperty(import.meta, 'env', {
-      value: { ...originalEnv, VITE_FEATURE_FLAG_GNOSIS: 'true' },
-      writable: true,
-    });
-  });
+  beforeEach(() => {
+    vi.unstubAllEnvs();
+    vi.stubEnv('VITE_FEATURE_FLAG_GNOSIS', 'true');
+  });

-  afterEach(() => {
-    // Reset environment
-    Object.defineProperty(import.meta, 'env', {
-      value: originalEnv,
-      writable: true,
-    });
-  });
+  afterEach(() => vi.unstubAllEnvs());

-    // XDAI should only be present when Gnosis feature flag is enabled
-    if (import.meta.env.VITE_FEATURE_FLAG_GNOSIS === 'true') {
-      expect(mobileScreen.getAllByText('XDAI')).toHaveLength(2);
-      expect(mobileScreen.getByText('$1.4m')).toBeInTheDocument();
-      expect(mobileScreen.getByText('$3,123')).toBeInTheDocument();
-      expect(mobileScreen.getByText('$1.0622')).toBeInTheDocument(); // rounded up with limitDigitsNumber helper function
-      expect(mobileScreen.getByText('3.1%')).toBeInTheDocument();
-      expect(mobileScreen.getByText('1423')).toBeInTheDocument();
-    }
+    // XDAI present when GNOSIS is enabled
+    expect(mobileScreen.getAllByText('XDAI')).toHaveLength(2);
+    expect(mobileScreen.getByText('$1.4m')).toBeInTheDocument();
+    expect(mobileScreen.getByText('$3,123')).toBeInTheDocument();
+    expect(mobileScreen.getByText('$1.0622')).toBeInTheDocument(); // rounded up with limitDigitsNumber helper function
+    expect(mobileScreen.getByText('3.1%')).toBeInTheDocument();
+    expect(mobileScreen.getByText('1423')).toBeInTheDocument();

Also applies to: 100-107, 161-169


201-201: Fix typo in test name (“rowsd”).

-it('renders the right number of rowsd', () => {
+it('renders the right number of rows', () => {
src/apps/pillarx-app/utils/constants.ts (1)

3-3: DRY the feature flag source.

Use the shared isGnosisEnabled export for consistency with other modules.

-const isGnosisEnabled = import.meta.env.VITE_FEATURE_FLAG_GNOSIS === 'true';
+import { isGnosisEnabled } from '../../../utils/blockchain';
src/apps/deposit/utils/blockchain.tsx (1)

37-37: DRY the feature flag source.

Import isGnosisEnabled from utils to keep a single truth source.

-const isGnosisEnabled = import.meta.env.VITE_FEATURE_FLAG_GNOSIS === 'true';
+import { isGnosisEnabled } from '../../../utils/blockchain';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65db2fa and 45d1db9.

📒 Files selected for processing (13)
  • src/apps/deposit/components/AssetsList/AssetsList.tsx (2 hunks)
  • src/apps/deposit/index.tsx (1 hunks)
  • src/apps/deposit/utils/blockchain.tsx (3 hunks)
  • src/apps/pillarx-app/components/TokensWithMarketDataTile/test/TokensWithMarketDataTile.test.tsx (3 hunks)
  • src/apps/pillarx-app/utils/constants.ts (2 hunks)
  • src/apps/pulse/constants/tokens.ts (2 hunks)
  • src/apps/pulse/utils/constants.ts (2 hunks)
  • src/apps/the-exchange/components/DropdownTokensList/test/DropdownTokensList.test.tsx (0 hunks)
  • src/apps/the-exchange/components/SelectDropdown/test/SelectDropdown.test.tsx (0 hunks)
  • src/apps/the-exchange/utils/blockchain.ts (3 hunks)
  • src/services/tokensData.ts (2 hunks)
  • src/utils/__tests__/blockchain.test.ts (1 hunks)
  • src/utils/blockchain.ts (11 hunks)
💤 Files with no reviewable changes (2)
  • src/apps/the-exchange/components/SelectDropdown/test/SelectDropdown.test.tsx
  • src/apps/the-exchange/components/DropdownTokensList/test/DropdownTokensList.test.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-05-23T14:44:33.911Z
Learnt from: RanaBug
PR: pillarwallet/x#315
File: src/apps/the-exchange/utils/wrappedTokens.ts:6-20
Timestamp: 2025-05-23T14:44:33.911Z
Learning: XDAI (Gnosis Chain) is intentionally excluded from the WRAPPED_NATIVE_TOKEN_ADDRESSES mapping in the exchange app's wrappedTokens utility.

Applied to files:

  • src/utils/blockchain.ts
  • src/apps/the-exchange/utils/blockchain.ts
🧬 Code graph analysis (8)
src/apps/deposit/index.tsx (1)
src/utils/blockchain.ts (1)
  • isGnosisEnabled (41-42)
src/utils/__tests__/blockchain.test.ts (1)
src/utils/blockchain.ts (1)
  • getNativeAssetForChainId (72-144)
src/apps/deposit/components/AssetsList/AssetsList.tsx (1)
src/utils/blockchain.ts (1)
  • isGnosisEnabled (41-42)
src/apps/pulse/constants/tokens.ts (1)
src/utils/blockchain.ts (1)
  • isGnosisEnabled (41-42)
src/apps/pillarx-app/utils/constants.ts (2)
src/utils/blockchain.ts (1)
  • isGnosisEnabled (41-42)
src/types/api.ts (1)
  • PrimeAssetType (766-766)
src/apps/deposit/utils/blockchain.tsx (2)
src/utils/blockchain.ts (1)
  • isGnosisEnabled (41-42)
src/apps/deposit/types/types.tsx (1)
  • Network (21-28)
src/apps/pulse/utils/constants.ts (1)
src/utils/blockchain.ts (1)
  • isGnosisEnabled (41-42)
src/apps/the-exchange/utils/blockchain.ts (1)
src/utils/blockchain.ts (1)
  • isGnosisEnabled (41-42)
⏰ 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). (3)
  • GitHub Check: unit-tests
  • GitHub Check: lint
  • GitHub Check: build
🔇 Additional comments (8)
src/utils/blockchain.ts (3)

146-159: Gating strategy for supported/compatible chains looks solid

The filter pattern cleanly excludes chainId 100 when the flag is off.

Also applies to: 290-324


174-176: Logo gating is correct

Returning the Gnosis logo only when enabled is consistent with visibility rules.


94-99: Sanity-check gating for Gnosis native asset
No calls to getNativeAssetForChainId(100) were found—confirm the actual native-asset lookup (e.g. your getNativeAsset function or mapping) is guarded by isGnosisEnabled (or asserts) so that, when Gnosis support is disabled, you never return the default POL asset for chain 100.

src/apps/pulse/utils/constants.ts (1)

13-15: Mobula chain list gating is correct

XDAI inclusion now respects the feature flag; default “All” path inherits CompatibleChains filtering.

src/apps/pulse/constants/tokens.ts (1)

13-15: LGTM: Gnosis gating via feature flag is correct.

src/apps/pillarx-app/utils/constants.ts (1)

20-22: LGTM: simple, clear gating of XDAI in PRIME_ASSETS_MOBULA.

src/apps/deposit/utils/blockchain.tsx (2)

71-75: Good guard: remapping Gnosis to mainnet when disabled.


122-167: Error handling looks solid; returns empty results rather than throwing.

Good defensive coding around unsupported chains and RPC failures.

Also applies to: 169-196

Comment on lines +53 to +57
const tokenLists = Object.fromEntries(
Object.entries(allTokenLists).filter(
([chainId]) => isGnosisEnabled || chainId !== '100'
)
);
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

Guard token list selection when Gnosis is disabled (prevents crash on chainId 100).

If a wallet is on Gnosis while the flag is off, tokenLists[100] is undefined and .tokens access throws. Use the remapped chain id from getNetworkViem and fallback safely.

-  const balancesForChain = await getAllBalances(
-    tokenLists[chainId as keyof typeof tokenLists].tokens as TokenList[]
-  );
+  const effectiveChainId = getNetworkViem(chainId).id;
+  const list = tokenLists[effectiveChainId as keyof typeof tokenLists]?.tokens as TokenList[] | undefined;
+  if (!list) {
+    console.warn(`Token list not available for chainId ${chainId} (effective ${effectiveChainId}). Skipping.`);
+    setBalances([]);
+    return;
+  }
+  const balancesForChain = await getAllBalances(list);
📝 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 tokenLists = Object.fromEntries(
Object.entries(allTokenLists).filter(
([chainId]) => isGnosisEnabled || chainId !== '100'
)
);
// ---- before (unchanged) ----
// (inside your async function, after defining `tokenLists` and `chainId`)
// Replace this line:
- const balancesForChain = await getAllBalances(
- tokenLists[chainId as keyof typeof tokenLists].tokens as TokenList[]
- );
// With the guarded, remapped‐chainId version:
const effectiveChainId = getNetworkViem(chainId).id;
const list = tokenLists[
effectiveChainId as keyof typeof tokenLists
]?.tokens as TokenList[] | undefined;
if (!list) {
console.warn(
`Token list not available for chainId ${chainId} (effective ${effectiveChainId}). Skipping.`
);
setBalances([]);
return;
}
const balancesForChain = await getAllBalances(list);
// ---- after (unchanged) ----
// (rest of your function)
🤖 Prompt for AI Agents
In src/apps/deposit/components/AssetsList/AssetsList.tsx around lines 53 to 57,
the tokenLists construction/filtering allows chainId '100' to be excluded when
Gnosis is disabled which later leads to tokenLists[100] being undefined and
.tokens access throwing; update the filtering and subsequent access to use the
remapped chain id from getNetworkViem and add a safe fallback so you never
dereference undefined (e.g., remap the current chain id via getNetworkViem
before indexing tokenLists and use optional chaining or default to an empty
array when reading .tokens, or adjust the Object.entries filter to compare
against the remapped id rather than the raw '100').

Comment on lines 4 to 6
// Mock the environment variable
const originalEnv = import.meta.env;

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

Env mocking won’t affect isGnosisEnabled; module is evaluated before you mutate import.meta.env.

isGnosisEnabled is computed at import-time in src/utils/blockchain.ts. Since this test imports getNativeAssetForChainId before overriding env, both “enabled/disabled” cases will use the same cached value. Re-import the module after stubbing env, or mock the export directly.

Apply this restructuring to load fresh module state per scenario:

-import { gnosis, polygon } from 'viem/chains';
-import { getNativeAssetForChainId } from '../blockchain';
+import { gnosis, polygon } from 'viem/chains';

 // Mock the environment variable
 const originalEnv = import.meta.env;

 describe('getNativeAssetForChainId', () => {
+  beforeEach(() => {
+    vi.unstubAllEnvs();
+    vi.resetModules();
+  });
   afterEach(() => {
-    // Reset environment
-    Object.defineProperty(import.meta, 'env', {
-      value: originalEnv,
-      writable: true,
-    });
+    vi.unstubAllEnvs();
+    Object.defineProperty(import.meta, 'env', { value: originalEnv, writable: true });
   });

   it('returns {POL} for polygon', async () => {
-    const asset = getNativeAssetForChainId(polygon.id);
+    const { getNativeAssetForChainId } = await import('../blockchain');
+    const asset = getNativeAssetForChainId(polygon.id);
     expect(asset.name).toBe('POL');
     expect(asset.symbol).toBe('POL');
   });

   describe('when Gnosis feature flag is enabled', () => {
-    beforeEach(() => {
-      Object.defineProperty(import.meta, 'env', {
-        value: { ...originalEnv, VITE_FEATURE_FLAG_GNOSIS: 'true' },
-        writable: true,
-      });
-    });
+    beforeEach(() => vi.stubEnv('VITE_FEATURE_FLAG_GNOSIS', 'true'));

     it('returns XDAI for Gnosis', async () => {
-      const asset = getNativeAssetForChainId(gnosis.id);
+      const { getNativeAssetForChainId } = await import('../blockchain');
+      const asset = getNativeAssetForChainId(gnosis.id);
       expect(asset.name).toBe('XDAI');
       expect(asset.symbol).toBe('XDAI');
     });
   });

   describe('when Gnosis feature flag is disabled', () => {
-    beforeEach(() => {
-      Object.defineProperty(import.meta, 'env', {
-        value: { ...originalEnv, VITE_FEATURE_FLAG_GNOSIS: 'false' },
-        writable: true,
-      });
-    });
+    beforeEach(() => vi.stubEnv('VITE_FEATURE_FLAG_GNOSIS', 'false'));

     it('returns POL for Gnosis (fallback)', async () => {
-      const asset = getNativeAssetForChainId(gnosis.id);
+      const { getNativeAssetForChainId } = await import('../blockchain');
+      const asset = getNativeAssetForChainId(gnosis.id);
       expect(asset.name).toBe('POL');
       expect(asset.symbol).toBe('POL');
     });
   });
 });

Also applies to: 26-33, 41-47

🤖 Prompt for AI Agents
In src/utils/__tests__/blockchain.test.ts around lines 4-6 (and similarly for
26-33 and 41-47), the test mutates import.meta.env after the module (which
computes isGnosisEnabled at import time) has already been evaluated, so the
cached value is used and the test cannot toggle enabled/disabled; fix by
setting/stubbing import.meta.env before importing the module or by explicitly
re-importing the module fresh (e.g., using dynamic import or
jest.resetModules()/requireFresh) after you mutate import.meta.env so
isGnosisEnabled is evaluated with the desired env, or alternatively mock the
isGnosisEnabled export directly for each scenario.

Comment on lines +234 to 237
return isGnosisEnabled
? `https://gnosisscan.io/${isAddress ? 'address' : 'tx'}/`
: '';
case 56:
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Empty block explorer URL can ripple; verify call sites

Returning '' for chain 100 when disabled is fine if callers guard against falsy URLs. Otherwise, return undefined or avoid concatenation at usage sites.

Quick scan for risky concatenations:


🏁 Script executed:

#!/bin/bash
rg -nP -C2 'getBlockScan\([^)]*\)\s*(\+|`)' --type=ts --type=tsx

Length of output: 27


🏁 Script executed:

#!/bin/bash
rg -nP -C2 'getBlockScan\([^)]*\)\s*(\+|`)' -g '*.ts' -g '*.tsx'

Length of output: 0


🏁 Script executed:

#!/bin/bash
rg -n -C2 'getBlockScan\(' -g '*.ts' -g '*.tsx'

Length of output: 3020


Prevent broken explorer links when getBlockScan returns empty

  • src/components/BottomMenuModal/HistoryModal/TransactionsHistory.tsx:140
  • src/components/BottomMenuModal/HistoryModal/TransactionInfo.tsx:213, 217, 237
  • src/apps/pulse/components/Status/TxStatus.tsx:37

Either have getBlockScan return undefined/null when the explorer is disabled, or guard each ${getBlockScan(...)}${…} concatenation so you don’t render or open a malformed URL.

🤖 Prompt for AI Agents
In src/utils/blockchain.ts around lines 234–237, getBlockScan currently returns
an empty string when the explorer is disabled which leads to malformed URLs at
call sites; change it to return undefined (or null) instead of '' when disabled,
and then update all places that build URLs (e.g.,
src/components/BottomMenuModal/HistoryModal/TransactionsHistory.tsx line ~140,
TransactionInfo.tsx lines ~213/217/237, and
src/apps/pulse/components/Status/TxStatus.tsx line ~37) to guard the
concatenation by only constructing/opening the URL when getBlockScan(...) is
truthy (e.g., if (scanner) { const url = `${scanner}${path}`; ... }) so you
never render or open a broken URL.

Comment on lines +363 to +368
const STABLECOIN_ADDRESSES = Object.fromEntries(
Object.entries(allStablecoinAddresses).filter(
([chainId]) => isGnosisEnabled || chainId !== '100'
)
) as Record<number, Set<string>>;

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

Case sensitivity bug in stablecoin detection

Sets contain mixed-case addresses but lookups use toLowerCase(), causing false negatives (e.g., BSC USDC/USDT). Normalize to lowercase when building the sets.

-const STABLECOIN_ADDRESSES = Object.fromEntries(
-  Object.entries(allStablecoinAddresses).filter(
-    ([chainId]) => isGnosisEnabled || chainId !== '100'
-  )
-) as Record<number, Set<string>>;
+const STABLECOIN_ADDRESSES = Object.fromEntries(
+  Object.entries(allStablecoinAddresses)
+    .filter(([chainId]) => isGnosisEnabled || chainId !== '100')
+    .map(([chainId, set]) => [
+      Number(chainId),
+      new Set([...set].map((a) => a.toLowerCase())),
+    ])
+) as Record<number, Set<string>>;
📝 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 STABLECOIN_ADDRESSES = Object.fromEntries(
Object.entries(allStablecoinAddresses).filter(
([chainId]) => isGnosisEnabled || chainId !== '100'
)
) as Record<number, Set<string>>;
const STABLECOIN_ADDRESSES = Object.fromEntries(
Object.entries(allStablecoinAddresses)
.filter(([chainId]) => isGnosisEnabled || chainId !== '100')
.map(([chainId, set]) => [
Number(chainId),
new Set([...set].map((a) => a.toLowerCase())),
])
) as Record<number, Set<string>>;
🤖 Prompt for AI Agents
In src/utils/blockchain.ts around lines 363 to 368, the stablecoin address sets
are built from allStablecoinAddresses but retain mixed-case values while lookups
use toLowerCase(), causing lookup failures; when constructing
STABLECOIN_ADDRESSES convert every address to lowercase (e.g., map each address
through .toLowerCase()) so the Record<number, Set<string>> contains lowercase
strings only, and keep the same Set type for membership checks to work
correctly.

Copy link
Collaborator

@IAmKio IAmKio left a comment

Choose a reason for hiding this comment

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

LGTM!

@RanaBug RanaBug merged commit 56be0de into staging Sep 8, 2025
6 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Nov 3, 2025
3 tasks
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.

2 participants