Skip to content

feat(transactions): rich decoded view for Smart Invoice escrow deploys - #995

Open
sktbrd wants to merge 3 commits into
BuilderOSS:stagingfrom
sktbrd:feat/escrow-tx-rich-view
Open

feat(transactions): rich decoded view for Smart Invoice escrow deploys#995
sktbrd wants to merge 3 commits into
BuilderOSS:stagingfrom
sktbrd:feat/escrow-tx-rich-view

Conversation

@sktbrd

@sktbrd sktbrd commented Aug 6, 2026

Copy link
Copy Markdown

Description

deployEscrow calls to the Smart Invoice escrow bundlers aren't recognized by the generic ABI-fetch decoder, so proposal escrow transactions (milestone payouts, e.g. this proposal's) render as a wall of "Raw only" calldata — proposers can't see what they're funding.

This detects known escrow bundler addresses and decodes the calldata locally into a rich card: the milestone breakdown with proportion bars + total, the client / provider / resolver as ENS-resolved identities, the escrow token symbol, and the safety-valve date. The raw-calldata toggle is preserved, and the shared decode hook is untouched.

Result

The same escrow that previously showed as raw hex, now:

escrow rich view

Implementation

  • EscrowTransactionCard/escrowDeploy.ts — pure, deterministic decode: matchEscrowBundler (known v2/legacy bundler addresses) + parseEscrowDeploy (decodeFunctionData with the existing deployEscrowAbi → milestones, total, parties via decodeEscrowData). No network, no side effects.
  • EscrowTransactionCard/escrowDeploy.test.ts8 unit tests (round-trip encode→decode: bundler matching, milestone totals, party/token extraction, negative cases).
  • EscrowTransactionCard/EscrowTransactionCard.tsx (+ .css.ts) — the rich view; token symbol via useTokenMetadataSingle, identities via useEnsData.
  • TransactionDisplay.tsx — 3-line change: when the target is a known escrow bundler, render the card (raw toggle preserved). No change to useDecodedTransactions.

Verification

  • type-check clean, lint clean (0 errors), 8/8 tests pass for packages/ui.
  • Verified on a live preview deploy against this proposal's escrow (screenshot above).

Bonus contribution from the Gnars team, alongside Prop 61 Milestone 3.

Summary by CodeRabbit

  • New Features
    • Added visual decoding for Smart Invoice escrow transactions.
    • Displays escrow type, token amounts, milestones, progress, involved parties, provider payout, resolver, and safety-valve date.
    • Supports current and legacy escrow formats across supported networks.
    • Uses token metadata and readable address names where available.
    • Preserves raw transaction viewing when selected.
  • Bug Fixes
    • Invalid, incomplete, or unrecognized escrow data now fails gracefully without showing misleading details.

deployEscrow calls to the known escrow bundlers aren't recognized by the generic
ABI-fetch decoder, so proposal escrow txs (e.g. milestone payouts) render as a
wall of 'Raw only' calldata. Detect known bundler addresses and decode the
calldata locally (parseEscrowDeploy, pure + unit-tested) into a rich card:
milestone breakdown with proportion bars + total, client/provider/resolver
identities (ENS-resolved), token symbol, and safety-valve date. The raw-calldata
toggle is preserved. No change to the shared decode hook.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

@sktbrd is attempting to deploy a commit to the Nouns Builder Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 41e7e29d-dae2-4701-9ce4-081e4a600b51

📥 Commits

Reviewing files that changed from the base of the PR and between 1b3368c and 0ad5aaa.

📒 Files selected for processing (3)
  • packages/ui/src/DecodedTransactions/EscrowTransactionCard/EscrowTransactionCard.tsx
  • packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.test.ts
  • packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.test.ts
  • packages/ui/src/DecodedTransactions/EscrowTransactionCard/EscrowTransactionCard.tsx
  • packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.ts

📝 Walkthrough

Walkthrough

Adds Smart Invoice escrow deployment decoding for v2 and legacy bundlers. The transaction display detects escrow calldata and renders a card with token amounts, milestones, parties, payouts, resolver data, and safety-valve dates.

Changes

Escrow transaction flow

Layer / File(s) Summary
Escrow bundler matching and calldata parsing
packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.ts, packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.test.ts
Recognizes v2 and legacy bundlers, decodes deployment calldata, computes milestone totals, and returns structured escrow data. Tests cover valid, unknown, case-insensitive, and invalid inputs.
Escrow detail card rendering
packages/ui/src/DecodedTransactions/EscrowTransactionCard/EscrowTransactionCard.tsx, packages/ui/src/DecodedTransactions/EscrowTransactionCard/EscrowTransactionCard.css.ts
Loads token metadata, formats amounts, and renders escrow type, milestones, parties, payouts, resolver data, and safety-valve dates.
Transaction display integration
packages/ui/src/DecodedTransactions/EscrowTransactionCard/index.ts, packages/ui/src/DecodedTransactions/TransactionDisplay/TransactionDisplay.tsx
Exports the escrow module and routes recognized escrow calldata to EscrowTransactionCard, including when generic decoding fails.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TransactionDisplay
  participant EscrowTransactionCard
  participant parseEscrowDeploy
  participant TokenMetadata
  TransactionDisplay->>EscrowTransactionCard: pass chainId, target, and calldata
  EscrowTransactionCard->>parseEscrowDeploy: decode escrow deployment
  parseEscrowDeploy-->>EscrowTransactionCard: return ParsedEscrowDeploy
  EscrowTransactionCard->>TokenMetadata: load token metadata
  TokenMetadata-->>EscrowTransactionCard: return token decimals and symbol
  EscrowTransactionCard-->>TransactionDisplay: render escrow details
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: a rich decoded view for Smart Invoice escrow deployments.
Description check ✅ Passed The description explains the motivation, implementation, testing, verification, and user-visible result in sufficient detail.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/ui/src/DecodedTransactions/EscrowTransactionCard/EscrowTransactionCard.tsx

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.test.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/DecodedTransactions/TransactionDisplay/TransactionDisplay.tsx (1)

120-141: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fall back to raw calldata when escrow parsing fails.

matchEscrowBundler validates only decoded.target. A known bundler can receive a different function call or malformed hexadecimal calldata. In that case, showEscrowCard is true, but EscrowTransactionCard returns null, so the default display is empty.

Require parseEscrowDeploy to succeed before selecting the escrow-card branch. Otherwise, render RawCalldataDisplay.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/ui/src/DecodedTransactions/TransactionDisplay/TransactionDisplay.tsx`
around lines 120 - 141, Update the escrow-card selection in TransactionDisplay
to require successful parseEscrowDeploy validation in addition to
matchEscrowBundler and the existing raw-calldata checks. When parsing fails,
bypass EscrowTransactionCard and preserve the fallback branch that renders
RawCalldataDisplay.
🧹 Nitpick comments (1)
packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.test.ts (1)

69-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a legacy deployment parsing test.

The legacy test only verifies bundler matching. It does not encode deployEscrowAbiLegacy calldata or assert decoded milestones, funding, and escrow fields. Add this test to protect the legacy positional-argument branch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.test.ts`
around lines 69 - 101, Add a legacy deployment case to the parseEscrowDeploy
test suite by encoding calldata with deployEscrowAbiLegacy and invoking
parseEscrowDeploy with it. Assert the legacy positional-argument branch decodes
milestone amounts and total, fund amount, provider, escrow parties, token,
termination time, and recipient addresses using the expected fixtures, while
retaining the existing bundler-mismatch coverage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.ts`:
- Line 84: Update the escrow deploy decoding flow around decodeEscrowData so
invalid nested escrow data returns null instead of constructing a partial
ParsedEscrowDeploy from an empty object. Validate the required decoded fields or
propagate a decoder error signal before the return, and add a regression test
covering ABI-valid outer calldata with malformed escrowData.

In
`@packages/ui/src/DecodedTransactions/EscrowTransactionCard/EscrowTransactionCard.tsx`:
- Around line 48-59: Update EscrowTransactionCard’s amount-rendering flow so
decimal token amounts are not formatted or displayed until
tokenMetadata?.decimals is available; remove the 18-decimal fallback. Preserve
the existing raw-calldata amount fallback for missing metadata, while continuing
to use tokenMetadata decimals and symbol when metadata exists.

---

Outside diff comments:
In
`@packages/ui/src/DecodedTransactions/TransactionDisplay/TransactionDisplay.tsx`:
- Around line 120-141: Update the escrow-card selection in TransactionDisplay to
require successful parseEscrowDeploy validation in addition to
matchEscrowBundler and the existing raw-calldata checks. When parsing fails,
bypass EscrowTransactionCard and preserve the fallback branch that renders
RawCalldataDisplay.

---

Nitpick comments:
In
`@packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.test.ts`:
- Around line 69-101: Add a legacy deployment case to the parseEscrowDeploy test
suite by encoding calldata with deployEscrowAbiLegacy and invoking
parseEscrowDeploy with it. Assert the legacy positional-argument branch decodes
milestone amounts and total, fund amount, provider, escrow parties, token,
termination time, and recipient addresses using the expected fixtures, while
retaining the existing bundler-mismatch coverage.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c2bb7b71-3ff3-4e0c-b4ab-9793ee304620

📥 Commits

Reviewing files that changed from the base of the PR and between ff9820c and 73d8609.

📒 Files selected for processing (6)
  • packages/ui/src/DecodedTransactions/EscrowTransactionCard/EscrowTransactionCard.css.ts
  • packages/ui/src/DecodedTransactions/EscrowTransactionCard/EscrowTransactionCard.tsx
  • packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.test.ts
  • packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.ts
  • packages/ui/src/DecodedTransactions/EscrowTransactionCard/index.ts
  • packages/ui/src/DecodedTransactions/TransactionDisplay/TransactionDisplay.tsx

Comment thread packages/ui/src/DecodedTransactions/EscrowTransactionCard/escrowDeploy.ts Outdated
Comment on lines +48 to +59
const tokenAddress = parsed?.escrow.tokenAddress as Address | undefined
const { tokenMetadata } = useTokenMetadataSingle(chainId, tokenAddress)

if (!parsed) return null

const decimals = tokenMetadata?.decimals ?? 18
const symbol =
tokenMetadata?.symbol ?? (tokenAddress ? walletSnippet(tokenAddress) : '')
const fmt = (v: bigint) =>
`${Number(formatUnits(v, decimals)).toLocaleString(undefined, {
maximumFractionDigits: 4,
})} ${symbol}`.trim()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not assume 18 token decimals.

When tokenMetadata is unavailable, decimals defaults to 18. A 6-decimal escrow token then renders its amount 10^12 times too small until metadata arrives, or permanently if metadata loading fails.

Do not render decimal token amounts until the token decimals are known. Preserve the raw-calldata fallback when metadata is unavailable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/ui/src/DecodedTransactions/EscrowTransactionCard/EscrowTransactionCard.tsx`
around lines 48 - 59, Update EscrowTransactionCard’s amount-rendering flow so
decimal token amounts are not formatted or displayed until
tokenMetadata?.decimals is available; remove the 18-decimal fallback. Preserve
the existing raw-calldata amount fallback for missing metadata, while continuing
to use tokenMetadata decimals and symbol when metadata exists.

sktbrd added 2 commits August 7, 2026 18:56
Per feedback, three separate per-milestone bars read like progress indicators.
Replace them with a single allocation bar split into one segment per milestone
(distinct green shades), plus a labeled amount list with matching colour dots.
…sume 18 decimals

- parseEscrowDeploy returns null when the nested escrow data decodes empty
  (no client/resolver), so the raw calldata view shows instead of a blank card.
- EscrowTransactionCard no longer defaults to 18 decimals for an arbitrary
  token: use live metadata, fall back to 18 only for the chain's wrapped native
  (reliably 18), and show a placeholder otherwise — a 6-decimal USDC escrow no
  longer renders 1e12 too small before metadata resolves.
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