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.
This commit is contained in:
r4bbit
2026-08-13 16:24:15 +02:00
parent cbb75c38fd
commit d43122f746
9 changed files with 102 additions and 66 deletions
+3 -3
View File
@@ -30,7 +30,7 @@ Rectangle {
property string editingSide: "sell"
property real slippageTolerancePercent: 0.5
// ── Pool resolution (backend.resolvePool) ───────────────────────────────
// ── Pool resolution (backend.resolvePoolAccount) ───────────────────────────────
// Existence and fee drive the UI; the swap quotes read the pool and
// price/orient the swap server-side, so the client no longer prices against
// the reserves. The raw reserves are still surfaced (observability only, not
@@ -128,13 +128,13 @@ Rectangle {
}
root.poolLoading = true
logos.watch(root.backend.resolvePool(reqSell, reqBuy),
logos.watch(root.backend.resolvePoolAccount(reqSell, reqBuy),
function (pool) {
if (isStale())
return
root.poolLoading = false
root.poolResolved = true
root.poolExists = !!(pool && pool.exists)
root.poolExists = !!(pool && pool.status === "ok")
root.poolReserveA = (pool && pool.reserveA) || "0"
root.poolReserveB = (pool && pool.reserveB) || "0"
// feeBps === 0 is a legitimate zero-fee pool; only fall back
+9 -9
View File
@@ -84,22 +84,22 @@ QtObject {
return
}
// Route on pool existence (read the pool account), like the swap card. resolvePool
// returns the reserves oriented to our requested token order (reserveA is tokenAId's).
root.runtime.watch(root.backend.resolvePool(built.request.tokenAId, built.request.tokenBId),
// Route on pool existence (read the pool account), like the swap card.
// resolvePoolAccount returns status:"ok" with reserves oriented to our requested token
// order (reserveA is tokenAId's), or status:"error" (no_pool / hard failure).
root.runtime.watch(root.backend.resolvePoolAccount(built.request.tokenAId, built.request.tokenBId),
function(pool) {
if (serial !== root.quoteSerial)
return
if (pool && pool.exists) {
if (pool && pool.status === "ok") {
root.poolExists = true
root.requestAddQuote(serial, built, pool)
return
}
// resolvePool returns exists:false for BOTH the normal "no pool yet" case and
// hard failures (no_program_bin, amm_not_initialized, bad_config). Only the
// former — an empty error or no_pool — is a create-pool signal; surface any other
// pool.error as a quote error instead of masking it as a create quote (which
// would hide the backend failure and enable the wrong flow).
// A status:"error" result is EITHER the normal "no pool yet" case (error
// "no_pool") or a hard failure (no_program_bin, amm_not_initialized, bad_config).
// Only the former is a create-pool signal; surface any other pool.error as a quote
// error instead of masking it as a create quote (which would enable the wrong flow).
var poolError = pool ? String(pool.error || "") : ""
if (poolError.length === 0 || poolError === "no_pool") {
root.poolExists = false
+2 -2
View File
@@ -177,9 +177,9 @@ void AmmUiBackend::syncWalletState()
setSequencerReachable(state.sequencerReachable);
}
QVariantMap AmmUiBackend::resolvePool(QString defAHex, QString defBHex)
QVariantMap AmmUiBackend::resolvePoolAccount(QString defAHex, QString defBHex)
{
return m_logos->amm_module.resolvePool(defAHex, defBHex);
return m_logos->amm_module.resolvePoolAccount(defAHex, defBHex);
}
QString AmmUiBackend::swapExactInput(QString defAHex, QString defBHex, QString userInputHoldingHex,
+1 -1
View File
@@ -54,7 +54,7 @@ public slots:
void disconnectWallet() override;
// AMM — all forwarded to the amm_module core module.
QVariantMap resolvePool(QString defAHex, QString defBHex) override;
QVariantMap resolvePoolAccount(QString defAHex, QString defBHex) override;
QString swapExactInput(QString defAHex, QString defBHex, QString userInputHoldingHex,
QString userOutputHoldingHex, QString amountInDecimal,
QString minOutDecimal, QString deadlineDecimal) override;
+8 -7
View File
@@ -41,13 +41,14 @@ class AmmUiBackend
SLOT(void disconnectWallet())
// AMM
// Derives the AMM pool's PDAs (config/pool/vaults/current-tick) from the
// deployed AMM program binary (see AMM_PROGRAM_BIN — a RISC Zero
// ProgramBinary .bin, not a raw ELF) and reads the pool's
// on-chain reserves. Returns `{ exists: false }` if the AMM program bin
// isn't configured, the AMM isn't initialized, or the pool has no
// liquidity yet.
SLOT(QVariantMap resolvePool(QString defAHex, QString defBHex))
// Derives the AMM pool PDA for (defAHex, defBHex) from the deployed AMM program
// binary (see AMM_PROGRAM_BIN — a RISC Zero ProgramBinary .bin, not a raw ELF)
// and reads/decodes the pool account. On success `{ status:"ok", error:"", poolId,
// defAHex, defBHex, vaultAId, vaultBId, lpDefinitionId, reserveA, reserveB,
// liquiditySupply, feeBps }` (A/B oriented to the requested order); otherwise
// `{ status:"error", error:<code> }` — `no_pool` for the ordinary "no pool /
// no liquidity yet" state, else no_program_bin / amm_not_initialized / bad_config.
SLOT(QVariantMap resolvePoolAccount(QString defAHex, QString defBHex))
// Submits a real on-chain SwapExactInput transaction against the pool for
// (defAHex, defBHex). amountInDecimal/minOutDecimal are decimal-string
// u128 amounts in base units; deadlineDecimal is a decimal-string u64 unix