feat(apps/amm): drive the exact-input swap preview from swapExactInQuote

Wire the Sell direction of the swap card to the module's server-side quote
instead of the client-side DummySwapState estimate. AmmUiBackend gains a
swapExactInQuote(tokenIn, tokenOut, amountInDecimal, slippageBps) slot that
forwards to amm_module and returns { expectedOutRaw, minReceivedRaw,
priceImpactBps } (read-only, no wallet guard).

SwapCard debounces a swapExactInQuote call as the user types a Sell amount and
sources the expected output, min received, and price impact from it. The exact
figures shown (buy field, confirmation snapshot) and the submitted min_out come
straight from the quote's raw integer strings, so the preview can't drift from
execution and the client no longer orients reserves (fixes the #236 defAHex
bug) or recomputes min_out in double/BigInt. A retyped amount invalidates the
quote immediately and blocks submit until the re-quote lands.

The Buy direction is unchanged — still a local DummySwapState preview, pending
the exact-output wiring. resolvePool stays as the source of pool existence, the
fee row, and the Buy-side reserves.
This commit is contained in:
r4bbit
2026-08-06 23:15:01 +02:00
parent 56b2d3a282
commit 37c2f294ac
7 changed files with 164 additions and 80 deletions
+5 -3
View File
@@ -47,8 +47,9 @@ public:
/// decimal string (JSON floats rejected); `slippage_bps` is basis points.
/// On failure: `{ status:"error", error:<code> }` — `no_pool` (no pool /
/// liquidity), `config_missing` (AMM_PROGRAM_BIN unset/unreadable),
/// `bad_amount`, or `backend_error`. Pool metadata (reserves, fee) comes from
/// `resolvePool`, so it isn't echoed here.
/// `bad_amount`, `invalid_slippage` (`slippage_bps` out of range), or
/// `backend_error`. Pool metadata (reserves, fee) comes from `resolvePool`,
/// so it isn't echoed here.
LogosMap swapExactInQuote(const std::string& token_in_hex,
const std::string& token_out_hex,
const nlohmann::json& amount_in,
@@ -61,7 +62,8 @@ public:
/// string (JSON floats rejected); `slippage_bps` is basis points. On failure:
/// `{ status:"error", error:<code> }` — `no_pool` (no pool / liquidity),
/// `output_exceeds_liquidity` (amount_out ≥ reserve), `config_missing`
/// (AMM_PROGRAM_BIN unset/unreadable), `bad_amount`, or `backend_error`.
/// (AMM_PROGRAM_BIN unset/unreadable), `bad_amount`, `invalid_slippage`
/// (`slippage_bps` out of range), or `backend_error`.
LogosMap swapExactOutQuote(const std::string& token_in_hex,
const std::string& token_out_hex,
const nlohmann::json& amount_out,