Skip to content

Conversation

@hernan-clich
Copy link
Member

@hernan-clich hernan-clich commented Aug 8, 2025

Walkthroughs

With MM

rec-fr-example-mm-1.mp4

With Dynamic

rec-fr-example-dyn-1.mp4

Summary by CodeRabbit

  • New Features

    • Dynamic wallet support with Universal Sign‑In toggle, dynamic connect widget, dynamic wallet controls, and separate Dynamic / EIP‑6963 app flows; chain-switch hooks for both wallet types.
  • Refactor

    • App split into Dynamic and EIP‑6963 flows; wallet contexts/hooks renamed to EIP‑6963 variants; header adapts to wallet type; removed chain selection modal and an unused icon; modal ARIA attributes adjusted.
  • Style

    • Added styles for dynamic connect button and clickable wallet controls; removed legacy input, font, and color utility rules.
  • Chores

    • Added wallet dependency and a centralized signer/provider helper.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 8, 2025

📝 Walkthrough

Walkthrough

Adds a toggleable dual-wallet architecture (dynamic Universal Sign‑In vs EIP‑6963): new dynamic components/hooks, EIP‑6963-specific renames, provider-agnostic signer/provider helper, chain-switch abstractions, UI/controller splits, CSS updates, removal of an unused modal and an icon, and dependency additions.

Changes

Cohort / File(s) Summary
App bootstrap & providers
examples/hello/frontend/src/App.tsx, examples/hello/frontend/src/main.tsx
Introduces UniversalSignInContextProvider usage behind USE_DYNAMIC_WALLET; adds ThemeProvider; conditionally wraps app with Eip6963WalletProvider when dynamic wallet is disabled.
App content dispatcher
examples/hello/frontend/src/AppContent.tsx, examples/hello/frontend/src/DynamicAppContent.tsx, examples/hello/frontend/src/Eip6963AppContent.tsx
AppContent now dispatches to DynamicAppContent or Eip6963AppContent based on USE_DYNAMIC_WALLET; new components encapsulate each flow and decide connected/disconnected rendering.
Dynamic wallet UI & helpers
examples/hello/frontend/src/components/ConnectDynamicWallet.tsx, .../ConnectDynamicWallet.css, .../DynamicWalletControls.tsx, .../hooks/useDynamicSwitchChain.ts, .../hooks/useDynamicSwitchChainHook.ts, .../utils/ethersHelpers.ts
Adds dynamic connect component, styles, dynamic wallet controls, hook(s) to switch chains for dynamic wallets, and getSignerAndProvider helper to resolve signer/provider from dynamic primary wallet or EIP‑6963 provider.
EIP‑6963 rename & hooks
examples/hello/frontend/src/context/Eip6963WalletContext.ts, examples/hello/frontend/src/context/Eip6963WalletProvider.tsx, examples/hello/frontend/src/hooks/useEip6963Wallet.ts, .../useEip6963WalletConnection.ts, .../useEip6963WalletEvents.ts, .../useEip6963WalletProviders.ts, .../useEip6963WalletState.ts, deleted examples/hello/frontend/src/hooks/useWallet.ts
Renames generic wallet context/hook identifiers to EIP‑6963 variants and removes the old useWallet wrapper.
Chain switching abstraction
examples/hello/frontend/src/hooks/useSwitchChain.ts, examples/hello/frontend/src/hooks/useEip6963SwitchChain.ts, examples/hello/frontend/src/hooks/useDynamicSwitchChain.ts, examples/hello/frontend/src/hooks/useDynamicSwitchChainHook.ts
Splits chain-switch logic into useEip6963SwitchChain and useDynamicSwitchChain; useSwitchChain delegates to the appropriate implementation.
Header & connect flow
examples/hello/frontend/src/components/Header.tsx, examples/hello/frontend/src/components/ConnectEip6963Wallet.tsx, examples/hello/frontend/src/components/WalletControls.tsx, examples/hello/frontend/src/components/WalletControls.css
Header split into Dynamic and EIP‑6963 variants behind the same Header export; ConnectWallet renamed to ConnectEip6963Wallet; WalletControls now uses EIP‑6963 hook and adds clickable styling.
Connected/Disconnected UI
examples/hello/frontend/src/DisconnectedContent.tsx, examples/hello/frontend/src/ConnectedContent.tsx, examples/hello/frontend/src/ConnectedContent.css
DisconnectedContent conditionally renders dynamic or EIP‑6963 connect components; ConnectedContent dispatches per flow and makes selectedProvider nullable; removed call/input CSS rules.
Message flow & signer resolution
examples/hello/frontend/src/MessageFlowCard.tsx, examples/hello/frontend/src/utils/ethersHelpers.ts
MessageFlowCard accepts nullable selectedProvider and optional primaryWallet, uses getSignerAndProvider helper to obtain signer/provider, and uses HELLO_UNIVERSAL_CONTRACT_ADDRESS as recipient.
Modal & icons cleanup
examples/hello/frontend/src/components/Modal.tsx, examples/hello/frontend/src/components/ChainSelectionModal.tsx (deleted), examples/hello/frontend/src/components/icons/IconExternalLink.tsx (deleted), examples/hello/frontend/src/components/icons/index.ts
Moved ARIA attributes into modal content; removed ChainSelectionModal and IconExternalLink; removed the icon export from icons index.
Constants, styles, deps
examples/hello/frontend/src/constants/wallets.ts, examples/hello/frontend/src/index.css, examples/hello/frontend/package.json
Adds USE_DYNAMIC_WALLET constant (true); removes .Inter and some text utility classes; adds dependency @zetachain/wallet@1.0.12 and resolutions for noble libs.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant App as App
  participant Theme as ThemeProvider
  participant USI as UniversalSignInContextProvider
  participant EIP as Eip6963WalletProvider

  Note over App,Theme: Startup & provider selection
  User->>App: Load
  App->>Theme: Initialize theme
  alt USE_DYNAMIC_WALLET = true
    Theme->>USI: Provide environment + theme
    USI->>App: Render Header + AppContent
  else
    Theme->>EIP: Provide EIP‑6963 wallet context
    EIP->>App: Render Header + AppContent
  end
Loading
sequenceDiagram
  autonumber
  participant AC as AppContent
  participant Flag as USE_DYNAMIC_WALLET
  participant Dyn as DynamicAppContent
  participant EIPApp as Eip6963AppContent

  AC->>Flag: Read flag
  alt Flag = true
    AC-->>Dyn: Render DynamicAppContent
  else
    AC-->>EIPApp: Render Eip6963AppContent
  end
Loading
sequenceDiagram
  autonumber
  participant SwitchHook as useSwitchChain
  participant DynHook as useDynamicSwitchChain
  participant EipHook as useEip6963SwitchChain
  participant USI as UniversalSignInContext
  participant Provider as EIP‑6963 Provider

  SwitchHook->>SwitchHook: check USE_DYNAMIC_WALLET
  alt Dynamic
    SwitchHook->>DynHook: switchChain(chainId)
    DynHook->>USI: primaryWallet.switchNetwork(chainId)
  else EIP‑6963
    SwitchHook->>EipHook: switchChain(chainId)
    EipHook->>Provider: provider.request(wallet_switchEthereumChain)
  end
Loading
sequenceDiagram
  autonumber
  participant MFC as MessageFlowCard
  participant Helper as getSignerAndProvider
  participant USI as UniversalSignInContext
  participant EIPProv as EIP‑6963 Provider
  participant EVM as EVM Contract

  MFC->>Helper: resolve(selectedProvider, primaryWallet)
  alt primaryWallet present
    Helper->>USI: getWeb3Provider()/getSigner()
    Helper-->>MFC: signer + provider
  else selectedProvider present
    Helper->>EIPProv: new BrowserProvider(...).getSigner()
    Helper-->>MFC: signer + provider
  else
    Helper-->>MFC: null -> throw
  end
  MFC->>EVM: call HELLO_UNIVERSAL_CONTRACT_ADDRESS with signer
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • fadeev

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title accurately and concisely describes the primary change: integrating a dynamic wallet into the Hello frontend example. It reflects the changeset which adds a USE_DYNAMIC_WALLET flag, new dynamic wallet components and hooks, conditional provider wiring, and package updates. The "feat:" prefix correctly signals a new feature. The title is specific enough for a teammate scanning history to understand the main purpose of the PR.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/integrate-dynamic-vite

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.

@hernan-clich hernan-clich changed the title Feat/integrate dynamic vite feat: Integrate dynamic wallet on Hello frontend example Aug 8, 2025
@hernan-clich hernan-clich marked this pull request as ready for review August 8, 2025 20:00
@hernan-clich hernan-clich requested a review from a team as a code owner August 8, 2025 20:00
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

🔭 Outside diff range comments (5)
examples/hello/frontend/src/context/Eip6963WalletContext.ts (1)

23-35: Fail fast if the context is used outside its provider

Providing a populated default masks configuration mistakes. Prefer an undefined default and throw in the hook if the provider is missing.

-export const Eip6963WalletContext = createContext<Eip6963WalletContextType>({
-  providers: [],
-  selectedProvider: null,
-  decimalChainId: null,
-  connecting: false,
-  reconnecting: false,
-  isConnected: false,
-  isSupportedChain: false,
-  error: null,
-  connectWallet: async () => ({ success: false }),
-  disconnectWallet: () => {},
-  account: null,
-});
+export const Eip6963WalletContext = createContext<Eip6963WalletContextType | undefined>(undefined);

And update the hook to enforce provider presence (in src/hooks/useEip6963Wallet.ts):

import { useContext } from 'react';
import { Eip6963WalletContext } from '../context/Eip6963WalletContext';

export const useEip6963Wallet = () => {
  const ctx = useContext(Eip6963WalletContext);
  if (!ctx) {
    throw new Error('useEip6963Wallet must be used within an Eip6963WalletProvider');
  }
  return ctx;
};
examples/hello/frontend/src/hooks/useEip6963WalletEvents.ts (2)

17-23: Validate event payloads and avoid unsafe casts

Avoid casting unknown without guards. Validate the payloads per EIP-1193 and pass normalized values downstream.

-    const handleAccountsChanged = (accounts: unknown) => {
-      onAccountsChanged(accounts as string[]);
-    };
+    const handleAccountsChanged = (accounts: unknown) => {
+      if (!Array.isArray(accounts)) return;
+      onAccountsChanged(accounts.filter((a): a is string => typeof a === 'string'));
+    };
 
-    const handleChainChanged = (chainId: unknown) => {
-      onChainChanged(chainId as string);
-    };
+    const handleChainChanged = (chainId: unknown) => {
+      if (typeof chainId !== 'string') return;
+      onChainChanged(chainId); // Typically 0x-prefixed hex per EIP-1193
+    };

25-35: Strengthen listener setup/cleanup and handle disconnect

  • Some providers expose off instead of removeListener; support both.
  • Add a disconnect listener to reset state upstream if needed.
-    provider.provider.on('accountsChanged', handleAccountsChanged);
-    provider.provider.on('chainChanged', handleChainChanged);
+    provider.provider.on('accountsChanged', handleAccountsChanged);
+    provider.provider.on('chainChanged', handleChainChanged);
+    const handleDisconnect = () => {
+      onAccountsChanged([]); // upstream will null the account
+    };
+    provider.provider.on?.('disconnect', handleDisconnect as any);
 
     return () => {
-      provider.provider.removeListener(
-        'accountsChanged',
-        handleAccountsChanged
-      );
-      provider.provider.removeListener('chainChanged', handleChainChanged);
+      const off =
+        (provider.provider as any).removeListener?.bind(provider.provider) ??
+        (provider.provider as any).off?.bind(provider.provider);
+      off?.('accountsChanged', handleAccountsChanged);
+      off?.('chainChanged', handleChainChanged);
+      off?.('disconnect', handleDisconnect);
     };
examples/hello/frontend/src/hooks/useEip6963WalletState.ts (2)

13-16: Parse hex chainId explicitly to avoid ambiguity

eth_chainId is a 0x-prefixed hex string. Use explicit hex parsing and fall back for non-standard providers.

   const decimalChainId = useMemo(() => {
     if (!chainId) return null;
-    return Number(chainId);
+    return chainId.startsWith('0x') ? parseInt(chainId, 16) : Number(chainId);
   }, [chainId]);

40-68: Prevent state updates after unmount/provider change and fetch concurrently

Guard async initialization against late resolves and fetch both values in parallel for responsiveness.

   useEffect(() => {
-    if (!provider) {
-      setAccount(null);
-      setChainId(null);
-      return;
-    }
-
-    const initializeWalletState = async () => {
-      try {
-        const accounts = (await provider.provider.request({
-          method: 'eth_accounts',
-        })) as string[];
-
-        if (accounts && accounts.length > 0) {
-          setAccount(accounts[0]);
-        }
-
-        const currentChainId = (await provider.provider.request({
-          method: 'eth_chainId',
-        })) as string;
-
-        setChainId(currentChainId);
-      } catch (error) {
-        console.error('Error initializing wallet state:', error);
-      }
-    };
-
-    initializeWalletState();
-  }, [provider]);
+    let cancelled = false;
+    if (!provider) {
+      setAccount(null);
+      setChainId(null);
+      return;
+    }
+
+    const initializeWalletState = async () => {
+      try {
+        // Optional: fetch concurrently
+        const [accounts, currentChainId] = await Promise.all([
+          provider.provider.request({ method: 'eth_accounts' }) as Promise<string[]>,
+          provider.provider.request({ method: 'eth_chainId' }) as Promise<string>,
+        ]);
+
+        if (cancelled) return;
+        setAccount(accounts?.[0] ?? null);
+        setChainId(currentChainId);
+      } catch (error) {
+        if (!cancelled) console.error('Error initializing wallet state:', error);
+      }
+    };
+
+    initializeWalletState();
+    return () => {
+      cancelled = true;
+    };
+  }, [provider]);
♻️ Duplicate comments (1)
examples/hello/frontend/src/MessageFlowCard.tsx (1)

26-27: Same unconditional Dynamic hook issue

useDynamicContext() is invoked regardless of the active wallet type.
Apply the same guard pattern suggested for AppContent.tsx.

🧹 Nitpick comments (21)
examples/hello/frontend/src/components/Modal.tsx (1)

39-39: Verify styling after removing "subheading" class

Confirm there’s no unintended visual regression on modal titles and remove any dead CSS rules referencing .subheading if now unused.

examples/hello/frontend/src/components/ConnectDynamicWallet.css (1)

1-11: Add basic interactive & accessibility states

Consider extending the styles with cursor: pointer, :hover, and :focus-visible rules (outline or box-shadow) so the button behaves like a conventional interactive element and meets WCAG focus guidelines.

examples/hello/frontend/src/constants/wallets.ts (1)

1-2: Move the feature flag to env configuration

A hard-coded true requires a commit to toggle environments. Reading from import.meta.env (Vite) or process.env allows runtime/CI selection without code changes, e.g.:

-export const USE_DYNAMIC_WALLET = true;
+export const USE_DYNAMIC_WALLET = import.meta.env.VITE_USE_DYNAMIC_WALLET === 'true';
examples/hello/frontend/src/hooks/useEip6963Wallet.ts (1)

1-5: Return type annotation for stronger IDE support

export const useEip6963Wallet = () => {
  return useContext(Eip6963WalletContext);
};

Annotating the return as Eip6963WalletContextType (exported with the context) makes the hook self-documenting and improves IntelliSense.

examples/hello/frontend/src/hooks/useEip6963WalletConnection.ts (1)

12-19: Update JSDoc to reflect new hook name

The docstring at Line 14 still says “Hook to manage wallet connections” but doesn’t mention EIP-6963. Rename it (or add @returns) to keep generated docs accurate.

examples/hello/frontend/src/hooks/useEip6963WalletProviders.ts (1)

37-50: Avoid unsafe casts in event listener; prefer typed wrapper

Casting to EventListener masks type mismatches. Wrap the handler to keep type-safety and remove the cast.

Apply:

   // Initialize EIP-6963 provider discovery
   useEffect(() => {
-    // Create handler for provider announcements
-    const announceHandler = createProviderAnnounceHandler(
-      handleAnnounceProvider
-    );
-
-    // Add event listener for provider announcements
-    window.addEventListener(
-      'eip6963:announceProvider',
-      announceHandler as EventListener
-    );
+    // Create handler for provider announcements
+    const announceHandler = createProviderAnnounceHandler(handleAnnounceProvider);
+    const onAnnounce: EventListener = (e) =>
+      announceHandler(e as CustomEvent<EIP6963ProviderDetail>);
+
+    // Add event listener for provider announcements
+    window.addEventListener('eip6963:announceProvider', onAnnounce);
 
     // Request providers
     requestEIP6963Providers();
 
     // Cleanup
     return () => {
-      window.removeEventListener(
-        'eip6963:announceProvider',
-        announceHandler as EventListener
-      );
+      window.removeEventListener('eip6963:announceProvider', onAnnounce);
     };
   }, [handleAnnounceProvider]);
examples/hello/frontend/src/components/WalletControls.tsx (1)

22-28: Add accessible label to the disconnect button

The icon-only button lacks an accessible name. Add aria-label/title for screen readers.

       <button
         className="wallet-controls-disconnect-button"
         type="button"
-        onClick={disconnectWallet}
+        onClick={disconnectWallet}
+        aria-label="Disconnect wallet"
+        title="Disconnect wallet"
       >
         <IconDisconnect />
       </button>
examples/hello/frontend/package.json (1)

13-15: Prefer narrower version ranges to reduce breakage surface

Caret ranges will pick future minor updates that may introduce breaking changes in rapidly evolving SDKs. Consider tilde or exact pins.

-    "@dynamic-labs/ethereum": "^4.25.9",
-    "@dynamic-labs/ethers-v6": "^4.25.9",
-    "@dynamic-labs/sdk-react-core": "^4.25.9",
+    "@dynamic-labs/ethereum": "~4.25.9",
+    "@dynamic-labs/ethers-v6": "~4.25.9",
+    "@dynamic-labs/sdk-react-core": "~4.25.9",

Additionally, ensure components that import Dynamic are code-split (dynamic import) and gated by the USE_DYNAMIC_WALLET flag so Vite can perform dead-code elimination when not in use.

examples/hello/frontend/src/main.tsx (1)

8-17: Conditionally mount EIP-6963 provider to avoid unnecessary listeners

If USE_DYNAMIC_WALLET is enabled, you can avoid attaching EIP-6963 listeners by not mounting its provider.

Apply within this file:

-import { Eip6963WalletProvider } from './context/Eip6963WalletProvider.tsx';
-import { ThemeProvider } from './context/ThemeProvider.tsx';
+import { Eip6963WalletProvider } from './context/Eip6963WalletProvider.tsx';
+import { ThemeProvider } from './context/ThemeProvider.tsx';
+import { USE_DYNAMIC_WALLET } from './constants/wallets.ts';
   <StrictMode>
-    <ThemeProvider>
-      <Eip6963WalletProvider>
-        <App />
-      </Eip6963WalletProvider>
-    </ThemeProvider>
+    <ThemeProvider>
+      {USE_DYNAMIC_WALLET ? (
+        <App />
+      ) : (
+        <Eip6963WalletProvider>
+          <App />
+        </Eip6963WalletProvider>
+      )}
+    </ThemeProvider>
   </StrictMode>

This keeps the EIP-6963 discovery effect out of the tree when not needed and slightly reduces startup work.

examples/hello/frontend/src/components/ConnectEip6963Wallet.tsx (1)

15-21: Minor UX: avoid blocking alert and memoize handlers

  • Replace alert(...) in handleConnectClick with a non-blocking notification/toast to avoid blocking the main thread.
  • Wrap handleConnectClick and handleSelectProvider in useCallback to prevent avoidable re-renders in children.
examples/hello/frontend/src/DisconnectedContent.tsx (1)

3-4: Code-split wallet UIs to avoid bundling unused paths

Both wallet UIs are statically imported and likely bundled. Lazy-load the active one to reduce bundle size.

-import { ConnectDynamicWallet } from './components/ConnectDynamicWallet';
-import { ConnectEip6963Wallet } from './components/ConnectEip6963Wallet';
+import { Suspense, lazy } from 'react';
+const ConnectDynamicWallet = lazy(() => import('./components/ConnectDynamicWallet'));
+const ConnectEip6963Wallet = lazy(() => import('./components/ConnectEip6963Wallet'));
-          {USE_DYNAMIC_WALLET ? (
-            <ConnectDynamicWallet />
-          ) : (
-            <ConnectEip6963Wallet />
-          )}
+          <Suspense fallback={null}>
+            {USE_DYNAMIC_WALLET ? <ConnectDynamicWallet /> : <ConnectEip6963Wallet />}
+          </Suspense>

Also applies to: 26-30

examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (1)

25-25: Optional: Configure widget appearance

Consider passing sizing/variant props to DynamicWidget (if supported) for consistent header layout, especially on small screens.

examples/hello/frontend/src/App.tsx (2)

15-15: Avoid hard-coding Dynamic environmentId

Externalize environmentId to configuration for portability and secrets hygiene. In Vite:

-        environmentId: 'eaec6949-d524-40e7-81d2-80113243499a',
+        environmentId: import.meta.env.VITE_DYNAMIC_ENVIRONMENT_ID!,

Add to your .env:

VITE_DYNAMIC_ENVIRONMENT_ID=eaec6949-d524-40e7-81d2-80113243499a

Optionally centralize in constants/wallets.ts if you prefer config colocation (following prior discussions for consistent config handling across files).


13-25: Optional: Mount Dynamic provider only when needed

To reduce overhead when using EIP‑6963 exclusively, consider gating DynamicContextProvider behind USE_DYNAMIC_WALLET, or split App into two small wrappers (Dynamic vs EIP‑6963), then select at the root by flag.

examples/hello/frontend/src/components/Header.tsx (1)

11-28: Branching works; consider splitting branches into subcomponents

This is correct and maintains hook rules. For clearer separation (and to avoid touching EIP‑6963 context when using Dynamic), consider extracting two small components, e.g., <HeaderEip6963/> and <HeaderDynamic/>, and branch at the top level.

examples/hello/frontend/src/hooks/useDynamicSwitchChain.ts (1)

20-35: Return an explicit success / failure outcome

switchChain currently swallows errors and always resolves void.
Returning a boolean (or re-throwing) lets callers handle failures
(e.g. show a toast).

- const switchChain = useCallback(async (chainId: number) => { … }, []);
+ const switchChain = useCallback(async (chainId: number): Promise<boolean> => {
+
+   try {
+     await primaryWallet.switchNetwork(chainId);
+     return true;
+   } catch (e) {
+
+     return false;
+   }
+ }, []);
examples/hello/frontend/src/hooks/useSwitchChain.ts (2)

8-10: Avoid unnecessary hook execution

useEip6963SwitchChain() is executed even when USE_DYNAMIC_WALLET
is true and vice-versa. Wrapping the calls in the predicate (or
lazily importing) prevents extra work and side-effects.

- const eip6963SwitchChain = useEip6963SwitchChain();
- const dynamicSwitchChain = useDynamicSwitchChain();
+ const eip6963SwitchChain = USE_DYNAMIC_WALLET
+   ? null
+   : useEip6963SwitchChain();
+ const dynamicSwitchChain = USE_DYNAMIC_WALLET
+   ? useDynamicSwitchChain()
+   : null;

11-20: Stabilise callback dependencies

dynamicSwitchChain and eip6963SwitchChain are objects whose identity
changes on every render, so the wrapped useCallback is recreated each
time. Destructure the actual function or memoise the returned objects
inside the respective hooks to keep switchChain stable.

examples/hello/frontend/src/AppContent.tsx (1)

24-25: Disconnected state may ignore wallet availability

isDisconnected only checks selectedProvider for the EIP-6963 path, but
the button flow in MessageFlowCard ultimately needs either a provider
or a Dynamic wallet. Consider also testing for !primaryWallet here to
avoid false positives.

examples/hello/frontend/src/MessageFlowCard.tsx (1)

169-176: Disable “Send Message” when no signer available

The button is disabled for input/chain checks but not for the absence of
a connected wallet (!primaryWallet && !selectedProvider).
Add this guard to prevent an avoidable error toast.

