Skip to content

feat/eoa-app#437

Merged
IAmKio merged 20 commits intostagingfrom
feat/eoa-app
Nov 17, 2025
Merged

feat/eoa-app#437
IAmKio merged 20 commits intostagingfrom
feat/eoa-app

Conversation

@IAmKio
Copy link
Collaborator

@IAmKio IAmKio commented Oct 21, 2025

Description

  • EOA app allows a user logged in to transfer assets out of their key wallet if they are on a 4337 account or indeed a 7702 wallet although this needs to be tested.

How Has This Been Tested?

  • Locally with injected wallet

Screenshots (if appropriate):

PillarX-10-22-2025_10_22_AM

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

    • Key Wallet app: view/manage EOA portfolio, send assets (direct & delegated), transaction tracking.
  • UI

    • Searchable assets list, asset rows with logo/balance/USD/24h change, send modal (recipient, MAX, USD estimate, network warnings), copyable wallet address, refresh balances, transaction list with explorer links.
  • Utilities & Types

    • Portfolio transformers, blockchain send/switch helpers, formatting/explorer helpers, media utilities, wallet-provider typings.
  • Documentation

    • Comprehensive Key Wallet README and app manifest.
  • Style

    • Tailwind config and consistent input focus styles.
  • Tests

    • Extensive unit tests for components and utilities.
  • Chores

    • Safer initialization fallback and provider synchronization improvements.

@IAmKio IAmKio self-assigned this Oct 21, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 21, 2025

Walkthrough

Adds a new Key Wallet app under src/apps/key-wallet: UI components, Tailwind styling/config, TypeScript types, blockchain & portfolio utilities, media helpers, provider context exposure, manifest/README, and comprehensive tests; implements portfolio fetching, asset transforms, send flows (chain switching, ERC‑20/native send), and transaction tracking.

Changes

