Skip to content

feat: show received amount for warp transfers with different scales#313

Open
Xaroz wants to merge 1 commit intomainfrom
xaroz/explorer-dest-amount-display
Open

feat: show received amount for warp transfers with different scales#313
Xaroz wants to merge 1 commit intomainfrom
xaroz/explorer-dest-amount-display

Conversation

@Xaroz
Copy link
Copy Markdown
Contributor

@Xaroz Xaroz commented Apr 16, 2026

Summary

Shows the destination received amount in the explorer when origin and destination tokens have different scales (e.g., VRA BSC scale=10 → ETH scale=1, or BSC USDT 18-dec scale-down).

Changes

Transfer details card

  • New "Received amount" row appears below "Amount" when scales differ
  • Both amounts formatted with comma separators via formatAmountWithCommas

Message list tooltip

  • Tooltip now shows amount conversion: 1 VRA → 10 VRA
  • Uses formatAmountCompact for both sides (K/M/B notation)
  • Appears when amounts differ OR when token symbols differ (previously only symbols)

Computation

  • destAmount computed in parseWarpRouteMessageDetails using existing getWarpRouteAmountParts with dest token's scale and getEffectiveDecimals with swapped token order
  • Only computed when scalesEqual(originToken.scale, destinationToken.scale) is false
  • null for same-scale routes (no UI change)

Files changed

  • src/types.ts — added destAmount: string | null to WarpRouteDetails
  • src/features/messages/utils.ts — compute destAmount in parseWarpRouteMessageDetails
  • src/features/messages/cards/WarpTransferDetailsCard.tsx — received amount row + comma formatting
  • src/features/messages/MessageTable.tsx — tooltip with formatted amounts

Related

🤖 Generated with Claude Code

- Add destAmount to WarpRouteDetails, computed when origin/dest scales differ
- Show "Received amount" row in transfer details card with comma formatting
- Update message list tooltip to show amount conversion (e.g. "1 VRA → 10 VRA")
  using formatAmountCompact on both sides
- Tooltip now appears when amounts differ, not just when token symbols differ
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperlane-explorer Ready Ready Preview, Comment Apr 16, 2026 9:02pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 16, 2026

📝 Walkthrough

Walkthrough

This pull request adds destination amount calculation and display to warp route messages. The WarpRouteDetails type is extended with a destAmount field, computed in message parsing when token scales differ, then formatted and displayed in relevant UI components.

Changes

Cohort / File(s) Summary
Data Layer
src/types.ts, src/features/messages/utils.ts
Added destAmount field to WarpRouteDetails interface and implemented destination amount calculation in parseWarpRouteMessageDetails() using effective destination decimals and token scales.
UI Components
src/features/messages/MessageTable.tsx, src/features/messages/cards/WarpTransferDetailsCard.tsx
Updated amount formatting using formatAmountCompact and formatAmountWithCommas, added conditional tooltip logic, and introduced "Received amount:" display when destination amount is available.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main feature: displaying received amounts for warp transfers when token scales differ, which aligns perfectly with all changes across the codebase.
Description check ✅ Passed The description is well-detailed and directly relevant to the changeset, explaining the feature, affected UI components, computation logic, modified files, and related references.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/features/messages/MessageTable.tsx (1)

261-261: Long inline template literal — consider extracting the tooltip string.

This one-liner packs origin amount, arrow, destination amount, and two symbols into a single template and blows well past the 100-char line width. Pulling it into a local const tooltipContent = ... (or a tiny helper) would make the conditional branch easier to scan and keep Prettier from producing a wrapped blob.

♻️ Suggested extraction
-            <div
-              className={styles.iconText}
-              data-tooltip-id="root-tooltip"
-              data-tooltip-content={`${formatAmountCompact(warpRouteDetails.amount)} ${warpRouteDetails.originToken.symbol}${showWarpTooltip ? ` → ${formatAmountCompact(warpRouteDetails.destAmount ?? warpRouteDetails.amount)} ${warpRouteDetails.destinationToken.symbol}` : ''}`}
-            >
+            <div
+              className={styles.iconText}
+              data-tooltip-id="root-tooltip"
+              data-tooltip-content={tooltipContent}
+            >

