Skip to content

Swap v1 GUI (#25) + CommitFlow surface refactor + QA fast-unlock - #66

Merged
hellno merged 3 commits into
mainfrom
hellno/test-merged-prs-gui
Jun 16, 2026
Merged

Swap v1 GUI (#25) + CommitFlow surface refactor + QA fast-unlock#66
hellno merged 3 commits into
mainfrom
hellno/test-merged-prs-gui

Conversation

@hellno

@hellno hellno commented Jun 16, 2026

Copy link
Copy Markdown
Owner

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-keyed fetch_portfolio, EthProvider::allowance, a key-less CowOrderbook whose 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 (keyless sepolia.drpc.org local-testing recipe). Net diff: 22 files, +3951/−1305; no dependency changes.

Linked issue

Closes #25

Definition of Done

  • cargo fmt --all --check is clean
  • just check is green — clippy -D warnings on both the default config and --features tray
  • cargo test --workspace is green
  • No new or changed dependencies in Cargo.toml / Cargo.lock (the diff touches neither file)
  • Visual/UI follows 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)
  • Secrets stay in Zeroizing and 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)
$ cargo fmt --all --check
(clean)

$ just check
cargo clippy --locked --workspace --all-targets -- -D warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.09s
cargo clippy --locked -p deckard-app --all-targets --features tray -- -D warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.74s

$ cargo test --workspace        # (TMPDIR=/tmp to avoid the macOS SUN_LEN socket-path limit)
test result: ok. 42 passed; 0 failed; ...   (deckard-app, incl. 12 swap + commit_flow/errors tests)
test result: ok. 55 passed; 0 failed; 1 ignored; ...   (deckard-core; 1 ignored = live-network CoW quote)
test result: ok. ... (deckard-signerd: resolver_auth, swap_lifecycle, shaped_approve, daemon_e2e, guardrail, ...)
# 18 test binaries, 0 failed across the workspace

Notes for reviewers

  • App-layer refactor first, Swap second. commit_flow.rs (epoch/hold/reset state machine + unit tests) and commit_view.rs (generic render_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.
  • Runtime fix worth a look: CoW HTTP (reqwest/hickory) needs a tokio reactor that GPUI's executor lacks, so it's routed through deckard-core::CowOrderbook::{quote,put_app_data,submit}_blocking (current-thread runtime + block_on, mirroring eth.rs/shielded.rs) — the app deliberately never touches tokio. The swap orchestrator (confirm_swap_blocking) is therefore fully synchronous and runs inside cx.background_spawn.
  • Trust invariants (carried from Swap v1 trust path: CoW module, contract order types, signerd EIP-712 signing + shaped approve + cancellation #24): the shaped approve is the bound order's gross sell amount, value==0, spender = GPv2 vault relayer, proposed after the order; resolve+sign ride the private AF_UNIX control channel; receiver is always the wallet.
  • Live-validated on a Sepolia fork through the order-review card (real CoW quote: 1 USDC → 0.608 COW). Known limitation: a true order-accepted+open on the live CoW orderbook is not reachable from a local fork — the orderbook validates balance/allowance against real Sepolia, not the fork — so that final step needs the Swap v1 agent + demo: MCP swap tools, fork-demo stub, swap-e2e harness #26 fork-demo orderbook stub (or a wallet with real Sepolia funds). Deliberately out of scope here.
  • Deferred (TODO in code): open-order status polling + in-app cancel; sell-token balance in the compose screen.
  • Test caveat: signerd socket tests need TMPDIR=/tmp on macs with a long $TMPDIR (the AF_UNIX SUN_LEN 104-char limit); the one ignored test is the live-network CoW quote.
  • A final codex adversarial pass couldn't run (session limit, then a transient server rate-limit); the in-workflow codex review returned compile-plausible with 0 must-fixes.

hellno added 3 commits June 15, 2026 18:35
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
hellno merged commit 06d0a35 into main Jun 16, 2026
5 checks passed
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.
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.

Swap v1 GUI: Swap surface, allowance + order cards, open-order lifecycle, chain-keyed holdings

1 participant