Commit Graph
10 Commits
Author SHA1 Message Date
r4bbit 1c1e138916 refactor(amm): remove the dead submitNewPosition path
Now that add-liquidity and pool creation submit via addLiquidity / createPool,
the legacy submitNewPosition path is dead. Remove it end to end.

App backend (AmmUiBackend):
- Drop the submitNewPosition slot/method and its newPositionError helper.

Module (AmmModuleImpl):
- Drop submitNewPosition, the m_requestPending guard, and the now-orphaned
  nowMs/parseU64 helpers (+ the <chrono> include).

FFI (amm_ffi) — the plan op only submitNewPosition called:
- Delete plan.rs; remove amm_plan (extern + regenerated header), api::plan,
  and PlanRequest.
- Remove the plan-only machinery it fed: QuoteBranch, NewPositionPlan,
  EvaluatedQuote.plan/quote_hash, and AccountPlan's wallet_args /
  requires_fresh_lp / contains / validate_ready, plus the plan construction
  in quote.rs.
- Tests: drop the plan-only tests/helpers; keep quote coverage by trimming
  the mixed tests to their quote assertions.

QML tests (tst_LiquidityPage):
- Remove the two legacy-submit tests (base58-only success) + the
  submitNewPosition mock and its now-unused fixtures. Keep the
  finishSubmitFailure test (unchanged behaviour).

quoteNewPosition and its machinery (amm_quote, buildQuoteInput, PairSnapshot,
AccountPlan preview/sources, commitment) stay — they retire with the legacy
quoting in the quote-migration vertical.
2026-08-11 12:43:31 +02:00
r4bbit e886c1d088 feat(modules/amm): add the add-liquidity API and quoting
Introduce the add-liquidity vertical mirroring the swap + createPool patterns,
for depositing into an existing pool via the AddLiquidity instruction.

FFI (amm_ffi):
- add_liquidity_quote — decode poolData, orient the caller's max amounts to the
  pool's canonical order, run the guest's exact ideal->actual->delta_lp math, and
  return { amountARaw, amountBRaw, expectedLpRaw, priceRaw } in display order.
  Slippage-free like create's quote; the min-LP floor is applied at submit.
- add_liquidity_plan — canonicalize (token, max-amount, holding) as one unit,
  take vaults + LP definition from poolData, emit the 10-account AddLiquidity
  order (only the user holdings a/b/LP sign). Takes minLpRaw directly, like
  swap_exact_in_plan takes min_out.

Shared with createPool: extract canonical_triples (the pair/amount/holding
canonical swap) and plan_response (the tx-submission envelope); both the create
and add plans now use them.
2026-08-11 12:18:32 +02:00
r4bbit 2a3be1278a feat(modules/amm): add tokenHoldings — list the wallet's token holdings
A thin source for the account selector: an amm_ffi op that decodes the wallet's
fungible TokenHoldings (owned by the configured token program) into
[{ accountId (hex), accountType:\"TokenHolding\", definitionId (base58),
definitionIdHex (hex), balanceRaw }] — one row per holding account, every token,
including zero-balance holdings; narrowing to a specific token is the selector's
job. Exposed via AmmModuleImpl::tokenHoldings and the AmmUiBackend tokenHoldings()
slot, both gated on wallet-open.
2026-08-11 12:04:48 +02:00
r4bbit 526d50bff1 feat(modules/amm): add createPool quote + plan ops and module methods
Bring pool creation onto the redesigned lean module surface
  mirroring the shipped swap vertical.

  FFI (amm_ffi):
  - amm_liquidity_quote: a pure create-pool preview from the two deposit
    amounts — expectedLpRaw / initialPriceRaw / lockedLpRaw via the shared
    amm_core opening-LP math (isqrt_product, MINIMUM_LIQUIDITY,
    spot_price_q64_64), so the preview equals what new_definition mints. No
    chain reads, no quoteHash, and no fee input (the fee is neither part of the
    pool PDA nor the pricing — one pool per pair).
  - amm_create_pool_plan: canonicalizes the pair, moving amounts and user
    holdings as one unit so each (vault, holding, amount) triple names the same
    token, then emits the fixed 11-account NewDefinition plan (only the user
    a/b and fresh LP holdings sign).

  Module (AmmModuleImpl):
  - liquidityQuote(request): thin preview wrapper, normalizes ids to hex.
  - createPool(request, fresh_lp_id): a new pool always needs a fresh LP
    holding, so an empty fresh_lp_id returns requiresFreshLp without
    submitting; otherwise builds the plan and submits, returning a hex
    transactionId.
2026-08-11 12:04:47 +02:00
r4bbit 56b2d3a282 feat(modules/amm): add swap_exact_out_plan op and module swapExactOutput
Server-side SwapExactOutput submission, mirroring the exact-in path. The
swap_exact_out_plan FFI op resolves the pool via the shared derive_pair,
encodes amm_core::Instruction::SwapExactOutput { exact_amount_out,
max_amount_in, deadline }, and returns the same four tx-submission fields as
swap_exact_in_plan (programId, accountIds, signingRequirements, instruction)
in the identical fixed 8-account IDL order — only the user's input holding
signs. Recoverable domain failures (same_token_pair, config_unavailable) go
through the FFI envelope as Err. C export amm_swap_exact_out_plan (cbindgen
header regenerated).

