Commit Graph
163 Commits
Author SHA1 Message Date
r4bbit 4d04d995c8 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:10:48 +02:00
r4bbit 4b6417bc31 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:07:44 +02:00
r4bbit 69b7d6c9e0 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:07:32 +02:00
r4bbit 76a8f539b3 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:00:49 +02:00
r4bbit b2f0e4b851 refactor(modules/amm): drop status/code from swap_exact_in_plan
The plan op is internal plumbing — its only consumer is the module's
swapExactInput, which just forwards the four tx-submission fields to the
wallet. Collapse its response onto the FFI envelope: return { programId,
accountIds, signingRequirements, instruction } directly and route the
recoverable domain failures (same_token_pair, config_unavailable) through
Err -> { ok: false, error } instead of a nested { status, code } object.
Also drop the deadlineMs echo — it's unused (the deadline is already encoded
in the instruction bytes). swapExactInput now treats any non-ok plan as a
failure. No behavior change for the UI: the plan JSON never crosses the
module boundary.
2026-08-06 14:31:49 +02:00
r4bbit bf1f76b051 fix(modules/amm): swap plan must use the pool's stored vault ids
The swap-submission plan derived the pool's vaults from the canonical token
order (compute_vault_pda(pool, canonical_token_a/b) via derive_pair), but the
guest asserts the provided vaults against the pool's stored vault_a_id /
vault_b_id, which are in the pool's *creation* order. compute_pool_pda_seed
canonicalizes, so the pool address is order-independent, but NewDefinition
stores def_a/def_b (and their vaults) as created — so for a pool created in
non-canonical order, the plan put vault_b in the vault_a slot and the guest
panicked with "Vault A was not provided", reverting the swap.

Read the pool account in swapExactInput and pass its data to
swap_exact_in_plan, which now uses pool.vault_a_id / pool.vault_b_id verbatim
for the vault slots (pool / current_tick / clock stay from derive_pair, since
those are order-independent). This restores the order-agnostic behavior the
original swap client had before it was rewired onto the canonical derive_pair
in 737b2f6.

Adds a regression test that builds a non-canonically-created pool (stored
def_a = the smaller-valued token) and asserts the plan emits the pool's stored
vaults, guarding that they differ from the canonical derivation.
2026-08-06 14:31:49 +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 cd8a84374b refactor(amm): extract swap-input formula into amm_core
Lift the inverse constant-product SwapExactOutput math out of the guest's
exact_output_swap_logic into amm_core::swap_exact_out_amounts(amount_out,
reserve_in, reserve_out, fee_bps) -> Option<(effective_in, required_in)>:
ceil(reserve_in * amount_out / (reserve_out - amount_out)) lifted through the
fee via mul_div_ceil. exact_output_swap_logic now calls it and keeps its
nonzero/exceeds-reserve asserts. Behavior-preserving (the panic-message tests
still pass); None (out >= reserve or zero fee multiplier) surfaces as the
existing expect.

Makes the on-chain exact-output pricing one reusable function so the
off-chain quote can produce byte-identical required-input figures instead of
re-deriving the inverse formula. Mirrors swap_exact_in_amounts.
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 c3dc9dd94f refactor(amm): extract swap-output formula into amm_core
Lift the constant-product SwapExactInput math out of the guest's swap_logic
into amm_core::swap_exact_in_amounts(amount_in, reserve_in, reserve_out,
fee_bps) -> (effective_in, amount_out). swap_logic now calls it and keeps its
nonzero input/output asserts. Behavior-preserving (the two panic-message
tests still pass); the only change is that the impossible-overflow expects
become saturating.

Makes the on-chain pricing one reusable function so the off-chain swap quote
can produce byte-identical expected-output figures instead of re-deriving the
formula.
2026-08-05 22:54:37 +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 de9a3d5320 fix(amm)!: restrict UpdateConfig to authority transfer only
UpdateConfig let the admin rewrite token_program_id and
twap_oracle_program_id in place. Both are immutable deployment
parameters — twap_oracle_program_id derives the current-tick PDA and
every price-observation/price account PDA, and token_program_id is the
program the AMM issues its vault transfers to — so changing either
after any pool exists would orphan every derived account and vault. A
genuine change requires redeploying the AMM, never an in-place edit.