Cohort / File(s) Summary
Documentation
src/apps/key-wallet/README.md
New README describing the Key Wallet app: purpose, features, supported chains, components, utilities/types, API integration, and transaction execution details.
App entry
src/apps/key-wallet/index.tsx
New main App component: derives EOA/provider, fetches portfolio, transforms assets, computes totals, manages selected asset & transactions, opens SendAssetModal, polls receipts, persists/clears tx entries, supports manual refresh.
UI — Display Components
src/apps/key-wallet/components/*
src/apps/key-wallet/components/WalletAddress.tsx, src/apps/key-wallet/components/AssetRow.tsx, src/apps/key-wallet/components/AssetsList.tsx, src/apps/key-wallet/components/TransactionStatus.tsx
New components: wallet address copy UI, asset row (logo/symbol/chain/balance/USD/24h change), searchable asset list with loading/empty states and totals, and transaction list with explorer links and clear action.
UI — Interactive Components
src/apps/key-wallet/components/SearchAssets.tsx, src/apps/key-wallet/components/SendAssetModal.tsx
New Fuse.js-based search component and SendAssetModal: recipient/amount inputs, MAX handling, validation, USD estimate, chain detection/switching, sendTransaction integration (provider & delegated flows), error/loading handling, and onSuccess callback.
Types
src/apps/key-wallet/types/index.ts
Adds exported interfaces: Asset, SendFormData, and TransactionStatus.
Blockchain utilities
src/apps/key-wallet/utils/blockchain.ts
New blockchain helpers: chains, getChainById, isNativeAsset, switchChain (with add-chain fallback), getCurrentChainId, sendTransaction (native & ERC‑20 via viem clients), getBlockExplorerUrl, formatBalance, formatUsdValue, shortenAddress.
Portfolio utilities
src/apps/key-wallet/utils/portfolio.ts
New helpers: transformPortfolioToAssets, getTotalPortfolioValue, groupAssetsByChain — flatten portfolio API data into Asset[], compute usdBalance, filter zero balances, sort/group by chain.
Styling & Tailwind
src/apps/key-wallet/styles/tailwindKeyWallet.css, src/apps/key-wallet/tailwind.keywallet.config.js
Tailwind entry CSS (form resets, focus styles) and app-scoped Tailwind config (content globs, dark mode, breakpoints, custom colors and font family).
Manifest
src/apps/key-wallet/manifest.json
New app manifest with title, description, and English translations.
Shared util
src/utils/media.ts
Media query utilities and hooks: MOBILE_QUERY, isClientMatchMedia, useMediaQuery, useIsMobile, isMobileViewport.
Tests
src/apps/key-wallet/components/__tests__/*, src/apps/key-wallet/utils/__tests__/*
Comprehensive unit tests for components (AssetRow, AssetsList, SearchAssets, SendAssetModal, TransactionStatus, WalletAddress) and utilities (blockchain, portfolio).
Provider & misc
src/providers/EtherspotTransactionKitProvider.tsx, index.html
Etherspot provider context now exposes walletProvider and syncs external provider from config; index.html wraps HypeLab.initialize in try/catch.
Types — wallet provider
src/types/walletProvider.ts
New provider-related types: JsonRpcRequestArguments, Eip1193LikeProvider, and WalletProviderLike union.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App as Key Wallet App
    participant API as Portfolio API
    participant Modal as SendAssetModal
    participant Blockchain as Blockchain Utils
    participant Provider as Wallet Provider

    User->>App: Open app / load wallet
    App->>API: Fetch portfolio for EOA
    API-->>App: Portfolio data
    App->>App: transformPortfolioToAssets(), compute totals
    App->>User: render AssetsList & WalletAddress

    User->>App: Click asset
    App->>Modal: open with asset

    User->>Modal: enter recipient & amount
    Modal->>Blockchain: getCurrentChainId(provider)
    Blockchain->>Provider: eth_chainId
    Provider-->>Blockchain: chainId

    alt chain mismatch
        Modal->>Blockchain: switchChain(asset.chainId, provider)
        Blockchain->>Provider: wallet_switchEthereumChain / wallet_addEthereumChain
        Provider-->>Blockchain: switched / added
    end

    User->>Modal: Click Send
    Modal->>Blockchain: sendTransaction(asset, recipient, amount, provider)
    Blockchain->>Provider: eth_sendTransaction (native or encoded ERC-20)
    Provider-->>Blockchain: txHash

    Modal->>App: onSuccess(txHash, chainId)
    App->>App: add tx (pending), refetch portfolio, poll receipt
    App->>User: update TransactionStatus entries
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Areas needing extra attention:
    • src/apps/key-wallet/utils/blockchain.ts — chain switching/add-chain fallback, viem WalletClient/transport usage, ERC‑20 calldata encoding, gas estimation, validation and error messages.
    • src/apps/key-wallet/components/SendAssetModal.tsx — input validation, MAX handling, chain-switch logic, delegated vs direct send flows, and integration with sendTransaction and onSuccess.
    • src/apps/key-wallet/index.tsx — transaction lifecycle: creation, polling receipts, persistence and refresh timing.
    • Tests that heavily mock provider/viem behavior — ensure mocks reflect runtime assumptions.

Possibly related PRs

Suggested reviewers

  • RanaBug
  • vignesha22

Poem

🐰 I twitched my whiskers, found a key,
Tokens lined up brave and free,
I hopped to search with fuzzy sight,
Switched a chain and sent by night,
Tiny hops — big confirmations! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'feat/eoa-app' is vague and uses a generic prefix pattern that doesn't clearly communicate the main feature or purpose to reviewers scanning history. Revise the title to be more descriptive, such as 'Add EOA wallet asset transfer feature' or 'feat: Create Key Wallet app for managing EOA assets', to clearly convey the primary functionality.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the core functionality and testing approach, but lacks comprehensive detail about the implementation scope, supported chains, and specific technical architecture decisions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/eoa-app

📜 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 e0bde17 and 8bfff01.

📒 Files selected for processing (2)
  • src/apps/key-wallet/components/SendAssetModal.tsx (1 hunks)
  • src/apps/key-wallet/components/__tests__/SendAssetModal.test.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.
📚 Learning: 2025-11-04T14:34:00.373Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.

Applied to files:

  • src/apps/key-wallet/components/SendAssetModal.tsx
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/apps/key-wallet/components/SendAssetModal.tsx
🧬 Code graph analysis (2)
src/apps/key-wallet/components/SendAssetModal.tsx (4)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
src/types/walletProvider.ts (1)
  • WalletProviderLike (15-15)
src/apps/key-wallet/utils/blockchain.ts (7)
  • getCurrentChainId (131-152)
  • getChainById (45-53)
  • isNativeAsset (55-60)
  • switchChain (66-126)
  • sendTransaction (154-284)
  • formatBalance (292-296)
  • formatUsdValue (298-302)
src/utils/eip7702Authorization.ts (1)
  • getEIP7702AuthorizationIfNeeded (16-95)
src/apps/key-wallet/components/__tests__/SendAssetModal.test.tsx (1)
src/types/walletProvider.ts (1)
  • WalletProviderLike (15-15)
🪛 Biome (2.1.2)
src/apps/key-wallet/components/__tests__/SendAssetModal.test.tsx

[error] 156-156: This number literal will lose precision at runtime.

The value at runtime will be 0.12345678901234568

(lint/correctness/noPrecisionLoss)

⏰ 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: lint
  • GitHub Check: unit-tests
  • GitHub Check: build
🔇 Additional comments (10)
src/apps/key-wallet/components/__tests__/SendAssetModal.test.tsx (3)

1-60: LGTM! Comprehensive test setup.

The mock configuration is thorough and correctly mirrors the component's dependencies, including the transaction kit hook, blockchain utilities, and asset fixtures.


153-179: Precision loss is expected and acceptable here.

The static analysis warning about precision loss at line 156 is correct—JavaScript cannot exactly represent 0.123456789012345678. However, this is acceptable in this test context because:

  1. The test validates formatMaxBalance behavior with JavaScript's actual number precision
  2. The expectedValue is computed from the runtime balance (line 160), so the test correctly validates real behavior rather than an idealized precision

181-491: LGTM! Excellent test coverage.

The test suite comprehensively covers validation, transaction flows, chain switching, error handling (including Smart Account-specific errors), and loading states. The tests effectively validate both the happy path and edge cases.

src/apps/key-wallet/components/SendAssetModal.tsx (7)

38-57: LGTM! Well-designed helper functions.

Both clampDecimals and formatMaxBalance handle edge cases properly (NaN, undefined, zero decimals, trailing zeros) and work together to format balances with up to 18 decimal places as needed.


59-104: LGTM! Robust error message extraction.

The extractErrorMessage function comprehensively handles various error shapes and provides user-friendly messaging for Smart Account fund issues. The case-insensitive substring matching for AA21 and Smart Account errors is a good approach.


112-174: LGTM! Well-structured state management.

The component properly initializes state, handles provider resolution, and manages chain detection for both direct wallet and delegated EOA flows. The defensive coding with optional chaining and error handling in isDelegatedEoa is appropriate.

Based on learnings


180-204: LGTM! Solid validation logic.

The form validation properly checks for recipient address format (Ethereum address regex), amount validity, and balance sufficiency with clear error messages.


206-273: LGTM! Well-structured transaction kit flow.

The sendWithTransactionKit function properly handles the delegated EOA transaction flow with comprehensive error checking at each step (estimate, send, userOpHash, txHash resolution). The user-friendly error messages for pending confirmations are a nice touch.


275-337: Transaction flow is solid, but consider the past review feedback.

The handleSend function properly handles both wallet provider and transaction kit paths with appropriate validation and error handling.

However, as noted in previous reviews, lines 297-299 use setError for the informational "Switching to..." message, which displays it in the error UI (red box). While functional, this could confuse users since it's not an error state.

If this UX concern hasn't been addressed intentionally, consider implementing the suggestion from the previous review to use a separate info message state instead of the error state for chain switching notifications.


341-489: LGTM! Well-designed modal UI.

The render logic provides a clear, accessible interface with proper image fallback handling, informative chain warnings, dynamic button states, and conditional USD estimates. The Tailwind styling creates a polished modal experience.


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.

@IAmKio IAmKio changed the title Initial commit feat/eoa-app Oct 21, 2025
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Oct 21, 2025

Deploying x with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8bfff01
Status: ✅  Deploy successful!
Preview URL: https://fa3d8644.x-e62.pages.dev
Branch Preview URL: https://feat-eoa-app.x-e62.pages.dev

View logs

@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) October 22, 2025 14:38 Inactive
@IAmKio IAmKio marked this pull request as ready for review November 3, 2025 11:49
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: 8

♻️ Duplicate comments (1)
src/apps/key-wallet/utils/blockchain.ts (1)

92-107: Apply the same type safety improvement as suggested for switchChain.

The walletProvider should be properly typed. See the earlier comment on switchChain for a suggested EIP1193Provider type.

🧹 Nitpick comments (3)
src/apps/key-wallet/utils/blockchain.ts (3)

39-87: Type the walletProvider parameter for better type safety.

The function logic and error handling are solid, but walletProvider: any loses type safety. Consider using a proper EIP-1193 provider type.

Create a type definition or import from viem:

interface EIP1193Provider {
  request: (args: { method: string; params?: any[] }) => Promise<any>;
}

export const switchChain = async (
  chainId: number,
  walletProvider: EIP1193Provider
): Promise<void> => {
  // ...
}

159-178: Consider returning null instead of empty string for unsupported chains.

Returning an empty string for unsupported chains or when Gnosis is disabled makes it harder for consumers to distinguish between "no explorer available" and an actual error.

-export const getBlockExplorerUrl = (chainId: number, txHash: string): string => {
+export const getBlockExplorerUrl = (chainId: number, txHash: string): string | null => {
   switch (chainId) {
     case 1:
       return `https://etherscan.io/tx/${txHash}`;
     // ... other cases
     case 100:
-      return isGnosisEnabled ? `https://gnosisscan.io/tx/${txHash}` : '';
+      return isGnosisEnabled ? `https://gnosisscan.io/tx/${txHash}` : null;
     // ... other cases
     default:
-      return '';
+      return null;
   }
 };

192-194: Consider adding length validation for edge cases.

While Ethereum addresses are always 42 characters, adding a guard would make the function more robust if called with unexpected input.

 export const shortenAddress = (address: string, chars: number = 4): string => {
+  if (address.length < chars * 2 + 2) {
+    return address;
+  }
   return `${address.slice(0, chars + 2)}...${address.slice(-chars)}`;
 };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a03b7b7 and e926ad9.

⛔ Files ignored due to path filters (2)
  • src/apps/key-wallet/icon.png is excluded by !**/*.png
  • src/apps/key-wallet/images/logo-unknown.png is excluded by !**/*.png
📒 Files selected for processing (14)
  • src/apps/key-wallet/README.md (1 hunks)
  • src/apps/key-wallet/components/AssetRow.tsx (1 hunks)
  • src/apps/key-wallet/components/AssetsList.tsx (1 hunks)
  • src/apps/key-wallet/components/SearchAssets.tsx (1 hunks)
  • src/apps/key-wallet/components/SendAssetModal.tsx (1 hunks)
  • src/apps/key-wallet/components/TransactionStatus.tsx (1 hunks)
  • src/apps/key-wallet/components/WalletAddress.tsx (1 hunks)
  • src/apps/key-wallet/index.tsx (1 hunks)
  • src/apps/key-wallet/manifest.json (1 hunks)
  • src/apps/key-wallet/styles/tailwindKeyWallet.css (1 hunks)
  • src/apps/key-wallet/tailwind.keywallet.config.js (1 hunks)
  • src/apps/key-wallet/types/index.ts (1 hunks)
  • src/apps/key-wallet/utils/blockchain.ts (1 hunks)
  • src/apps/key-wallet/utils/portfolio.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/apps/key-wallet/components/TransactionStatus.tsx
  • src/apps/key-wallet/index.tsx
📚 Learning: 2025-03-28T09:22:22.712Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 275
File: src/apps/the-exchange/components/DropdownTokensList/DropdownTokenList.tsx:180-195
Timestamp: 2025-03-28T09:22:22.712Z
Learning: In the Exchange app, `swapTokenList` and `receiveTokenList` are derived from `searchTokenResult` when search is active, so including `searchToken` in the useEffect dependency array that uses these lists would be redundant as the lists will update when search results change.

Applied to files:

  • src/apps/key-wallet/components/AssetsList.tsx
  • src/apps/key-wallet/components/SearchAssets.tsx
🧬 Code graph analysis (8)
src/apps/key-wallet/components/TransactionStatus.tsx (2)
src/apps/key-wallet/types/index.ts (1)
  • TransactionStatus (21-26)
src/apps/key-wallet/utils/blockchain.ts (2)
  • getBlockExplorerUrl (159-178)
  • shortenAddress (192-194)
src/apps/key-wallet/components/AssetRow.tsx (2)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
src/apps/key-wallet/utils/blockchain.ts (2)
  • formatBalance (180-184)
  • formatUsdValue (186-190)
src/apps/key-wallet/index.tsx (2)
src/apps/key-wallet/types/index.ts (2)
  • Asset (1-14)
  • TransactionStatus (21-26)
src/apps/key-wallet/utils/portfolio.ts (2)
  • transformPortfolioToAssets (14-51)
  • getTotalPortfolioValue (53-55)
src/apps/key-wallet/components/AssetsList.tsx (2)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
src/apps/key-wallet/utils/blockchain.ts (1)
  • formatUsdValue (186-190)
src/apps/key-wallet/components/SearchAssets.tsx (1)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
src/apps/key-wallet/components/SendAssetModal.tsx (2)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
src/apps/key-wallet/utils/blockchain.ts (6)
  • getCurrentChainId (92-107)
  • getChainById (24-26)
  • switchChain (39-87)
  • sendTransaction (109-157)
  • formatBalance (180-184)
  • formatUsdValue (186-190)
src/apps/key-wallet/utils/blockchain.ts (1)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
src/apps/key-wallet/utils/portfolio.ts (2)
src/types/api.ts (1)
  • PortfolioData (739-748)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
🔇 Additional comments (10)
src/apps/key-wallet/README.md (1)

1-105: LGTM!

The documentation is comprehensive and well-structured, providing clear guidance on features, components, usage, and technical details.

src/apps/key-wallet/manifest.json (1)

1-10: LGTM!

The manifest structure is correct and includes proper localization support.

src/apps/key-wallet/types/index.ts (1)

1-27: LGTM!

The type definitions are well-structured and comprehensive for managing assets, send operations, and transaction tracking.

src/apps/key-wallet/components/TransactionStatus.tsx (1)

1-87: LGTM!

The component is well-structured with proper status handling, accessibility attributes (rel="noopener noreferrer"), and clean separation of concerns. The early return pattern and conditional rendering are appropriate.

src/apps/key-wallet/components/SearchAssets.tsx (1)

37-37: Review comment is incorrect—no action required.

The parent component (AssetsList.tsx) properly memoizes handleFilteredAssetsChange using useCallback with an empty dependency array (line 27–29), ensuring a stable function reference across renders. Including onFilteredAssetsChange in the SearchAssets dependency array is both safe and appropriate. No infinite loop risk exists.

Likely an incorrect or invalid review comment.

src/apps/key-wallet/utils/blockchain.ts (5)

1-14: LGTM! Clean imports.

The imports are well-organized and appropriate. Using viem for blockchain interactions is a solid choice.


16-22: LGTM! Feature flag implementation is appropriate.

The Gnosis feature flag pattern and chain filtering logic work correctly.


28-33: LGTM! Native asset detection is correct.

The function properly handles both zero address and the EEE...EEE sentinel value used by some APIs.


180-184: LGTM! Balance formatting handles edge cases well.

The function appropriately handles zero and very small balances with clear display values.


186-190: LGTM! USD value formatting is well-implemented.

The function handles edge cases and provides consistent currency formatting.

@@ -0,0 +1,42 @@
import { useState } from 'react';
import { shortenAddress } from '../utils/blockchain';
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove unused import.

The shortenAddress utility is imported but never used in this component. The address is displayed in full on Line 25.

Apply this diff:

-import { shortenAddress } from '../utils/blockchain';
📝 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
import { shortenAddress } from '../utils/blockchain';
🤖 Prompt for AI Agents
In src/apps/key-wallet/components/WalletAddress.tsx around line 2, the import
"shortenAddress" from '../utils/blockchain' is unused; remove that import
statement to clean up unused code and avoid lint warnings. Ensure no other
references to shortenAddress remain in the file and save the file.

@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 3, 2025 16:34 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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e926ad9 and b4b6a93.

📒 Files selected for processing (6)
  • src/apps/key-wallet/components/WalletAddress.tsx (1 hunks)
  • src/apps/key-wallet/index.tsx (1 hunks)
  • src/apps/key-wallet/styles/tailwindKeyWallet.css (1 hunks)
  • src/apps/key-wallet/tailwind.keywallet.config.js (1 hunks)
  • src/apps/key-wallet/utils/blockchain.ts (1 hunks)
  • src/utils/media.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/apps/key-wallet/components/WalletAddress.tsx
  • src/apps/key-wallet/styles/tailwindKeyWallet.css
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/apps/key-wallet/index.tsx
🧬 Code graph analysis (2)
src/apps/key-wallet/index.tsx (3)
src/apps/key-wallet/types/index.ts (2)
  • Asset (1-14)
  • TransactionStatus (21-26)
src/apps/key-wallet/utils/portfolio.ts (2)
  • transformPortfolioToAssets (14-51)
  • getTotalPortfolioValue (53-55)
src/apps/key-wallet/utils/blockchain.ts (1)
  • getChainById (24-32)
src/apps/key-wallet/utils/blockchain.ts (2)
src/utils/blockchain.ts (1)
  • isGnosisEnabled (41-42)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
⏰ 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: lint
  • GitHub Check: unit-tests
  • GitHub Check: build

Comment on lines +123 to +125
const chain = getChainById(chainId);
const publicClient = createPublicClient({ chain, transport: http() });

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

Point the public client at a real RPC.
http() with no URL dials http://127.0.0.1:8545, so waitForTransactionReceipt always fails outside local dev and you end up marking successful transactions as failed. Please resolve the chain’s RPC URL before instantiating the client.

-    const chain = getChainById(chainId);
-    const publicClient = createPublicClient({ chain, transport: http() });
+    const chain = getChainById(chainId);
+    const rpcUrl =
+      chain.rpcUrls.public?.http?.[0] ?? chain.rpcUrls.default.http?.[0];
+    if (!rpcUrl) {
+      console.error(`No RPC URL configured for ${chain.name}`);
+      return;
+    }
+    const publicClient = createPublicClient({ chain, transport: http(rpcUrl) });
🤖 Prompt for AI Agents
In src/apps/key-wallet/index.tsx around lines 123 to 125, the public client is
created with http() which defaults to localhost; resolve the chain's RPC URL
first (e.g., from chain.rpcUrls.default.http[0] or a configured RPC mapping) and
pass that URL into the transport (http({ url })) when calling
createPublicClient; include a sensible fallback or throw/log an error if no RPC
URL is available so waitForTransactionReceipt points at a real node.

Comment on lines +140 to +145
const balanceInWei = parseUnits(asset.balance.toString(), asset.decimals);

// Ensure amount doesn't exceed balance
if (amountInWei > balanceInWei) {
throw new Error('Insufficient balance');
}
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

Don’t feed scientific-notation balances into parseUnits.
asset.balance is a JS number; small values become strings like 1e-7, which parseUnits rejects. That throws for many ERC‑20s with tiny balances and blocks transfers. Please carry the on-chain balance in an exact string/BigInt (e.g., store the raw wei amount on Asset and reuse it here) or otherwise normalize the decimal string before calling parseUnits, so the comparison always works for subunit balances.

🤖 Prompt for AI Agents
In src/apps/key-wallet/utils/blockchain.ts around lines 140 to 145,
asset.balance is a JS number that can be serialized to scientific notation (e.g.
"1e-7") which parseUnits rejects; update the code to use an exact on‑chain
representation (e.g. add/consume an Asset field like balanceWei or balanceRaw as
a string or BigInt containing the raw wei amount) or otherwise convert the JS
number to a precise non‑scientific decimal string before calling parseUnits.
Concretely: stop passing a JS number into parseUnits, read or add the raw wei
string/BigInt on Asset and compare using ethers BigNumber (or normalize with a
safe decimal library) so the amountInWei vs balanceInWei comparison uses exact
integers and never receives scientific-notation strings.

Comment on lines +161 to +165
// Create public client for gas estimation
const publicClient = createPublicClient({
chain,
transport: http(),
});
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

Use an actual RPC endpoint for gas estimation.
http() without arguments still points to localhost, so both gas estimation and subsequent sends explode once you leave a local node. Resolve the chain’s configured RPC (public/default) and pass it to http(...) before creating the public client.

-  const publicClient = createPublicClient({
-    chain,
-    transport: http(),
-  });
+  const rpcUrl =
+    chain.rpcUrls.public?.http?.[0] ?? chain.rpcUrls.default.http?.[0];
+  if (!rpcUrl) {
+    throw new Error(`No RPC URL configured for ${chain.name}`);
+  }
+  const publicClient = createPublicClient({
+    chain,
+    transport: http(rpcUrl),
+  });
📝 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
// Create public client for gas estimation
const publicClient = createPublicClient({
chain,
transport: http(),
});
// Create public client for gas estimation
const rpcUrl =
chain.rpcUrls.public?.http?.[0] ?? chain.rpcUrls.default.http?.[0];
if (!rpcUrl) {
throw new Error(`No RPC URL configured for ${chain.name}`);
}
const publicClient = createPublicClient({
chain,
transport: http(rpcUrl),
});
🤖 Prompt for AI Agents
In src/apps/key-wallet/utils/blockchain.ts around lines 161 to 165, the public
client is created with http() which defaults to localhost; change it to resolve
the chain's configured RPC endpoint (prefer the chain's public or default RPC
URL) and pass that URL into http(<url>) when creating the public client; ensure
you pick a safe fallback if the chain object has no RPC configured (throw or use
a configured global default) and validate the URL before calling
createPublicClient.

@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 3, 2025 17:01 Inactive
@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 3, 2025 17:02 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: 1

♻️ Duplicate comments (1)
src/utils/media.ts (1)

26-26: Safari/iOS < 14 compatibility issue.

As previously noted, addEventListener is not supported in Safari/iOS < 14. The code needs to detect and fall back to the legacy addListener/removeListener methods when addEventListener is unavailable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b4b6a93 and 30ac060.

📒 Files selected for processing (1)
  • src/utils/media.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.
⏰ 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: lint
  • GitHub Check: unit-tests
  • GitHub Check: build
🔇 Additional comments (3)
src/utils/media.ts (3)

3-7: LGTM!

The SSR guard and fallback logic are appropriate.


32-32: LGTM!

Standard mobile breakpoint definition.


34-36: LGTM!

Clean convenience wrappers for the mobile breakpoint.

@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 3, 2025 23:06 Inactive
@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 3, 2025 23:39 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

🧹 Nitpick comments (6)
src/apps/key-wallet/components/__tests__/WalletAddress.test.tsx (2)

38-44: Consider a more robust mocking strategy for mobile tests.

The mockReturnValueOnce(true) approach after a dynamic import may be fragile if the component calls useIsMobile multiple times or if test execution order varies. Consider resetting and configuring the mock explicitly within this test:

  it('displays shortened address on mobile', async () => {
    const { useIsMobile } = await import('../../../../utils/media');
-   vi.mocked(useIsMobile).mockReturnValueOnce(true);
+   vi.mocked(useIsMobile).mockReturnValue(true);

    render(<WalletAddress address={mockAddress} />);
    expect(screen.getByText(mockShortenedAddress)).toBeInTheDocument();
+   
+   // Reset to default for subsequent tests
+   vi.mocked(useIsMobile).mockReturnValue(false);
  });

Alternatively, wrap the mock setup in a helper function to make desktop/mobile test contexts more explicit.


109-135: Consider verifying UI state on clipboard failure.

The error handling test properly verifies that errors are logged, but doesn't check the UI state. Consider adding an assertion to ensure the "Copied" state doesn't appear when the clipboard operation fails:

    // Verify the error was logged
    expect(mockWriteText).toHaveBeenCalledWith(mockAddress);
    expect(consoleErrorSpy).toHaveBeenCalledWith(
      'Failed to copy address:',
      expect.any(Error)
    );
+   
+   // Verify "Copied" state is not shown on error
+   expect(screen.queryByText(/copied/i)).not.toBeInTheDocument();

    consoleErrorSpy.mockRestore();

This ensures users don't receive misleading feedback when the copy operation fails.

src/apps/key-wallet/components/__tests__/TransactionStatus.test.tsx (1)

21-34: Consider whether snapshot testing adds value here.

Snapshot tests can be brittle and may pass even when bugs are introduced if snapshots are updated without careful review. Consider replacing with more specific assertions that validate the actual behavior and structure you care about.

src/apps/key-wallet/utils/__tests__/blockchain.test.ts (1)

1-1: Remove unused afterEach import.

The afterEach hook is imported but never used in the test suite.

Apply this diff:

-import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
+import { describe, it, expect, vi, beforeEach } from 'vitest';
src/apps/key-wallet/utils/__tests__/portfolio.test.ts (2)

186-214: Consider using proper typing instead of as any.

While using null as any at lines 196-197 effectively tests the edge case of missing price values, it bypasses TypeScript's type safety. Consider using a more type-safe approach, such as creating a partial object or using optional properties if the PortfolioData type supports them.

Alternative approach:

-            price: null as any,
-            price_change_24h: null as any,
+            price: undefined,
+            price_change_24h: undefined,

Or explicitly define the test type:

-      } as PortfolioData;
+      } as Partial<PortfolioData> as PortfolioData;

157-184: Add defensive validation for chainId parsing; test malformed formats.

The concern is valid but incomplete. The implementation at portfolio.ts line 26 uses contract.chainId.split(':')[1] without guards, and identical unguarded parsing exists in pillarXApiWalletPortfolio.ts line 31-38. Currently only valid "eip155:XXX" format is tested.

While the code is defensively safe (malformed input converts to NaN → 'Unknown' chain name), explicit validation and test coverage for edge cases would improve clarity and maintainability:

  • Missing ':' separator → split(':')[1] returns undefinedNumber(undefined)NaN
  • Non-numeric suffix → Number('abc')NaN
  • Both cases safely fall back to 'Unknown' but are untested

Add tests for malformed formats (missing ':', non-numeric values, empty strings) and consider adding explicit format validation before parsing.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30ac060 and 3546a47.

⛔ Files ignored due to path filters (8)
  • src/apps/key-wallet/components/__tests__/__snapshots__/AssetRow.test.tsx.snap is excluded by !**/*.snap
  • src/apps/key-wallet/components/__tests__/__snapshots__/AssetsList.test.tsx.snap is excluded by !**/*.snap
  • src/apps/key-wallet/components/__tests__/__snapshots__/SearchAssets.test.tsx.snap is excluded by !**/*.snap
  • src/apps/key-wallet/components/__tests__/__snapshots__/SendAssetModal.test.tsx.snap is excluded by !**/*.snap
  • src/apps/key-wallet/components/__tests__/__snapshots__/TransactionStatus.test.tsx.snap is excluded by !**/*.snap
  • src/apps/key-wallet/components/__tests__/__snapshots__/WalletAddress.test.tsx.snap is excluded by !**/*.snap
  • src/apps/pillarx-app/components/TokenMarketDataRow/tests/__snapshots__/LeftColumnTokenMarketDataRow.test.tsx.snap is excluded by !**/*.snap
  • src/apps/pillarx-app/components/TokensWithMarketDataTile/test/__snapshots__/TokensWithMarketDataTile.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (9)
  • src/apps/key-wallet/components/SearchAssets.tsx (1 hunks)
  • src/apps/key-wallet/components/__tests__/AssetRow.test.tsx (1 hunks)
  • src/apps/key-wallet/components/__tests__/AssetsList.test.tsx (1 hunks)
  • src/apps/key-wallet/components/__tests__/SearchAssets.test.tsx (1 hunks)
  • src/apps/key-wallet/components/__tests__/SendAssetModal.test.tsx (1 hunks)
  • src/apps/key-wallet/components/__tests__/TransactionStatus.test.tsx (1 hunks)
  • src/apps/key-wallet/components/__tests__/WalletAddress.test.tsx (1 hunks)
  • src/apps/key-wallet/utils/__tests__/blockchain.test.ts (1 hunks)
  • src/apps/key-wallet/utils/__tests__/portfolio.test.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.
