lez-programs/apps/amm/README.md
r4bbit 44b355ac7d
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-24 17:33:37 +02:00

9.2 KiB

AMM UI

A QML UI application for the Automated Market Maker (AMM) program.

See the Logos QML UI App Tutorial for more information.

Wallet / chain integration

This app is a ui_qml module with a hand-written C++ backend (src/AmmUiBackend.*, plugin in src/AmmUiPlugin.*) that depends on the core logos_execution_zone wallet module. The backend calls the core module's wallet FFI through m_logos->logos_execution_zone.* and exposes an async QtRO surface (src/AmmUiBackend.rep) plus an account list model to the QML view.

Onboarding is non-invasive. The app opens straight to the Trade screen; the navbar shows Connect (opens a password-only modal) or Connected + the account selector. There is no path picking — the wallet uses LEZ's canonical home, ~/.lee/wallet/ (override with LEE_WALLET_HOME_DIR, the same var LEZ honors), and its config (wallet_config.json) self-initializes.

Account/keystore sharing follows the runtime:

  • Standalone (nix run .#amm-ui): own core-module instance, but the canonical ~/.lee/wallet keystore is shared with the LEZ wallet UI and any other LEZ app on the machine. A previously-created wallet auto-opens on launch.
  • Inside Basecamp: the core wallet module is a single shared instance, so on startup the backend adopts the already-open wallet (see openOrAdoptWallet()), surfacing shared accounts across apps.

Follow-up: the app reconstructs the wallet paths itself because the logos_execution_zone module only exposes path-taking create_new/open. LEZ's wallet FFI now provides path-free variants (wallet_ffi_create_new_default, wallet_ffi_open_default, plus wallet_ffi_default_config_path / _storage_path / wallet_ffi_wallet_exists_default). Once the module surfaces those over QtRO, the app can drop its defaultWalletHome/Config/Storage logic.

Setup

This project requires Nix with experimental features enabled. If you haven't already, enable them permanently:

mkdir -p ~/.config/nix && echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf

Install the Logos package manager CLI globally (one-time):

nix profile install 'github:logos-co/logos-package-manager#cli'

This makes lgpm available as a global command.

Running the UI standalone

The app is built from the repository-root flake (which also provides the amm_client_ffi library it links). From the repo root, launch it with its named attribute:

nix run .#amm-ui

This builds and runs the application in development mode. The Logos bridge is unavailable in standalone mode, but the UI layout and mock data are fully functional.

Build just the FFI crate with nix build .#amm_client_ffi. (Each UI is exposed under its own name, so future apps are nix run .#<name> — there is no bare nix run . default.)

Running inside Logos Basecamp

This app is a UI plugin that depends on the core wallet module logos_execution_zone (see the Wallet / chain integration section above). Both have to be installed into Basecamp — the UI plugin alone will show the AMM tab but fail to open it with Failed to load core dependencies for amm_ui.

1. Build the LGX packages

# The AMM UI plugin — development variant (requires nix store at runtime)
nix build '.#lgx' --out-link result-lgx

# Portable variant (self-contained, works without nix)
nix build '.#lgx-portable' --out-link result-lgx-portable

# The core wallet module it depends on. These are the same immutable upstream
# revisions used by this app's flake, including the merged macOS Metal fix.
nix build 'github:logos-blockchain/logos-execution-zone-module?rev=d70225ced646934d2294fd9e8f8b03615c104b80#lgx' \
  --override-input logos-execution-zone \
  'github:logos-blockchain/logos-execution-zone?rev=a7e06a660940a00093b1760560d37ff84aff5a05' \
  --out-link result-core

2. Install into Basecamp

# Launch Basecamp once to initialise its data directory, then quit (see below)

# Set the data directory path
# macOS:
BASECAMP_DIR="$HOME/Library/Application Support/Logos/LogosBasecampDev"
# Linux:
# BASECAMP_DIR="$HOME/.local/share/Logos/LogosBasecampDev"

# Install the core wallet module first (into the modules dir), then the UI plugin
lgpm --modules-dir "$BASECAMP_DIR/modules" \
  install --file result-core/*.lgx
lgpm --ui-plugins-dir "$BASECAMP_DIR/plugins" \
  install --file result-lgx/*.lgx

Note: Use matching variants throughout — dev with dev, portable with portable. Mixing variants causes loading failures. The portable build uses the LogosBasecamp data directory instead of LogosBasecampDev.

3. Launch Basecamp

nix build 'github:logos-co/logos-basecamp' --accept-flake-config -o ~/.basecamp-result
~/.basecamp-result/bin/LogosBasecamp

The AMM UI appears as a new tab in the Basecamp sidebar.

Note: nix run 'github:logos-co/logos-basecamp' currently fails with unable to execute ... No such file or directory — the flake's default app is named logos-basecamp but the packaged binary is LogosBasecamp. Build the package and run the bin/LogosBasecamp wrapper directly, as above.

Installing via the Basecamp UI

Alternatively, use the built-in package manager. Install both packages — the core module from result-core/ and the UI plugin from result-lgx/:

  1. Launch Basecamp
  2. Open Package Manager
  3. Select "Install from file"
  4. Choose the core module .lgx from result-core/, then the UI plugin .lgx from result-lgx/

To actually use the on-chain views (Swap and Liquidity) you must also set AMM_PROGRAM_BIN and TOKENS_CONFIG (both explained below). Both views read the same two — the AMM program id is derived from AMM_PROGRAM_BIN, the token set from TOKENS_CONFIG, and the sequencer from the wallet config. Run this from the repo root — use absolute paths ($(pwd)/…), because nix run may not preserve the working directory, so relative paths won't resolve:

AMM_PROGRAM_BIN=$(pwd)/programs/amm/methods/guest/target/riscv32im-risc0-zkvm-elf/docker/amm.bin \
TOKENS_CONFIG=$(pwd)/amm-tokens.json \
nix run .#amm-ui

Without AMM_PROGRAM_BIN the Swap and Liquidity views stay disabled; without TOKENS_CONFIG the token picker is empty. Each is detailed below.

AMM program binary (required for swaps and liquidity)

To execute a swap, the app must submit a transaction against the exact AMM program you deployed (its ELF determines the program id, and therefore every pool/vault/config PDA and the transaction's target). The app therefore needs the deployed amm.bin bytes at runtime — it does not derive them from the wallet module (whose embedded AMM program may differ from your deployment).

Point the app at your deployed binary with the AMM_PROGRAM_BIN environment variable (absolute path):

AMM_PROGRAM_BIN=/abs/path/to/amm.bin nix run .#amm-ui

This is the same amm.bin you deployed via the testnet runbook (programs/amm/methods/guest/target/riscv32im-risc0-zkvm-elf/docker/amm.bin). The app reads it, derives the AMM program id (amm_client_program_id_from_elf), and reads the on-chain AMM config account to discover the TWAP oracle program id for the pool's current-tick PDA. If AMM_PROGRAM_BIN is unset or unreadable, the Swap view stays disabled (no pool can be resolved).

Golden rule (from the runbook): recompiling the AMM changes its program id and every derived PDA. After any redeploy, point AMM_PROGRAM_BIN at the new amm.bin — never mix a stale binary with a fresh deployment.

Token list config (required for the Swap token picker)

The Swap view's token picker is config-driven: it doesn't derive tokens from chain state, it reads a flat JSON list from the TOKENS_CONFIG environment variable (absolute path). Each entry needs, at minimum, the token's definitionId and your own holding account address for that token (the account the wallet will sign transfers from/to for that token):

[
  {
    "symbol": "TKA",
    "name": "Token A",
    "definitionId": "9qbX…",
    "holding": "4T69…",
    "decimals": 18
  }
]

If TOKENS_CONFIG is unset, unreadable, or not a valid JSON array, the token picker stays empty (a qWarning naming the exact cause is logged to stderr; no swap can be started). definitionId/holding may be given as base58 (as the wallet/runbook display them) or hex — the app normalizes both to hex.

Full command with both variables set (absolute paths, from the repo root):

AMM_PROGRAM_BIN=$(pwd)/programs/amm/methods/guest/target/riscv32im-risc0-zkvm-elf/docker/amm.bin \
TOKENS_CONFIG=$(pwd)/amm-tokens.json \
nix run .#amm-ui

Validation

New Position validation commands and acceptance criteria live in VALIDATION.md.

Updating Dependencies

To update the pinned versions of dependencies in flake.lock:

nix flake update

Troubleshooting

Stale QML cache after rebuild:

QML_DISABLE_DISK_CACHE=1 ~/.basecamp-result/bin/LogosBasecamp

Reset Basecamp data directory:

# macOS
rm -rf ~/Library/Application\ Support/Logos/LogosBasecampDev
# Linux
rm -rf ~/.local/share/Logos/LogosBasecampDev