- Instruction::UpdateConfig now carries a single required field,
  new_authority; the two program-id fields are removed.
- update_config assigns the new authority directly (no Option/if-let);
  PDA + admin + signature preconditions unchanged.
- Guest handler and IDL updated to match.
- Drop the integration test that mutated token_program_id (it
  exercised the vulnerability); keep reject-non-admin and
  authority-handoff, and assert program ids survive a transfer.

BREAKING CHANGE: the UpdateConfig instruction ABI changed — the
token_program_id and twap_oracle_program_id fields are removed and
new_authority is now required (was Option). Any client constructing
UpdateConfig must be updated. The instruction enum change also alters
the program ImageID: redeploy and update every ImageID-derived value
(deployed program ids, client/config files, PDA-derived addresses,
AMM/ATA program-id inputs) before submitting
2026-08-05 18:08:43 +02:00
Andrea Franz 200f429ec6 test(amm-ui): feed setup password via stdin so bootstrap never blocks 2026-08-05 17:19:38 +02:00
Andrea Franz c5b9508e4d docs(amm-ui): clarify test wallet password (throwaway setup vs restore-keys) 2026-08-05 17:19:38 +02:00
Andrea Franz 0576b10dcb test(amm-ui): feed wallet setup password non-interactively 2026-08-05 17:19:38 +02:00
Andrea Franz 191942f2a8 docs(amm-ui): document isolated UI test flow in tests/README.md 2026-08-05 17:19:38 +02:00
Andrea Franz 2ed13506d1 test(amm-ui): isolate test token config, fix repo-root resolution 2026-08-05 17:19:38 +02:00
Andrea Franz 9a1f76ff6b test(amm-ui): bootstrap deterministic test wallet in testnet setup 2026-08-05 17:19:38 +02:00
Andrea Franz 380bbff308 test(amm-ui): add isolated AMM testnet setup script 2026-08-05 17:19:38 +02:00
Andrea Franz fc7b07174c test(amm-ui): add swap UI test 2026-08-05 17:19:38 +02:00
r4bbit 8cc4055f64 ci: trigger gh actions on any PR 2026-08-05 11:49:45 +02:00
r4bbit 96dc69c39f docs(apps/amm): remove stale BIN reference 2026-08-03 23:11:06 +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
r4bbit afeba568d8 refactor(amm): drop the new-position schema version tag
The add-liquidity flow stamped a `new-position.v1` schema tag on every
request and response and validated it across all three layers — the QML
plugin, the amm_module core module, and the amm_client Rust crate. It was a
cross-version compatibility guard, but these artifacts always ship together,
so the contract is honored implicitly, and the swap view already works fine
without one. Dropping it makes the liquidity view consistent with swap and
removes a layer of ceremony.

- amm_client: remove PositionRequest.schema and the unsupported_schema check
  in compute_quote; drop QuoteCommitment.schema (changes quoteHash, which is
  internal-only) and every "schema" response stamp; delete the SCHEMA /
  NEW_POSITION_SCHEMA constants and the public export.
- amm_module: remove the SCHEMA constant and its four response stamps.
- AmmUiBackend: remove its local NEW_POSITION_SCHEMA and the stamps in
  loadingContext() / newPositionError().
- QML: drop the "schema" fields from the request/envelope builders and relax
  the validity gates to check status / canSubmit instead (the sole check in
  NewPositionFlow now guards on a missing `status`); strip the now-dead
  schema fields from the liquidity QML test fixtures.
- Also removes the last stale comment references to the deleted *Runtime
  classes.
2026-08-03 23:01:53 +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
r4bbit 737b2f674a refactor(amm): consolidate the two client FFIs into one JSON crate
The AMM host FFI was split across two crates with two ABI styles: the
typed-C `amm_client_ffi` (swap primitives, under programs/) and the
JSON/wire `amm_client` (new-position flow, under apps/). Fold both into a
single `amm_client` crate exposing one JSON C ABI, and delete
`programs/amm/client-ffi`.

Rust:
- Re-express the swap path as `api/swap.rs` operations on the existing
  `call::<T>` dispatch — swap_pair, resolve_pool, swap_plan, program_id —
  reusing `pair::derive_pair` (no more duplicated PDA derivation) and
  `risc0_zkvm::serde` for the SwapExactInput words (the same encoding the
  guest decodes). The account list and signer flags stay byte-identical
  to the old typed path.
