Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
target/
*.bin
**/*/result
# Nix build symlinks (result, result-mcp, result-core, result-lgx, …) at any depth
result
result-*
**/*/.DS_Store
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ To use the Swap view, also set `AMM_PROGRAM_BIN` (your deployed `amm.bin`) and

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

Expand Down
7 changes: 7 additions & 0 deletions apps/amm/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
result
result-*
modules/

# Personal token/account config — copy amm-tokens.json.example and fill in
# your own definitionId/holding accounts. Never commit your real accounts.
amm-tokens.json

# Screenshot artifacts written by tests/swap.mjs
tests/*.png
68 changes: 66 additions & 2 deletions apps/amm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ not preserve the working directory, so relative paths won't resolve:

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

Expand Down Expand Up @@ -197,6 +197,14 @@ account the wallet will sign transfers from/to for that token):
]
```

The quickest start is to copy the checked-in template and edit it:

```bash
cp apps/amm/amm-tokens.json.example apps/amm/amm-tokens.json # then replace the REPLACE_… placeholders
```

`amm-tokens.json` is git-ignored so your own accounts never get committed.

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
Expand All @@ -206,7 +214,7 @@ Full command with both variables set (absolute paths, from the repo root):

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

Expand All @@ -215,6 +223,62 @@ nix run .#amm-ui
New Position validation commands and acceptance criteria live in
[VALIDATION.md](VALIDATION.md).

## Running the UI tests

The UI tests live in `apps/amm/tests/` (e.g. `swap.mjs`). They drive the running
app through a QML inspector: each test connects to the inspector's TCP server,
finds elements, clicks them, and asserts on the resulting state. `swap.mjs`
selects two tokens, enters a sell amount, submits a swap end-to-end, and then
verifies the pool reserves actually changed on-chain (read back from the
sequencer via the app's `resolvePool`).

The test framework itself — the `test()` / `run()` / `app.*` API that the tests
import from `test-framework/framework.mjs` — comes from the
[**`logos-co/logos-qt-mcp`**](https://github.com/logos-co/logos-qt-mcp) repo.
It isn't vendored here; the `nix build .#test-framework` step below materializes
it (Nix resolves it via this app's flake inputs, pinned in `flake.lock`).

Run everything **from the repository root** (the `apps/amm` flake can't resolve
`amm_client_ffi` on its own).

**Prerequisites** for the swap test to complete:

- a token list with ≥2 tokens — copy `apps/amm/amm-tokens.json.example` to
`apps/amm/amm-tokens.json` and fill it in (see [Token list config](#token-list-config-required-for-the-swap-token-picker)),
- the AMM program binary (see [AMM program binary](#amm-program-binary-required-for-swaps)),
- a running sequencer with a pool + liquidity for that token pair, and an open
wallet — otherwise the swap resolves to "No pool / no liquidity" and can't submit.

**From scratch:**

```bash
# 1. Build the JS test framework once. The -o path is where the tests expect it
# (apps/amm/tests/swap.mjs imports ../result-mcp); or set LOGOS_QT_MCP instead.
nix build .#test-framework -o apps/amm/result-mcp

# 2. Terminal 1 — launch the AMM UI with a real, visible window. The inspector
# listens on localhost:3768. Absolute paths ($(pwd)/…) because nix run may
# not preserve the working directory.
AMM_DEBUG=1 \
AMM_PROGRAM_BIN=$(pwd)/programs/amm/methods/guest/target/riscv32im-risc0-zkvm-elf/docker/amm.bin \
TOKENS_CONFIG=$(pwd)/apps/amm/amm-tokens.json \
nix run .#amm-ui

# 3. Terminal 2 — run a test against the running app; watch it drive the UI.
node apps/amm/tests/swap.mjs
```

On failure the test prints the relevant `SwapCard` state and saves screenshot
PNGs next to the test (`apps/amm/tests/swap-*.png`, git-ignored) for inspection.

**Headless CI variant** (no window, launches the app itself, pass/fail only):

```bash
nix build .#integration-test -L
```

It runs every `*.mjs` under `apps/amm/tests/` with `QT_QPA_PLATFORM=offscreen`.

## Updating Dependencies

To update the pinned versions of dependencies in `flake.lock`:
Expand Down
16 changes: 16 additions & 0 deletions apps/amm/amm-tokens.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[
{
"symbol": "TKA",
"name": "Token A",
"definitionId": "REPLACE_WITH_TOKEN_A_DEFINITION_ID",
"holding": "REPLACE_WITH_YOUR_TOKEN_A_HOLDING_ACCOUNT",
"decimals": 18
},
{
"symbol": "TKB",
"name": "Token B",
"definitionId": "REPLACE_WITH_TOKEN_B_DEFINITION_ID",
"holding": "REPLACE_WITH_YOUR_TOKEN_B_HOLDING_ACCOUNT",
"decimals": 18

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decimals don't exist in the program. Is this used anywhere? Otherwise we can get rid off it.

}
]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably want to make this an app-config.json or something like that, I could imagine adding other things here that the app wants to show by default (like a list of pools etc)

5 changes: 5 additions & 0 deletions apps/amm/qml/components/swap/SwapCard.qml
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ Rectangle {
Layout.fillWidth: true
theme: root.theme
label: "Sell"
inputObjectName: "swapSellInput"
buttonObjectName: "swapSellTokenButton"
amount: root.sellDisplay
token: root.sellToken
active: root.editingSide === "sell"
Expand Down Expand Up @@ -377,6 +379,8 @@ Rectangle {
Layout.fillWidth: true
theme: root.theme
label: "Buy"
inputObjectName: "swapBuyInput"
buttonObjectName: "swapBuyTokenButton"
amount: root.buyDisplay
token: root.buyToken
active: root.editingSide === "buy"
Expand Down Expand Up @@ -440,6 +444,7 @@ Rectangle {

Rectangle {
id: ctaBox
objectName: "swapSubmitButton"
Layout.fillWidth: true
Layout.topMargin: 8
Layout.bottomMargin: 8
Expand Down
7 changes: 7 additions & 0 deletions apps/amm/qml/components/swap/TokenInput.qml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ Rectangle {
// string (see decimalToU128Le in AmmUiBackend.cpp); fractional/decimal
// input there fails opaquely rather than being scaled.
property bool digitsOnly: false
// objectName forwarded to the inner TextInput so UI tests can target the
// sell/buy amount fields deterministically (see apps/amm/tests/).
property alias inputObjectName: tiInput.objectName
// objectName forwarded to the token-select button, so tests can open the
// right picker by objectId rather than fuzzy text.
property alias buttonObjectName: tokenButton.objectName

signal tokenClicked()
signal inputEdited(string newValue)
Expand Down Expand Up @@ -99,6 +105,7 @@ Rectangle {
}

Rectangle {
id: tokenButton
height: 40
radius: 20
color: tokenBtnHover.containsMouse ? theme.colors.panelHoverBg : theme.colors.panelBg
Expand Down
6 changes: 6 additions & 0 deletions apps/amm/qml/components/swap/TokenSelectorModal.qml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import "TokenVisuals.js" as TokenVisuals
Item {
id: root

// Stable hook so UI tests can read `visible` to know the picker is open.
objectName: "tokenSelectorModal"

property var theme
property var tokens: []
property string searchText: ""
Expand Down Expand Up @@ -160,6 +163,9 @@ Item {
})
delegate: TokenListItem {
width: tokenList.width
// Stable hook for UI tests to enumerate the list (QML
// file-defined types aren't matchable via findByType).
objectName: "tokenListItem"
theme: root.theme
tokenName: modelData.name
tokenSymbol: modelData.symbol
Expand Down
2 changes: 2 additions & 0 deletions apps/amm/qml/pages/SwapPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Item {

SwapCard {
id: swapCard
objectName: "swapCard"
Layout.alignment: Qt.AlignHCenter
theme: pageTheme
tokens: root.tokens
Expand Down Expand Up @@ -172,6 +173,7 @@ Item {

TransactionConfirmationDialog {
id: swapConfirmationDialog
objectName: "swapConfirmDialog"
title: qsTr("Confirm swap")
confirmText: qsTr("Confirm swap")
summary: swapConfirmationSummary
Expand Down
Loading
Loading