Skip to content

feat: parse and display warp route fees#290

Draft
paulbalaji wants to merge 84 commits intomainfrom
pb/fee-parsing
Draft

feat: parse and display warp route fees#290
paulbalaji wants to merge 84 commits intomainfrom
pb/fee-parsing

Conversation

@paulbalaji
Copy link
Collaborator

@paulbalaji paulbalaji commented Mar 6, 2026

Summary

  • Parse warp route fees from origin transaction logs by comparing ERC20 Transfer events (total tokens sent to router) vs SentTransferRemote event (net bridged amount)
  • Display "Warp fee" and "Total sent" rows in the Warp Transfer Details card when fees are detected
  • Only applies to EVM ERC20 token routes; native token routes and non-EVM chains are gracefully skipped

Changes

File Description
src/features/messages/warpFees/types.ts WarpFeeBreakdown type
src/features/messages/warpFees/fetchWarpFees.ts Parse fees from tx receipt logs
src/features/messages/warpFees/useWarpFees.ts React hook for fee fetching
src/features/messages/warpFees/fetchWarpFees.test.ts Unit tests
src/features/messages/cards/WarpTransferDetailsCard.tsx Display fee rows

How it works

  1. Fetch origin tx receipt for EVM warp transfers
  2. Parse SentTransferRemote(uint32,bytes32,uint256) to get net bridged amount
  3. Parse ERC20 Transfer(from,to,value) events from sender → router for total tokens pulled
  4. Warp fee = total transferred - sent amount
  5. Only display when fee > 0

Test plan

  • pnpm run typecheck — clean
  • pnpm run lint — clean
  • pnpm run test — 3 new tests pass, no regressions
  • Manual: navigate to a warp transfer with fees, verify fee rows appear
  • Manual: non-warp message shows no fee rows
  • Manual: warp transfer with no fees shows no fee rows

🤖 Generated with Claude Code


Note

Medium Risk
Adds new RPC-backed fee parsing by decoding origin transaction receipts and ERC20/router logs; incorrect assumptions about sender/router/token addresses or decimals could cause missing/incorrect fee display or extra RPC load. Changes are otherwise additive and gated to EVM ERC20 warp routes with graceful null fallbacks.

Overview
Warp transfers now surface bridge fees. The message details view fetches the origin transaction receipt for EVM ERC20 warp routes and computes a fee breakdown by comparing ERC20 Transfer totals into the router vs the router’s SentTransferRemote amount (with decimal/scale normalization).

When a non-zero fee is detected, WarpTransferDetailsCard shows new Warp fee and Total sent rows, backed by a new useWarpFees react-query hook and unit tests for the log parsing/decimal normalization helpers.

Also includes small formatting-only tweaks (imports/line wrapping) and minor CSS hex-case cleanup.

Written by Cursor Bugbot for commit 26ee161. This will update automatically on new commits. Configure here.

@vercel
Copy link

vercel bot commented Mar 6, 2026

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

Project Deployment Actions Updated (UTC)
hyperlane-explorer Ready Ready Preview, Comment Mar 13, 2026 5:53pm

Request Review

paulbalaji and others added 21 commits March 13, 2026 14:03
- Primary purple scale (#9A0DFF base)
- Accent pink scale (#FF4FE9 base)
- Blue scale updated to complement purple
- Default black changed to umbra (#3d304c)
- Default white changed to glint white (#f8f8ff)
- New gradient backgrounds for cards and accents
- PP Valve as primary sans-serif font
- PP Fraktion Mono as monospace/secondary font
- Remove old Neue Haas Display and Space Grotesk fonts
- Add font-face declarations and widget overrides in global.css
- Purple scrollbar and timeline styling
- Gitignore public/fonts (fonts loaded at runtime)
- New purple Hyperlane logo shape
- Purple/pink gradient arrow
- Updated favicon in all sizes
- Add HyperlaneGradientLogo component
- Update header and footer with new logo
- Apply purple theme to buttons, cards, search bars
- Update card icons to use black (#3d304c)
- Softer code block backgrounds for glint white contrast
- Update theme color meta tag
- Dark purple background (#110821)
- Purple/pink gradient top border
- PP Valve for titles, PP Fraktion Mono for technical values
- Adjusted message ID background opacity
- Chain-colored boxes with dynamic color extraction
- Update brand-gradient to radial ellipse (200% 150%) for smoother transition
- Deepen widget timeline purple to #4c1d95 for better contrast
- Simplify widget font overrides (PP Valve default, mono for technical)
- Update favicon to transparent gradient logo
- Remove legacy NeueHaasDisplay and SpaceGrotesk fonts
- Add glint white (bg-white) to search inputs
- Update CodeBlock background to #e5e7eb
- Remove bold styling from footer links
- Update icons to purple theme

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Scale favicon logo to fill more space (1.6x)
- Desaturate OG arrow gradient for subtler appearance
- Fix OG footer layout with flex: 1 for true centering
- Add matching pill backgrounds to MESSAGE ID and SENT
- Color delivery time green if <5m, gray otherwise
- Fix SENT right alignment
- Update filter clear button to pink-300/80

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add scripts/fetch-fonts.mjs to download fonts from S3 at build time
- Add prebuild script that runs before 'pnpm build'
- Add fetch-fonts script for manual font download with local env
- Consolidate CSS to use variable fonts (reduces 8 font files to 2)
- Add .env.example documenting required AWS credentials
- Add @aws-sdk/client-s3 as devDependency

Font files downloaded:
- PPValve-PlainVariable.woff2 (CSS)
- PPFraktionMono-Variable.woff2 (CSS)
- PPValve-PlainMedium.ttf (OG image API)
- PPFraktionMono-Regular.ttf (OG image API)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@paulbalaji
Copy link
Collaborator Author

@claude review

@claude
Copy link

claude bot commented Mar 13, 2026

Claude finished @paulbalaji's task in 1m 43s —— View job


Review Complete

  • Run code review analysis
  • Post inline PR comments

Submitted a consolidated PR review with 2 inline suggestions:

  1. useWarpFees.ts:17 — Add staleTime: Infinity to avoid redundant RPC refetches (tx receipts are immutable)
  2. fetchWarpFees.ts:75 — Consider adding collateralAddressOrDenom to TokenArgsWithWireDecimals type for explicitness

Overall the feature looks solid — good fail-closed design, proper decimal normalization, and clean test coverage. Nothing blocking.

Copy link

@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, well-structured feature. The fee parsing logic is sound — normalizing SentTransferRemote amounts back to native decimals via getWarpRouteAmountParts/getEffectiveDecimals is the right approach, and the fail-closed design (return null on any anomaly) is appropriate. The cursor bugbot issues from the earlier revision have been addressed.

Two minor items below worth considering; nothing blocking.

Nits

  • The formatting-only changes (AppLayout, QuestionMarkIcon, MessageTable, WarpRouteVisualizationCard, Footer, Header, og.tsx, global.css) are fine but could've been a separate commit to keep the feature diff focused.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nels

The CSS rule `[data-headlessui-state].htw-bg-white` was applying the
dark mobile nav background to all Headless UI panels including the
chain search modal, time range popover, and status dropdown. Move the
dark bg styling to the mobile nav menuClassname prop instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add leading and titleSize props to SectionCard
- TransactionCard uses chain logo as leading element (size 24)
- Transaction card headers use medium (text-base) title size

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
paulbalaji and others added 9 commits March 13, 2026 17:52
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Parse ERC20 Transfer events and SentTransferRemote events from the
origin transaction receipt to compute warp fees. Displays "Warp fee"
and "Total sent" rows in the Warp Transfer Details card when fees > 0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix decimals mismatch: normalize SentTransferRemote wireDecimals to
  native token decimals before computing fee
- Switch useWarpFees to useQuery (fixes race condition, adds caching)
- Export and test actual parsing helpers instead of inline math
- Hoist interface objects to module scope
- Remove unused bridgeFeeRaw field and unnecessary types.ts
- Use formatAmountCompact for display
- Warn instead of silent fallback for missing decimals/negative fees
- Remove redundant isEvmChain check from hook

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Only count Transfer events emitted by the actual warp token contract,
not any ERC20 in the tx. For collateral routes, use collateralAddressOrDenom;
for synthetic routes, the router itself is the ERC20.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SentTransferRemote emits localAmount * scale on scale routes, not
wireDecimals-normalized amounts. Divide by scale before subtracting
from ERC20 Transfer total to get correct fee.

Also export and test normalizeDecimals.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use existing getEffectiveDecimals() instead of hardcoded wireDecimals
fallback. Handles Cosmos, scale, wireDecimals, and maxDecimals cases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Delegate scale handling to existing getWarpRouteAmountParts instead
of manual BigNumber.div. Combined with getEffectiveDecimals, all
amount decoding logic now reuses shared utils.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tx receipts are immutable once confirmed, so there's no need to
refetch on remount.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Base automatically changed from ui-rebrand-more to pb/fetch-fonts-script March 13, 2026 17:58
Base automatically changed from pb/fetch-fonts-script to ui-rebrand March 13, 2026 17:59
Base automatically changed from ui-rebrand to main March 16, 2026 17:58
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.

1 participant