📚 Learning: 2025-03-28T09:22:22.712Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 275
File: src/apps/the-exchange/components/DropdownTokensList/DropdownTokenList.tsx:180-195
Timestamp: 2025-03-28T09:22:22.712Z
Learning: In the Exchange app, `swapTokenList` and `receiveTokenList` are derived from `searchTokenResult` when search is active, so including `searchToken` in the useEffect dependency array that uses these lists would be redundant as the lists will update when search results change.

Applied to files:

  • src/apps/key-wallet/components/SearchAssets.tsx
🧬 Code graph analysis (5)
src/apps/key-wallet/utils/__tests__/blockchain.test.ts (1)
src/apps/key-wallet/utils/blockchain.ts (9)
  • getChainById (24-32)
  • isNativeAsset (34-39)
  • switchChain (45-93)
  • getCurrentChainId (98-113)
  • sendTransaction (115-223)
  • getBlockExplorerUrl (225-244)
  • formatBalance (246-250)
  • formatUsdValue (252-256)
  • shortenAddress (258-260)
src/apps/key-wallet/utils/__tests__/portfolio.test.ts (2)
src/apps/key-wallet/utils/portfolio.ts (3)
  • transformPortfolioToAssets (14-51)
  • getTotalPortfolioValue (53-55)
  • groupAssetsByChain (57-71)
src/types/api.ts (1)
  • PortfolioData (739-748)
src/apps/key-wallet/components/__tests__/TransactionStatus.test.tsx (1)
src/apps/key-wallet/types/index.ts (1)
  • TransactionStatus (21-26)
src/apps/key-wallet/components/__tests__/WalletAddress.test.tsx (1)
src/utils/media.ts (1)
  • useIsMobile (34-34)
src/apps/key-wallet/components/SearchAssets.tsx (1)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
⏰ 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: lint
  • GitHub Check: unit-tests
  • GitHub Check: build
🔇 Additional comments (17)
src/apps/key-wallet/components/__tests__/WalletAddress.test.tsx (2)

1-26: LGTM: Clean test setup with proper mocking and lifecycle hooks.

The test setup properly mocks the clipboard API and media query utilities, with good cleanup in afterEach.


46-100: Excellent coverage of copy functionality with proper async handling.

The tests properly handle asynchronous clipboard operations, use fake timers to test timeout behavior, and correctly wrap state updates in act(). The restoration of real timers ensures no side effects on subsequent tests.

src/apps/key-wallet/components/__tests__/TransactionStatus.test.tsx (4)

1-12: LGTM!

The test setup follows standard patterns with proper mock initialization and cleanup.


36-82: LGTM!

The status variant tests comprehensively cover pending, success, and failed states with appropriate assertions.


116-152: LGTM!

Block explorer link tests properly validate chain-specific URLs for both Ethereum and Polygon networks.


173-194: LGTM!

The multiple transactions test effectively verifies that the component handles rendering multiple transaction statuses with independent clear buttons.

src/apps/key-wallet/components/__tests__/AssetRow.test.tsx (1)

