API call for latest transactions
This commit is contained in:
parent
eeab81c761
commit
528cc79725
6 changed files with 125 additions and 3 deletions
|
|
@ -1,3 +1,8 @@
|
|||
// Copyright (C) 2024 Riften Labs AS
|
||||
//
|
||||
// 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 std::time;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
// Copyright (C) 2024 Riften Labs AS
|
||||
//
|
||||
// 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 crate::{
|
||||
bcmr::{parsedbcmr::parse_all_bcmr_identities, utilurl::get_url},
|
||||
db::{
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use anyhow::Result;
|
||||
use bitcoin_hashes::hex::ToHex;
|
||||
use bitcoincash::{BlockHash, Txid};
|
||||
use bitcoin_hashes::hex::{FromHex, ToHex};
|
||||
use bitcoincash::{BlockHash, TokenID, Txid};
|
||||
use rusqlite::{params, Connection};
|
||||
|
||||
pub fn create_table(conn: &Connection) {
|
||||
|
|
@ -49,3 +49,59 @@ pub fn insert_mempool_tx(conn: &rusqlite::Connection, txid: &Txid) -> Result<()>
|
|||
conn.execute(sql, params![&txid.to_hex(), current_timestamp])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn latest(
|
||||
conn: &rusqlite::Connection,
|
||||
limit: usize,
|
||||
offset: usize,
|
||||
token_id: Option<TokenID>,
|
||||
) -> Result<Vec<(Txid, Option<BlockHash>, u64)>, rusqlite::Error> {
|
||||
let (sql, params) = match token_id {
|
||||
Some(tid) => (
|
||||
"SELECT DISTINCT tx.txid, tx.blockhash, tx.mtp_timestamp, tx.first_seen_timestamp
|
||||
FROM tx
|
||||
JOIN utxo_funding ON tx.txid = utxo_funding.txid
|
||||
WHERE utxo_funding.token_id = ?
|
||||
ORDER BY COALESCE(tx.first_seen_timestamp, tx.mtp_timestamp) DESC
|
||||
LIMIT ? OFFSET ?",
|
||||
params![tid.to_hex(), limit, offset],
|
||||
),
|
||||
None => (
|
||||
"SELECT tx.txid, tx.blockhash, tx.mtp_timestamp, tx.first_seen_timestamp
|
||||
FROM tx
|
||||
ORDER BY COALESCE(tx.first_seen_timestamp, tx.mtp_timestamp) DESC
|
||||
LIMIT ? OFFSET ?",
|
||||
params![limit, offset],
|
||||
),
|
||||
};
|
||||
|
||||
let mut stmt = conn.prepare(sql)?;
|
||||
let tx_iter = stmt.query_map(params, |row| {
|
||||
let txid_hex: String = row.get(0)?;
|
||||
let blockhash_hex: Option<String> = row.get(1)?;
|
||||
let mtp_timestamp: Option<u64> = row.get(2).ok(); // Handle potential NULL
|
||||
let first_seen_timestamp: Option<u64> = row.get(3).ok(); // Handle potential NULL
|
||||
|
||||
// Use first_seen_timestamp if available, otherwise mtp_timestamp, or return an error if both are NULL
|
||||
let timestamp = first_seen_timestamp.or(mtp_timestamp).ok_or_else(|| {
|
||||
rusqlite::Error::InvalidColumnType(
|
||||
2,
|
||||
"timestamp".to_string(),
|
||||
rusqlite::types::Type::Null,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok((
|
||||
Txid::from_hex(&txid_hex).expect("Invalid Txid hex"),
|
||||
blockhash_hex.map(|hex| BlockHash::from_hex(&hex).expect("Invalid BlockHash hex")),
|
||||
timestamp,
|
||||
))
|
||||
})?;
|
||||
|
||||
let mut txs = Vec::with_capacity(limit);
|
||||
for tx in tx_iter {
|
||||
txs.push(tx?);
|
||||
}
|
||||
|
||||
Ok(txs)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,7 +253,8 @@ fn launch() -> _ {
|
|||
rpc::contract::contract_count_token,
|
||||
rpc::contract::contract_count_all,
|
||||
rpc::contract::contract_volume,
|
||||
rpc::user::unique_addresses
|
||||
rpc::user::unique_addresses,
|
||||
rpc::tx::tx_latest,
|
||||
],
|
||||
)
|
||||
.mount(
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ pub mod pool;
|
|||
pub mod price;
|
||||
pub mod tokens;
|
||||
pub mod tvl;
|
||||
pub mod tx;
|
||||
pub mod user;
|
||||
|
||||
pub type ResponseCache = Arc<Mutex<HashMap<String, (i64 /* timestamp */, Vec<serde_json::Value>)>>>;
|
||||
|
|
|
|||
54
src/rpc/tx.rs
Normal file
54
src/rpc/tx.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (C) 2024 Riften Labs AS
|
||||
//
|
||||
// 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 bitcoin_hashes::hex::FromHex;
|
||||
use bitcoincash::TokenID;
|
||||
use rocket::{get, http::Status, response::status::Custom, serde::json::Json, State};
|
||||
use serde_json::json;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::db::DB;
|
||||
|
||||
#[get("/tx/latest?<limit>&<offset>&<token>")]
|
||||
pub fn tx_latest(
|
||||
limit: Option<usize>,
|
||||
offset: Option<usize>,
|
||||
token: Option<&str>,
|
||||
conn: &State<DB>,
|
||||
) -> Result<Json<Value>, Custom<String>> {
|
||||
let db = conn.cauldron_r.get().map_err(|_| {
|
||||
Custom(
|
||||
Status::InternalServerError,
|
||||
"Failed to get DB connection".into(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let limit = limit.unwrap_or(100).min(10000);
|
||||
let offset = offset.unwrap_or(0);
|
||||
|
||||
let token = match token {
|
||||
None => None,
|
||||
Some(tokenhex) => match TokenID::from_hex(tokenhex) {
|
||||
Ok(token) => Some(token),
|
||||
Err(_) => return Err(Custom(Status::BadRequest, "Invalid token ID".into())),
|
||||
},
|
||||
};
|
||||
|
||||
let txs = crate::db::cauldron::tx::latest(&db, limit, offset, token)
|
||||
.map_err(|e| Custom(Status::InternalServerError, format!("Error: {}", e)))?;
|
||||
|
||||
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(Json(json!(txs_json)))
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue