Swap v1 GUI (#25) + CommitFlow surface refactor + QA fast-unlock - #66
Merged
Conversation
Restructure the GPUI app's commit-flow surfaces and lay the groundwork for the Swap GUI (#25). Tree green: just check (default + tray) + workspace tests. App-layer refactor (Send + Shield onto a shared CommitFlow): - commit_flow.rs: CommitFlow + entity-free CommitState core (epoch/hold/reset state machine) + Proposal (collapses the two identical proposal structs) + unit tests for the epoch/hold/reset invariants. - commit_view.rs: one generic render_commit + shared hold-to-confirm widget, driven by a &'static CommitView descriptor (fee/net rows, honesty lines, static + dynamic compose hints, glyph tone). - errors.rs: short_err/humanize_deny/is_session_ended extracted from shell.rs + tests. - send_view.rs / shield_view.rs reduced to SEND_VIEW / SHIELD_VIEW descriptors. - shell.rs: send_*/shield_* flat fields -> pub send/shield: CommitFlow; shared finish_review tail; ~470 lines of duplicated handlers/views collapsed. Swap core plumbing (Unit A, deckard-core): - chain-keyed fetch_portfolio(chain_id) via tokens_for; TokenBalance.address. - EthProvider::spawn(rpc, chain_id) + EthProvider::allowance(owner, spender, token). - CowOrderbook wrapper (owns the reqwest::Client; app never names reqwest). DevEx + docs: - just qa-vault / just qa: pre-sealed fast-KDF throwaway vault (anvil acct 0, prefunded) so clicky QA skips onboarding. examples/qa-vault.rs. - docs/dev/railgun-local-testing.md + README: keyless sepolia.drpc.org fork path.
…Flow refactor
Built on the new commit_flow/commit_view foundation via a plan→build→review→fix
workflow. Tree green: just check (default + tray) + cargo test --workspace (247 passed).
- swap.rs: pure helpers (quote_request, order_from_quote, gross/min_receive,
needs_approval, token lookups) + confirm_swap_blocking orchestrator: re-quote →
propose_order (NeedsApproval) → allowance check → exact-gross shaped approve
(proposed AFTER the order; spender=GPV2VaultRelayer, value=0) → re-read allowance
guard → resolve+sign over the control channel → put_app_data → submit → uid.
- swap_view.rs: Swap surface (sell-amount + sell/buy token pickers + Get quote,
quote summary, order review clear-signing card) reusing commit_view's hold-to-confirm.
- shell.rs: Surface::Swap + swap_* state + handlers; un-gated Swap button (welcome.rs);
palette 'swap' command; v_flex render arm so the card centers (gpui-div-defaults-block).
- 12 new swap unit tests; humanize_swap_deny swap-worded copy.
Runtime fix: CoW HTTP (reqwest/hickory) needs a tokio reactor, which GPUI's executor
lacks. Routed through deckard-core CowOrderbook::{quote,put_app_data,submit}_blocking
(core owns tokio; the app never touches it — matches eth.rs/shielded.rs). Caught via
live GUI drive (compose renders + token pickers verified on a Sepolia fork).
KNOWN-PENDING: the post-fix LIVE quote→order-submit round-trip was not re-confirmed —
the Mac auto-locked mid-verification. Compose/pickers verified; the fix compiles + the
allowance/sequence logic is unit-tested, but a final live quote→accepted+open confirm
is owed once the screen is unlocked.
Live GUI drive caught a gap: the bespoke swap compose showed Get quote + the quote summary but never rendered the descriptor's review button, so a user could quote but not proceed to the order-review/hold-to-confirm. Add the primary "Review order" CTA (gated on swap_quote.is_some(), disabled while busy) below the quote summary, wired to the already-implemented Shell::review_swap. just check green (default + tray). Live-validated on a Sepolia fork: compose + token pickers render centered; a real CoW Sepolia quote returns (1 USDC -> 0.608 COW, no tokio panic — the prior runtime fix holds); Review order -> the clear-signing card (receiver=wallet, 0.5% slippage, valid-until, honesty box) renders correctly.
hellno
added a commit
that referenced
this pull request
Jun 16, 2026
…lock #66) Resolved conflicts so the Swap surface (main) and the Activity feed (this branch) coexist: - Surface enum + view_label + render dispatch: keep BOTH Surface::Activity and Surface::Swap. - lock() reset: take main's shield/send/swap sub-struct resets (the CommitFlow refactor moved shield/send state into self.shield/self.send and added self.swap) AND keep this branch's activity STOP-banner/arming/review clears. - Send/Shield render dispatch: take main's generic render_commit(&{SEND,SHIELD}_VIEW); insert the Activity arm; keep main's Swap arm. - Shield/Send review propose: take main's recipient-snapshot return tuple AND keep this branch's `origin: ProposalOrigin::App` arg (the wire's Propose requires origin). - palette_commands: keep main's `swap` command + this branch's activity/approvals/STOP commands; dropped main's brittle `assert_eq!(COMMANDS.len(), 11)` (the merged registry is larger) in favor of membership asserts. - swap.rs: main's swap shaped-approve propose_blocking now passes ProposalOrigin::App (App-origin — the user's foreground swap from the GUI), required by this branch's wire change. Verified green on the merged tree: cargo fmt --check, clippy -D warnings (default + tray), full workspace tests (0 failures), shellcheck.
This was referenced Jun 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ships the Swap v1 GUI (#25) — CoW Protocol market swaps (compose → live quote → clear-signing order review → hold-to-confirm → sign over the private control channel → submit) — on top of an app-layer refactor that collapses the duplicated Send/Shield surfaces onto a shared
CommitFlow+commit_view(one tested hold-to-confirm / epoch / clear-signing path; adding a surface is now a descriptor, not a copy). Also lands the swap core plumbing (chain-keyedfetch_portfolio,EthProvider::allowance, a key-lessCowOrderbookwhose blocking wrappers own tokio so the GPUI app never touches it), a DevEx fast-unlock (just qa-vault/just qa— a throwaway pre-sealed vault that skips onboarding), and docs (keylesssepolia.drpc.orglocal-testing recipe). Net diff: 22 files, +3951/−1305; no dependency changes.Linked issue
Closes #25
Definition of Done
cargo fmt --all --checkis cleanjust checkis green — clippy-D warningson both the default config and--features traycargo test --workspaceis greenCargo.toml/Cargo.lock(the diff touches neither file)DESIGN.md(swap reuses the shared clear-signing card + amber hold-to-confirm; cool token swatches off the cyan/agent + amber/human axes; centered like Send/Shield)Zeroizingand are never logged/Debug-printed (this PR adds no seed/key handling; the swap path signs via the existing key-less signer wire)Evidence (paste command output here)
Notes for reviewers
commit_flow.rs(epoch/hold/reset state machine + unit tests) andcommit_view.rs(genericrender_commit+ shared hold-to-confirm + per-surface descriptor) are the foundation; Send and Shield were migrated onto it byte-for-byte (verified live) before Swap was built native. Reviewing in that order helps.deckard-core::CowOrderbook::{quote,put_app_data,submit}_blocking(current-thread runtime +block_on, mirroringeth.rs/shielded.rs) — the app deliberately never touches tokio. The swap orchestrator (confirm_swap_blocking) is therefore fully synchronous and runs insidecx.background_spawn.value==0, spender = GPv2 vault relayer, proposed after the order; resolve+sign ride the private AF_UNIX control channel; receiver is always the wallet.TMPDIR=/tmpon macs with a long$TMPDIR(the AF_UNIXSUN_LEN104-char limit); the one ignored test is the live-network CoW quote.codexadversarial pass couldn't run (session limit, then a transient server rate-limit); the in-workflow codex review returned compile-plausible with 0 must-fixes.