The module swapExactOutput(defA, defB, userIn, userOut, amountOut, maxIn,
deadline) method reads the AMM config, calls the op, and submits the plan via
send_generic_public_transaction, returning the tx hash (empty string on
failure). Same argument conventions and I/O-free split as swapExactInput.

Not yet wired into AmmUiBackend/QML — the SwapCard rewire is a follow-up. The
existing swap UI path is untouched, so nothing breaks.
2026-08-06 23:15:01 +02:00
r4bbit a389dc6056 refactor(modules/amm): rename swap_plan to swap_exact_in_plan
Rename the swap-submission planning op for symmetry with the exact-in/exact-out
split already applied to the quote ops (swap_exact_in_quote /
swap_exact_out_quote): swap_plan -> swap_exact_in_plan, SwapPlanRequest ->
SwapExactInPlanRequest, and the C export amm_swap_plan -> amm_swap_exact_in_plan
(cbindgen header regenerated). The module's swapExactInput call site and trace
are updated to match.
2026-08-06 14:31:49 +02:00
r4bbit d038b3d060 feat(modules/amm): add swap_exact_out_quote op and module swapExactOutQuote
Server-side SwapExactOutput preview. The swap_exact_out_quote FFI op orients
the pool reserves to the requested in/out direction, prices via the shared
amm_core::swap_exact_out_amounts (so requiredIn matches the chain), and
derives the slippage ceiling: { requiredInRaw, maxInRaw, priceImpactBps }.
no_pool and output_exceeds_liquidity (amount_out >= reserve) are returned as
errors. Read-only — no quoteHash; the on-chain max_amount_in is the real
guard.

The module swapExactOutQuote(tokenIn, tokenOut, amountOut, slippageBps)
method derives the pool via the config-free pool_id op, reads it, and wraps
the op in the { status, error, ... } envelope. Mirrors swapExactInQuote.

Not yet consumed by the QML swap view, so nothing breaks. Lets the buy field
stay editable when the SwapCard is rewired in a follow-up.
2026-08-06 14:31:49 +02:00
r4bbit abc6d27a9f feat(modules/amm): add swap_exact_in_quote op and module swapExactInQuote
Server-side SwapExactInput preview. The swap_exact_in_quote FFI op orients the pool reserves to the requested in/out direction, prices via the shared amm_core::swap_exact_in_amounts (so expectedOut matches the chain), and derives the slippage floor: { expectedOutRaw, minReceivedRaw, priceImpactBps }. no_pool is returned as an error; pool metadata (reserves/fee) comes from resolve_pool, so it isn't echoed. Read-only — no quoteHash; the on-chain min_amount_out is the real guard.

The module swapExactInQuote(tokenIn, tokenOut, amountIn, slippageBps) method derives the pool via the config-free pool_id op, reads it, and wraps the op in the { status, error, ... } envelope; call() now surfaces the op error code so no_pool propagates.

The QML swap view still uses the old path, so nothing breaks. Will make the QML consume it in a follow-up.
2026-08-06 14:31:49 +02:00
r4bbit 6a951f3cad feat(modules/amm): add pool_id operation
Derives a pool's PDA from the AMM program id and the two token ids —
compute_pool_pda(amm_program, canonical(token_in, token_out)) — returning
{ poolId }. Tokens may be given in either order; the op canonicalizes.

Unlike swap_pair, which derives the whole pair account-set (including the
current-tick PDA, which depends on config.twap_oracle_program_id and so
requires reading the config account), the pool address depends only on the
program id and the token pair. So a caller that just needs to locate and
read the pool can skip the config read entirely.

Exposed as the amm_pool_id C ABI export. Additive — no existing op changes.
It backs the config-free pool lookup the upcoming swap-quote path needs (and,
later, resolvePoolAccount).
2026-08-05 20:24:24 +02:00
r4bbit f5ff9b829f refactor(apps/amm): move the amm_client crate into modules/amm/ffi as amm_ffi
After the amm_module refactor, this crate is linked only by the module (the
UI delegates to modules().amm_module and links nothing), so its home under
apps/amm/ and the name "client" were both misnomers: it's the AMM business
logic the module wraps, reached across an FFI boundary — the same relationship
logos_execution_zone has with wallet_ffi. Co-locate it with the module that
owns it and name it for that role.

The FFI surface is unchanged — the exported functions are already amm_* (not
amm_client_*) — so only the crate, directory, generated header, dylib, and
package names move. The module's call sites are untouched; it just includes the
renamed header.

- apps/amm/client → modules/amm/ffi (git-tracked rename; history preserved)
- crate amm_client → amm_ffi: package name, include/amm_ffi.h, libamm_ffi.dylib,
  AMM_FFI_H guard, build.rs output path, tests/public_api.rs import
- workspace member path + Cargo.lock
- flake.nix: pname, -p, header-copy path, dylib install_name, packages.amm_ffi,
  ammModuleOutputs externalLibInputs, DYLD wrapper
- modules/amm: metadata external_libraries, CMakeLists EXTERNAL_LIBS, impl
  #include + comments, README, flake note
- apps/amm/flake.nix: drop the now-dead amm_client external-lib input (the UI
  links no external lib of its own)

Resulting layout:
  modules/amm/
    src/   # C++ module  (amm_module_impl.{h,cpp})
    ffi/   # Rust crate  amm_ffi  (Cargo.toml, src/, include/amm_ffi.h)
2026-08-03 23:11:06 +02:00