-
Notifications
You must be signed in to change notification settings - Fork 61
feat: Integrate dynamic wallet on Hello frontend example #280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughWalkthroughAdds 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
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
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
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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this 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 providerProviding a populated default masks configuration mistakes. Prefer an
undefineddefault 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 castsAvoid casting
unknownwithout 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 handledisconnect
- Some providers expose
offinstead ofremoveListener; support both.- Add a
disconnectlistener 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_chainIdis 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 concurrentlyGuard 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 forAppContent.tsx.
🧹 Nitpick comments (21)
examples/hello/frontend/src/components/Modal.tsx (1)
39-39: Verify styling after removing "subheading" classConfirm there’s no unintended visual regression on modal titles and remove any dead CSS rules referencing
.subheadingif now unused.examples/hello/frontend/src/components/ConnectDynamicWallet.css (1)
1-11: Add basic interactive & accessibility statesConsider extending the styles with
cursor: pointer,:hover, and:focus-visiblerules (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 configurationA hard-coded
truerequires a commit to toggle environments. Reading fromimport.meta.env(Vite) orprocess.envallows 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 supportexport 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 nameThe 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 wrapperCasting 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 buttonThe 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 surfaceCaret 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_WALLETflag 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 listenersIf
USE_DYNAMIC_WALLETis 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 blockingalertand memoize handlers
- Replace
alert(...)inhandleConnectClickwith a non-blocking notification/toast to avoid blocking the main thread.- Wrap
handleConnectClickandhandleSelectProviderinuseCallbackto prevent avoidable re-renders in children.examples/hello/frontend/src/DisconnectedContent.tsx (1)
3-4: Code-split wallet UIs to avoid bundling unused pathsBoth 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 appearanceConsider 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 environmentIdExternalize
environmentIdto 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-80113243499aOptionally centralize in
constants/wallets.tsif you prefer config colocation (following prior discussions for consistent config handling across files).
13-25: Optional: Mount Dynamic provider only when neededTo reduce overhead when using EIP‑6963 exclusively, consider gating
DynamicContextProviderbehindUSE_DYNAMIC_WALLET, or splitAppinto 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 subcomponentsThis 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
switchChaincurrently swallows errors and always resolvesvoid.
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 whenUSE_DYNAMIC_WALLET
istrueand 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
dynamicSwitchChainandeip6963SwitchChainare objects whose identity
changes on every render, so the wrappeduseCallbackis recreated each
time. Destructure the actual function or memoise the returned objects
inside the respective hooks to keepswitchChainstable.examples/hello/frontend/src/AppContent.tsx (1)
24-25: Disconnected state may ignore wallet availability
isDisconnectedonly checksselectedProviderfor the EIP-6963 path, but
the button flow inMessageFlowCardultimately needs either a provider
or a Dynamic wallet. Consider also testing for!primaryWallethere to
avoid false positives.examples/hello/frontend/src/MessageFlowCard.tsx (1)
169-176: Disable “Send Message” when no signer availableThe 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: CacheBrowserProviderinstanceA new
BrowserProvideris created on every call, which can be
expensive. Memoise perselectedProvider(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
⛔ Files ignored due to path filters (1)
examples/hello/frontend/yarn.lockis 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 touseWalletProvidersremainA repository-wide search across
.ts,.tsx,.js, and.jsxfiles returned zero occurrences of the old hook name or import paths. The rename touseEip6963WalletProvidersis complete and safe to merge.examples/hello/frontend/src/components/WalletControls.tsx (1)
3-9: Hook swap aligns with new contextSwitching to
useEip6963Walletis 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 ^6ethereum: only depends on viem, no conflicting ethers requirementNo changes are needed before merging.
examples/hello/frontend/src/ConnectedContent.tsx (1)
11-11: Nullable provider: downstream null-safety
useEip6963SwitchChaincorrectly checks forselectedProviderbefore calling its.provider.request(...), andgetSignerAndProvideronly constructs anethers.BrowserProviderwhenselectedProvideris non-null. However, we haven’t been able to confirm howMessageFlowCard.tsx(and any other consumers) handle anullprovider. Please verify that:
- In MessageFlowCard.tsx, any access to
selectedProvider.provideror its properties is guarded (e.g., via conditional rendering or early returns).- Any additional components receiving
selectedProvider: EIP6963ProviderDetail | nullapply the same null-safety checks to prevent runtime errors.examples/hello/frontend/src/main.tsx (1)
13-17: Provider hierarchy update looks soundWrapping
<App />withThemeProviderandEip6963WalletProvideris 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 stateEnsure
primaryWallet?.addressis 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
primaryWalletto ensure we standardize on the right property across the app?
16-21: Verify styling prop for DynamicConnectButtonDouble‑check whether the component expects
buttonClassNameorclassName. 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 valueThe 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
ConnectDynamicWalletself-manages connected vs disconnected state; pairing withThemeTogglekeeps UX consistent.examples/hello/frontend/src/hooks/useDynamicSwitchChain.ts (1)
21-27: EnsurechainIdformat matches connector expectation
primaryWallet.switchNetworkis invoked with a decimalnumber.
Most EIP-1193 based wallets (and MetaMask in particular) expect thechainId
string in hex prefixed with0x(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.
There was a problem hiding this 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/srcAlso 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 onlyThe 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 enabledRendering 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.
📒 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 overridesYou 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
walletsFilterfunction.- 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 IDThe
DynamicContextProvideris 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 existingUSE_DYNAMIC_WALLETflag.
– SourceenvironmentIdfromimport.meta.env.VITE_DYNAMIC_ENV_ID, with a controlled fallback during transition.
– Normalize thethemeprop 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) inexamples/hello/frontendwith
VITE_DYNAMIC_ENV_ID=<your-environment-id>.- Ensure Vite injects this var in both local and CI builds.
- Review other
USE_DYNAMIC_WALLETusages (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.
There was a problem hiding this 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 reviewEarlier concern about
useThemethrowing outside a provider was already verified as safe becauseThemeProviderwraps<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 disabledThe 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
evmNetworksshould 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 varHardcoding 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.envto 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
.envkeyVITE_DYNAMIC_ENV_IDper environment.- Optional
VITE_DYNAMIC_ENABLE_SOCIALsurfaces social sign-in alongside wallets.- Optional
VITE_DYNAMIC_WALLETS_WHITELISTallows a comma-separated allowlist in different demos.
24-31: Social login visibility may be suppressed by the current view configOnly the Wallet section is rendered. If the PR demo intends to show Google login, add the
Socialsection (or gate it via an env flag as in the diff above).
32-34: Narrow wallet filter hides most connectorsFiltering to
universalsigninevmwill 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 valuesEnsure the provider receives a supported value. If
themecan 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.
📒 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 DynamicUserProfilePlacement inside the provider is correct and provides a quick path to user settings without additional plumbing.
There was a problem hiding this 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-v6peersethers, enforce a singleethersversion 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-levelenginesfield to lock Node versionsAll 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.
⛔ Files ignored due to path filters (1)
examples/hello/frontend/yarn.lockis 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 theethers-v6adapter, which matches yourethers@^6.13.2. No immediate concerns from a dependency perspective.
13-16: Ensure dependencies are installed before verifying peer-dependenciesIt 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 firstnpm 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-coresupportsreact@^19.0.0(andreact-dom@^19.0.0),@dynamic-labs/ethereumand@dynamic-labs/ethers-v6align withethers@^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.
There was a problem hiding this 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:
- Import the component:
import { DynamicWidget, + DynamicConnectButton, useDynamicContext, } from '@dynamic-labs/sdk-react-core';
- 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.
📒 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
There was a problem hiding this 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.
📒 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)
There was a problem hiding this 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’siconprop 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
selectWalletOptionreturns 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
DynamicWidgetlikely 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 leakageImporting a global stylesheet risks class collisions (
.dynamic-connect-button,.dynamic-connect-button-content). ConsiderConnectDynamicWallet.module.cssandimport styles from './ConnectDynamicWallet.module.css', then referencestyles.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 mappingThe
HELLO_UNIVERSAL_CONTRACT_ADDRESSconstant is currently defined here and used only inMessageFlowCard.tsx(import on line 12, usage on line 53) and in its own export insrc/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 bychainId.- In
MessageFlowCard.tsx, select the address bysupportedChain.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 beforeThis 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.
⛔ Files ignored due to path filters (1)
examples/hello/frontend/yarn.lockis 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 flowThe conditional render based on
primaryWallet?.addressis straightforward and aligns with the intended UX. Good separation of disconnected vs. connected states.
12-15: ConnectDynamicWallet usages are correctly wrapped by UniversalSignInContextProviderI’ve verified that:
- In
App.tsx, all children—includingHeader,AppContent, and transitivelyConnectDynamicWallet—are nested inside<UniversalSignInContextProvider …>, so there’s no immediate risk ofuseUniversalSignInContext()throwing at runtime.- There are no Storybook stories (
*.stories.tsx) or Jest tests referencingConnectDynamicWalletor directly callinguseUniversalSignInContext, 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
ConnectDynamicWalletmust live under aUniversalSignInContextProvider.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 passselectedProvider(includingnullwhen 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.
There was a problem hiding this 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
DynamicWidgetuntil 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-buttonhas a visible:focus-visiblestyle 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.
📒 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?.addressare correct.
There was a problem hiding this 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.
⛔ Files ignored due to path filters (1)
examples/hello/frontend/yarn.lockis 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/toolkitand reduces supply-chain drift for the example app. LGTM.
14-14: Peer dependencies validated –@zetachain/wallet@1.0.1declares 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.
|
|
|
Other than that this looks really really good. I've disabled mainnet chains on sandbox in Dynamic's dashboard. |
There was a problem hiding this 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-onlyrisks 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
networkmay 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 bundleBoth 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 flowIf Dynamic prevents manual chain switching (as suggested), hide the selector in that path to avoid user confusion. Add an optional prop to
ConnectedContentand pass it only whenUSE_DYNAMIC_WALLETis true.Example diff (usage here):
- return USE_DYNAMIC_WALLET ? <DynamicAppContent /> : <Eip6963AppContent />; + return USE_DYNAMIC_WALLET + ? <DynamicAppContent /> + : <Eip6963AppContent />;And outside this file, extend
ConnectedContentwithhideNetworkSelector?: booleanand render the selector conditionally; passhideNetworkSelectorfrom the Dynamic path.examples/hello/frontend/src/Eip6963AppContent.tsx (2)
9-11: Normalize EIP‑6963 chainId before lookupMirror the normalization used in the Dynamic path to avoid mismatches when
decimalChainIdis 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
normalizeChainIdhelper as proposed forDynamicAppContent.tsx.
15-17: Disconnected heuristic may be too coarse
!selectedProvidertreats a selected-but-not-permissioned wallet as disconnected. Consider deriving connection from accounts (e.g.,eth_accountslength) 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.
⛔ Files ignored due to path filters (1)
examples/hello/frontend/yarn.lockis 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.tsxexamples/hello/frontend/src/components/DynamicWalletControls.tsxexamples/hello/frontend/src/DynamicAppContent.tsxexamples/hello/frontend/src/components/Header.tsxexamples/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 mountedConfirm
UniversalSignInContextProviderwraps this component path; otherwiseuseUniversalSignInContext()will throw at runtime.
24-27: Validate null provider path
selectedProvider={null}relies onConnectedContent/MessageFlowCardtoleratingnull. Confirm prop types and code paths explicitly handle this.
There was a problem hiding this 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.tsxEip6963WalletProvider 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.
📒 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
There was a problem hiding this 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
getWeb3Providermay not always be aBrowserProvider. 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
useCallbackwrapper introduces unnecessary function allocation overhead sinceuseEip6963SwitchChainalready returns a memoizedswitchChainfunction.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 DynamicAlso 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 ofAbstractSigner; 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.
📒 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.tsexamples/hello/frontend/src/hooks/useSwitchChain.tsexamples/hello/frontend/src/ConnectedContent.tsxexamples/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 falsysignerAndProviderand throws before use (examples/hello/frontend/src/MessageFlowCard.tsx — lines 42–51); no othergetSignerAndProvidercallers were found.
|
@fadeev Feel free to take another look.
|
fadeev
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect! 🤩

Walkthroughs
With MM
rec-fr-example-mm-1.mp4
With Dynamic
rec-fr-example-dyn-1.mp4
Summary by CodeRabbit
New Features
Refactor
Style
Chores