Commit Graph
18 Commits
Author SHA1 Message Date
r4bbit 08fa86a9ca fix(apps/amm): match swap holdings on the configured id encoding
Moving the token list app-side (readTokensConfig) dropped the module
tokenList()'s base58->hex normalization, so a token's definitionId now
reaches the swap view in whatever encoding TOKENS_CONFIG uses — base58 in
practice. The swap account selector filtered holdings on the hex
definitionIdHex field using that value, so a base58 id matched nothing and
every token showed "No funds", blocking swaps.

Make the selector encoding-aware: a 64-char hex id filters definitionIdHex,
otherwise the base58 definitionId. tokenHoldings already emits both encodings
per holding, so this matches whichever the config uses, needs no app-side
base58 decoder, and mirrors how the liquidity view already filters. The swap
quote/submit path already normalizes base58->hex in the module, so nothing
else changes.
2026-08-19 15:51:41 +02:00
r4bbit 92f55652a8 refactor(amm): enrich resolvePool into resolvePoolAccount
Return the pool's full derived state from one read instead of just existence +
reserves, so callers get the derived accounts (for future account views / oracle
setup) without re-deriving.

FFI resolve_pool: drop the `exists` boolean — the presence of data is the signal.
An existing pool returns { status:"ok", ..., poolId, defAHex, defBHex, vaultAId,
vaultBId, lpDefinitionId, reserveA, reserveB, liquiditySupply, feeBps }; a missing /
uninitialized pool is the { status:"error", error:"no_pool", poolId } error (still
carrying the derived poolId for address derivation).

Module: resolvePool -> resolvePoolAccount — { status:"error", error } envelope for
hard failures, and orient reserves + defs + vaults to the caller's requested order.

Backend + QML: rename the slot; SwapCard and NewPositionFlow switch the existence
check from pool.exists to pool.status === "ok" (reserve field names unchanged, so
no other consumer edits). no_pool still routes to create-pool; hard errors still
surface.
2026-08-13 16:55:45 +02:00
r4bbit bf63070a9e fix(apps/amm): disable the already-selected token in the swap token picker
The token selector let you pick the same token on both sides of a swap.
That drove resolvePool into amm_client_pool_pda with def_a == def_b, which
hits `panic!("Definitions match")` in amm_core (a pool needs two distinct
tokens). Because that panic crosses the `#[no_mangle]` FFI boundary — which
can't unwind — it aborts, taking the whole UI process down.

Guard it at the source: the picker now disables (dims, no hover/click, tags
"Selected") whichever token is already chosen on the opposite side, so the
two sides can never match.

- TokenListItem: add a `disabled` state (opacity, inert MouseArea, tag)
- TokenSelectorModal: add `disabledDefinitionId`; gate both the popular-token
  pills and the list rows on it
- SwapPage: on open, set it to the opposite side's selected token
2026-08-13 13:03:58 +02:00
r4bbit 86575a65aa test(apps/amm): select the funding account before submitting a swap
canSubmit now requires a chosen holding on both swap sides, so the swap e2e
test must select them first. The selector auto-selects a single holding, but
pick it explicitly (robust to multi-account wallets) via a new selectAccount
step that waits for the holdings to load and selects the first match.

Expose each side's selector via selectorObjectName (swapSell/BuyAccountSelector)
and surface sellHolding/buyHolding in the failure diagnostics.
2026-08-11 12:04:48 +02:00
r4bbit 968001066c feat(apps/amm): pick the token account per swap side
Add a per-slot account selector to the swap card's sell/buy inputs, sourced
from the wallet's token holdings, so a user holding a token in several accounts
chooses which funds each side. A single holding auto-selects.

- SwapPage fetches backend.tokenHoldings(), refetching when the wallet opens
- TokenInput embeds ProgramAccountSelector (half width, right-aligned) in a
  full-width row below the amount/token row; exposes it as selectedHoldingId
- SwapCard drives swapExactInput/Output with the selected holdings and blocks
  submit ("Select token accounts") until both are chosen
- ProgramAccountSelector gains showWhenSingle + textAlignment opt-ins
2026-08-11 12:04:48 +02:00
r4bbit 4363f13912 feat(apps/amm): submit exact-output swaps and drop client-side swap math
Wire the Buy direction to submit: AmmUiBackend gains a swapExactOutput slot
(guarded like swapExactInput), SwapCard.executeSwap branches on direction —
sell -> swapExactInput(minReceivedRaw), buy -> swapExactOutput(maxInRaw) — and
canSubmit/submitButtonText/buildSnapshot handle both. The confirmation dialog
switches wording by mode ("You pay at most" / "You receive exactly" for exact
output). The Buy field is now digitsOnly since its value is submitted as a raw
base-units integer.

With both directions priced and oriented by the module, the client no longer
needs any pool math. Remove the reserve-orientation chain (sellIsPoolA,
buy/sellReserveNum, poolReserveA/B, poolDefAHex) — resolvePool now reports only
existence and fee — and the now-dead helpers (formatBaseUnits/formatAmountValue,
DummySwapState.amountInFor/minReceived/priceImpactPercent/maxSent). The
impossible-swap guard moves from a client reserve compare to the module's
output_exceeds_liquidity error, surfaced as "Insufficient liquidity".
2026-08-06 23:15:02 +02:00
r4bbit d9876d08ca feat(apps/amm): drive the exact-output swap preview from swapExactOutQuote
Wire the Buy direction of the swap card to the module's server-side
swapExactOutQuote, mirroring the exact-input path. AmmUiBackend gains a
swapExactOutQuote(tokenIn, tokenOut, amountOutDecimal, slippageBps) slot
returning { requiredInRaw, maxInRaw, priceImpactBps } (read-only).

Editing the Buy amount now debounces a swapExactOutQuote call and sources the
required input (shown in the Sell field), the price impact, and the slippage
ceiling from it — the exact figures come straight from the quote's raw integer
strings, so the preview matches execution and no reserve orientation happens
client-side. A retyped amount invalidates the quote up front.

SwapSummary's last row is generalised from a hardcoded "Min received" to a
direction-aware bound (boundLabel/boundText): "Min received" (min out) for
exact input, "Maximum sent" (max in) for exact output. SwapConfirmationSummary
is updated for the renamed property.

The Buy field remains preview-only — canSubmit is still sell-only, pending the
exact-output submit wiring. The now-orphaned DummySwapState pricing helpers are
left for a follow-up cleanup.
2026-08-06 23:15:02 +02:00
r4bbit 37c2f294ac 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.
2026-08-06 23:15:01 +02:00
Andrea Franz fc7b07174c test(amm-ui): add swap UI test 2026-08-05 17:19:38 +02:00
r4bbit 72b330122d feat(amm): move all AMM logic into the amm_module core module; flip UI to consume it
Introduce modules/amm — the AMM business logic as a universal core Logos module,
consumed identically by the QML UI (via modules().amm_module) and headlessly
(logoscore call amm_module ...). The module is a thin transport adapter: the
domain math lives in the Rust amm_client crate (the transport-independent JSON
FFI), and the module sequences those pure ops with chain I/O delegated to the
logos_execution_zone wallet module. It reaches the same shared wallet instance
the UI opened (Basecamp loads core modules as singletons; standalone the
LogosAPI client cache dedups the connection), so it never opens a second wallet.

The module owns the full AMM surface — not just swaps:
- resolvePool / swapExactInput / tokenList (the swap path)
- newPositionContext / quoteNewPosition / submitNewPosition (add-liquidity)

apps/amm: delete the app-side orchestration (SwapRuntime, NewPositionRuntime,
AmmClient/BundledAmmClient) and the amm_client link. AmmUiBackend now owns only
wallet-session lifecycle and forwards every AMM slot to modules().amm_module.
The one wallet-keyset mutation add-liquidity needs — creating a fresh LP holding
— stays in the backend (via its wallet provider, keeping the account model and
on-disk storage coherent): the module returns "requires_fresh_lp" without
submitting, the backend creates the account and resubmits with its id.

flake.nix / CMakeLists / metadata: the module links the amm_client crate; the UI
links no external lib and depends on amm_module (injected into the UI builder's
flakeInputs so the dependency resolves).

- amounts/deadline declared nlohmann::json so the generated dispatch accepts a
  JSON number (bare small ints on the CLI) or a string (exact u128 from the UI,
  or a quote-wrapped big value on the CLI); JSON floats are rejected rather than
  submit a silently-rounded amount.
- AMM_DEBUG-gated tracing for the swap path.
- Drop tests/cpp/NewPositionRuntimeTest.cpp with the class it covered
  (module-level tests to follow).
- modules/amm/README.md: architecture, headless prerequisites, logoscore recipe.
2026-08-03 23:01:53 +02:00
Ricardo Guilherme Schmidt 64ce091045 feat(wallet): add reusable wallet modules
Add program-neutral wallet access, a stable account model, reusable QML controls, transaction confirmation, submitted-transaction presentation, and isolated contract tests.
2026-07-27 12:14:27 +02:00
Andrea Franz cf92e5d111 fix(amm): guard resolvePool against stale callbacks; clarify deadline-ms and program-binary docs 2026-07-23 16:17:25 +02:00
Andrea Franz fe41baf210 fix(amm): address Copilot review — exact BigInt min_out, fail on oversized pool fee, sync flake run docs 2026-07-23 16:17:25 +02:00
Andrea Franz c0568e3a88 feat(amm): byte-encode swap instruction for QtRO + AMM_DEBUG tracing + token config 2026-07-23 16:17:25 +02:00
Andrea Franz caf53d0409 fix(amm): order swap holdings/reserves by pool token order; accept base58 ids 2026-07-23 16:17:25 +02:00
Andrea Franz 906aa65b4f feat(amm): config-driven token picker wired to on-chain swaps 2026-07-23 16:17:25 +02:00
Andrea Franz a0c8983302 feat(amm): wire Swap UI to on-chain resolvePool + swapExactInput 2026-07-23 16:17:25 +02:00
r4bbit 3622016e6c refactor: move programs into programs and UIs into apps
This refactors the repository structure as it has grown over time.
2026-05-26 14:05:52 +02:00