feat: add warp route visualization card for message details - #257
Conversation
Add a collapsible card that visualizes the entire warp route when viewing warp transfer messages. Shows all chains in the route with their token type (collateral, synthetic, native, xERC20), addresses, owners, and collateral balances for EVM chains. Features: - Graph visualization with origin/destination highlighted - Token type badges with color coding - Collateral balances and synthetic supply display - Transfer amount shown on edge between origin/destination - Block explorer links for addresses - Manual refresh button for balances - Collapsed view for routes with >6 chains - Destination marked red if insufficient collateral Limitations: - Balance fetching only supported for EVM chains (Solana/StarkNet adapters require native dependencies that don't bundle for browser)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughAdds a Warp Route visualization feature: new visualization types and hooks, a WarpRouteGraph component and WarpRouteVisualizationCard UI, store/type plumbing for warpRouteConfigs, and MessageDetails now renders the new card. (<=50 words) Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User
participant MD as MessageDetails
participant C as WarpRouteVisualizationCard
participant V as useWarpRouteVisualization
participant B as useWarpRouteBalances
participant S as Store/MultiProvider
participant G as WarpRouteGraph
U->>MD: open message view
MD->>C: mount card (message, warpRouteDetails, blur)
C->>V: request visualization data
V->>S: read warpRouteConfigs / chain metadata
V-->>C: return visualization (or undefined)
C->>B: request balances when expanded
B->>S: create adapters / fetch bridged supplies
B-->>C: return balances + refresh()
C->>G: render graph with tokens, chains, balances
G-->>U: display interactive graph
U->>C: click refresh
C->>B: call refresh()
B->>S: refetch adapter queries
B-->>C: updated balances
C->>G: re-render with updated balances
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Fix all issues with AI agents
In `@src/features/messages/cards/WarpRouteVisualizationCard.tsx`:
- Around line 33-38: The code uses the || operator when falling back to decimals
(e.g., in the call to toWei inside the useWarpRouteBalances invocation and the
similar call around line 55), which treats 0 as falsy and incorrectly replaces
zero decimals with 18; change those fallbacks from "originToken.decimals || 18"
(and the corresponding "destinationToken.decimals || 18") to use nullish
coalescing "originToken.decimals ?? 18" and "destinationToken.decimals ?? 18" so
zero-decimal tokens are preserved.
- Around line 74-79: Replace the template-string conditional class on the Card
with clsx: import clsx at the top of WarpRouteVisualizationCard.tsx, then change
the Card's className to use clsx to combine "w-full" with the conditional blur
class (based on the blur prop/state). Also update the button's className usage
if similar patterns exist; refer to the Card component, the blur identifier, and
the isExpanded/setIsExpanded toggle to locate the change.
In `@src/features/messages/warpVisualization/useWarpRouteVisualization.ts`:
- Around line 26-30: The current match uses toLowerCase() unconditionally which
can corrupt case‑sensitive IDs; update the matching in useWarpRouteVisualization
so you only normalize case for hex addresses: detect hex by testing tokenAddress
(and t.addressOrDenom when present) against a hex pattern like
/^0x[0-9a-fA-F]+$/ and apply toLowerCase() only in that branch, otherwise
compare tokenAddress and t.addressOrDenom as-is; keep the chainName equality
check unchanged and ensure you reference tokenAddress, t.addressOrDenom, and
config.tokens.find when making the change.
- Around line 162-177: In the tokens mapping inside useWarpRouteVisualization
(mapping warpRoute.config.tokens to WarpRouteTokenVisualization), replace the
fallback for decimals that currently uses "decimals || 18" with the nullish
coalescing operator so legitimate zero values are preserved (i.e., use "decimals
?? 18"); update the decimals assignment in the returned object to use
token.decimals ?? 18 so only null/undefined get the default.
In `@src/features/messages/warpVisualization/WarpRouteGraph.tsx`:
- Around line 351-354: The isExpanded state is initialized from
shouldCollapseByDefault but never syncs when tokens load asynchronously, so
routes that grow beyond COLLAPSE_THRESHOLD remain expanded; update the component
to watch tokens (or tokens.length) and set isExpanded to
!shouldCollapseByDefault when tokens change unless the user has manually toggled
expansion—implement a manualToggle flag (e.g., a useRef or separate state like
userToggled) that is set when setIsExpanded is invoked by user action, and add a
useEffect that recalculates shouldCollapseByDefault and only calls
setIsExpanded(!shouldCollapseByDefault) when userToggled is false, referencing
shouldCollapseByDefault, isExpanded, setIsExpanded, tokens, and
COLLAPSE_THRESHOLD (apply same change for the other occurrences around the lines
noted).
- Around line 96-118: The formatCompactBalance function currently converts the
full-precision string from fromWei to a JS Number, which can lose precision for
very large values; update formatCompactBalance to use BigNumber (from
bignumber.js or the project's BigNumber helper) or use fromWeiRounded() to parse
the value safely, then perform the comparisons/divisions and formatting using
BigNumber methods (or convert the rounded value to a string first) so the output
remains consistent (e.g., "1.2M", "500K", fixed decimals or exponential) without
switching to a raw unformatted string; locate formatCompactBalance and replace
Number()/value math with BigNumber-based comparisons, division and
toFixed/toFormat calls to preserve precision and consistent display.
🧹 Nitpick comments (2)
src/store.ts (1)
183-190: Log registry fetch failures with context.
The catch block drops the error, which makes debugging harder. Consider logging the error object.🔧 Suggested tweak
- } catch { - logger.debug( - 'Failed to build warp route data from GithubRegistry. Using published warp route configs.', - ); + } catch (err) { + logger.warn( + 'Failed to build warp route data from GithubRegistry. Using published warp route configs.', + err, + );src/features/messages/warpVisualization/WarpRouteGraph.tsx (1)
172-214: Useclsx()for those conditional classNames.
Right now you've got template literals scattered about with conditional bits sprinkled in.clsx()is already in your dependencies and keeps things tidier, just like your coding guidelines suggest.♻️ Example refactor (apply similarly in other spots)
-import { useMemo, useState } from 'react'; +import { useMemo, useState } from 'react'; +import clsx from 'clsx'; ... - <div - className={`flex min-w-[130px] flex-col items-center rounded-lg border-2 bg-white p-2 shadow-sm ${borderColor} ${hasInsufficientBalance ? 'bg-red-50' : ''}`} - > + <div + className={clsx( + 'flex min-w-[130px] flex-col items-center rounded-lg border-2 bg-white p-2 shadow-sm', + borderColor, + hasInsufficientBalance && 'bg-red-50', + )} + > ... - <div - className={`mt-1 rounded px-1.5 py-0.5 text-[10px] font-medium ${ - hasInsufficientBalance - ? 'bg-red-100 text-red-700' - : isSynthetic - ? 'bg-purple-100 text-purple-700' - : 'bg-gray-100 text-gray-700' - }`} - > + <div + className={clsx( + 'mt-1 rounded px-1.5 py-0.5 text-[10px] font-medium', + hasInsufficientBalance + ? 'bg-red-100 text-red-700' + : isSynthetic + ? 'bg-purple-100 text-purple-700' + : 'bg-gray-100 text-gray-700', + )} + >Also applies to: 236-243, 578-586
- Fix ESLint errors for missing queryKey dependencies with disable comments - Use nullish coalescing (??) for decimals to preserve zero values - Use clsx for conditional classNames in WarpRouteVisualizationCard - Fix case-folding for non-hex addresses (preserve base58 case) - Sync expanded state when tokens load asynchronously in WarpRouteGraph - Apply prettier formatting
Co-authored-by: Xaroz <xaroz@users.noreply.github.com>
- Replace custom formatCompactBalance with formatAmountCompact from utils/amount.ts - Replace custom truncateAddress with shortenAddress from @hyperlane-xyz/utils - Replace synchronous getExplorerAddressUrl with async tryGetBlockExplorerAddressUrl - Use refetch from useQuery instead of custom refresh callback via queryClient - Change origin border color from green to blue (both use same color now) - Remove Origin/Destination text labels (arrow direction is self-explanatory) - Add stopPropagation to CopyButton wrapper in header to prevent toggle on copy
The async useExplorerUrls hook was causing excessive network requests on every render due to the tokens array being a new reference each time. Reverted to the original synchronous approach that builds URLs from local chain metadata without making network calls.
- Use fixed width (w-[140px]) for CompactChainNode and (w-[80px]) for MinimalChainNode - Use chain displayName from metadata instead of chainName - Add text-center for better text alignment - Replace custom normalizeAddress with normalizeAddressToHex from utils/yamlParsing Co-authored-by: paulbalaji <paulbalaji@users.noreply.github.com> Co-authored-by: Xaroz <xaroz@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/features/messages/warpVisualization/WarpRouteGraph.tsx`:
- Around line 586-590: The expanded node rendering in WarpRouteGraph.tsx is
using node.token.chainName directly instead of the metadata displayName like
CompactChainNode and MinimalChainNode; update the expanded view to resolve
displayName from multiProvider.tryGetChainMetadata (or use the displayName
computed in nodePositions memo) and render that value in place of
node.token.chainName so all node views consistently use the chain metadata
displayName.
🧹 Nitpick comments (1)
src/features/messages/warpVisualization/WarpRouteGraph.tsx (1)
578-586: Consider usingclsx()for complex conditional classNames.The nested ternary for the border/background classes works, but per the coding guidelines,
clsx()would make this a bit easier to read – especially when ye got multiple conditions piled up like layers in an onion.♻️ Optional refactor with clsx()
+import clsx from 'clsx'; ... <div - className={`flex min-w-[120px] flex-col items-center rounded-lg border-2 bg-white p-2 shadow-sm ${ - isOrigin || isDestination - ? hasInsufficientBalance - ? 'border-red-500 bg-red-50' - : 'border-blue-500' - : 'border-gray-200' - }`} + className={clsx( + 'flex min-w-[120px] flex-col items-center rounded-lg border-2 bg-white p-2 shadow-sm', + { + 'border-red-500 bg-red-50': (isOrigin || isDestination) && hasInsufficientBalance, + 'border-blue-500': (isOrigin || isDestination) && !hasInsufficientBalance, + 'border-gray-200': !isOrigin && !isDestination, + } + )} >This applies to similar patterns throughout the file (lines 157, 195-201, 650-656). As per coding guidelines: use
clsx()for conditional className assignment.
Remove fully connected graph expansion per Jason's feedback. Keep only simple view: origin/destination at top with transfer amount arrow, remaining chains below in compact view. Add xERC20 support with total supply and lockbox balance display. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
so xerc20's were previously not supported either, but i just added support for it and then removed the graph visualization |
Keep both warpRouteConfigs (for visualization) and warpRouteIdToAddressesMap (for main's functionality). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/features/messages/warpVisualization/WarpRouteGraph.tsx`:
- Around line 37-48: The getTokenTypeLabel function currently checks for
'XERC20' before 'Lockbox', so values like "EvmHypXERC20Lockbox" match XERC20
first and never return Lockbox; update getTokenTypeLabel to detect Lockbox
before XERC20 (move the if (standard.includes('Lockbox')) branch above the
XERC20 check) or explicitly handle the combined case (e.g., if
standard.includes('XERC20') && standard.includes('Lockbox') return 'xERC20
Lockbox') so Lockbox labels surface correctly.
🧹 Nitpick comments (1)
src/features/messages/warpVisualization/WarpRouteGraph.tsx (1)
123-165: Use clsx for conditional className branches.
There are a few template‑string conditionals; swapping toclsxkeeps styling consistent with the project standard.As per coding guidelines: Use `clsx()` for conditional classNames instead of manual concatenation.♻️ Example refactor (apply similarly in MinimalChainNode)
- <span - className={`mt-1 rounded border px-1.5 py-0.5 text-[9px] font-medium ${getTokenTypeColor(token.standard)}`} - > + <span + className={clsx( + 'mt-1 rounded border px-1.5 py-0.5 text-[9px] font-medium', + getTokenTypeColor(token.standard), + )} + > {getTokenTypeLabel(token.standard)} </span>- <div - className={`mt-1 rounded px-1.5 py-0.5 text-[10px] font-medium ${ - hasInsufficientBalance - ? 'bg-red-100 text-red-700' - : isSynthetic - ? 'bg-purple-100 text-purple-700' - : 'bg-gray-100 text-gray-700' - }`} - > + <div + className={clsx( + 'mt-1 rounded px-1.5 py-0.5 text-[10px] font-medium', + hasInsufficientBalance + ? 'bg-red-100 text-red-700' + : isSynthetic + ? 'bg-purple-100 text-purple-700' + : 'bg-gray-100 text-gray-700', + )} + >Also applies to: 214-224
- Remove unnecessary balancesWithSufficiency memo in useWarpRouteBalances - Fix CopyButton toggle issue by moving route ID pill outside toggle button Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Addressed in 38f2eb3. Changed |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/features/messages/warpVisualization/WarpRouteGraph.tsx`:
- Around line 326-333: The transfer label pill in WarpRouteGraph is being
rendered even when transferAmountDisplay or tokenSymbol are undefined, causing
"undefined undefined" to appear; update the JSX rendering logic around the pill
(the div containing transferAmountDisplay and tokenSymbol) to only render when
the values are present (e.g., guard on transferAmountDisplay and tokenSymbol) so
the pill is hidden until both values are ready.
🧹 Nitpick comments (2)
src/features/messages/warpVisualization/WarpRouteGraph.tsx (2)
152-161: Preferclsxfor conditional badge styles.
Right now the conditional classes are stitched in template strings. Usingclsxkeeps it cleaner and consistent with the codebase style.As per coding guidelines: Use `clsx()` for conditional classNames instead of manual concatenation.♻️ Suggested refactor
- <div - className={`mt-1 rounded px-1.5 py-0.5 text-[10px] font-medium ${ - hasInsufficientBalance - ? 'bg-red-100 text-red-700' - : isSynthetic - ? 'bg-purple-100 text-purple-700' - : 'bg-gray-100 text-gray-700' - }`} - > + <div + className={clsx( + 'mt-1 rounded px-1.5 py-0.5 text-[10px] font-medium', + hasInsufficientBalance + ? 'bg-red-100 text-red-700' + : isSynthetic + ? 'bg-purple-100 text-purple-700' + : 'bg-gray-100 text-gray-700', + )} + >- <div - className={`mt-0.5 rounded px-1 py-0.5 text-[8px] font-medium ${ - isSynthetic ? 'bg-purple-100 text-purple-700' : 'bg-gray-100 text-gray-700' - }`} - > + <div + className={clsx( + 'mt-0.5 rounded px-1 py-0.5 text-[8px] font-medium', + isSynthetic ? 'bg-purple-100 text-purple-700' : 'bg-gray-100 text-gray-700', + )} + >Also applies to: 221-224
262-292: Guard against stale explorer URL results.
Iftokenschanges quickly, an older fetch can still win and overwrite newer URLs. A simple “still active” flag keeps state updates from going swampy.🛠️ Safer async pattern
useEffect(() => { + let isActive = true; const fetchExplorerUrls = async () => { const urls: Record<string, string | null> = {}; const fetchTasks: { key: string; promise: Promise<string | null> }[] = []; @@ - setExplorerUrls(urls); + if (isActive) { + setExplorerUrls(urls); + } }; if (tokens.length > 0) { fetchExplorerUrls(); } + + return () => { + isActive = false; + }; }, [tokens, multiProvider]);


Summary
Add a collapsible card that visualizes the entire warp route when viewing warp transfer messages. Shows all chains in the route with their token type (collateral, synthetic, native, xERC20), addresses, and collateral balances for EVM chains.
Uses registry data for token types (no RPC calls on page load). Balance fetches are deferred until the user expands the card.
Features
standardfield)Performance
Limitations
Test URLs
Files Changed
WarpRouteVisualizationCard.tsx- Main collapsible card componentwarpVisualization/directory with:WarpRouteGraph.tsx- SVG + HTML graph visualizationuseWarpRouteVisualization.ts- Hook to build visualization from registryuseWarpRouteBalances.ts- Hook to fetch balances (deferred)types.ts- TypeScript interfacesMessageDetails.tsx- Integrated the new cardstore.ts- Added warpRouteConfigs to storetypes.ts- Added WarpRouteConfigs typeSummary by CodeRabbit
New Features
Chores