With something like this computed alongside showWarpTooltip:

const originPart = `${formatAmountCompact(warpRouteDetails.amount)} ${warpRouteDetails.originToken.symbol}`;
const destPart = showWarpTooltip
  ? ` → ${formatAmountCompact(warpRouteDetails.destAmount ?? warpRouteDetails.amount)} ${warpRouteDetails.destinationToken.symbol}`
  : '';
const tooltipContent = `${originPart}${destPart}`;

As per coding guidelines: "Use single quotes, trailing commas, and maintain 100 character line width".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/messages/MessageTable.tsx` at line 261, Extract the long inline
template used in the data-tooltip-content prop into a local const (e.g.
tooltipContent) to improve readability and keep lines under 100 chars: compute
originPart using formatAmountCompact(warpRouteDetails.amount) and
warpRouteDetails.originToken.symbol, compute destPart conditionally based on
showWarpTooltip using formatAmountCompact(warpRouteDetails.destAmount ??
warpRouteDetails.amount) and warpRouteDetails.destinationToken.symbol, then set
tooltipContent to originPart + destPart and use that in the data-tooltip-content
attribute; follow project style (single quotes, trailing commas) when adding the
new const near the component render.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/features/messages/MessageTable.tsx`:
- Line 261: Extract the long inline template used in the data-tooltip-content
prop into a local const (e.g. tooltipContent) to improve readability and keep
lines under 100 chars: compute originPart using
formatAmountCompact(warpRouteDetails.amount) and
warpRouteDetails.originToken.symbol, compute destPart conditionally based on
showWarpTooltip using formatAmountCompact(warpRouteDetails.destAmount ??
warpRouteDetails.amount) and warpRouteDetails.destinationToken.symbol, then set
tooltipContent to originPart + destPart and use that in the data-tooltip-content
attribute; follow project style (single quotes, trailing commas) when adding the
new const near the component render.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 98be2a35-74ca-4425-87fe-8042cf8e243e

📥 Commits

Reviewing files that changed from the base of the PR and between e3c80b1 and ae57616.

📒 Files selected for processing (4)
  • src/features/messages/MessageTable.tsx
  • src/features/messages/cards/WarpTransferDetailsCard.tsx
  • src/features/messages/utils.ts
  • src/types.ts

@Xaroz Xaroz requested review from paulbalaji and xeno097 April 16, 2026 21:20
@paulbalaji
Copy link
Copy Markdown
Collaborator

@claude review

@claude
Copy link
Copy Markdown

claude bot commented Apr 17, 2026

Claude finished @paulbalaji's task in 3m 9s —— View job


Claude review complete

  • Run /claude-review via agent team
  • Post consolidated review with inline comments via /inline-pr-comments

Posted a consolidated review (link) with 5 inline comments. Summary:

  • utils.ts:75-78 — Redundant scale guard; !scalesEqual(...) alone is sufficient
  • utils.ts:84 — Missing unit test for new destAmount branch (CLAUDE.md calls for util tests)
  • MessageTable.tsx:261 — Long template literal (~240 chars) exceeds 100-char guideline; extract tooltipContent. Still unresolved from CodeRabbit's review.
  • MessageTable.tsx:261formatAmountCompact may over-truncate in the tooltip; consider formatAmountWithCommas for the hover reveal
  • WarpTransferDetailsCard.tsx:99 — Nit: destAmount !== null reads more intentfully than destAmount &&

Plus a UX note in the body about same-symbol / different-scale routes showing origin amount in the cell but destination amount in the tooltip — worth confirming intent.

Nothing blocking.
• Branch: xaroz/explorer-dest-amount-display

Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Review Summary

Clean, minimal implementation. The destination amount is computed only when scales diverge, reused via existing helpers (getEffectiveDecimals, getWarpRouteAmountParts), and surfaced in both the list tooltip and the details card. Nice incidental cleanup to shorthand property syntax in parseWarpRouteMessageDetails.

A few things to consider:

  • The scale-divergence guard in utils.ts is redundant with scalesEqual and obscures intent — see inline comment.
  • The new destAmount path in parseWarpRouteMessageDetails has no test coverage. CLAUDE.md calls for unit tests on util changes, and the scale-difference branch is the whole point of this PR. A lightweight addition covering the VRA-style case would pin the math down.
  • The long template literal on MessageTable.tsx:261 is worth extracting for readability (also flagged by CodeRabbit — still unresolved).
  • UX note: when scales differ but token symbol/logo are identical (e.g. same-token scale-down), the row cell shows a single icon and a single amount (formatAmountCompact(originAmount)), while the tooltip reveals X VRA → Y VRA. That's the stated intent, but some users will read the cell as the received amount. Worth confirming the cell should show the origin amount rather than the destination amount (or indicating the scale mismatch visually).

Nothing blocking — mostly polish and test coverage.

Comment on lines +75 to +78
if (
(originToken.scale || destinationToken.scale) &&
!scalesEqual(originToken.scale, destinationToken.scale)
) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The (originToken.scale || destinationToken.scale) guard is redundant: if both scales are undefined, scalesEqual(undefined, undefined) returns true (both normalize to 1:1), so !scalesEqual(...) already short-circuits. Suggest dropping the guard:

if (!scalesEqual(originToken.scale, destinationToken.scale)) {

If you're guarding against a specific scalesEqual edge case (e.g. undefined vs undefined semantics in the SDK), add a comment explaining why — as written the combined condition reads like belt-and-braces and invites confusion next time someone touches it.

decimals: destDecimals,
scale: destinationToken.scale,
});
destAmount = fromWei(destAmountParts.amount.toString(), destAmountParts.decimals);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No test coverage for the new destAmount branch. Given the core promise of the PR is "correct destination amount across scales," a small unit test for parseWarpRouteMessageDetails covering the VRA-style case (origin scale=10, dest scale=1, same wireDecimals) would guard against regressions in the scale math. Per CLAUDE.md: "Unit tests for utils."

className={styles.iconText}
data-tooltip-id="root-tooltip"
data-tooltip-content={`${warpRouteDetails.amount} ${warpRouteDetails.originToken.symbol}${isDifferentWarpToken ? ` → ${warpRouteDetails.destinationToken.symbol}` : ''}`}
data-tooltip-content={`${formatAmountCompact(warpRouteDetails.amount)} ${warpRouteDetails.originToken.symbol}${showWarpTooltip ? ` → ${formatAmountCompact(warpRouteDetails.destAmount ?? warpRouteDetails.amount)} ${warpRouteDetails.destinationToken.symbol}` : ''}`}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Already raised by CodeRabbit — still unresolved. The inline template is ~240 chars, well past the 100-char guideline in CLAUDE.md. Extracting into a tooltipContent const alongside showWarpTooltip would make this block legible:

const originPart = `${formatAmountCompact(warpRouteDetails.amount)} ${warpRouteDetails.originToken.symbol}`;
const destPart = showWarpTooltip
  ? ` → ${formatAmountCompact(warpRouteDetails.destAmount ?? warpRouteDetails.amount)} ${warpRouteDetails.destinationToken.symbol}`
  : '';
const tooltipContent = `${originPart}${destPart}`;

className={styles.iconText}
data-tooltip-id="root-tooltip"
data-tooltip-content={`${warpRouteDetails.amount} ${warpRouteDetails.originToken.symbol}${isDifferentWarpToken ? ` → ${warpRouteDetails.destinationToken.symbol}` : ''}`}
data-tooltip-content={`${formatAmountCompact(warpRouteDetails.amount)} ${warpRouteDetails.originToken.symbol}${showWarpTooltip ? ` → ${formatAmountCompact(warpRouteDetails.destAmount ?? warpRouteDetails.amount)} ${warpRouteDetails.destinationToken.symbol}` : ''}`}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

formatAmountCompact will truncate precision (e.g. <0.0001, 1.23K) on both sides of the arrow. Since the tooltip is the "reveal on hover" view, users might reasonably expect full precision here. Consider using formatAmountWithCommas for the tooltip content and reserving the compact format for the inline cell text. Minor — flag only, open to other takes.

blurValue={blur}
showCopy
/>
{destAmount && (
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit: {destAmount && ...}destAmount is typed string | null. Technically safe today (fromWei won't return ''), but {destAmount !== null && ...} reads more intentfully and won't bite if the type ever broadens. Take it or leave it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants