Migrate off the deprecated bitcoincash 0.29 API (Amount/Version types, FromHex -> FromStr/parse, non-exhaustive Network) across the indexer and tests. Add indexing of native token-A <-> token-B (TokenToken) AMM pools: - tokentoken_pool / tokentoken_pool_history_entry tables in cauldron.db, created via an always-run idempotent migration (no DB_VERSION bump, so existing databases upgrade in place) - block-path indexing sharing the cauldron write transaction and reorg undo, with creation/swap/withdrawal state tracking and reserve deltas - mempool indexing: electrum mempool.get filters on the tokentoken contract code (spends) and the CONJURE op_return hint (creations); first_seen_timestamp reconciles with mtp on confirmation - RPC endpoints /tokentoken/pool/active (pair lookup, order-insensitive) and /tokentoken/tokens Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
75 lines
2.3 KiB
Rust
75 lines
2.3 KiB
Rust
// Copyright (C) 2024-2026 Whiterun LLC
|
|
//
|
|
// This software is licensed under the GNU Affero General Public License (AGPL), version 3.0 or later.
|
|
// A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/agpl-3.0.html
|
|
|
|
use bitcoincash::TokenID;
|
|
use rocket::{get, State};
|
|
use serde_json::json;
|
|
use serde_json::Value;
|
|
|
|
use crate::db::DB;
|
|
use crate::rpc::err::{bad_request, db_error, ApiErrorCode, CachedApiResult};
|
|
use crate::rpc::response::{cached_ok, CACHE_NONE};
|
|
|
|
/// Get the latest Cauldron transactions, optionally filtered by token.
|
|
///
|
|
/// Status: Stable
|
|
///
|
|
/// - limit: Maximum number of results (default: 100, max: 10000)
|
|
/// - offset: Pagination offset (default: 0)
|
|
/// - token: The 32 byte token ID to filter by (optional)
|
|
///
|
|
/// Note: `blockhash` is `null` for unconfirmed transactions.
|
|
/// `timestamp_guess` is an estimate derived from block time.
|
|
///
|
|
/// **Response Example:**
|
|
/// ```json
|
|
/// [
|
|
/// {
|
|
/// "txid": "94a933a0fa55093a0965eb867f1b9cac2bb07488ced4825bc31f86c9371f76aa",
|
|
/// "blockhash": "000000000000000002b4e6c0a1f4b3d2e5c7f891a2b3c4d5e6f7a8b9c0d1e2f3",
|
|
/// "timestamp_guess": 1709468902
|
|
/// }
|
|
/// ]
|
|
/// ```
|
|
#[get("/tx/latest?<limit>&<offset>&<token>")]
|
|
pub async fn tx_latest(
|
|
limit: Option<usize>,
|
|
offset: Option<usize>,
|
|
token: Option<&str>,
|
|
conn: &State<DB>,
|
|
) -> CachedApiResult<Value> {
|
|
let limit = limit.unwrap_or(100).min(10000);
|
|
let offset = offset.unwrap_or(0);
|
|
|
|
let token = match token {
|
|
None => None,
|
|
Some(tokenhex) => match tokenhex.parse::<TokenID>() {
|
|
Ok(token) => Some(token),
|
|
Err(_) => {
|
|
return Err(bad_request(
|
|
ApiErrorCode::InvalidTokenId,
|
|
"Invalid token ID",
|
|
))
|
|
}
|
|
},
|
|
};
|
|
|
|
let txs = crate::db::cauldron::tx::latest(&conn.cauldron_r, limit, offset, token)
|
|
.await
|
|
.map_err(db_error)?;
|
|
|
|
let txs_json: Vec<Value> = txs
|
|
.into_iter()
|
|
.map(|tx| {
|
|
json!({
|
|
"txid": tx.0.to_string(),
|
|
"blockhash": tx.1.map(|blockhex| blockhex.to_string()),
|
|
"timestamp_guess": tx.2
|
|
})
|
|
})
|
|
.collect();
|
|
|
|
Ok(cached_ok(json!(txs_json), CACHE_NONE))
|
|
}
|