- Generate a single header (`include/amm_client.h`) covering all ops via
  cbindgen; bump cbindgen 0.27 -> 0.28 for `#[unsafe(no_mangle)]` support.

C++:
- Extend the `AmmClient` wrapper with the four swap ops.
- Add `SwapRuntime` (mirrors `NewPositionRuntime`): reads accounts through
  the wallet, drives the swap ops, submits the transaction.
- `AmmUiBackend` swap methods now delegate to `SwapRuntime`, dropping ~390
  lines of typed-FFI and byte-twiddling. `program_id` becomes a JSON op,
  and the swap clock is derived via `derive_pair` (clock_core::CLOCK_01)
  instead of a hardcoded base58 literal — same account, verified.
2026-08-03 15:37:37 +02:00
r4bbit 8358cfa2f1 fix(amm-ui): cache network snapshot to avoid remote calls on the hot path
networkSnapshot() rebuilt the new-position network context on every call —
deriving ammProgramId from $AMM_PROGRAM_BIN and resolving the $TOKENS_CONFIG
token ids, which run tokenList()'s remote account_id_from_base58 conversions.
It is called on the quote hot path (every keystroke) and, critically, from
inside runtime reply callbacks: after a create-pool submit, pool activation
runs refreshContext() from within a quoteNewPosition reply, so the nested
synchronous remote calls reentered the module connection and hung the reply.
refreshNewPositionContext never completed, contextLoading never cleared, and
the token selectors (gated on !contextLoading) stayed disabled — the view
became unusable after creating a pool.

$AMM_PROGRAM_BIN and $TOKENS_CONFIG are fixed for the process lifetime, so
resolve ammProgramId and the token ids once and cache them; networkSnapshot()
now returns the cached values with no per-call remote work. (Still gated to
"loading" until wallet state resolves, so the one-time resolve happens at
startup, not inside a callback.)
2026-07-27 12:14:27 +02:00
r4bbit f9bd85336f fix(wallet): send tx instruction as a byte string, not QVariantList<u32>
LogosWalletProvider::submitPublicTransaction passed the RISC0 instruction
words to send_generic_public_transaction as a QVariantList<u32>. That param
is a byte string (bstr): the module's QtRO glue mangles a list-of-u32 crossing
the module process boundary, so the guest deserialized a corrupted Instruction
variant and panicked, e.g.

  Guest panicked: called `Result::unwrap()` on an `Err` value:
  Custom("invalid value: integer `53765`, expected variant index 0 <= i < 10")

(53765 = 0x0000D205 — the guest read a 4-byte word where 0x05, the AddLiquidity
variant, is a single byte.) This broke every submit through the shared provider,
including the new-position / add-liquidity flow.

Send the little-endian bytes of the u32 words as a QByteArray instead — the same
encoding the AMM swap path already uses (it calls the module directly and never
went through this provider). signingRequirements stays a QVariantList<bool>;
only the instruction needed the bstr encoding.
2026-07-27 12:14:27 +02:00
r4bbit e585489a60 factor(amm-ui): source new-position network context from AMM_PROGRAM_BIN/TOKENS_CONFIG/wallet
The create-pool / new-position flow carried its own network layer:
AMM_UI_NETWORK + AMM_UI_DEVNET_FILE (a devnet.json) or a bundled
config/networks.json supplied the AMM program id and token set, and a
JSON-RPC channel/checkpoint "identity probe" gated the flow to a verified
network. Main already exposes all of this the way the Swap view consumes it,
so collapse onto those sources instead of a parallel system:

- ammProgramId  <- $AMM_PROGRAM_BIN (derived like swapExactInput's program id;
                   doubles as the quote's network fingerprint so a quote can't
                   be replayed against a different deployment)
- tokenIds      <- $TOKENS_CONFIG (amm-tokens.json), same as the Swap picker
- sequencer     <- the wallet config (already surfaced via syncWalletState)

networkSnapshot() builds the ActiveNetworkSnapshot from those; status is
"ready"/"config_missing", gated to "loading" until wallet state resolves so no
module reads happen during construction. The channel probe is gone — submit
needs no channelId (the wallet module supplies the channel via
submitPublicTransaction), so the whole verification apparatus was overhead.

Removes: AMM_UI_NETWORK / AMM_UI_DEVNET_FILE, devnet.json / networks.json, the
ActiveNetwork class (+ its test) and its QNetwork channel probe, and Qt6Network.
ActiveNetwork.h keeps only the ActiveNetworkSnapshot struct. Run command drops
the AMM_UI_* vars:
```
  LEE_WALLET_HOME_DIR=… AMM_PROGRAM_BIN=… TOKENS_CONFIG=… nix run .#amm-ui
```
2026-07-27 12:14:27 +02:00
r4bbit d7017a2515 chore: remove unnecessary macos prerequisites docs 2026-07-27 12:14:27 +02:00
Ricardo Guilherme Schmidt 54afb26087 fix(amm): align execution zone dependencies 2026-07-27 12:14:27 +02:00
Ricardo Guilherme Schmidt aafe5e900b fix(amm-ui): require explicit liquidity inputs
Keep wallet assets as token choices without automatically selecting a pair. Keep pool-probe amounts internal so active-pool quotes do not populate the form before user input.
2026-07-27 12:14:27 +02:00
r4bbit 01829a280c feat(apps/amm): add create-pool / new liquidity position flow
Add a "Create Pool" flow to the AMM app that lets a user open a new
liquidity position — seeding a pool's initial liquidity — from token
selection and amount entry, through a confirmation dialog, to on-chain
submission.

- client crate (apps/amm/client): pure, testable protocol logic — account
  decoding, pair/position modelling, and quote/plan computation — exposed to
  the app over a C ABI (config/networks.json drives network selection).
- C++ runtime + backend: AmmClient, ActiveNetwork, and NewPositionRuntime,
  wired into AmmUiBackend (new resolve/quote/submit slots).
- QML flow: NewPositionForm, NewPositionFlow state, NewPositionConfirmation-
  Dialog, TokenSelectorModal, and reusable Amm* presentational components
  (theme, surfaces, buttons) + AmountMath.js.
- tests: C++ (NewPositionRuntimeTest, ActiveNetworkTest) and QML
  (tst_NewPositionForm, tst_LiquidityPage, tst_TokenAmountInput, …).
2026-07-27 12:14:27 +02:00
r4bbit 266c20a90e fix(apps/amm): ensure changing endpoint works 2026-07-27 12:14:27 +02:00
r4bbit 1dcfb784f8 fix(apps/wallet): use @loader_path rpath for the QML plugin on macOS
The Logos.Wallet QML plugin was installed with an ELF "$ORIGIN" rpath.
macOS dyld does not expand "$ORIGIN", so loading the plugin failed with

  Cannot load library liblogos_wallet_qmlplugin.dylib:
  Library not loaded: @rpath/liblogos_wallet_qml.dylib

even though liblogos_wallet_qml.dylib sits in the same directory. Select
the rpath per platform: @loader_path on Apple, $ORIGIN elsewhere, so
@rpath/liblogos_wallet_qml.dylib resolves to the sibling library.
2026-07-27 12:14:27 +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 e03164baf3 fix(amm): LE-serialize instruction words; correct ELF→ProgramBinary docs; clarify sharedWalletIsOpen 2026-07-23 16:17:25 +02:00
Andrea Franz f1fc061664 style(amm): cargo fmt 2026-07-23 16:17:25 +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 9b7a3dcaac feat(amm): swap via d70225ced program_id_hex API; pin wallet-module core to sequencer rev 415964d7 2026-07-23 16:17:25 +02:00
Andrea Franz 69cd58d377 build(amm): fork logos_execution_zone for QtRO byte-string tx args + align amm_client_ffi to lee_core v0.2.0 2026-07-23 16:17:25 +02:00
Andrea Franz 8f67c4c4b6 build(amm): load amm_client_ffi via absolute store-path id + DYLD fallback on macOS 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
Andrea Franz b51a71ddf2 feat(amm): resolvePool + swapExactInput backend slots for on-chain swaps 2026-07-23 16:17:25 +02:00
Andrea Franz cfb62e4d2f build(amm): link amm_client_ffi into the AMM UI module 2026-07-23 16:17:25 +02:00