29-142: Excellent test coverage!

The test suite comprehensively covers rendering, snapshots, asset display, balance formatting, price change styling, logo handling with fallbacks, click interactions, and graceful handling of missing data.

src/apps/key-wallet/components/__tests__/SendAssetModal.test.tsx (1)

1-421: Comprehensive test suite with excellent coverage!

The test suite thoroughly covers modal behavior including null asset handling, display, validation (address, amount, balance), USD estimation, successful transactions, chain switching, error handling, and loading states. The mocking strategy is appropriate and the async test patterns are correctly implemented.

src/apps/key-wallet/utils/__tests__/blockchain.test.ts (1)

32-361: Excellent test coverage for blockchain utilities!

The test suite comprehensively covers all exported utilities including chain management, native asset detection, chain switching with various error scenarios, transaction sending for both native and ERC-20 tokens, block explorer URLs, formatting functions, and address shortening. The mocking strategy appropriately isolates viem dependencies.

src/apps/key-wallet/components/__tests__/AssetsList.test.tsx (1)

1-160: Well-structured test suite with good coverage!

The test suite effectively covers loading states, empty states, portfolio value display, asset count variations (singular/plural), asset rendering, and search component integration. The test structure is clear and assertions are appropriate.

src/apps/key-wallet/components/__tests__/SearchAssets.test.tsx (1)

1-217: Thorough test coverage for search functionality!

The test suite comprehensively covers search input rendering, filtering by multiple fields (symbol, name, chain), fuzzy search behavior, clear button functionality, query display, and edge cases like empty assets. The tests properly verify the callback mechanism and filtering results.

src/apps/key-wallet/components/SearchAssets.tsx (2)

43-97: Clean and accessible search UI!

The component provides a well-structured search interface with proper accessibility attributes (aria-label on the clear button), visual feedback (search icon, clear button), and user-friendly features (showing current search query). The styling uses consistent design tokens.


28-37: Callback memoization verified—no issues found.

Verification confirms that the parent component (AssetsList) properly memoizes onFilteredAssetsChange using useCallback with an empty dependency array. The callback reference is stable across renders, so including onFilteredAssetsChange in the SearchAssets useEffect dependency array will not cause unnecessary re-runs or performance issues.

src/apps/key-wallet/utils/__tests__/portfolio.test.ts (4)

1-9: LGTM!

The test structure and imports are well-organized. The file properly imports the utilities under test and follows vitest conventions.


10-155: Excellent test coverage for the main scenarios.

The test suite covers the critical paths including undefined inputs, empty data, zero-balance filtering, sorting, and the transformation logic itself. The assertions are precise and the test data is realistic.


217-257: LGTM!

The test suite for getTotalPortfolioValue appropriately covers both the empty array edge case and the calculation logic with multiple assets. The assertions are clear and correct.


259-317: LGTM!

The test suite for groupAssetsByChain covers the essential scenarios including empty input and multi-chain grouping. The test correctly verifies that assets are properly grouped by chain name while preserving references to the original asset objects.

@@ -0,0 +1,142 @@
import { describe, it, expect, vi } from 'vitest';
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

Missing beforeEach import.

Line 25 uses beforeEach but it's not included in the imports from vitest. This will cause a runtime error.

Apply this diff:

-import { describe, it, expect, vi } from 'vitest';
+import { describe, it, expect, vi, beforeEach } from 'vitest';
📝 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
import { describe, it, expect, vi } from 'vitest';
import { describe, it, expect, vi, beforeEach } from 'vitest';
🤖 Prompt for AI Agents
In src/apps/key-wallet/components/__tests__/AssetRow.test.tsx around lines 1 to
1, the test uses beforeEach on line 25 but the import from 'vitest' at the top
does not include beforeEach; update the import statement to include beforeEach
(e.g., add beforeEach to the destructured imports alongside describe, it,
expect, vi) so the runtime error is resolved.

Comment on lines 84 to 98
it('displays shortened transaction hash', () => {
const transactions: TxStatus[] = [
{
hash: '0x1234567890123456789012345678901234567890123456789012345678901234',
chainId: 1,
status: 'pending',
timestamp: Date.now(),
},
];

render(<TransactionStatus transactions={transactions} onClear={mockOnClear} />);

// Should show shortened hash (0x12345678...56789012)
expect(screen.getByText(/0x12345678/)).toBeInTheDocument();
});
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

Strengthen the shortened hash assertion.

The test only verifies that "0x12345678" appears but doesn't confirm the full shortened format mentioned in the comment (0x12345678...56789012). Consider testing for the complete pattern including the ellipsis and ending portion.

Apply this diff to improve the assertion:

-    // Should show shortened hash (0x12345678...56789012)
-    expect(screen.getByText(/0x12345678/)).toBeInTheDocument();
+    // Should show shortened hash (0x12345678...56789012)
+    expect(screen.getByText(/0x12345678.*56789012/)).toBeInTheDocument();
📝 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
it('displays shortened transaction hash', () => {
const transactions: TxStatus[] = [
{
hash: '0x1234567890123456789012345678901234567890123456789012345678901234',
chainId: 1,
status: 'pending',
timestamp: Date.now(),
},
];
render(<TransactionStatus transactions={transactions} onClear={mockOnClear} />);
// Should show shortened hash (0x12345678...56789012)
expect(screen.getByText(/0x12345678/)).toBeInTheDocument();
});
it('displays shortened transaction hash', () => {
const transactions: TxStatus[] = [
{
hash: '0x1234567890123456789012345678901234567890123456789012345678901234',
chainId: 1,
status: 'pending',
timestamp: Date.now(),
},
];
render(<TransactionStatus transactions={transactions} onClear={mockOnClear} />);
// Should show shortened hash (0x12345678...56789012)
expect(screen.getByText(/0x12345678.*56789012/)).toBeInTheDocument();
});
🤖 Prompt for AI Agents
In src/apps/key-wallet/components/__tests__/TransactionStatus.test.tsx around
lines 84 to 98, the test currently only asserts that "0x12345678" appears;
update the assertion to verify the full shortened hash format (e.g.
"0x12345678...56789012") by matching the complete pattern including the ellipsis
and trailing characters — use a regex that escapes the dots (for example
/0x12345678\.\.\.56789012/) or otherwise assert the element's textContent equals
the expected shortened string so the test confirms both start, ellipsis, and end
portions.

@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 3, 2025 23:51 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: 0

♻️ Duplicate comments (2)
src/apps/key-wallet/components/__tests__/TransactionStatus.test.tsx (2)

98-112: Shortened hash assertion still incomplete.

The test continues to verify only the hash prefix (/0x12345678/) rather than the complete shortened format including the ellipsis and ending portion (e.g., 0x12345678...56789012) mentioned in the comment on line 110. This limitation could mask bugs in the hash formatting logic.


167-184: Hash format inconsistency remains unaddressed.

The test still uses a short hash '0xtxhash' on line 170, which is inconsistent with the full 64-character hashes (66 characters including the 0x prefix) used in other tests throughout this file. This inconsistency could mask bugs if the TransactionStatus component performs validation or formatting based on hash length.

🧹 Nitpick comments (1)
src/apps/key-wallet/components/__tests__/TransactionStatus.test.tsx (1)

186-207: Consider using full-length hashes for consistency.

Similar to the concern raised for the onClear test, this test uses short hashes ('0xhash1' and '0xhash2') instead of the full 64-character format used in other tests. While the short hashes make it easier to distinguish between transactions in this specific test scenario, using consistent full-length hashes across all tests would better verify that the component handles standard transaction hash formats correctly and doesn't have hidden length-dependent behavior.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3546a47 and 83221cf.

⛔ Files ignored due to path filters (1)
  • src/apps/key-wallet/components/__tests__/__snapshots__/TransactionStatus.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (1)
  • src/apps/key-wallet/components/__tests__/TransactionStatus.test.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.
🧬 Code graph analysis (1)
src/apps/key-wallet/components/__tests__/TransactionStatus.test.tsx (1)
src/apps/key-wallet/types/index.ts (1)
  • TransactionStatus (21-26)
⏰ 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: lint
  • GitHub Check: unit-tests
  • GitHub Check: build
🔇 Additional comments (1)
src/apps/key-wallet/components/__tests__/TransactionStatus.test.tsx (1)

16-26: Excellent fix for timestamp test determinism!

The addition of Date mocking in the test setup directly addresses the previous concern about environment-dependent timestamp tests. By mocking both Date.now() and Date.prototype.toLocaleString() to return fixed values, the tests are now deterministic and will produce consistent results across different CI environments and developer machines with varying locale/timezone settings.

@IAmKio IAmKio requested review from RanaBug and vignesha22 November 5, 2025 11:28
- Mobile-first responsive design
- Clean, modern UI using Tailwind CSS
- Master-detail view pattern (list → detail modal)
- Accessible and intuitive navigation
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is it really "accessible"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not sure but i have removed it - not sure this whole README is needed

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok thanks, yes we do not have README for other apps within PillarX

// types
import { Asset } from '../types';

const isGnosisEnabled = import.meta.env.VITE_FEATURE_FLAG_GNOSIS === 'true';
Copy link
Collaborator

Choose a reason for hiding this comment

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

This utils feels very familiar, I think most of those function exists elsewhere in the repo, this is repeated code

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah yes i have updated this 👍

@@ -0,0 +1,36 @@
import { useEffect, useState } from 'react';

export const isClientMatchMedia = (query: string): boolean => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we actually need media queries here? Usually media queries can be managed by the screen sizes set in tailwind (desktop, mobile etc.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes good point i've changed to Tailwind breakpoints but left this media utility in for JS based detection should it be needed

Copy link
Collaborator

@RanaBug RanaBug Nov 17, 2025

Choose a reason for hiding this comment

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

ok hopefully both will not clash

@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 13, 2025 02:38 Inactive
@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 13, 2025 02:42 Inactive
@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 13, 2025 02:57 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: 0

🧹 Nitpick comments (5)
src/apps/key-wallet/components/SendAssetModal.tsx (5)

80-84: Optional: Remove redundant null coalescing.

The nullish coalescing on line 83 (delegatedChainId ?? null) is redundant because delegatedChainId is already guaranteed to be either a number or null from line 82.

Apply this diff to simplify:

       if (isDelegatedEoa && kit && asset) {
         const delegatedChainId =
           kit.getEtherspotProvider?.().getChainId?.() ?? asset.chainId ?? null;
-        setCurrentChainId(delegatedChainId ?? null);
+        setCurrentChainId(delegatedChainId);
         return;
       }

119-122: Prefer viem's isAddress for address validation.

The regex validation is less robust than viem's isAddress function, which is already imported in blockchain.ts and handles checksum validation. For consistency and correctness, consider using isAddress here.

You can import it at the top:

 // Viem
-import { encodeFunctionData, erc20Abi, parseUnits } from 'viem';
+import { encodeFunctionData, erc20Abi, parseUnits, isAddress } from 'viem';

Then use it in validation:

-    if (!recipient.match(/^0x[a-fA-F0-9]{40}$/)) {
+    if (!isAddress(recipient)) {
       setError('Invalid Ethereum address');
       return false;
     }

139-139: Consider a more concise error message.

The error message is quite verbose for a user-facing error. Users typically don't need to know internal details like "PillarX is not connected." A simpler message like "Wallet not connected. Please reload or try logging in again." would be clearer.

-      throw new Error('Sorry, PillarX is not connected to a wallet - please try reloading the site or logging out and back in.');
+      throw new Error('Wallet not connected. Please reload the page or try logging in again.');

189-195: Extract magic numbers to named constants.

The timeout values 120000 (120 seconds) and 3000 (3 seconds) would be more maintainable as named constants.

At the top of the file or in a constants section:

const TX_HASH_TIMEOUT_MS = 120000; // 2 minutes
const TX_HASH_POLL_INTERVAL_MS = 3000; // 3 seconds

Then use them:

     const txHash =
       (await kit.getTransactionHash(
         userOpHash,
         asset.chainId,
-        120000,
-        3000
+        TX_HASH_TIMEOUT_MS,
+        TX_HASH_POLL_INTERVAL_MS
       )) || undefined;

354-362: Consider using type="text" for better UX.

Using type="number" for amount inputs can cause UX issues: scientific notation display (e.g., 1e-7), accidental scroll-wheel changes, and inconsistent browser behavior. Many crypto apps use type="text" with inputMode="decimal" and pattern validation for better control.

             <input
-              type="number"
+              type="text"
+              inputMode="decimal"
               value={amount}
               onChange={(e) => setAmount(e.target.value)}
               placeholder="0.0"
-              step="any"
               className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder-white/40 focus:outline-none focus:border-purple_medium transition-colors pr-20"
               disabled={isLoading}
             />

You may also want to add input sanitization in the onChange handler to allow only valid decimal numbers.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63a10ca and ab17eee.

⛔ Files ignored due to path filters (3)
  • src/apps/key-wallet/components/__tests__/__snapshots__/SendAssetModal.test.tsx.snap is excluded by !**/*.snap
  • src/apps/key-wallet/components/__tests__/__snapshots__/TransactionStatus.test.tsx.snap is excluded by !**/*.snap
  • src/apps/key-wallet/components/__tests__/__snapshots__/WalletAddress.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • src/apps/key-wallet/components/SendAssetModal.tsx (1 hunks)
  • src/apps/key-wallet/components/TransactionStatus.tsx (1 hunks)
  • src/apps/key-wallet/components/WalletAddress.tsx (1 hunks)
  • src/apps/key-wallet/index.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/apps/key-wallet/index.tsx
  • src/apps/key-wallet/components/WalletAddress.tsx
  • src/apps/key-wallet/components/TransactionStatus.tsx
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.
📚 Learning: 2025-11-04T14:34:00.373Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.

Applied to files:

  • src/apps/key-wallet/components/SendAssetModal.tsx
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/apps/key-wallet/components/SendAssetModal.tsx
🧬 Code graph analysis (1)
src/apps/key-wallet/components/SendAssetModal.tsx (3)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
src/apps/key-wallet/utils/blockchain.ts (7)
  • getCurrentChainId (126-145)
  • getChainById (42-50)
  • isNativeAsset (52-57)
  • switchChain (63-121)
  • sendTransaction (147-277)
  • formatBalance (300-304)
  • formatUsdValue (306-310)
src/utils/eip7702Authorization.ts (1)
  • getEIP7702AuthorizationIfNeeded (16-95)
⏰ 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: lint
  • GitHub Check: unit-tests
  • GitHub Check: build

@IAmKio IAmKio requested a review from RanaBug November 13, 2025 09:09
@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 13, 2025 14:36 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: 1

♻️ Duplicate comments (2)
src/apps/key-wallet/utils/blockchain.ts (2)

172-178: Fix scientific notation issue in balance comparison.

asset.balance is a JavaScript number that serializes to scientific notation for small values (e.g., 0.0000001 becomes "1e-7"), which parseUnits rejects. This will cause transaction failures for tokens with tiny balances.

Solution: Store and use the raw on-chain balance (in wei/smallest unit) as a string or BigInt on the Asset type, then compare amountInWei directly against that exact value.

Update the Asset interface in src/apps/key-wallet/types/index.ts:

 export interface Asset {
   id: number;
   name: string;
   symbol: string;
   logo: string;
   balance: number;
+  balanceRaw: string; // Raw balance in wei/smallest unit as string
   decimals: number;
   price: number;
   price_change_24h: number;
   contract: string;
   chainId: number;
   chainName: string;
   usdBalance: number;
 }

Then update this code:

-  // Convert asset balance to wei for comparison
-  const balanceInWei = parseUnits(asset.balance.toString(), asset.decimals);
+  // Use raw balance (already in wei) for comparison
+  const balanceInWei = BigInt(asset.balanceRaw);
   
   // Ensure amount doesn't exceed balance
   if (amountInWei > balanceInWei) {
     throw new Error('Insufficient balance');
   }

214-218: Use the chain's configured RPC endpoint for gas estimation.

http() without arguments defaults to http://127.0.0.1:8545, causing gas estimation and transactions to fail outside local development environments.

Apply this diff:

   // Create public client for gas estimation
+  const rpcUrl =
+    chain.rpcUrls.public?.http?.[0] ?? chain.rpcUrls.default.http[0];
+  if (!rpcUrl) {
+    throw new Error(`No RPC URL configured for chain ${chain.name}`);
+  }
   const publicClient = createPublicClient({
     chain,
-    transport: http(),
+    transport: http(rpcUrl),
   });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab17eee and a09660b.

📒 Files selected for processing (2)
  • src/apps/key-wallet/utils/blockchain.ts (1 hunks)
  • src/utils/blockchain.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.
📚 Learning: 2025-11-04T14:34:00.373Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.

Applied to files:

  • src/apps/key-wallet/utils/blockchain.ts
📚 Learning: 2025-08-12T07:42:24.656Z
Learnt from: IAmKio
Repo: pillarwallet/x PR: 351
File: src/apps/pulse/utils/intent.ts:44-53
Timestamp: 2025-08-12T07:42:24.656Z
Learning: In the Pulse app's intent utilities (src/apps/pulse/utils/intent.ts), the team has chosen to use floating-point arithmetic for token amount calculations despite potential precision issues, accepting JavaScript's decimal place limitations as a valid trade-off for their use case.

Applied to files:

  • src/apps/key-wallet/utils/blockchain.ts
🧬 Code graph analysis (1)
src/apps/key-wallet/utils/blockchain.ts (2)
src/utils/blockchain.ts (2)
  • isGnosisEnabled (41-42)
  • getBlockScan (225-246)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
⏰ 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: lint
  • GitHub Check: unit-tests
  • GitHub Check: build
🔇 Additional comments (2)
src/apps/key-wallet/utils/blockchain.ts (1)

281-284: Good refactoring to reuse shared utility.

The function now properly delegates to getBlockScan from the shared utilities, eliminating the duplicated chain-to-explorer mapping. This addresses the duplication concern raised in previous reviews.

src/utils/blockchain.ts (1)

242-242: LGTM!

The URL protocol update to HTTPS ensures secure connections to Arbiscan and aligns with security best practices.

@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 13, 2025 14:48 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: 0

🧹 Nitpick comments (1)
src/apps/key-wallet/components/__tests__/WalletAddress.test.tsx (1)

97-123: Robust error handling test with proper spy management.

The test correctly mocks a clipboard failure, verifies error logging, and properly restores the console.error spy. The double Promise.resolve() on lines 112-113 ensures the catch block has executed.

Consider replacing the double Promise.resolve() pattern with waitFor for better clarity:

-    // Wait for the async promise rejection to be handled
-    await Promise.resolve();
-    await Promise.resolve(); // Need to flush the catch block execution
-
-    // Verify the error was logged
-    expect(mockWriteText).toHaveBeenCalledWith(mockAddress);
-    expect(consoleErrorSpy).toHaveBeenCalledWith(
-      'Failed to copy address:',
-      expect.any(Error)
-    );
+    // Wait for the async promise rejection to be handled
+    await waitFor(() => {
+      expect(mockWriteText).toHaveBeenCalledWith(mockAddress);
+      expect(consoleErrorSpy).toHaveBeenCalledWith(
+        'Failed to copy address:',
+        expect.any(Error)
+      );
+    });

This makes the intent clearer and is more resilient to timing changes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a09660b and 3948b6a.

⛔ Files ignored due to path filters (1)
  • src/apps/key-wallet/components/__tests__/__snapshots__/WalletAddress.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (2)
  • src/apps/key-wallet/components/WalletAddress.tsx (1 hunks)
  • src/apps/key-wallet/components/__tests__/WalletAddress.test.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/apps/key-wallet/components/WalletAddress.tsx
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.
⏰ 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: lint
  • GitHub Check: unit-tests
  • GitHub Check: build
🔇 Additional comments (4)
src/apps/key-wallet/components/__tests__/WalletAddress.test.tsx (4)

1-21: LGTM! Clean test setup with proper mock lifecycle.

The beforeEach/afterEach hooks correctly initialize and clean up the clipboard mock, ensuring test isolation.


23-37: Good coverage of basic rendering scenarios.

The tests verify essential UI elements including both address formats (responsive design) and use accessible role queries for the button. The snapshot test provides visual regression protection.


39-88: Excellent async and timer handling!

The clipboard tests properly handle asynchronous operations with waitFor and act. The timer test (lines 61-88) correctly uses fake timers to verify the 2-second reset behavior, with proper cleanup and microtask flushing.


90-95: LGTM! Clear verification of instructional text.

The test ensures the user-facing guidance is present in the component.

@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 13, 2025 18:41 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: 1

♻️ Duplicate comments (4)
src/apps/key-wallet/components/SendAssetModal.tsx (1)

221-245: Avoid using error state for chain-switching status.

The "Switching to {chain}..." message on lines 229-230 is displayed in the error UI (red box), which confuses users since chain switching is an expected operation, not an error. This creates poor UX.

Consider using a separate info/loading state:

 const [error, setError] = useState('');
 const [isLoading, setIsLoading] = useState(false);
+const [infoMessage, setInfoMessage] = useState('');

 // In handleSend:
 if (currentChainId !== asset.chainId) {
-  setError(
-    `Switching to ${targetChain.name}... Please approve in your wallet.`
-  );
+  setInfoMessage(
+    `Switching to ${targetChain.name}... Please approve in your wallet.`
+  );
   
   try {
     await switchChain(asset.chainId, resolvedProvider);
-    setError('');
+    setInfoMessage('');

Then render an info box (not error box) for infoMessage in your JSX.

src/apps/key-wallet/utils/blockchain.ts (3)

30-30: Import isGnosisEnabled from shared utilities.

The isGnosisEnabled constant is duplicated from src/utils/blockchain.ts. Import it alongside getBlockScan to maintain a single source of truth.

Apply this diff:

-import { getBlockScan } from '../../../utils/blockchain';
+import { getBlockScan, isGnosisEnabled } from '../../../utils/blockchain';
 import type {
   WalletProviderLike,
   Eip1193LikeProvider,
 } from '../../../types/walletProvider';
 
-const isGnosisEnabled = import.meta.env.VITE_FEATURE_FLAG_GNOSIS === 'true';
-
 const allChains = [mainnet, polygon, gnosis, base, bsc, optimism, arbitrum];

Based on learnings


180-186: Critical: Scientific notation breaks balance comparison.

Converting asset.balance (a JS number) to string via .toString() produces scientific notation for small values (e.g., 0.0000001 becomes "1e-7"), which parseUnits rejects. This prevents transfers of tokens with tiny balances.

The Asset type should store the raw on-chain balance as a string or BigInt to avoid precision loss. For example:

// In src/apps/key-wallet/types/index.ts
export interface Asset {
  // ... existing fields
  balance: number;  // Display value
  balanceRaw?: string;  // Raw wei/atomic units as string
}

Then in sendTransaction:

-  const balanceInWei = parseUnits(asset.balance.toString(), asset.decimals);
+  const balanceInWei = asset.balanceRaw 
+    ? BigInt(asset.balanceRaw)
+    : parseUnits(asset.balance.toString(), asset.decimals);

Alternatively, if balanceRaw cannot be added, normalize the decimal string before calling parseUnits to avoid scientific notation.


222-226: Critical: http() without RPC URL fails in non-local environments.

Calling http() without arguments defaults to localhost, causing gas estimation to fail in production. Pass the chain's configured RPC endpoint explicitly.

Apply this diff:

+  const rpcUrl =
+    chain.rpcUrls.public?.http?.[0] ?? chain.rpcUrls.default.http?.[0];
+  if (!rpcUrl) {
+    throw new Error(`No RPC URL configured for ${chain.name}`);
+  }
   const publicClient = createPublicClient({
     chain,
-    transport: http(),
+    transport: http(rpcUrl),
   });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3948b6a and 024e899.

⛔ Files ignored due to path filters (1)
  • src/apps/key-wallet/components/__tests__/__snapshots__/WalletAddress.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (7)
  • src/apps/key-wallet/components/SendAssetModal.tsx (1 hunks)
  • src/apps/key-wallet/components/WalletAddress.tsx (1 hunks)
  • src/apps/key-wallet/components/__tests__/SendAssetModal.test.tsx (1 hunks)
  • src/apps/key-wallet/index.tsx (1 hunks)
  • src/apps/key-wallet/utils/blockchain.ts (1 hunks)
  • src/providers/EtherspotTransactionKitProvider.tsx (3 hunks)
  • src/types/walletProvider.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/apps/key-wallet/index.tsx
  • src/apps/key-wallet/components/tests/SendAssetModal.test.tsx
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/providers/EtherspotTransactionKitProvider.tsx
  • src/apps/key-wallet/components/SendAssetModal.tsx
📚 Learning: 2025-11-04T14:34:00.373Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.

Applied to files:

  • src/providers/EtherspotTransactionKitProvider.tsx
  • src/apps/key-wallet/components/SendAssetModal.tsx
  • src/apps/key-wallet/utils/blockchain.ts
📚 Learning: 2025-08-12T07:42:24.656Z
Learnt from: IAmKio
Repo: pillarwallet/x PR: 351
File: src/apps/pulse/utils/intent.ts:44-53
Timestamp: 2025-08-12T07:42:24.656Z
Learning: In the Pulse app's intent utilities (src/apps/pulse/utils/intent.ts), the team has chosen to use floating-point arithmetic for token amount calculations despite potential precision issues, accepting JavaScript's decimal place limitations as a valid trade-off for their use case.

Applied to files:

  • src/apps/key-wallet/utils/blockchain.ts
🧬 Code graph analysis (4)
src/providers/EtherspotTransactionKitProvider.tsx (2)
__mocks__/EtherspotTransactionKitProvider.tsx (1)
  • EtherspotTransactionKitContextType (5-12)
src/types/walletProvider.ts (1)
  • WalletProviderLike (15-15)
src/apps/key-wallet/components/SendAssetModal.tsx (4)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
src/types/walletProvider.ts (1)
  • WalletProviderLike (15-15)
src/apps/key-wallet/utils/blockchain.ts (7)
  • getCurrentChainId (133-154)
  • getChainById (47-55)
  • isNativeAsset (57-62)
  • switchChain (68-128)
  • sendTransaction (156-286)
  • formatBalance (294-298)
  • formatUsdValue (300-304)
src/utils/eip7702Authorization.ts (1)
  • getEIP7702AuthorizationIfNeeded (16-95)
src/apps/key-wallet/components/WalletAddress.tsx (1)
src/apps/key-wallet/utils/blockchain.ts (1)
  • shortenAddress (306-308)
src/apps/key-wallet/utils/blockchain.ts (3)
src/utils/blockchain.ts (2)
  • isGnosisEnabled (41-42)
  • getBlockScan (225-246)
src/types/walletProvider.ts (2)
  • WalletProviderLike (15-15)
  • Eip1193LikeProvider (8-13)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
⏰ 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 (13)
src/types/walletProvider.ts (1)

1-15: LGTM! Clean type definitions.

The wallet provider types are well-structured and provide good flexibility for supporting both Viem WalletClient and EIP-1193-style providers. The type definitions follow standard patterns and enable safe interoperability.

src/providers/EtherspotTransactionKitProvider.tsx (3)

13-13: LGTM! Context type correctly extended.

The import and context type extension properly expose the wallet provider to consuming components.

Also applies to: 20-20


37-41: Type assertion is acceptable for config initialization.

The type assertion to access config.provider is a reasonable approach given that the config type may not explicitly include the provider property. The initialization logic correctly handles the optional provider.


66-72: LGTM! Provider sync effect is correct.

The effect correctly synchronizes the external provider state when the config changes, ensuring the exposed walletProvider remains current.

src/apps/key-wallet/components/WalletAddress.tsx (1)

1-61: LGTM! Proper timeout cleanup implemented.

The component correctly manages the copy timeout using a ref and cleans it up on unmount. The implementation handles the user experience well.

Note: The useMemo on line 36 is a minor optimization for a simple string operation and could be removed, but it's not causing any issues.

src/apps/key-wallet/components/SendAssetModal.tsx (3)

44-55: LGTM! Provider resolution logic is sound.

The component correctly resolves the wallet provider from multiple sources with appropriate fallbacks. The type assertion is necessary given the context typing.


57-66: LGTM! Defensive wallet mode detection.

The delegated EOA check uses appropriate optional chaining and error handling to safely determine wallet mode without crashing if methods are unavailable.


138-205: LGTM! Comprehensive delegated EOA transaction flow.

The function properly handles the complete transaction lifecycle for delegated EOA wallets, including authorization, estimation, sending, and hash retrieval with appropriate error handling at each step.

src/apps/key-wallet/utils/blockchain.ts (5)

38-62: LGTM! Utility functions are well-implemented.

The type guard, chain resolution, and native asset detection are all correctly implemented with appropriate error handling.


68-128: LGTM! Comprehensive chain switching with fallback.

The function correctly handles chain switching for EIP-1193 providers and gracefully adds the chain if it's not already in the wallet. Error messages guide users appropriately.


133-154: LGTM! Safe chain ID retrieval.

The function correctly handles both provider types and returns null on error rather than throwing, which is appropriate for a query operation.


228-286: Transaction flows are well-structured.

Both native and ERC-20 send flows properly estimate gas before sending and include comprehensive error handling (aside from the RPC URL issue flagged separately).


289-308: LGTM! Display utilities are well-implemented.

The block explorer URL correctly reuses the shared getBlockScan utility, and the formatting functions provide appropriate user-friendly display thresholds.

@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 13, 2025 18:50 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: 0

♻️ Duplicate comments (3)
src/providers/EtherspotTransactionKitProvider.tsx (2)

20-20: Interface update looks good.

The addition of walletProvider to the context data interface correctly exposes the external provider to consumers. The past review comment on lines 76-84 already flagged that the mock file needs updating to match this interface.


76-84: Context data correctly exposes the wallet provider.

The memoized context data properly includes walletProvider and has the correct dependency array. The past review comment on this range already identified that the mock file needs updating to match.

src/apps/key-wallet/components/SendAssetModal.tsx (1)

69-408: Stop using the error state for chain-switch info
Repurposing the error UI to display “Switching to …” brings back the confusion flagged in the earlier review—the red error banner fires for a non-error state. Please introduce a neutral info state (e.g. infoMessage) instead of overloading error, clear it alongside error, and render it with a non-error style. This keeps real errors distinct from expected prompts. See sketch diff below.

@@
-  const [error, setError] = useState('');
+  const [error, setError] = useState('');
+  const [infoMessage, setInfoMessage] = useState('');
@@
-    setError('');
+    setError('');
+    setInfoMessage('');
@@
-          setError(
+          setInfoMessage(
             `Switching to ${targetChain.name}... Please approve in your wallet.`
           );
@@
-            setError(''); // Clear the switching message
+            setInfoMessage('');
@@
-            setError(
+            setInfoMessage('');
+            setError(
@@
-      onSuccess(txHash, asset.chainId);
+      onSuccess(txHash, asset.chainId);
       onClose();
@@
-      setError(
+      setInfoMessage('');
+      setError(
@@
-          {error && (
+          {infoMessage && (
+            <div className="bg-blue-500/10 border border-blue-500/20 rounded-xl p-3">
+              <p className="text-sm text-blue-300">{infoMessage}</p>
+            </div>
+          )}
+          {error && (
🧹 Nitpick comments (1)
src/providers/EtherspotTransactionKitProvider.tsx (1)

37-43: Consider extracting duplicated provider extraction logic.

The logic for extracting provider from config is duplicated between the state initializer (lines 40-42) and the sync effect (lines 69-72). Extract this into a helper function to improve maintainability.

Apply this refactor:

+const getProviderFromConfig = (config: EtherspotTransactionKitConfig): WalletProviderLike | undefined => {
+  return 'provider' in config
+    ? (config as { provider?: WalletProviderLike }).provider
+    : undefined;
+};
+
 export const EtherspotTransactionKitProvider: React.FC<
   EtherspotTransactionKitProviderProps
 > = ({ config, children }) => {
   const [walletAddress, setWalletAddress] = useState<string>();
   const kitRef = useRef<EtherspotTransactionKit | null>(null);
   const [externalProvider, setExternalProvider] = useState<
     WalletProviderLike | undefined
-  >(() =>
-    'provider' in config
-      ? (config as { provider?: WalletProviderLike }).provider
-      : undefined
-  );
+  >(() => getProviderFromConfig(config));

   // ... kit creation ...

   useEffect(() => {
-    setExternalProvider(
-      'provider' in config
-        ? (config as { provider?: WalletProviderLike }).provider
-        : undefined
-    );
+    setExternalProvider(getProviderFromConfig(config));
   }, [config]);

Also applies to: 68-74

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 024e899 and cd77012.

📒 Files selected for processing (3)
  • src/apps/key-wallet/components/SendAssetModal.tsx (1 hunks)
  • src/apps/key-wallet/components/__tests__/SendAssetModal.test.tsx (1 hunks)
  • src/providers/EtherspotTransactionKitProvider.tsx (3 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/providers/EtherspotTransactionKitProvider.tsx
  • src/apps/key-wallet/components/SendAssetModal.tsx
📚 Learning: 2025-11-04T14:34:00.373Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.

Applied to files:

  • src/providers/EtherspotTransactionKitProvider.tsx
  • src/apps/key-wallet/components/SendAssetModal.tsx
🧬 Code graph analysis (3)
src/providers/EtherspotTransactionKitProvider.tsx (2)
__mocks__/EtherspotTransactionKitProvider.tsx (1)
  • EtherspotTransactionKitContextType (5-12)
src/types/walletProvider.ts (1)
  • WalletProviderLike (15-15)
src/apps/key-wallet/components/__tests__/SendAssetModal.test.tsx (1)
src/types/walletProvider.ts (1)
  • WalletProviderLike (15-15)
src/apps/key-wallet/components/SendAssetModal.tsx (4)
src/apps/key-wallet/types/index.ts (1)
  • Asset (1-14)
src/types/walletProvider.ts (1)
  • WalletProviderLike (15-15)
src/apps/key-wallet/utils/blockchain.ts (7)
  • getCurrentChainId (133-154)
  • getChainById (47-55)
  • isNativeAsset (57-62)
  • switchChain (68-128)
  • sendTransaction (156-286)
  • formatBalance (294-298)
  • formatUsdValue (300-304)
src/utils/eip7702Authorization.ts (1)
  • getEIP7702AuthorizationIfNeeded (16-95)
🪛 Biome (2.1.2)
src/apps/key-wallet/components/__tests__/SendAssetModal.test.tsx

[error] 156-156: This number literal will lose precision at runtime.

The value at runtime will be 0.12345678901234568

(lint/correctness/noPrecisionLoss)

⏰ 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: lint
  • GitHub Check: unit-tests
  • GitHub Check: build
🔇 Additional comments (1)
src/providers/EtherspotTransactionKitProvider.tsx (1)

13-13: LGTM!

The import is necessary for exposing the wallet provider in the context.

@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 14, 2025 09:19 Inactive
@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 14, 2025 13:44 Inactive
@IAmKio IAmKio merged commit d62f231 into staging Nov 17, 2025
6 checks passed
@IAmKio IAmKio deleted the feat/eoa-app branch November 17, 2025 12:16
@IAmKio IAmKio restored the feat/eoa-app branch November 19, 2025 15:48
@github-actions github-actions bot temporarily deployed to Preview (feat/eoa-app) November 19, 2025 15:49 Inactive
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