From 9d0eaff22cc7b55488f70293af3c76ebfa60fb17 Mon Sep 17 00:00:00 2001 From: Dagur Valberg Johannsson Date: Wed, 17 Jun 2026 13:16:43 +0200 Subject: [PATCH] tokentoken: add GET /tokentoken/pools (list all active pools) List every active TokenToken pool (no pair filter) so the frontend can present the set of pooled token pairs without probing each candidate pair. Mirrors db_active_pools_for_pair minus the pair WHERE clause; served at a distinct /pools path (the param'd /pool/active already matches param-less requests via its Option guards, so reusing it would collide). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/db/cauldron/tokentoken.rs | 54 +++++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/rpc/tokentoken.rs | 14 ++++++++- 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/db/cauldron/tokentoken.rs b/src/db/cauldron/tokentoken.rs index 95b7f51..473c8f0 100644 --- a/src/db/cauldron/tokentoken.rs +++ b/src/db/cauldron/tokentoken.rs @@ -439,6 +439,60 @@ pub async fn db_active_pools_for_pair( Ok(out) } +/// Every active pool (`withdrawn_in_txid IS NULL`) at its latest state, with no +/// pair filter. Drives the frontend pooled-pair selector, which needs the full +/// set of pools to show which token pairs have liquidity (without probing each +/// candidate pair individually). +pub async fn db_all_active_pools(pool: &SqlitePool) -> Result> { + let rows = sqlx::query( + "SELECT p.creation_utxo, p.nft_owner, p.token_a_id, p.token_b_id, p.fee_rate, p.min_fee, + phe.reserve_a, phe.reserve_b, phe.main_sats, phe.storage_sats, + phe.txid, phe.tx_pos, phe.storage_tx_pos + FROM tokentoken_pool p + JOIN tokentoken_pool_history_entry phe ON phe.pool = p.creation_utxo + AND phe.sequence = ( + SELECT MAX(sequence) FROM tokentoken_pool_history_entry WHERE pool = p.creation_utxo + ) + WHERE p.withdrawn_in_txid IS NULL", + ) + .fetch_all(pool) + .await?; + + let mut out = Vec::with_capacity(rows.len()); + for row in rows { + let pool_blob: Vec = row.get(0); + let nft_owner_blob: Vec = row.get(1); + let token_a_blob: Vec = row.get(2); + let token_b_blob: Vec = row.get(3); + let fee_rate: i64 = row.get(4); + let min_fee: i64 = row.get(5); + let reserve_a: i64 = row.get(6); + let reserve_b: i64 = row.get(7); + let main_sats: i64 = row.get(8); + let storage_sats: i64 = row.get(9); + let txid_blob: Vec = row.get(10); + let tx_pos: i64 = row.get(11); + let storage_tx_pos: i64 = row.get(12); + + out.push(ActiveTokenTokenPool { + pool_id: blob_to_display_hex::(&pool_blob)?, + nft_owner: hex::encode(&nft_owner_blob), + token_a_id: blob_to_display_hex::(&token_a_blob)?, + token_b_id: blob_to_display_hex::(&token_b_blob)?, + reserve_a: reserve_a as u64, + reserve_b: reserve_b as u64, + fee_rate: fee_rate as u32, + min_fee: min_fee as u64, + main_sats: main_sats as u64, + storage_sats: storage_sats as u64, + txid: blob_to_display_hex::(&txid_blob)?, + main_vout: tx_pos as u32, + storage_vout: storage_tx_pos as u32, + }); + } + Ok(out) +} + /// Distinct token ids that appear in at least one active TokenToken pool. pub async fn db_pair_tokens(pool: &SqlitePool) -> Result> { let rows = sqlx::query( diff --git a/src/main.rs b/src/main.rs index 1f1fcaf..894f123 100644 --- a/src/main.rs +++ b/src/main.rs @@ -630,6 +630,7 @@ async fn launch() -> _ { "/tokentoken", routes![ rpc::tokentoken::list_active_pools, + rpc::tokentoken::list_all_pools, rpc::tokentoken::list_tokens, ], ) diff --git a/src/rpc/tokentoken.rs b/src/rpc/tokentoken.rs index c92c43a..d128cf6 100644 --- a/src/rpc/tokentoken.rs +++ b/src/rpc/tokentoken.rs @@ -7,7 +7,7 @@ use crate::{ db::{ - cauldron::tokentoken::{db_active_pools_for_pair, db_pair_tokens}, + cauldron::tokentoken::{db_active_pools_for_pair, db_all_active_pools, db_pair_tokens}, DB, }, rpc::err::{bad_request, db_error, ApiErrorCode, CachedApiResult}, @@ -67,3 +67,15 @@ pub async fn list_tokens(conn: &State) -> CachedApiResult { let tokens = db_pair_tokens(&conn.cauldron_r).await.map_err(db_error)?; Ok(cached_ok(json!({ "tokens": tokens }), CACHE_AGGREGATE)) } + +/// List ALL active TokenToken pools, regardless of pair — every pool at its +/// latest state. Drives the frontend pooled-pair selector, so it can present the +/// token pairs that have liquidity (with which token is A/main vs B/storage) +/// without probing each candidate pair individually. +#[get("/pools")] +pub async fn list_all_pools(conn: &State) -> CachedApiResult { + let active = db_all_active_pools(&conn.cauldron_r) + .await + .map_err(db_error)?; + Ok(cached_ok(json!({ "active": active }), CACHE_NONE)) +}