- disabled={
+ disabled={
     !stringValue.length ||
     !supportedChain ||
+    (!primaryWallet && !selectedProvider) ||
     getStringByteLength(stringValue) > MAX_STRING_LENGTH
   }
examples/hello/frontend/src/utils/ethersHelpers.ts (1)

38-44: Cache BrowserProvider instance

A new BrowserProvider is created on every call, which can be
expensive. Memoise per selectedProvider (e.g., WeakMap) or reuse an
existing instance to reduce overhead in rapid calls (polling UIs, etc.).

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4795dd and 567e850.

⛔ Files ignored due to path filters (1)
  • examples/hello/frontend/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (32)
  • examples/hello/frontend/package.json (1 hunks)
  • examples/hello/frontend/src/App.tsx (1 hunks)
  • examples/hello/frontend/src/AppContent.tsx (1 hunks)
  • examples/hello/frontend/src/ConnectedContent.css (0 hunks)
  • examples/hello/frontend/src/ConnectedContent.tsx (1 hunks)
  • examples/hello/frontend/src/DisconnectedContent.tsx (2 hunks)
  • examples/hello/frontend/src/MessageFlowCard.tsx (2 hunks)
  • examples/hello/frontend/src/components/ChainSelectionModal.tsx (0 hunks)
  • examples/hello/frontend/src/components/ConnectDynamicWallet.css (1 hunks)
  • examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (1 hunks)
  • examples/hello/frontend/src/components/ConnectEip6963Wallet.tsx (1 hunks)
  • examples/hello/frontend/src/components/Header.tsx (1 hunks)
  • examples/hello/frontend/src/components/Modal.tsx (1 hunks)
  • examples/hello/frontend/src/components/WalletControls.tsx (1 hunks)
  • examples/hello/frontend/src/components/icons/IconExternalLink.tsx (0 hunks)
  • examples/hello/frontend/src/components/icons/index.ts (0 hunks)
  • examples/hello/frontend/src/constants/chains.ts (1 hunks)
  • examples/hello/frontend/src/constants/wallets.ts (1 hunks)
  • examples/hello/frontend/src/context/Eip6963WalletContext.ts (2 hunks)
  • examples/hello/frontend/src/context/Eip6963WalletProvider.tsx (3 hunks)
  • examples/hello/frontend/src/hooks/useDynamicSwitchChain.ts (1 hunks)
  • examples/hello/frontend/src/hooks/useEip6963SwitchChain.ts (1 hunks)
  • examples/hello/frontend/src/hooks/useEip6963Wallet.ts (1 hunks)
  • examples/hello/frontend/src/hooks/useEip6963WalletConnection.ts (2 hunks)
  • examples/hello/frontend/src/hooks/useEip6963WalletEvents.ts (2 hunks)
  • examples/hello/frontend/src/hooks/useEip6963WalletProviders.ts (2 hunks)
  • examples/hello/frontend/src/hooks/useEip6963WalletState.ts (3 hunks)
  • examples/hello/frontend/src/hooks/useSwitchChain.ts (1 hunks)
  • examples/hello/frontend/src/hooks/useWallet.ts (0 hunks)
  • examples/hello/frontend/src/index.css (0 hunks)
  • examples/hello/frontend/src/main.tsx (1 hunks)
  • examples/hello/frontend/src/utils/ethersHelpers.ts (1 hunks)
💤 Files with no reviewable changes (6)
  • examples/hello/frontend/src/components/icons/index.ts
  • examples/hello/frontend/src/ConnectedContent.css
  • examples/hello/frontend/src/hooks/useWallet.ts
  • examples/hello/frontend/src/components/icons/IconExternalLink.tsx
  • examples/hello/frontend/src/components/ChainSelectionModal.tsx
  • examples/hello/frontend/src/index.css
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: hernan-clich
PR: zeta-chain/example-contracts#276
File: examples/hello/frontend/tsconfig.node.json:16-22
Timestamp: 2025-07-28T19:20:39.900Z
Learning: The user hernan-clich prefers to reference previous discussions when the same issue appears in multiple files, indicating a preference for consistent resolution approaches across similar configuration files.
🧬 Code Graph Analysis (16)
examples/hello/frontend/src/hooks/useEip6963Wallet.ts (1)
examples/hello/frontend/src/context/Eip6963WalletContext.ts (1)
  • Eip6963WalletContext (23-35)
examples/hello/frontend/src/components/WalletControls.tsx (1)
examples/hello/frontend/src/hooks/useEip6963Wallet.ts (1)
  • useEip6963Wallet (5-5)
examples/hello/frontend/src/hooks/useEip6963SwitchChain.ts (1)
examples/hello/frontend/src/hooks/useEip6963Wallet.ts (1)
  • useEip6963Wallet (5-5)
examples/hello/frontend/src/ConnectedContent.tsx (1)
examples/hello/frontend/src/types/wallet.ts (1)
  • EIP6963ProviderDetail (18-21)
examples/hello/frontend/src/components/ConnectEip6963Wallet.tsx (1)
examples/hello/frontend/src/hooks/useEip6963Wallet.ts (1)
  • useEip6963Wallet (5-5)
examples/hello/frontend/src/hooks/useEip6963WalletConnection.ts (1)
examples/hello/frontend/src/types/wallet.ts (1)
  • EIP6963ProviderDetail (18-21)
examples/hello/frontend/src/context/Eip6963WalletProvider.tsx (4)
examples/hello/frontend/src/hooks/useEip6963WalletProviders.ts (1)
  • useEip6963WalletProviders (12-55)
examples/hello/frontend/src/hooks/useEip6963WalletConnection.ts (1)
  • useEip6963WalletConnection (17-193)
examples/hello/frontend/src/hooks/useEip6963WalletState.ts (1)
  • useEip6963WalletState (7-76)
examples/hello/frontend/src/context/Eip6963WalletContext.ts (1)
  • Eip6963WalletContext (23-35)
examples/hello/frontend/src/hooks/useEip6963WalletState.ts (2)
examples/hello/frontend/src/types/wallet.ts (1)
  • EIP6963ProviderDetail (18-21)
examples/hello/frontend/src/hooks/useEip6963WalletEvents.ts (1)
  • useEip6963WalletEvents (10-36)
examples/hello/frontend/src/hooks/useEip6963WalletEvents.ts (1)
examples/hello/frontend/src/types/wallet.ts (1)
  • EIP6963ProviderDetail (18-21)
examples/hello/frontend/src/DisconnectedContent.tsx (3)
examples/hello/frontend/src/constants/wallets.ts (1)
  • USE_DYNAMIC_WALLET (2-2)
examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (1)
  • ConnectDynamicWallet (11-26)
examples/hello/frontend/src/components/ConnectEip6963Wallet.tsx (1)
  • ConnectEip6963Wallet (11-45)
examples/hello/frontend/src/hooks/useSwitchChain.ts (3)
examples/hello/frontend/src/hooks/useEip6963SwitchChain.ts (1)
  • useEip6963SwitchChain (5-28)
examples/hello/frontend/src/hooks/useDynamicSwitchChain.ts (1)
  • useDynamicSwitchChain (4-41)
examples/hello/frontend/src/constants/wallets.ts (1)
  • USE_DYNAMIC_WALLET (2-2)
examples/hello/frontend/src/components/Header.tsx (6)
examples/hello/frontend/src/hooks/useEip6963Wallet.ts (1)
  • useEip6963Wallet (5-5)
examples/hello/frontend/src/constants/wallets.ts (1)
  • USE_DYNAMIC_WALLET (2-2)
examples/hello/frontend/src/components/ConnectEip6963Wallet.tsx (1)
  • ConnectEip6963Wallet (11-45)
examples/hello/frontend/src/components/WalletControls.tsx (1)
  • WalletControls (7-31)
examples/hello/frontend/src/components/ThemeToggle.tsx (1)
  • ThemeToggle (8-27)
examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (1)
  • ConnectDynamicWallet (11-26)
examples/hello/frontend/src/App.tsx (3)
examples/hello/frontend/src/hooks/useTheme.ts (1)
  • useTheme (5-11)
examples/hello/frontend/src/components/Header.tsx (1)
  • Header (10-40)
examples/hello/frontend/src/AppContent.tsx (1)
  • AppContent (9-36)
examples/hello/frontend/src/AppContent.tsx (3)
examples/hello/frontend/src/hooks/useEip6963Wallet.ts (1)
  • useEip6963Wallet (5-5)
examples/hello/frontend/src/constants/wallets.ts (1)
  • USE_DYNAMIC_WALLET (2-2)
examples/hello/frontend/src/constants/chains.ts (1)
  • SUPPORTED_CHAINS (9-52)
examples/hello/frontend/src/MessageFlowCard.tsx (3)
examples/hello/frontend/src/types/wallet.ts (1)
  • EIP6963ProviderDetail (18-21)
examples/hello/frontend/src/utils/ethersHelpers.ts (1)
  • getSignerAndProvider (17-48)
examples/hello/frontend/src/constants/contracts.ts (1)
  • HELLO_UNIVERSAL_CONTRACT_ADDRESS (1-2)
examples/hello/frontend/src/utils/ethersHelpers.ts (1)
examples/hello/frontend/src/types/wallet.ts (1)
  • EIP6963ProviderDetail (18-21)
⏰ 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: slither (examples/call, call.sarif)
  • GitHub Check: slither (examples/swap, swap.sarif)
  • GitHub Check: slither (examples/hello, hello.sarif)
🔇 Additional comments (10)
examples/hello/frontend/src/hooks/useEip6963WalletProviders.ts (1)

12-12: No stale references to useWalletProviders remain

A repository-wide search across .ts, .tsx, .js, and .jsx files returned zero occurrences of the old hook name or import paths. The rename to useEip6963WalletProviders is complete and safe to merge.

examples/hello/frontend/src/components/WalletControls.tsx (1)

3-9: Hook swap aligns with new context

Switching to useEip6963Wallet is consistent with the EIP-6963 provider tree and keeps the component logic unchanged. LGTM.

examples/hello/frontend/package.json (1)

13-15: Peer Dependencies Verified: No Conflicts Detected
All three @dynamic-labs packages at v4.25.9 declare compatible peer requirements:

  • sdk-react-core: React >=18.0.0 <20.0.0 (covers React 19)
  • ethers-v6: ethers ^6
  • ethereum: only depends on viem, no conflicting ethers requirement

No changes are needed before merging.

examples/hello/frontend/src/ConnectedContent.tsx (1)

11-11: Nullable provider: downstream null-safety
useEip6963SwitchChain correctly checks for selectedProvider before calling its .provider.request(...), and getSignerAndProvider only constructs an ethers.BrowserProvider when selectedProvider is non-null. However, we haven’t been able to confirm how MessageFlowCard.tsx (and any other consumers) handle a null provider. Please verify that:

  • In MessageFlowCard.tsx, any access to selectedProvider.provider or its properties is guarded (e.g., via conditional rendering or early returns).
  • Any additional components receiving selectedProvider: EIP6963ProviderDetail | null apply the same null-safety checks to prevent runtime errors.
examples/hello/frontend/src/main.tsx (1)

13-17: Provider hierarchy update looks sound

Wrapping <App /> with ThemeProvider and Eip6963WalletProvider is coherent with the new hook usage and keeps effects contained. LGTM.

examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (2)

12-12: Confirm address source for connection state

Ensure primaryWallet?.address is the correct indicator across all enabled Dynamic connectors (including social logins). If Dynamic exposes connection state via a different field (e.g., user/session), leverage that to avoid false negatives.

Would you like me to scan usages of primaryWallet to ensure we standardize on the right property across the app?


16-21: Verify styling prop for DynamicConnectButton

Double‑check whether the component expects buttonClassName or className. A mismatch will silently drop styles.

If it’s className, apply this change:

-      <DynamicConnectButton buttonClassName="dynamic-connect-button">
+      <DynamicConnectButton className="dynamic-connect-button">
examples/hello/frontend/src/context/Eip6963WalletProvider.tsx (1)

10-16: Clean provider wiring and memoized context value

The provider correctly composes discovery, connection, and state hooks and memoizes the context payload. This is production‑ready.

Also applies to: 24-27, 28-55, 58-60

examples/hello/frontend/src/components/Header.tsx (1)

33-35: LGTM on Dynamic header path

ConnectDynamicWallet self-manages connected vs disconnected state; pairing with ThemeToggle keeps UX consistent.

examples/hello/frontend/src/hooks/useDynamicSwitchChain.ts (1)

21-27: Ensure chainId format matches connector expectation

primaryWallet.switchNetwork is invoked with a decimal number.
Most EIP-1193 based wallets (and MetaMask in particular) expect the chainId
string in hex prefixed with 0x (wallet_switchEthereumChain › params[0].chainId).
Passing a decimal may silently fail on some connectors.

- await primaryWallet.switchNetwork(chainId);
+ await primaryWallet.switchNetwork(Number(chainId));
+ // or, if the connector follows EIP-1193:
+ // await primaryWallet.switchNetwork(`0x${chainId.toString(16)}`);

Please verify the Dynamic SDK implementation and normalise the value accordingly.

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 (1)
examples/hello/frontend/src/App.tsx (1)

11-12: Gate Dynamic usage with the existing feature flag and confirm theme compatibility

  • Import the existing USE_DYNAMIC_WALLET flag so this file can avoid mounting Dynamic when the feature is off (see suggestion below at Lines 41–51).
  • Prior review confirmed ThemeProvider wraps App in main.tsx, so useTheme() is safe. Re-affirming for posterity.
  • Verify that your theme values match Dynamic’s accepted values (commonly 'light' | 'dark'). If your ThemeContext supports additional values (e.g., 'system'), map them before passing to Dynamic.

Apply this import addition:

+import { USE_DYNAMIC_WALLET } from './constants/wallets';

If a mapping is required:

const dynamicTheme: 'light' | 'dark' = theme === 'dark' ? 'dark' : 'light';

To verify theme values in your codebase:

#!/bin/bash
# Inspect ThemeContext to ensure values are 'light' | 'dark' or provide a mapping.
rg -n -C3 --type tsx --type ts "ThemeContext|theme\s*:\s*'light'|'dark'|interface\s+Theme" examples/hello/frontend/src

Also applies to: 15-16

🧹 Nitpick comments (2)
examples/hello/frontend/src/App.tsx (2)

1-1: Scope the side-effect import to the EIP-6963 path only

The side-effect import of @zetachain/wallet/ethereum executes unconditionally. If the app is running with Dynamic wallets only, this import is unnecessary overhead. Consider moving it to where EIP-6963 is actually enabled (e.g., Eip6963WalletProvider in main.tsx or the ConnectEip6963Wallet component) or lazy-loading it.


54-55: Render DynamicUserProfile only when Dynamic is enabled

Rendering the profile unconditionally requires the context to be present. The gating in the previous suggestion ensures it is mounted only when the provider is active.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 567e850 and 47304e8.

📒 Files selected for processing (1)
  • examples/hello/frontend/src/App.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
examples/hello/frontend/src/App.tsx (3)
examples/hello/frontend/src/hooks/useTheme.ts (1)
  • useTheme (5-11)
examples/hello/frontend/src/components/Header.tsx (1)
  • Header (10-40)
examples/hello/frontend/src/AppContent.tsx (1)
  • AppContent (9-36)
⏰ 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). (13)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/call)
  • GitHub Check: slither (examples/hello, hello.sarif)
  • GitHub Check: slither (examples/call, call.sarif)
  • GitHub Check: slither (examples/swap, swap.sarif)
🔇 Additional comments (3)
examples/hello/frontend/src/App.tsx (3)

3-7: Overall wiring looks coherent

  • Correct usage of EthereumWalletConnectors and overrides.evmNetworks.
  • Theme propagation into Dynamic is appropriate once values are confirmed.
  • The provider composition with Header and AppContent is sound.

Also applies to: 11-12, 41-51


17-38: Use SDK configuration instead of CSS overrides

You can replace the brittle CSS hacks with first-class SDK settings to:

  • Disable email and social sign-in by overriding the Login view to show only wallets.
  • Allow-list the Universal Sign In (EVM) wallet via the walletsFilter function.
  • Suppress “View all wallets” naturally, since only one wallet remains.

Example (copy-pasteable):

import { DynamicContextProvider, DynamicWidget } from "@dynamic-labs/sdk-react-core";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
import { SdkViewType, SdkViewSectionType } from "@dynamic-labs/sdk-api";

export default function App() {
  return (
    <DynamicContextProvider
      settings={{
        environmentId: "REPLACE-WITH-YOUR-ENVIRONMENT-ID",
        // Only the EVM connector is bundled here
        walletConnectors: [EthereumWalletConnectors],

        // 1. Override Login to show *only* the wallet picker (hides email/social)
        overrides: {
          views: [
            {
              type: SdkViewType.Login,
              sections: [
                { type: SdkViewSectionType.Wallet },
              ],
            },
          ],
        },

        // 2. Filter wallets down to the Universal Sign In (EVM) option only
        walletsFilter: (wallets) =>
          wallets.filter((w) => w.key === "universalsigninevm"),
      }}
    >
      <DynamicWidget />
    </DynamicContextProvider>
  );
}
  • The Login view override ensures no email or social sections render (docs.dynamic.xyz).
  • The walletsFilter allow-lists the wallet with key universalsigninevm, automatically hiding any “View all wallets” button (dynamic.xyz).

This approach is stable across SDK upgrades and avoids reliance on internal class names.


41-51: Gate the DynamicContextProvider and Externalize the Environment ID

The DynamicContextProvider is currently mounted unconditionally, incurring CSS injection and initialization overhead even when the EIP-6963 path (i.e. USE_DYNAMIC_WALLET=false) is active. For a more production-grade, environment-agnostic setup:

• File: examples/hello/frontend/src/App.tsx (lines ~41–51)
– Wrap <DynamicContextProvider> in a conditional on the existing USE_DYNAMIC_WALLET flag.
– Source environmentId from import.meta.env.VITE_DYNAMIC_ENV_ID, with a controlled fallback during transition.
– Normalize the theme prop to 'light' or 'dark' explicitly.

Suggested diff:

-    <DynamicContextProvider
-      settings={{
-        environmentId: 'eaec6949-d524-40e7-81d2-80113243499a',
-        walletConnectors: [EthereumWalletConnectors],
-        overrides: {
-          evmNetworks,
-        },
-        cssOverrides,
-      }}
-      theme={theme}
-    >
-      <Header />
-      <AppContent />
-      <DynamicUserProfile />
-    </DynamicContextProvider>
+    {USE_DYNAMIC_WALLET ? (
+      <DynamicContextProvider
+        settings={{
+          // Read from Vite env; fallback preserves existing behavior during rollout
+          environmentId:
+            import.meta.env.VITE_DYNAMIC_ENV_ID ||
+            'eaec6949-d524-40e7-81d2-80113243499a',
+          walletConnectors: [EthereumWalletConnectors],
+          overrides: { evmNetworks },
+          cssOverrides,
+        }}
+        theme={theme === 'dark' ? 'dark' : 'light'}
+      >
+        <Header />
+        <AppContent />
+        <DynamicUserProfile />
+      </DynamicContextProvider>
+    ) : (
+      <>
+        <Header />
+        <AppContent />
+      </>
+    )}

To enforce a fail-fast in production and avoid accidental fallbacks:

const envId = import.meta.env.VITE_DYNAMIC_ENV_ID;
if (USE_DYNAMIC_WALLET && !envId) {
  console.error('VITE_DYNAMIC_ENV_ID is required when USE_DYNAMIC_WALLET=true');
  throw new Error('Missing VITE_DYNAMIC_ENV_ID');
}

Action items:

  • Create an .env (or .env.local) in examples/hello/frontend with
    VITE_DYNAMIC_ENV_ID=<your-environment-id>.
  • Ensure Vite injects this var in both local and CI builds.
  • Review other USE_DYNAMIC_WALLET usages (e.g. in AppContent, DisconnectedContent) to confirm consistent gating.

This optional refactor minimizes bundle overhead when dynamic wallets aren’t in use and centralizes environment configuration.

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)
examples/hello/frontend/src/App.tsx (1)

16-16: useTheme usage confirmed safe per prior review

Earlier concern about useTheme throwing outside a provider was already verified as safe because ThemeProvider wraps <App /> in main.tsx.

🧹 Nitpick comments (6)
examples/hello/frontend/src/App.tsx (6)

1-1: Gate side‑effect import to avoid cost when Dynamic is disabled

The side-effect import executes unconditionally at startup. Consider gating or lazy-loading it so it’s only included when Dynamic wallet is enabled.

Apply this diff to remove the unconditional import:

-import '@zetachain/wallet/ethereum';

Then, load it only when needed (example in main.tsx):

// examples/hello/frontend/src/main.tsx
if (import.meta.env.VITE_USE_DYNAMIC_WALLET === 'true') {
  await import('@zetachain/wallet/ethereum');
}

12-13: Add explicit types for chain overrides to catch config drift early

evmNetworks should satisfy Dynamic’s expected EVM network shape. Adding an explicit type guards against silent shape changes.

Example (in constants/chains.ts):

import type { EvmNetwork } from '@dynamic-labs/ethereum';
// ...
export const evmNetworks: readonly EvmNetwork[] = [/* ... */] as const;

This allows overrides: { evmNetworks } here to be type-checked end-to-end.


19-36: Gate Dynamic provider and move environmentId to Vite env var

Hardcoding the environmentId and always mounting the provider forces Dynamic into all builds. Gate it behind your existing flag and pull the ID from import.meta.env to ease environment switching and local forks.

Apply this diff:

+import { USE_DYNAMIC_WALLET } from './constants/wallets';
@@
 function App() {
   const { theme } = useTheme();
+  const environmentId = import.meta.env.VITE_DYNAMIC_ENV_ID as string | undefined;
@@
-  return (
-    <DynamicContextProvider
-      settings={{
-        environmentId: 'eaec6949-d524-40e7-81d2-80113243499a',
-        walletConnectors: [EthereumWalletConnectors],
-        overrides: {
-          evmNetworks,
-          views: [
-            {
-              type: SdkViewType.Login,
-              sections: [{ type: SdkViewSectionType.Wallet }],
-            },
-          ],
-        },
-        walletsFilter: (wallets) =>
-          wallets.filter((w) => w.key === 'universalsigninevm'),
-      }}
-      theme={theme}
-    >
-      <Header />
-      <AppContent />
-      <DynamicUserProfile />
-    </DynamicContextProvider>
-  );
+  return (
+    USE_DYNAMIC_WALLET && environmentId ? (
+      <DynamicContextProvider
+        settings={{
+          environmentId,
+          walletConnectors: [EthereumWalletConnectors],
+          overrides: {
+            evmNetworks,
+            views: [
+              {
+                type: SdkViewType.Login,
+                sections: [
+                  ...(import.meta.env.VITE_DYNAMIC_ENABLE_SOCIAL === 'true'
+                    ? [{ type: SdkViewSectionType.Social }]
+                    : []),
+                  { type: SdkViewSectionType.Wallet },
+                ],
+              },
+            ],
+          },
+          walletsFilter:
+            import.meta.env.VITE_DYNAMIC_WALLETS_WHITELIST
+              ? (wallets) =>
+                  wallets.filter((w) =>
+                    (import.meta.env.VITE_DYNAMIC_WALLETS_WHITELIST as string)
+                      .split(',')
+                      .map((s) => s.trim())
+                      .includes(w.key)
+                  )
+              : (wallets) => wallets,
+        }}
+        theme={theme}
+      >
+        <Header />
+        <AppContent />
+        <DynamicUserProfile />
+      </DynamicContextProvider>
+    ) : (
+      <>
+        <Header />
+        <AppContent />
+      </>
+    )
+  );

Notes:

  • Add .env key VITE_DYNAMIC_ENV_ID per environment.
  • Optional VITE_DYNAMIC_ENABLE_SOCIAL surfaces social sign-in alongside wallets.
  • Optional VITE_DYNAMIC_WALLETS_WHITELIST allows a comma-separated allowlist in different demos.

24-31: Social login visibility may be suppressed by the current view config

Only the Wallet section is rendered. If the PR demo intends to show Google login, add the Social section (or gate it via an env flag as in the diff above).


32-34: Narrow wallet filter hides most connectors

Filtering to universalsigninevm will exclude MetaMask, WalletConnect, Coinbase, etc. Confirm this is deliberate for the example; otherwise, prefer an allowlist via an env var or remove the filter.


35-35: Normalize theme to accepted values

Ensure the provider receives a supported value. If theme can be more than 'light'|'dark', normalize here.

-      theme={theme}
+      theme={theme === 'dark' ? 'dark' : 'light'}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 47304e8 and c345f03.

📒 Files selected for processing (1)
  • examples/hello/frontend/src/App.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
examples/hello/frontend/src/App.tsx (3)
examples/hello/frontend/src/hooks/useTheme.ts (1)
  • useTheme (5-11)
examples/hello/frontend/src/components/Header.tsx (1)
  • Header (10-40)
examples/hello/frontend/src/AppContent.tsx (1)
  • AppContent (9-36)
⏰ 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). (10)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/token)
  • GitHub Check: slither (examples/swap, swap.sarif)
  • GitHub Check: slither (examples/hello, hello.sarif)
  • GitHub Check: slither (examples/call, call.sarif)
🔇 Additional comments (1)
examples/hello/frontend/src/App.tsx (1)

37-40: LGTM on adding DynamicUserProfile

Placement inside the provider is correct and provides a quick path to user settings without additional plumbing.

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 (2)
examples/hello/frontend/package.json (2)

13-16: Prevent duplicate ethers and keep bundles lean.

Because @dynamic-labs/ethers-v6 peers ethers, enforce a single ethers version across the tree and consider code-splitting Dynamic UI if it’s behind a feature flag (e.g., USE_DYNAMIC_WALLET) to avoid shipping it on the EIP‑6963-only path.

Add an overrides block to dedupe ethers (top-level package.json):

{
  "overrides": {
    "ethers": "^6.13.2"
  }
}

Optionally add bundle analysis to keep an eye on Dynamic’s footprint:

{
  "devDependencies": {
    "rollup-plugin-visualizer": "^5.12.0"
  },
  "scripts": {
    "analyze": "vite build && npx rollup-plugin-visualizer --json stats.json dist/assets/*.js && echo 'stats.json written'"
  }
}

Ensure Dynamic components are dynamically imported when disabled, e.g., const ConnectDynamicWallet = lazy(() => import('./ConnectDynamicWallet')), to allow Vite to tree‑shake them out of the EIP‑6963 build.


1-40: Add a top-level engines field to lock Node versions

All of the referenced toolchain releases are available on npm, so it’s safe to proceed with pinning versions and declaring a Node engine range to prevent CI drift.

• Verified releases:

  • typescript@~5.8.3 → 5.8.3
  • eslint@^9.30.1 → 9.30.1, 9.31.0, 9.32.0, 9.33.0, 9.34.0
  • typescript-eslint@^8.35.1 → 8.35.1 … 8.40.0
  • @vitejs/plugin-react@^4.6.0 → 4.6.0, 4.7.0
  • vite@^7.0.4 → 7.0.4 … 7.1.3

• Suggested change (insert under the existing fields in examples/hello/frontend/package.json):

 {
   "name": "frontend",
   "private": true,
   "version": "0.0.0",
+  "engines": {
+    "node": ">=18.18.0"
+  },
   "type": "module",
   "scripts": { … }
 }

This ensures that all contributors and CI environments use Node 18.18.0 or higher, avoiding unexpected runtime errors.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c345f03 and b4172ec.

⛔ Files ignored due to path filters (1)
  • examples/hello/frontend/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (2)
  • examples/hello/frontend/package.json (1 hunks)
  • examples/hello/frontend/src/App.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/hello/frontend/src/App.tsx
⏰ 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). (13)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/swap)
  • GitHub Check: slither (examples/swap, swap.sarif)
  • GitHub Check: slither (examples/hello, hello.sarif)
  • GitHub Check: slither (examples/call, call.sarif)
🔇 Additional comments (2)
examples/hello/frontend/package.json (2)

13-16: Dynamic Labs dependencies added — versions look aligned with ethers v6; proceed.

The four @dynamic-labs/* packages are added at compatible versions and include the ethers-v6 adapter, which matches your ethers@^6.13.2. No immediate concerns from a dependency perspective.


13-16: Ensure dependencies are installed before verifying peer-dependencies

It looks like the peer-dependency check didn’t actually run because the Dynamic packages (and React/Vite) aren’t installed in your workspace. To get accurate results:

  • In examples/hello/frontend, install all dependencies first
    npm install          # or npm ci
  • Then run the peer-dependency introspection and tree-view:
    # 1. Inspect declared peerDependencies
    node -e '
      for (const p of [
        "@dynamic-labs/sdk-react-core",
        "@dynamic-labs/ethereum",
        "@dynamic-labs/ethers-v6"
      ]) {
        const pkg = require(`${p}/package.json`);
        console.log(`${p} →`, pkg.peerDependencies);
      }
    '
    
    # 2. Verify resolution and look for warnings/duplicates
    npm ls react react-dom ethers \
      @dynamic-labs/sdk-react-core \
      @dynamic-labs/ethereum \
      @dynamic-labs/ethers-v6 || true
  • Confirm that:
    • @dynamic-labs/sdk-react-core supports react@^19.0.0 (and react-dom@^19.0.0),
    • @dynamic-labs/ethereum and @dynamic-labs/ethers-v6 align with ethers@^6.13.2,
    • No “UNMET PEER DEPENDENCY” or duplicate versions appear.

Once you’ve verified with the install in place and received a clean output, we can consider this safe for React 19 / Vite 7 upgrades.

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 (4)
examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (4)

16-27: Avoid hard-coding a wallet key; trigger the auth flow via the SDK instead.

Relying on 'universalsigninevm' is brittle and environment-specific. The docs recommend either opening the auth flow programmatically or using the SDK’s connect button, rather than invoking an opaque wallet/group key. Replace the hard-coded selectWalletOption call with setShowAuthFlow(true) from useDynamicContext. (dynamic.xyz)

Apply this diff:

 import {
   DynamicWidget,
   useDynamicContext,
-  useWalletOptions,
 } from '@dynamic-labs/sdk-react-core';

 export const ConnectDynamicWallet = () => {
-  const { primaryWallet } = useDynamicContext();
-  const { selectWalletOption } = useWalletOptions();
+  const { primaryWallet, setShowAuthFlow } = useDynamicContext();

   if (!primaryWallet?.address) {
     return (
-      <Button
-        className="dynamic-connect-button"
-        onClick={() => selectWalletOption('universalsigninevm')}
-      >
+      <Button
+        className="dynamic-connect-button"
+        onClick={() => setShowAuthFlow(true)}
+      >
         <div className="dynamic-connect-button-content">
           <IconWallet />
           Connect Wallet
         </div>
       </Button>
     );
   }

If you truly need to target a specific wallet option, derive it at runtime instead of hard-coding. Example (headless approach):

import { useWalletOptions, FilterChain } from '@dynamic-labs/sdk-react-core';

const { selectWalletOption, getFilteredWalletOptions } = useWalletOptions();
const evmOptions = getFilteredWalletOptions(FilterChain('EVM'));
// pick a group or option key from evmOptions, then call selectWalletOption(key)

Docs for useWalletOptions and filtering: (dynamic.xyz)

To verify there’s no regression, temporarily log walletOptions in dev and confirm a click opens the auth flow. (dynamic.xyz)

Also applies to: 13-15


18-27: Alternatively, use DynamicConnectButton and keep your custom visuals.

This avoids key management entirely and delegates connection UX to the SDK while preserving your styling. (dynamic.xyz)

Apply these diffs:

  1. Import the component:
 import {
   DynamicWidget,
+  DynamicConnectButton,
   useDynamicContext,
 } from '@dynamic-labs/sdk-react-core';
  1. Swap the custom Button for DynamicConnectButton:
-      <Button
-        className="dynamic-connect-button"
-        onClick={() => setShowAuthFlow(true)}
-      >
-        <div className="dynamic-connect-button-content">
-          <IconWallet />
-          Connect Wallet
-        </div>
-      </Button>
+      <DynamicConnectButton buttonClassName="dynamic-connect-button">
+        <div className="dynamic-connect-button-content">
+          <IconWallet />
+          Connect Wallet
+        </div>
+      </DynamicConnectButton>

Also applies to: 3-7


16-31: Optional UX simplification: let DynamicWidget own both “connect” and “connected” states.

Instead of conditionally rendering your own connect button, you can always render DynamicWidget and pass innerButtonComponent to customize the built‑in connect button. This reduces branching and keeps behavior aligned with upstream SDK updates. (docs.dynamic.xyz)

Example:

return (
  <DynamicWidget
    // 'modal' | 'dropdown'
    variant="modal"
    innerButtonComponent={
      <div className="dynamic-connect-button-content">
        <IconWallet />
        Connect Wallet
      </div>
    }
  />
);

16-16: Consider useIsLoggedIn for gating UI, not only primaryWallet?.address.

useIsLoggedIn abstracts “connected vs authenticated” and handles connect-only vs connect-and-sign modes; it can prevent edge-case UI flicker during onboarding. Low effort improvement. (dynamic.xyz)

Illustrative tweak:

import { useIsLoggedIn } from '@dynamic-labs/sdk-react-core';

const isLoggedIn = useIsLoggedIn();
if (!isLoggedIn) {
  // render connect button
} else {
  return <DynamicWidget />;
}

Also applies to: 30-31

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b4172ec and 42c190e.

📒 Files selected for processing (2)
  • examples/hello/frontend/src/App.tsx (1 hunks)
  • examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/hello/frontend/src/App.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (1)
examples/hello/frontend/src/components/Button.tsx (1)
  • Button (13-40)
⏰ 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). (9)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/swap)
  • GitHub Check: slither (examples/swap, swap.sarif)
  • GitHub Check: slither (examples/call, call.sarif)
  • GitHub Check: slither (examples/hello, hello.sarif)
🔇 Additional comments (1)
examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (1)

3-7: Imports are correct for Dynamic v4; usage of DynamicWidget is appropriate.

DynamicWidget and the hooks you’re using are exported from @dynamic-labs/sdk-react-core in v4, so the import surface looks good. Ensure the app is wrapped with DynamicContextProvider and you’ve passed the needed walletConnectors (e.g., EthereumWalletConnectors) at a higher level; otherwise hooks will throw at runtime. (docs.dynamic.xyz, dynamic.xyz)

Also applies to: 30-31

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)
examples/hello/frontend/src/App.tsx (1)

5-9: useTheme usage is valid (ThemeProvider wraps App upstream).

This mirrors an earlier review that verified ThemeProvider presence in main.tsx. No action needed here.

🧹 Nitpick comments (2)
examples/hello/frontend/src/App.tsx (2)

11-14: Externalize the 'environment' value to a Vite env variable to avoid accidental sandbox/prod mismatches.

Hardcoding environment="sandbox" is brittle. Read from import.meta.env with a safe default.

Apply this minimal change:

-    <UniversalSignInContextProvider environment="sandbox" theme={theme}>
+    <UniversalSignInContextProvider
+      environment={import.meta.env.VITE_ZETAWALLET_ENV ?? 'sandbox'}
+      theme={theme}
+    >

Optionally, strengthen types by declaring the env var (e.g., src/vite-env.d.ts):

interface ImportMetaEnv {
  readonly VITE_ZETAWALLET_ENV?: 'sandbox' | 'staging' | 'production';
}

And document it in .env.example:

VITE_ZETAWALLET_ENV=sandbox

11-14: Consider lifting UniversalSignInContextProvider to main.tsx alongside ThemeProvider for consistent provider composition.

Co-locating top-level providers in main.tsx simplifies ownership, prevents accidental remounts when App re-renders, and keeps entry-point wiring explicit. If other parts of the app (e.g., toasts, error boundaries) ever need wallet context, this avoids refactors later. If main.tsx already wraps with a different wallet provider by design, disregard.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 42c190e and e9232c8.

📒 Files selected for processing (1)
  • examples/hello/frontend/src/App.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
examples/hello/frontend/src/App.tsx (1)
examples/hello/frontend/src/hooks/useTheme.ts (1)
  • useTheme (5-11)
⏰ 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). (13)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/call)
  • GitHub Check: slither (examples/hello, hello.sarif)
  • GitHub Check: slither (examples/swap, swap.sarif)
  • GitHub Check: slither (examples/call, call.sarif)

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: 2

♻️ Duplicate comments (1)
examples/hello/frontend/src/utils/ethersHelpers.ts (1)

12-15: Widen provider type and remove unsafe casts (repeat of prior review).

getWeb3Provider may not return a BrowserProvider. Casting narrows unsafely and can mislead consumers. Return an AbstractProvider (or a union) and drop the casts.

Apply this diff:

-interface GetSignerAndProviderResult {
-  signer: ethers.AbstractSigner<ethers.Provider | null>;
-  provider: ethers.BrowserProvider;
-}
+interface GetSignerAndProviderResult {
+  signer: ethers.AbstractSigner;
+  provider: ethers.AbstractProvider;
+}
@@
-      return {
-        signer: signer as ethers.AbstractSigner,
-        provider: provider as ethers.BrowserProvider,
-      };
+      return {
+        signer,
+        provider,
+      };

If the exported types from @zetachain/wallet are narrower, consider a union:
provider: ethers.BrowserProvider | ethers.AbstractProvider.

Also applies to: 27-30

🧹 Nitpick comments (9)
examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (3)

9-11: Replace magic string and streamline button markup + a11y

  • Avoid the hard-coded 'universalsigninevm' to reduce the risk of breakage if the option key changes upstream; centralize it in a shared constant.
  • Reuse Button’s icon prop to drop the extra wrapper and simplify the DOM.
  • Add an accessible name and hide the decorative icon from screen readers.

Apply:

 import { Button } from './Button';
 import { IconWallet } from './icons';
+import { UNIVERSAL_SIGN_IN_EVM_OPTION } from '../constants/wallets';
@@
-      <Button
-        className="dynamic-connect-button"
-        onClick={() => selectWalletOption('universalsigninevm')}
-      >
-        <div className="dynamic-connect-button-content">
-          <IconWallet />
-          Connect Wallet
-        </div>
-      </Button>
+      <Button
+        className="dynamic-connect-button"
+        aria-label="Connect wallet"
+        onClick={() => selectWalletOption(UNIVERSAL_SIGN_IN_EVM_OPTION)}
+        icon={<span aria-hidden="true"><IconWallet /></span>}
+      >
+        Connect Wallet
+      </Button>

If selectWalletOption returns a Promise, consider explicitly ignoring it to satisfy linters, or disable the button while pending:

-        onClick={() => selectWalletOption(UNIVERSAL_SIGN_IN_EVM_OPTION)}
+        onClick={() => void selectWalletOption(UNIVERSAL_SIGN_IN_EVM_OPTION)}

Add the shared constant (outside this file):

// examples/hello/frontend/src/constants/wallets.ts
export const UNIVERSAL_SIGN_IN_EVM_OPTION = 'universalsigninevm' as const;

Also applies to: 18-26


3-7: Consider lazy-loading DynamicWidget to trim initial bundle size

DynamicWidget likely pulls non-trivial UI/logic. Lazy-load it so the disconnected landing stays light.

Diff within this file:

-import {
-  DynamicWidget,
-  useUniversalSignInContext,
-  useWalletOptions,
-} from '@zetachain/wallet/react';
+import {
+  useUniversalSignInContext,
+  useWalletOptions,
+} from '@zetachain/wallet/react';
@@
-  return <DynamicWidget />;
+  return (
+    <Suspense fallback={null}>
+      <DynamicWidget />
+    </Suspense>
+  );

And add (outside the selected ranges):

// near the top of this file, after other imports
import { lazy, Suspense } from 'react';

const DynamicWidget = lazy(() =>
  import('@zetachain/wallet/react').then((m) => ({ default: m.DynamicWidget }))
);

Optionally provide a small fallback (e.g., skeleton) instead of null.

Also applies to: 30-31


1-1: CSS scope: prefer modules to avoid global leakage

Importing a global stylesheet risks class collisions (.dynamic-connect-button, .dynamic-connect-button-content). Consider ConnectDynamicWallet.module.css and import styles from './ConnectDynamicWallet.module.css', then reference styles.dynamicConnectButton, etc.

examples/hello/frontend/src/MessageFlowCard.tsx (3)

41-49: Harden signer acquisition: clarify error semantics and preflight availability.

Differentiate “no wallet connected” vs. “failed to resolve signer” to help UX and telemetry. Also preflight-check before calling to avoid throwing for expected states.

Apply this small improvement:

-      if (!signerAndProvider) {
-        throw new Error('Failed to get signer');
-      }
+      if (!signerAndProvider) {
+        throw new Error('No connected wallet or signer resolution failed');
+      }

Optional follow-ups outside this range:

  • Gate the Send button when both primaryWallet and selectedProvider are absent to avoid entering this branch at all.
  • Surface a toast or inline error on failure instead of only console.error, to improve UX.

50-50: Consider retaining provider to validate chain before send.

You destructure signer but not provider. Optionally read provider from signer.provider and ensure it matches supportedChain to prevent mis-sends on the wrong network.

Example (outside this range):

const { signer, provider } = signerAndProvider;
// Ensure chain match before sending
const network = await (provider ?? signer.provider)?.getNetwork?.();
if (supportedChain && network && Number(network.chainId) !== supportedChain.id) {
  // optional: attempt switch via EIP-1193 or show actionable error
  throw new Error(`Wrong network: expected ${supportedChain.name}`);
}

53-53: Introduce chain-aware contract address mapping

The HELLO_UNIVERSAL_CONTRACT_ADDRESS constant is currently defined here and used only in MessageFlowCard.tsx (import on line 12, usage on line 53) and in its own export in src/constants/contracts.ts. Relying on a single hard-coded address risks cross-network mis-calls if you deploy to multiple chains.

To address this, consider:

  • In src/constants/contracts.ts, replace the single constant with a mapping keyed by chainId.
  • In MessageFlowCard.tsx, select the address by supportedChain.id, falling back to a default if needed.

— Files to update
examples/hello/frontend/src/constants/contracts.ts
examples/hello/frontend/src/MessageFlowCard.tsx

— Suggested refactor

// src/constants/contracts.ts
export const HELLO_UNIVERSAL_CONTRACT_ADDRESSES: Record<number, `0x${string}`> = {
  1:  '0xMainnetAddress...', // Ethereum Mainnet
  5:  '0xGoerliAddress...',  // Goerli Testnet
  1337: '0xLocalhostAddress...', // Local dev network
};

// Optionally retain a default for backwards-compatibility
export const HELLO_UNIVERSAL_CONTRACT_ADDRESS = 
  HELLO_UNIVERSAL_CONTRACT_ADDRESSES[1];

// -----------------------------------------------------------------------

// src/MessageFlowCard.tsx
import {
  HELLO_UNIVERSAL_CONTRACT_ADDRESSES,
  HELLO_UNIVERSAL_CONTRACT_ADDRESS,
} from './constants/contracts';



const receiver =
  supportedChain?.id && HELLO_UNIVERSAL_CONTRACT_ADDRESSES[supportedChain.id]
    ? HELLO_UNIVERSAL_CONTRACT_ADDRESSES[supportedChain.id]
    : HELLO_UNIVERSAL_CONTRACT_ADDRESS;

// then use `receiver` as before

This ensures each deployment targets the correct address per chain and avoids accidental mainnet/testnet cross-calls.

examples/hello/frontend/src/utils/ethersHelpers.ts (3)

38-40: Avoid unnecessary cast on signer from BrowserProvider.

getSigner() is already a subclass of AbstractSigner; the cast is not needed.

Apply this diff:

-    const signer = (await provider.getSigner()) as ethers.AbstractSigner;
+    const signer = await provider.getSigner();

21-34: Log context on Dynamic path failures and consider non-throw fallback telemetry.

Current catch only logs the error. Consider logging a structured payload (e.g., source='dynamic', step='getSigner/getWeb3Provider') to aid triage. The subsequent EIP-6963 fallback is appropriate.

Example (outside this range):

console.error('getSignerAndProvider', { source: 'dynamic', error });

47-48: Returning null is fine; consider an explicit Result type or Either for callers.

Optionally return a discriminated union to avoid null checks downstream.

Example:

type GetSignerAndProviderResult =
  | { ok: true; signer: ethers.AbstractSigner; provider: ethers.AbstractProvider }
  | { ok: false; reason: 'no-wallet' | 'resolution-failed' };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e9232c8 and f3b2676.

⛔ Files ignored due to path filters (1)
  • examples/hello/frontend/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (5)
  • examples/hello/frontend/src/AppContent.tsx (1 hunks)
  • examples/hello/frontend/src/MessageFlowCard.tsx (2 hunks)
  • examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (1 hunks)
  • examples/hello/frontend/src/hooks/useDynamicSwitchChain.ts (1 hunks)
  • examples/hello/frontend/src/utils/ethersHelpers.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/hello/frontend/src/hooks/useDynamicSwitchChain.ts
  • examples/hello/frontend/src/AppContent.tsx
🧰 Additional context used
🧬 Code graph analysis (3)
examples/hello/frontend/src/MessageFlowCard.tsx (3)
examples/hello/frontend/src/types/wallet.ts (1)
  • EIP6963ProviderDetail (18-21)
examples/hello/frontend/src/utils/ethersHelpers.ts (1)
  • getSignerAndProvider (17-48)
examples/hello/frontend/src/constants/contracts.ts (1)
  • HELLO_UNIVERSAL_CONTRACT_ADDRESS (1-2)
examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (1)
examples/hello/frontend/src/components/Button.tsx (1)
  • Button (13-40)
examples/hello/frontend/src/utils/ethersHelpers.ts (1)
examples/hello/frontend/src/types/wallet.ts (1)
  • EIP6963ProviderDetail (18-21)
⏰ 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). (8)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/call)
  • GitHub Check: slither (examples/call, call.sarif)
  • GitHub Check: slither (examples/hello, hello.sarif)
  • GitHub Check: slither (examples/swap, swap.sarif)
🔇 Additional comments (6)
examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (2)

12-31: LGTM: Clear connect-or-widget control flow

The conditional render based on primaryWallet?.address is straightforward and aligns with the intended UX. Good separation of disconnected vs. connected states.


12-15: ConnectDynamicWallet usages are correctly wrapped by UniversalSignInContextProvider

I’ve verified that:

  • In App.tsx, all children—including Header, AppContent, and transitively ConnectDynamicWallet—are nested inside <UniversalSignInContextProvider …>, so there’s no immediate risk of useUniversalSignInContext() throwing at runtime.
  • There are no Storybook stories (*.stories.tsx) or Jest tests referencing ConnectDynamicWallet or directly calling useUniversalSignInContext, so no uncovered usage paths currently exist.

No action is required to fix existing code. As a best practice for future stories or tests that import this component in isolation, consider:

  • Adding a Storybook decorator (or a wrapper component) that includes the UniversalSignInContextProvider.
  • Documenting in the component README or InlineDoc that ConnectDynamicWallet must live under a UniversalSignInContextProvider.
examples/hello/frontend/src/MessageFlowCard.tsx (3)

4-5: UniversalSignInContext integration and ethers import look correct.

Using the context at the component boundary is appropriate, and importing ZeroAddress from ethers v6 is the right approach.


26-26: Correctly sourcing primaryWallet from UniversalSignInContext.

No issues. This keeps wallet resolution centralized.


18-18: All <MessageFlowCard> call sites correctly pass selectedProvider (including null when unselected).

No further changes are needed.

examples/hello/frontend/src/utils/ethersHelpers.ts (1)

17-20: Export signature is clean and matches the new usage pattern.

Centralizing wallet resolution behind a single helper improves cohesion and reduces duplication.

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

🧹 Nitpick comments (2)
examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (2)

3-7: Optional: lazy-load DynamicWidget to trim the disconnected bundle.

Most users initially hit the disconnected path; defer DynamicWidget until connected.

Apply this diff on top of the previous change:

-import { useCallback, useState } from 'react';
+import { lazy, Suspense, useCallback, useState } from 'react';
 import './ConnectDynamicWallet.css';

-import {
-  useConnectUniversalSignIn,
-  useUniversalSignInContext,
-} from '@zetachain/wallet/react';
+import { useConnectUniversalSignIn, useUniversalSignInContext } from '@zetachain/wallet/react';
+
+const DynamicWidget = lazy(async () => {
+  const mod = await import('@zetachain/wallet/react');
+  return { default: mod.DynamicWidget };
+});

@@
-  // `DynamicWidget` rendered in the connected branch (see optional lazy-load below).
-  return null;
+  return (
+    <Suspense fallback={null}>
+      <DynamicWidget />
+    </Suspense>
+  );

Also applies to: 30-31


1-1: Keyboard focus styling check.

Ensure .dynamic-connect-button has a visible :focus-visible style consistent with your design system.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f3b2676 and cb09977.

📒 Files selected for processing (1)
  • examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (1)
examples/hello/frontend/src/components/Button.tsx (1)
  • Button (13-40)
⏰ 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). (13)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/call)
  • GitHub Check: slither (examples/hello, hello.sarif)
  • GitHub Check: slither (examples/call, call.sarif)
  • GitHub Check: slither (examples/swap, swap.sarif)
🔇 Additional comments (1)
examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (1)

12-15: LGTM: idiomatic hook usage and clear render gate.

Context consumption and the presence check on primaryWallet?.address are correct.

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)
examples/hello/frontend/package.json (1)

14-14: Optional: add a lightweight bundle analysis step.

To monitor the impact of the new wallet on bundle size without touching Vite config, add source-map-explorer and an analyze script.

Apply:

   "scripts": {
     "dev": "vite",
     "build": "tsc -b && vite build",
     "lint": "eslint .",
-    "preview": "vite preview"
+    "preview": "vite preview",
+    "analyze": "vite build --sourcemap && npx source-map-explorer 'dist/assets/*.js'"
   },
 ...
   "devDependencies": {
     "@eslint/js": "^9.30.1",
     "@types/react": "^19.1.8",
     "@types/react-dom": "^19.1.6",
     "@vitejs/plugin-react": "^4.6.0",
+    "source-map-explorer": "^2.6.2",
     "eslint": "^9.30.1",
     "eslint-config-prettier": "^10.1.1",
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between cb09977 and c3e1d36.

⛔ Files ignored due to path filters (1)
  • examples/hello/frontend/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (2)
  • examples/hello/frontend/package.json (1 hunks)
  • examples/hello/frontend/src/components/Modal.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/hello/frontend/src/components/Modal.tsx
⏰ 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). (11)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/hello)
  • GitHub Check: slither (examples/call, call.sarif)
  • GitHub Check: slither (examples/hello, hello.sarif)
  • GitHub Check: slither (examples/swap, swap.sarif)
🔇 Additional comments (2)
examples/hello/frontend/package.json (2)

14-14: Addition of @zetachain/wallet v1.0.1 looks consistent.

Pinning the Zeta packages to exact versions mirrors @zetachain/toolkit and reduces supply-chain drift for the example app. LGTM.


14-14: Peer dependencies validated@zetachain/wallet@1.0.1 declares peer deps on React ^18.0.0 || ^19.0.0, react-dom ^18.0.0 || ^19.0.0, and viem ^2.7.12. Our app’s React ^19.1.0, react-dom ^19.1.0, and viem ^2.31.7 all satisfy these ranges, and there are no peer deps on ethers or Vite to address.

@fadeev
Copy link
Member

fadeev commented Aug 29, 2025

yarn dev was throwing an error, so I had to install @noble/hashes. Perhaps we should include it as a dependency to the wallet.

✘ [ERROR] No matching export in "node_modules/@walletconnect/utils/node_modules/@noble/hashes/esm/utils.js" for import "anumber"

    node_modules/@walletconnect/utils/node_modules/viem/node_modules/@noble/curves/esm/abstract/modular.js:8:9:
      8 │ import { anumber } from '@noble/hashes/utils';
        ╵          ~~~~~~~

/Users/fadeev/github.com/zeta-chain/example-contracts/examples/hello/frontend/node_modules/esbuild/lib/main.js:1467
  let error = new Error(text);
              ^

Error: Error during dependency optimization:

✘ [ERROR] No matching export in "node_modules/@walletconnect/utils/node_modules/@noble/hashes/esm/utils.js" for import "anumber"

    node_modules/@walletconnect/utils/node_modules/viem/node_modules/@noble/curves/esm/abstract/modular.js:8:9:
      8 │ import { anumber } from '@noble/hashes/utils';
        ╵          ~~~~~~~


    at failureErrorWithLog (/Users/fadeev/github.com/zeta-chain/example-contracts/examples/hello/frontend/node_modules/esbuild/lib/main.js:1467:15)
    at /Users/fadeev/github.com/zeta-chain/example-contracts/examples/hello/frontend/node_modules/esbuild/lib/main.js:926:25
    at /Users/fadeev/github.com/zeta-chain/example-contracts/examples/hello/frontend/node_modules/esbuild/lib/main.js:1345:9
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5) {
  errors: [Getter/Setter],
  warnings: [Getter/Setter]
}

@fadeev
Copy link
Member

fadeev commented Aug 29, 2025

I propose we remove network switcher if Dynamic allows this (to do easily, without rewriting the components). The idea is that users should not think that they're "currently on an X chain". Chain switching if needed should be handled by an app before user signs a transaction.

Screenshot 2025-08-29 at 10 24 57

If Dynamic doesn't have a setting to disable chain switching, we don't have to spend time on this.

@fadeev
Copy link
Member

fadeev commented Aug 29, 2025

Other than that this looks really really good. I've disabled mainnet chains on sandbox in Dynamic's dashboard.

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

🧹 Nitpick comments (9)
examples/hello/frontend/src/components/WalletControls.css (2)

32-36: Add visible keyboard focus and respect reduced‑motion preferences.

Buttons need a clear focus indicator and motion‑sensitive users should be able to opt out of transitions.

Apply:

 .wallet-controls-clickable {
   cursor: pointer;
-  transition: opacity 0.2s ease;
+  transition: opacity 0.2s ease;
   color: inherit;
 }
+
+.wallet-controls-clickable:focus-visible {
+  outline: 2px solid #7aa9ff;
+  outline-offset: 2px;
+}
+
+@media (prefers-reduced-motion: reduce) {
+  .wallet-controls-clickable {
+    transition: none;
+  }
+}

38-40: Avoid reducing text contrast on hover.

Lowering opacity can degrade readability. Prefer targeting the decorative icon only.

Apply:

-.wallet-controls-clickable:hover {
-  opacity: 0.8;
-}
+.wallet-controls-clickable:hover .wallet-controls-icon {
+  opacity: 0.85;
+}
examples/hello/frontend/src/components/DynamicWalletControls.tsx (1)

26-36: Improve a11y: label the control, expose full address to SRs, hide decorative icon.

Screen readers shouldn’t announce a truncated address; provide a descriptive label and hide the decorative block.

Apply:

-      <button
+      <button
         className="wallet-controls-container wallet-controls-clickable"
         type="button"
-        onClick={handleClick}
+        onClick={handleClick}
+        aria-label={`Open wallet profile for ${account}`}
+        title={account}
       >
-        <div className="wallet-controls-icon" />
+        <div className="wallet-controls-icon" aria-hidden="true" />
examples/hello/frontend/src/components/Header.tsx (1)

19-25: Ensure a connect path exists on small screens.

Wrapping the connect buttons in lg-only risks hiding the only connect affordance on mobile.

Option: remove the wrapper so the buttons are always available.

-        {!isConnected ? (
-          <div className="lg-only">
-            <ConnectEip6963Wallet />
-          </div>
-        ) : (
+        {!isConnected ? (
+          <ConnectEip6963Wallet />
+        ) : (
           <WalletControls />
         )}
-        {!isDynamicConnected ? (
-          <div className="lg-only">
-            <ConnectDynamicWallet />
-          </div>
-        ) : (
+        {!isDynamicConnected ? (
+          <ConnectDynamicWallet />
+        ) : (
           <DynamicWalletControls />
         )}

Also applies to: 39-45

examples/hello/frontend/src/DynamicAppContent.tsx (1)

11-15: Normalize chainId to avoid lookup misses

network may be hex, bigint, or undefined. Normalize before comparing; fall back cleanly when unsupported.

Apply this diff:

-  const decimalChainId = network || null;
-
-  const supportedChain = SUPPORTED_CHAINS.find(
-    (chain) => chain.chainId === decimalChainId
-  );
+  const decimalChainId = normalizeChainId(network);
+  const supportedChain = decimalChainId
+    ? SUPPORTED_CHAINS.find((chain) => chain.chainId === decimalChainId)
+    : undefined;

Add this helper in the file (outside the component):

function normalizeChainId(id: unknown): number | null {
  if (typeof id === 'number') return id;
  if (typeof id === 'bigint') return Number(id);
  if (typeof id === 'string') {
    const trimmed = id.trim();
    const isHex = /^0x[0-9a-f]+$/i.test(trimmed);
    const n = isHex ? parseInt(trimmed, 16) : Number(trimmed);
    return Number.isFinite(n) ? n : null;
  }
  return null;
}
examples/hello/frontend/src/AppContent.tsx (2)

1-3: Avoid pulling both wallet stacks into the bundle

Both flows are statically imported, so their deps are included even when unused. Consider lazy loading to reduce bundle size and decouple stacks.

Example:

import { Suspense, lazy } from 'react';
const DynamicAppContent = lazy(() => import('./DynamicAppContent'));
const Eip6963AppContent = lazy(() => import('./Eip6963AppContent'));

export function AppContent() {
  return (
    <Suspense fallback={null}>
      {USE_DYNAMIC_WALLET ? <DynamicAppContent /> : <Eip6963AppContent />}
    </Suspense>
  );
}

6-6: Gate the network selector for Dynamic wallet flow

If Dynamic prevents manual chain switching (as suggested), hide the selector in that path to avoid user confusion. Add an optional prop to ConnectedContent and pass it only when USE_DYNAMIC_WALLET is true.

Example diff (usage here):

-  return USE_DYNAMIC_WALLET ? <DynamicAppContent /> : <Eip6963AppContent />;
+  return USE_DYNAMIC_WALLET
+    ? <DynamicAppContent />
+    : <Eip6963AppContent />;

And outside this file, extend ConnectedContent with hideNetworkSelector?: boolean and render the selector conditionally; pass hideNetworkSelector from the Dynamic path.

examples/hello/frontend/src/Eip6963AppContent.tsx (2)

9-11: Normalize EIP‑6963 chainId before lookup

Mirror the normalization used in the Dynamic path to avoid mismatches when decimalChainId is hex/bigint/string.

Apply this diff:

-  const supportedChain = SUPPORTED_CHAINS.find(
-    (chain) => chain.chainId === decimalChainId
-  );
+  const normalizedId = normalizeChainId(decimalChainId);
+  const supportedChain = normalizedId
+    ? SUPPORTED_CHAINS.find((chain) => chain.chainId === normalizedId)
+    : undefined;

Add (in this file or a small shared util) the same normalizeChainId helper as proposed for DynamicAppContent.tsx.


15-17: Disconnected heuristic may be too coarse

!selectedProvider treats a selected-but-not-permissioned wallet as disconnected. Consider deriving connection from accounts (e.g., eth_accounts length) via the hook and gating on that instead for clearer UX.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c3e1d36 and 5131416.

⛔ Files ignored due to path filters (1)
  • examples/hello/frontend/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (8)
  • examples/hello/frontend/package.json (1 hunks)
  • examples/hello/frontend/src/AppContent.tsx (1 hunks)
  • examples/hello/frontend/src/DynamicAppContent.tsx (1 hunks)
  • examples/hello/frontend/src/Eip6963AppContent.tsx (1 hunks)
  • examples/hello/frontend/src/components/DynamicWalletControls.tsx (1 hunks)
  • examples/hello/frontend/src/components/Header.tsx (2 hunks)
  • examples/hello/frontend/src/components/WalletControls.css (1 hunks)
  • examples/hello/frontend/src/utils/ethersHelpers.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/hello/frontend/package.json
  • examples/hello/frontend/src/utils/ethersHelpers.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: hernan-clich
PR: zeta-chain/example-contracts#280
File: examples/hello/frontend/src/App.tsx:8-9
Timestamp: 2025-09-17T20:09:36.385Z
Learning: In the zeta-chain/example-contracts repository, commit c9e419c6c8d6f045b06b2ba94c3c9f9b4ab05d0f addressed a runtime issue by splitting AppContent.tsx into DynamicAppContent.tsx and Eip6963AppContent.tsx components, providing better separation of concerns between dynamic wallet and EIP-6963 wallet implementations.
📚 Learning: 2025-09-17T20:09:36.385Z
Learnt from: hernan-clich
PR: zeta-chain/example-contracts#280
File: examples/hello/frontend/src/App.tsx:8-9
Timestamp: 2025-09-17T20:09:36.385Z
Learning: In the zeta-chain/example-contracts repository, commit c9e419c6c8d6f045b06b2ba94c3c9f9b4ab05d0f addressed a runtime issue by splitting AppContent.tsx into DynamicAppContent.tsx and Eip6963AppContent.tsx components, providing better separation of concerns between dynamic wallet and EIP-6963 wallet implementations.

Applied to files:

  • examples/hello/frontend/src/Eip6963AppContent.tsx
  • examples/hello/frontend/src/components/DynamicWalletControls.tsx
  • examples/hello/frontend/src/DynamicAppContent.tsx
  • examples/hello/frontend/src/components/Header.tsx
  • examples/hello/frontend/src/AppContent.tsx
🧬 Code graph analysis (5)
examples/hello/frontend/src/Eip6963AppContent.tsx (4)
examples/hello/frontend/src/hooks/useEip6963Wallet.ts (1)
  • useEip6963Wallet (5-5)
examples/hello/frontend/src/constants/chains.ts (1)
  • SUPPORTED_CHAINS (9-52)
examples/hello/frontend/src/DisconnectedContent.tsx (1)
  • DisconnectedContent (10-39)
examples/hello/frontend/src/ConnectedContent.tsx (1)
  • ConnectedContent (15-51)
examples/hello/frontend/src/components/DynamicWalletControls.tsx (1)
examples/hello/frontend/src/utils/truncate.ts (1)
  • truncateAddress (1-3)
examples/hello/frontend/src/DynamicAppContent.tsx (3)
examples/hello/frontend/src/constants/chains.ts (1)
  • SUPPORTED_CHAINS (9-52)
examples/hello/frontend/src/DisconnectedContent.tsx (1)
  • DisconnectedContent (10-39)
examples/hello/frontend/src/ConnectedContent.tsx (1)
  • ConnectedContent (15-51)
examples/hello/frontend/src/components/Header.tsx (5)
examples/hello/frontend/src/hooks/useEip6963Wallet.ts (1)
  • useEip6963Wallet (5-5)
examples/hello/frontend/src/components/ConnectEip6963Wallet.tsx (1)
  • ConnectEip6963Wallet (11-45)
examples/hello/frontend/src/components/ConnectDynamicWallet.tsx (1)
  • ConnectDynamicWallet (12-31)
examples/hello/frontend/src/components/DynamicWalletControls.tsx (1)
  • DynamicWalletControls (10-40)
examples/hello/frontend/src/constants/wallets.ts (1)
  • USE_DYNAMIC_WALLET (2-2)
examples/hello/frontend/src/AppContent.tsx (3)
examples/hello/frontend/src/constants/wallets.ts (1)
  • USE_DYNAMIC_WALLET (2-2)
examples/hello/frontend/src/DynamicAppContent.tsx (1)
  • DynamicAppContent (7-29)
examples/hello/frontend/src/Eip6963AppContent.tsx (1)
  • Eip6963AppContent (6-25)
⏰ 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). (12)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/token)
  • GitHub Check: slither (examples/call, call.sarif)
  • GitHub Check: slither (examples/swap, swap.sarif)
  • GitHub Check: slither (examples/hello, hello.sarif)
🔇 Additional comments (4)
examples/hello/frontend/src/components/DynamicWalletControls.tsx (1)

37-38: Confirm single mount of DynamicUserProfile to avoid duplicate instances.

If this component is rendered in multiple places, you may end up mounting several profiles. Ensure it’s mounted once near the app root or via a portal as intended.

examples/hello/frontend/src/components/Header.tsx (1)

3-9: Pin/align @noble and viem deps to resolve esbuild “anumber” import error.

  • Verification: package.json does not contain @noble/hashes and yarn.lock contains no @noble/(hashes|curves) or viem matches — no transitive occurrences found in the repo.
  • Action: add @noble/hashes (dev dep) and force compatible versions for @noble/hashes and @noble/curves (e.g. ^1.x) using Yarn "resolutions" or npm/pnpm "overrides"/"pnpm.overrides"; re-run the build and report if the error persists.
examples/hello/frontend/src/DynamicAppContent.tsx (2)

7-9: Ensure the context provider is always mounted

Confirm UniversalSignInContextProvider wraps this component path; otherwise useUniversalSignInContext() will throw at runtime.


24-27: Validate null provider path

selectedProvider={null} relies on ConnectedContent/MessageFlowCard tolerating null. Confirm prop types and code paths explicitly handle this.

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 (2)
examples/hello/frontend/src/main.tsx (2)

15-21: Wrap the lazy provider branch in Suspense.

Required when using React.lazy; also avoids a brief flash if the provider chunk loads slowly.

-      {USE_DYNAMIC_WALLET ? (
-        <App />
-      ) : (
-        <Eip6963WalletProvider>
-          <App />
-        </Eip6963WalletProvider>
-      )}
+      {USE_DYNAMIC_WALLET ? (
+        <App />
+      ) : (
+        <Suspense fallback={null}>
+          <Eip6963WalletProvider>
+            <App />
+          </Eip6963WalletProvider>
+        </Suspense>
+      )}

8-10: Lazy‑load Eip6963WalletProvider in main.tsx

Eip6963WalletProvider is imported eagerly in examples/hello/frontend/src/main.tsx while USE_DYNAMIC_WALLET is enabled; lazy‑load it and gate with Suspense to avoid bundling and slower dev startup.

Apply within the selected range:

-import { USE_DYNAMIC_WALLET } from './constants/wallets.ts';
-import { Eip6963WalletProvider } from './context/Eip6963WalletProvider.tsx';
-import { ThemeProvider } from './context/ThemeProvider.tsx';
+import { USE_DYNAMIC_WALLET } from './constants/wallets.ts';
+import { ThemeProvider } from './context/ThemeProvider.tsx';

Additionally apply outside the selected range to support lazy loading:

- import { StrictMode } from 'react';
+ import { StrictMode, Suspense, lazy } from 'react';
// place near imports
const Eip6963WalletProvider = lazy(() =>
  import('./context/Eip6963WalletProvider.tsx').then((m) => ({
    default: m.Eip6963WalletProvider,
  })),
);

Drop “.ts/.tsx” extensions on static imports unless allowImportingTsExtensions is enabled in your tsconfig.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ce19b3c and 2452439.

📒 Files selected for processing (2)
  • examples/hello/frontend/src/App.tsx (1 hunks)
  • examples/hello/frontend/src/main.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/hello/frontend/src/App.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: hernan-clich
PR: zeta-chain/example-contracts#280
File: examples/hello/frontend/src/App.tsx:8-9
Timestamp: 2025-09-17T20:09:36.385Z
Learning: In the zeta-chain/example-contracts repository, commit c9e419c6c8d6f045b06b2ba94c3c9f9b4ab05d0f addressed a runtime issue by splitting AppContent.tsx into DynamicAppContent.tsx and Eip6963AppContent.tsx components, providing better separation of concerns between dynamic wallet and EIP-6963 wallet implementations.
📚 Learning: 2025-09-17T20:09:36.385Z
Learnt from: hernan-clich
PR: zeta-chain/example-contracts#280
File: examples/hello/frontend/src/App.tsx:8-9
Timestamp: 2025-09-17T20:09:36.385Z
Learning: In the zeta-chain/example-contracts repository, commit c9e419c6c8d6f045b06b2ba94c3c9f9b4ab05d0f addressed a runtime issue by splitting AppContent.tsx into DynamicAppContent.tsx and Eip6963AppContent.tsx components, providing better separation of concerns between dynamic wallet and EIP-6963 wallet implementations.

Applied to files:

  • examples/hello/frontend/src/main.tsx
🧬 Code graph analysis (1)
examples/hello/frontend/src/main.tsx (3)
examples/hello/frontend/src/context/ThemeProvider.tsx (1)
  • ThemeProvider (10-40)
examples/hello/frontend/src/constants/wallets.ts (1)
  • USE_DYNAMIC_WALLET (2-2)
examples/hello/frontend/src/context/Eip6963WalletProvider.tsx (1)
  • Eip6963WalletProvider (10-62)
⏰ 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). (13)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/swap)
  • GitHub Check: slither (examples/hello, hello.sarif)
  • GitHub Check: slither (examples/swap, swap.sarif)
  • GitHub Check: slither (examples/call, call.sarif)
🔇 Additional comments (1)
examples/hello/frontend/src/main.tsx (1)

14-14: Root ThemeProvider wrapper: LGTM.

Good placement for global theming; minimal surface area and predictable context availability.

Also applies to: 22-22

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: 2

♻️ Duplicate comments (1)
examples/hello/frontend/src/utils/ethersHelpers.ts (1)

12-15: Widen result types to avoid over-narrowing to BrowserProvider.

Dynamic’s getWeb3Provider may not always be a BrowserProvider. Return the common base types to keep this helper correct across providers.

 interface GetSignerAndProviderResult {
-  signer: ethers.AbstractSigner<ethers.Provider | null>;
-  provider: ethers.BrowserProvider;
+  signer: ethers.AbstractSigner;
+  provider: ethers.AbstractProvider;
 }
🧹 Nitpick comments (7)
examples/hello/frontend/src/hooks/useSwitchChain.ts (1)

9-14: Consider eliminating the wrapper function for better performance.

While the current implementation maintains API compatibility, the additional useCallback wrapper introduces unnecessary function allocation overhead since useEip6963SwitchChain already returns a memoized switchChain function.

Apply this diff to eliminate the wrapper:

 export const useSwitchChain = () => {
   const eip6963SwitchChain = useEip6963SwitchChain();
-
-  const switchChain = useCallback(
-    async (chainId: number) => {
-      return eip6963SwitchChain.switchChain(chainId);
-    },
-    [eip6963SwitchChain]
-  );
-
-  return { switchChain };
+  return eip6963SwitchChain;
 };
examples/hello/frontend/src/hooks/useDynamicSwitchChainHook.ts (2)

5-17: Remove thin wrapper; re-export the underlying hook.

This wrapper adds indirection without new behavior. Prefer a direct re‑export to reduce surface area and avoid extra closures.

-import { useCallback } from 'react';
-
-import { useDynamicSwitchChain } from './useDynamicSwitchChain';
-
-// Hook for Dynamic wallets - separate file to avoid context issues
-export const useDynamicSwitchChainHook = () => {
-  const dynamicSwitchChain = useDynamicSwitchChain();
-
-  const switchChain = useCallback(
-    async (chainId: number) => {
-      return dynamicSwitchChain.switchChain(chainId);
-    },
-    [dynamicSwitchChain]
-  );
-
-  return { switchChain };
-};
+export { useDynamicSwitchChain as useDynamicSwitchChainHook } from './useDynamicSwitchChain';

9-14: If you keep the wrapper, narrow the dependency and avoid object identity churn.

-  const switchChain = useCallback(
-    async (chainId: number) => {
-      return dynamicSwitchChain.switchChain(chainId);
-    },
-    [dynamicSwitchChain]
-  );
+  const { switchChain: doSwitchChain } = dynamicSwitchChain;
+  const switchChain = useCallback(
+    async (chainId: number) => doSwitchChain(chainId),
+    [doSwitchChain]
+  );
examples/hello/frontend/src/ConnectedContent.tsx (2)

7-7: Hide/disable the network switcher in Dynamic flow unless explicitly allowed and supported.

Aligns with the suggestion to avoid implying the user is “on X chain.” Gate the control behind a flag and wallet capability.

-import { USE_DYNAMIC_WALLET } from './constants/wallets';
+import { USE_DYNAMIC_WALLET, DYNAMIC_SHOW_NETWORK_SELECTOR } from './constants/wallets';
-            <NetworkSelector
-              selectedChain={supportedChain}
-              onNetworkSelect={handleNetworkSelect}
-            />
+            {DYNAMIC_SHOW_NETWORK_SELECTOR &&
+              primaryWallet?.connector?.supportsNetworkSwitching?.() && (
+                <NetworkSelector
+                  selectedChain={supportedChain}
+                  onNetworkSelect={handleNetworkSelect}
+                />
+              )}

Add the config toggle (outside this file):

// examples/hello/frontend/src/constants/wallets.ts
export const USE_DYNAMIC_WALLET = true;
export const DYNAMIC_SHOW_NETWORK_SELECTOR = false; // default off for Dynamic

Also applies to: 37-41


25-29: Await the chain switch for predictable UX and error surfacing.

Avoids unhandled rejections and makes it easier to hook loading/toast states later.

-  const handleNetworkSelect = (chain: SupportedChain) => {
-    switchChain(chain.chainId);
-  };
+  const handleNetworkSelect = async (chain: SupportedChain) => {
+    await switchChain(chain.chainId);
+  };
examples/hello/frontend/src/utils/ethersHelpers.ts (2)

38-40: Remove unnecessary type assertion on signer.

provider.getSigner() already returns a subtype of AbstractSigner; the cast is redundant.

-    const signer = (await provider.getSigner()) as ethers.AbstractSigner;
+    const signer = await provider.getSigner();

17-34: Confirm precedence and failure semantics.

Current behavior prefers Dynamic when available and silently falls back to EIP‑6963 on failure. Verify this is intended UX, or consider exposing the chosen source for observability.

Optional discriminator if you want explicit source tracking:

 interface GetSignerAndProviderResult {
   signer: ethers.AbstractSigner;
   provider: ethers.AbstractProvider;
+  source: 'dynamic' | 'eip6963';
 }
@@
-      return {
-        signer: signer,
-        provider: provider,
-      };
+      return { signer, provider, source: 'dynamic' };
@@
-    return {
-      signer,
-      provider,
-    };
+    return { signer, provider, source: 'eip6963' };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3f30d8d and 021ff23.

📒 Files selected for processing (6)
  • examples/hello/frontend/src/ConnectedContent.tsx (2 hunks)
  • examples/hello/frontend/src/DynamicAppContent.tsx (1 hunks)
  • examples/hello/frontend/src/MessageFlowCard.tsx (2 hunks)
  • examples/hello/frontend/src/hooks/useDynamicSwitchChainHook.ts (1 hunks)
  • examples/hello/frontend/src/hooks/useSwitchChain.ts (1 hunks)
  • examples/hello/frontend/src/utils/ethersHelpers.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/hello/frontend/src/DynamicAppContent.tsx
  • examples/hello/frontend/src/MessageFlowCard.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: hernan-clich
PR: zeta-chain/example-contracts#280
File: examples/hello/frontend/src/App.tsx:8-9
Timestamp: 2025-09-17T20:09:36.385Z
Learning: In the zeta-chain/example-contracts repository, commit c9e419c6c8d6f045b06b2ba94c3c9f9b4ab05d0f addressed a runtime issue by splitting AppContent.tsx into DynamicAppContent.tsx and Eip6963AppContent.tsx components, providing better separation of concerns between dynamic wallet and EIP-6963 wallet implementations.
📚 Learning: 2025-09-17T20:09:36.385Z
Learnt from: hernan-clich
PR: zeta-chain/example-contracts#280
File: examples/hello/frontend/src/App.tsx:8-9
Timestamp: 2025-09-17T20:09:36.385Z
Learning: In the zeta-chain/example-contracts repository, commit c9e419c6c8d6f045b06b2ba94c3c9f9b4ab05d0f addressed a runtime issue by splitting AppContent.tsx into DynamicAppContent.tsx and Eip6963AppContent.tsx components, providing better separation of concerns between dynamic wallet and EIP-6963 wallet implementations.

Applied to files:

  • examples/hello/frontend/src/hooks/useDynamicSwitchChainHook.ts
  • examples/hello/frontend/src/hooks/useSwitchChain.ts
  • examples/hello/frontend/src/ConnectedContent.tsx
  • examples/hello/frontend/src/utils/ethersHelpers.ts
🧬 Code graph analysis (4)
examples/hello/frontend/src/hooks/useDynamicSwitchChainHook.ts (1)
examples/hello/frontend/src/hooks/useDynamicSwitchChain.ts (1)
  • useDynamicSwitchChain (4-41)
examples/hello/frontend/src/hooks/useSwitchChain.ts (1)
examples/hello/frontend/src/hooks/useEip6963SwitchChain.ts (1)
  • useEip6963SwitchChain (5-28)
examples/hello/frontend/src/ConnectedContent.tsx (6)
examples/hello/frontend/src/types/wallet.ts (1)
  • EIP6963ProviderDetail (18-21)
examples/hello/frontend/src/constants/chains.ts (1)
  • SupportedChain (1-7)
examples/hello/frontend/src/hooks/useDynamicSwitchChainHook.ts (1)
  • useDynamicSwitchChainHook (6-17)
examples/hello/frontend/src/components/NetworkSelector.tsx (1)
  • NetworkSelector (14-59)
examples/hello/frontend/src/MessageFlowCard.tsx (1)
  • MessageFlowCard (23-185)
examples/hello/frontend/src/constants/wallets.ts (1)
  • USE_DYNAMIC_WALLET (2-2)
examples/hello/frontend/src/utils/ethersHelpers.ts (1)
examples/hello/frontend/src/types/wallet.ts (1)
  • EIP6963ProviderDetail (18-21)
⏰ 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). (13)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/token)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/nft)
  • GitHub Check: test (examples/swap)
  • GitHub Check: test (examples/hello)
  • GitHub Check: test (examples/call)
  • GitHub Check: test (examples/token)
  • GitHub Check: slither (examples/call, call.sarif)
  • GitHub Check: slither (examples/hello, hello.sarif)
  • GitHub Check: slither (examples/swap, swap.sarif)
🔇 Additional comments (5)
examples/hello/frontend/src/hooks/useSwitchChain.ts (3)

3-3: LGTM: Clean abstraction layer implementation.

The import of the EIP-6963 specific hook aligns well with the architectural separation between Dynamic and EIP-6963 wallet implementations as established in commit c9e419c.


5-5: LGTM: Clear documentation of hook purpose.

The comment effectively communicates the specific wallet type this hook targets, which is essential given the dual-wallet architecture introduced in this PR.


7-7: LGTM: Proper delegation to specialized hook.

The implementation correctly delegates to the EIP-6963 specific hook, maintaining the same public API while benefiting from the specialized error handling and provider management in useEip6963SwitchChain. The dependency array is correctly updated to reflect the new hook dependency.

Also applies to: 11-11, 13-13

examples/hello/frontend/src/ConnectedContent.tsx (1)

100-117: Feature flag is fine; ensure it’s constant-foldable for tree-shaking.

If USE_DYNAMIC_WALLET is static at build time (as in constants), Vite/Rollup will DCE the unused branch. Keep it as a top-level const and avoid reassignments.

examples/hello/frontend/src/utils/ethersHelpers.ts (1)

47-48: No action required — consumers already guard for null. MessageFlowCard.tsx checks for a falsy signerAndProvider and throws before use (examples/hello/frontend/src/MessageFlowCard.tsx — lines 42–51); no other getSignerAndProvider callers were found.

@hernan-clich
Copy link
Member Author

@fadeev Feel free to take another look.

  • Network switcher removed from Dynamic header.
  • Applied lib resolutions in the wallet lib.
  • Videos re-recorded in the PR description.

Copy link
Member

@fadeev fadeev left a comment

Choose a reason for hiding this comment

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

Perfect! 🤩

@hernan-clich hernan-clich merged commit 0703496 into main Sep 19, 2025
20 checks passed
@hernan-clich hernan-clich deleted the feat/integrate-dynamic-vite branch September 19, 2025 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants