Skip to content

perf(ui): Use indicative quotes for dead addresses - #1130

Merged
pedromcunha merged 3 commits into
mainfrom
perf/indicative-dead-address-quotes
Sep 17, 2026
Merged

pedromcunha merged 3 commits into
mainfrom
perf/indicative-dead-address-quotes

Conversation

@scout-relayprotocol

Copy link
Copy Markdown
Contributor

The Swap Widget now requests an indicative quote when its sender or recipient uses a dead-address placeholder. This reduces executable quote work while a wallet or recipient is missing.

Deposit-address flows remain executable because the quote API does not allow indicativeQuote with useDepositAddress.

When valid addresses become available, the query parameters change and the widget requests a normal executable quote.

This also adds a patch changeset for @relayprotocol/relay-kit-ui.

Action taken on behalf of Pedro Cunha.

Checks:

  • UI typecheck
  • UI and hooks lint
  • Changeset lint
  • git diff --check

Requested by Pedro Cunha.

--

View Junior Session [Sentry]

Co-authored-by: Pedro Cunha <6723339+pedromcunha@users.noreply.github.com>
@vercel

vercel Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated
relay-sdk-demo Ready Ready Preview Sep 17, 2026 9:04am UTC

Request Review

@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Claude finished @pedromcunha's task in 51s —— View job


Code Review

  • Gather context (diff, commits)
  • Read surrounding code
  • Review changes

Code Review — No issues found.

Reviewed 2 files, 14 lines changed. Checked for correctness, security, error handling, and repo guidelines compliance.

What I verified:

  • The !useDepositAddress guard correctly avoids the invalid indicativeQuote + useDepositAddress combination the API rejects (matches the PR description).
  • isDeadAddress(fromAddressWithFallback) || isDeadAddress(toAddressWithFallback) is sound: addressWithFallback (packages/ui/src/utils/address.ts:103) returns the real address only when valid and otherwise a per-VM dead placeholder, so a dead result reliably signals a missing/placeholder sender or recipient.
  • No risk of executing an indicative quote: execute.ts:66-72 throws when the quote sender/recipient is a dead address, and an indicative quote is only requested precisely in that placeholder state. Once valid addresses arrive, indicativeQuote resolves to undefined and the params change triggers a normal executable refetch.
  • indicativeQuote is a valid optional field on the quote body (packages/sdk/src/types/api.ts:1072), and isDeadAddress is exported from @relayprotocol/relay-sdk.
  • Changeset is customer-facing, names the package, and follows the changelog conventions.

@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Claude finished @pedromcunha's task in 26s —— View job


📝 Docs Drift Check — No docs impact

This PR does not touch surfaces that require a relayprotocol/relay-docs update.

@greptile-apps

greptile-apps Bot commented Sep 16, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

Safe to merge.

Summary

This update prevents placeholder dead addresses from being treated as valid sender or recipient addresses when preparing swap quotes, while retaining the deposit-address flow.

Reviews (3) · Last reviewed commit: "fix(ui): Treat burn addresses as placeho..."

ted-palmer
ted-palmer previously approved these changes Sep 17, 2026
pedromcunha
pedromcunha previously approved these changes Sep 17, 2026
Indicative quotes return an empty steps array with the id only at the top
level, so extractQuoteId fell back to undefined for QUOTE_RECEIVED and the
swap analytics events. Thread the response requestId through as a fallback.

Gate indicative quotes on the widget's own address validity rather than on
isDeadAddress of the resolved address, so a user who deliberately enters a
burn address still gets an executable quote, and guard swap() against a
quote with no steps (executeSteps treats an empty step list as complete).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pedromcunha
pedromcunha dismissed stale reviews from ted-palmer and themself via 2059991 September 17, 2026 08:29
@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Claude finished @pedromcunha's task in 1m 7s —— View job


Code Review

  • Gather context (diff, commits)
  • Read surrounding code
  • Review changes

Code Review — No issues found.

Reviewed 4 files, 41 lines changed. Checked for correctness, security, error handling, and repo guidelines compliance.

What I verified:

  • Indicative-quote gating (SwapWidgetRenderer.tsx:504-507): indicativeQuote is set only when !useDepositAddress && (!isValidFromAddress || !isValidToAddress), correctly avoiding the API-rejected indicativeQuote + useDepositAddress combination. The isValid* flags (371-408) already incorporate the token-contract/destination-token checks, so !isValidToAddress reliably signals a placeholder recipient — consistent with toAddressWithFallback collapsing to the dead placeholder in those same cases. When valid addresses arrive, indicativeQuote becomes undefined, changing the query params and triggering a normal executable refetch.
  • quote_id fallback (quote.ts:207-213): indicative quotes return no steps, so extractQuoteId now falls back to the top-level requestId. The ?? requestId ordering preserves prior behavior for executable quotes (steps present) while filling the gap for indicative ones. All getSwapEventData call sites (index.tsx:1517, SwapWidgetRenderer.tsx:810/873/929) were updated to thread quote?.requestId through; the unchanged deposit-address call sites (DepositAddressModal*, TransactionModal) always have steps, so they need no fallback.
  • Execution guard (SwapWidgetRenderer.tsx:884): adding || !quote?.steps?.length correctly prevents attempting to execute an indicative (stepless) quote — it throws "Missing a quote" rather than proceeding. This is defensive and does not affect deposit-address or normal flows, which always return steps.
  • Changeset (.changeset/calm-quotes-preview.md): customer-facing, names @relayprotocol/relay-kit-ui, leads with the outcome, and follows the changelog conventions.

No correctness, security, or error-handling concerns found. The two commits together keep both the executability guard and the analytics quote_id correct for the new indicative-quote state.

@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Claude finished @pedromcunha's task in 39s —— View job


📝 Docs Drift Check — No docs impact

This PR does not touch surfaces that require a relayprotocol/relay-docs update.

Comment thread packages/ui/src/components/widgets/SwapWidgetRenderer.tsx
A burn address passes syntax validation, so a user-entered one counted as a
valid recipient: the widget requested an executable quote and ran swap(),
while the API silently rewrites a burn recipient, so the SDK's dead-address
guard never fired.

Exclude burn addresses from the from/to validity checks and from
addressWithFallback, so they consistently collapse to the per-chain
placeholder and yield a preview-only quote that cannot be executed. Stop the
address modal from resolving one, and drop stale burn addresses from recents.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Claude finished @pedromcunha's task in 39s —— View job


📝 Docs Drift Check — No docs impact

This PR does not touch surfaces that require a relayprotocol/relay-docs update.

@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Claude finished @pedromcunha's task in 1m 3s —— View job


Code Review

  • Gather context (diff, commits)
  • Read surrounding code
  • Review changes

Code Review — No issues found.

Reviewed 6 files, ~75 lines changed. Checked for correctness, security, error handling, and repo guidelines compliance.

Note on the earlier Greptile P1 ("Dead addresses remain executable"): that finding was raised against commit 20599919, before the fix. The latest commit c9ffb7db resolves it — isValidFromAddress (SwapWidgetRenderer.tsx:372-380) and isValidToAddress (:408-412) now both include !isDeadAddress(...), so a syntactically-valid burn placeholder (e.g. 0x…dEaD) makes the isValid* flag false, which in turn sets indicativeQuote: true at :508-511. Dead-address placeholders therefore produce preview-only quotes as intended. I verified isDeadAddress (packages/sdk/src/constants/address.ts:37-56) matches the EVM burn address plus the per-VM placeholders returned by getDeadAddress, so the check and the fallback are symmetric.

What I verified:

  • Indicative-quote gating (SwapWidgetRenderer.tsx:508-511): set only when !useDepositAddress && (!isValidFromAddress || !isValidToAddress), correctly avoiding the API-rejected indicativeQuote + useDepositAddress combination. Once real, non-dead addresses arrive, the flag collapses to undefined, changing query params and triggering a normal executable refetch.
  • Execution guard (SwapWidgetRenderer.tsx:884): !executeSwap || !quote?.steps?.length prevents attempting to execute a stepless indicative quote — it throws "Missing a quote" rather than proceeding. Defensive-in-depth alongside the SDK's execute.ts:66-72 burn-address throw.
  • quote_id fallback (quote.ts:207-213): indicative quotes have no steps, so extractQuoteId now falls back to top-level requestId. ?? requestId ordering preserves prior behavior for executable quotes. All getSwapEventData/extractQuoteId call sites in the swap flow (index.tsx:1517, SwapWidgetRenderer.tsx:568/819/883/939) thread quote?.requestId through. The deposit-address call sites (DepositAddressModal*, TransactionModal) are intentionally unchanged since those flows always return steps.
  • addressWithFallback (address.ts:110-118): now collapses a dead-address input to the per-VM placeholder, keeping fromAddressWithFallback/toAddressWithFallback consistent with the isValid* predicates.
  • CustomAddressModal (:110-118, :176-188): filters dead addresses out of recent-address suggestions and refuses to select a dead-address input — consistent behavior, no regressions to the ENS/Lighter branches.
  • Changeset (.changeset/calm-quotes-preview.md): customer-facing, names @relayprotocol/relay-kit-ui, leads with the outcome, follows changelog conventions.

No correctness, security, or error-handling concerns found.

@pedromcunha
pedromcunha merged commit a1f3a58 into main Sep 17, 2026
9 checks passed
@pedromcunha
pedromcunha deleted the perf/indicative-dead-address-quotes branch September 17, 2026 09:39
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