Skip to content

Split price quote from swap quote#505

Draft
its-everdred wants to merge 2 commits into
mainfrom
kevin/split-price-quote
Draft

Split price quote from swap quote#505
its-everdred wants to merge 2 commits into
mainfrom
kevin/split-price-quote

Conversation

@its-everdred

@its-everdred its-everdred commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Closes #435

Problem

actions.swap.getQuote(...) (no wallet bound) returned a SwapQuote whose
recipient: Address was the Universal Router msg.sender sentinel
(0x0000000000000000000000000000000000000001) when no recipient was supplied.
The type said "ready to execute" while the value was un-executable — a footgun
for any consumer reading quote.recipient for display, accounting, or analytics.

Change (Option B — split the type)

  • New PriceQuote type: pricing, amounts, route, metadata only — no
    recipient, no execution. Structurally un-executable. SwapQuote now
    extends PriceQuote and adds execution, recipient, approvalMode.
  • actions.swap.getQuote / getQuotes return PriceQuote (stripped at the
    namespace boundary via toPriceQuote). wallet.swap.getQuote / getQuotes
    still return the full SwapQuote; wallet.swap.execute is unchanged.
  • BaseSwapNamespace.getQuote/getQuotes became protected resolveQuote/ resolveQuotes (a covariant override can't widen the return type), with each
    namespace exposing its own public method.
  • Consumers updated: CLI printer + actions swap quote(s) commands, demo
    backend getQuote, demo frontend EarnOperations/useSwap/SwapAction/
    actionsApi.

Providers are untouched — the sentinel remains a valid internal Universal
Router encoding detail; it's simply never exposed.

Why B

The demo's executeSwap re-quotes from raw params via wallet.swap.execute
and never executes an actions-level quote, so the execution/recipient on a
price quote were already dead weight (and un-executable post-#434). Dropping
them breaks no execution path and gives price-display consumers a smaller,
honest surface.

Note: passing a PriceQuote to execute is structurally accepted as raw
params, but it carries quotedAt, so it routes to the pre-built-quote path and
throws on the missing recipient — a fail-loud runtime guard (regression-tested).

Testing

  • SDK 666, CLI 261, demo backend 130, demo frontend 73, contracts 18 — all green.
  • New tests: actions.swap.getQuote strips recipient/execution/approvalMode
    (the leak fix); execute rejects a price-only quote.
  • Manual CLI verification: actions swap quote (live, base-sepolia) JSON output
    contains no recipient/execution/sentinel; human output renders cleanly.
  • pnpm typecheck && pnpm lint && pnpm test clean (zero new warnings).
  • Changeset added (minor; breaking type change documented).

Plan: docs/plans/2026-06-16-001-refactor-split-price-quote-from-swap-quote-plan.md.

@netlify

netlify Bot commented Jun 18, 2026

Copy link
Copy Markdown

Deploy Preview for actions-ui ready!

Name Link
🔨 Latest commit f80314f
🔍 Latest deploy log https://app.netlify.com/projects/actions-ui/deploys/6a3336e8c2558c0008d0c59e
😎 Deploy Preview https://deploy-preview-505--actions-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

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.

Sentinel recipient leaks via actions.swap.getQuote — split price quote from executable quote

1 participant