2024-10-31 10:55:29 +00:00
|
|
|
|
// 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 anyhow::{Context, Result};
|
|
|
|
|
|
use bitcoin_hashes::hex::FromHex;
|
|
|
|
|
|
use bitcoincash::TokenID;
|
|
|
|
|
|
use r2d2::Pool;
|
|
|
|
|
|
use r2d2_sqlite::SqliteConnectionManager;
|
|
|
|
|
|
use rayon::prelude::*;
|
|
|
|
|
|
use rusqlite::params;
|
|
|
|
|
|
use rusqlite::Connection;
|
|
|
|
|
|
|
|
|
|
|
|
type TokenBasicInfo = (String, Option<String>, Option<String>);
|
|
|
|
|
|
type TokenVolumeInfo = (String, Option<String>, Option<String>, u64);
|
|
|
|
|
|
|
2025-09-05 13:53:10 +00:00
|
|
|
|
use crate::db::bcmr::{get_token_bcmr, get_well_known_bcmr};
|
|
|
|
|
|
use crate::db::cauldron::tokenlist::db_utils::order_clause;
|
|
|
|
|
|
use crate::db::cauldron::tokenlist::db_utils::CachedSort;
|
|
|
|
|
|
use crate::db::cauldron::tokenlist::list_cached::TokenListItemCached;
|
|
|
|
|
|
|
2024-10-31 10:55:29 +00:00
|
|
|
|
fn search_bcmr(bcmr_conn: &Connection, search_query: &str) -> Result<Vec<TokenBasicInfo>> {
|
|
|
|
|
|
let is_full_hex_token_id = TokenID::from_hex(search_query).is_ok();
|
|
|
|
|
|
// Fetch the latest (highest) record that has BCMR data.
|
|
|
|
|
|
let sql = if is_full_hex_token_id {
|
|
|
|
|
|
"SELECT token_id, name, symbol
|
|
|
|
|
|
FROM (
|
|
|
|
|
|
SELECT a.token_id, b.name, b.symbol, a.height,
|
|
|
|
|
|
ROW_NUMBER() OVER (PARTITION BY a.token_id ORDER BY a.height DESC) AS rn
|
|
|
|
|
|
FROM auth_chain_entry a
|
|
|
|
|
|
LEFT JOIN bcmr_data b ON a.utxo = b.utxo
|
|
|
|
|
|
) subquery
|
|
|
|
|
|
WHERE rn = 1
|
|
|
|
|
|
AND (token_id = ?1);"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
"SELECT token_id, name, symbol
|
|
|
|
|
|
FROM (
|
|
|
|
|
|
SELECT a.token_id, b.name, b.symbol, a.height,
|
|
|
|
|
|
ROW_NUMBER() OVER (PARTITION BY a.token_id ORDER BY a.height DESC) AS rn
|
|
|
|
|
|
FROM auth_chain_entry a
|
|
|
|
|
|
LEFT JOIN bcmr_data b ON a.utxo = b.utxo
|
|
|
|
|
|
WHERE a.bcmr_data IS NOT NULL
|
|
|
|
|
|
) subquery
|
|
|
|
|
|
WHERE rn = 1
|
|
|
|
|
|
AND (name LIKE ?1 OR symbol LIKE ?1);"
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let mut statement = bcmr_conn.prepare(sql)?;
|
|
|
|
|
|
let search_pattern = if is_full_hex_token_id {
|
|
|
|
|
|
search_query.to_string()
|
|
|
|
|
|
} else {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
format!("%{search_query}%")
|
2024-10-31 10:55:29 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let mut bcmr_data: Vec<TokenBasicInfo> = Vec::new();
|
|
|
|
|
|
let mut query = statement.query([search_pattern.as_str()])?;
|
|
|
|
|
|
|
|
|
|
|
|
while let Some(result) = query.next()? {
|
|
|
|
|
|
let token_id: String = result.get(0)?;
|
|
|
|
|
|
let name: Option<String> = result.get(1)?;
|
|
|
|
|
|
let ticker: Option<String> = result.get(2)?;
|
|
|
|
|
|
bcmr_data.push((token_id, name, ticker));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Ok(bcmr_data)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn search_crc20(crc20_conn: &Connection, search_query: &str) -> Result<Vec<TokenBasicInfo>> {
|
|
|
|
|
|
let is_full_hex_token_id = TokenID::from_hex(search_query).is_ok();
|
|
|
|
|
|
|
|
|
|
|
|
let sql = if is_full_hex_token_id {
|
|
|
|
|
|
// Search by token_id if it's a full hex token ID
|
|
|
|
|
|
"
|
|
|
|
|
|
SELECT token_id, name, symbol
|
|
|
|
|
|
FROM crc20
|
|
|
|
|
|
WHERE token_id = ?1;
|
|
|
|
|
|
"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Only search by name or symbol otherwise
|
|
|
|
|
|
"
|
|
|
|
|
|
SELECT token_id, name, symbol
|
|
|
|
|
|
FROM crc20
|
|
|
|
|
|
WHERE name LIKE ?1 OR symbol LIKE ?1;
|
|
|
|
|
|
"
|
|
|
|
|
|
};
|
|
|
|
|
|
let mut statement = crc20_conn.prepare(sql)?;
|
|
|
|
|
|
let search_pattern = if is_full_hex_token_id {
|
|
|
|
|
|
search_query.to_string()
|
|
|
|
|
|
} else {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
format!("%{search_query}%")
|
2024-10-31 10:55:29 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let mut crc20_data: Vec<TokenBasicInfo> = Vec::new();
|
|
|
|
|
|
let mut query = statement.query([&search_pattern])?;
|
|
|
|
|
|
|
|
|
|
|
|
while let Some(result) = query.next()? {
|
|
|
|
|
|
let token_id: String = result.get(0)?;
|
|
|
|
|
|
let name: Option<String> = result.get(1)?;
|
|
|
|
|
|
let ticker: Option<String> = result.get(2)?;
|
|
|
|
|
|
crc20_data.push((token_id, name, ticker));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Ok(crc20_data)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn token_volume(
|
|
|
|
|
|
cauldron_pool: Pool<SqliteConnectionManager>,
|
|
|
|
|
|
tokens: Vec<TokenBasicInfo>,
|
|
|
|
|
|
) -> Result<Vec<TokenVolumeInfo>> {
|
|
|
|
|
|
let result: Vec<TokenVolumeInfo> = tokens
|
2025-08-18 22:30:10 +02:00
|
|
|
|
.into_par_iter()
|
|
|
|
|
|
.map(|(token_id, name, ticker)| {
|
|
|
|
|
|
let conn = cauldron_pool
|
|
|
|
|
|
.get()
|
|
|
|
|
|
.context("Failed to get connection from pool")?;
|
|
|
|
|
|
|
|
|
|
|
|
let mut statement = conn
|
|
|
|
|
|
.prepare(
|
|
|
|
|
|
"
|
2024-10-31 10:55:29 +00:00
|
|
|
|
WITH TradeData AS (
|
|
|
|
|
|
SELECT
|
2025-08-15 17:48:25 +02:00
|
|
|
|
p.token_id,
|
|
|
|
|
|
ABS(phe.sats_delta) as trade_volume
|
|
|
|
|
|
FROM pool_history_entry phe
|
|
|
|
|
|
JOIN pool p ON phe.pool = p.creation_utxo
|
|
|
|
|
|
JOIN tx ON phe.txid = tx.txid
|
2025-08-18 22:30:10 +02:00
|
|
|
|
WHERE tx.effective_timestamp >= (strftime('%s', 'now') - 2592000)
|
2025-08-15 17:48:25 +02:00
|
|
|
|
AND p.token_id = ?
|
2024-10-31 10:55:29 +00:00
|
|
|
|
)
|
|
|
|
|
|
SELECT
|
|
|
|
|
|
token_id,
|
|
|
|
|
|
COALESCE(SUM(trade_volume), 0) as total_trade_volume
|
|
|
|
|
|
FROM TradeData;
|
|
|
|
|
|
",
|
2025-08-18 22:30:10 +02:00
|
|
|
|
)
|
|
|
|
|
|
.context("Failed to prepare statement")?;
|
2024-10-31 10:55:29 +00:00
|
|
|
|
|
2025-08-18 22:30:10 +02:00
|
|
|
|
let trade_volume: u64 = statement
|
|
|
|
|
|
.query_row(params![&token_id], |row| row.get(1))
|
|
|
|
|
|
.unwrap_or(0);
|
2024-10-31 10:55:29 +00:00
|
|
|
|
|
2025-08-18 22:30:10 +02:00
|
|
|
|
// Return result as Ok tuple for successful case
|
|
|
|
|
|
Ok((token_id, name, ticker, trade_volume))
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect::<Result<Vec<_>>>()?; // Collect results into a Vec, propagating errors
|
2024-10-31 10:55:29 +00:00
|
|
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn search_tokens_by_volume(
|
|
|
|
|
|
cauldron_pool: &Pool<SqliteConnectionManager>,
|
|
|
|
|
|
bcmr_conn: &Connection,
|
|
|
|
|
|
crc20_conn: &Connection,
|
|
|
|
|
|
search_query: &str,
|
|
|
|
|
|
) -> Result<Vec<TokenVolumeInfo>> {
|
|
|
|
|
|
let bcmr_data = search_bcmr(bcmr_conn, search_query)?;
|
|
|
|
|
|
let crc20_data = search_crc20(crc20_conn, search_query)?;
|
|
|
|
|
|
let combined_tokens = [bcmr_data, crc20_data].concat();
|
|
|
|
|
|
|
|
|
|
|
|
let mut result = token_volume(cauldron_pool.clone(), combined_tokens)?;
|
|
|
|
|
|
|
|
|
|
|
|
result.sort_by(|a, b| b.3.cmp(&a.3));
|
|
|
|
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-05 13:53:10 +00:00
|
|
|
|
/// Search the cached list by token_id (exact), or display_name/symbol (LIKE),
|
|
|
|
|
|
/// then sort by *whatever* using CachedSort.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - `query`:
|
|
|
|
|
|
/// - empty/whitespace => returns everything
|
|
|
|
|
|
/// - full 32-byte hex => exact token_id match
|
|
|
|
|
|
/// - otherwise => display_name LIKE or display_symbol LIKE
|
|
|
|
|
|
/// - `filter_zero_tvl`: skip rows with tvl_sats=0 (usually you want `true`)
|
|
|
|
|
|
pub fn db_search_tokens_cached(
|
|
|
|
|
|
cauldron_conn: &Connection,
|
|
|
|
|
|
bcmr_conn: &Connection,
|
|
|
|
|
|
q: &str,
|
|
|
|
|
|
sort: CachedSort,
|
|
|
|
|
|
limit: usize,
|
|
|
|
|
|
offset: usize,
|
|
|
|
|
|
) -> Result<Vec<TokenListItemCached>> {
|
|
|
|
|
|
use rusqlite::types::ToSql;
|
|
|
|
|
|
|
|
|
|
|
|
let order_sql = order_clause(sort);
|
|
|
|
|
|
|
|
|
|
|
|
enum Filter {
|
|
|
|
|
|
None,
|
|
|
|
|
|
Id(String),
|
|
|
|
|
|
Pat(String),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let mut where_sql = String::new();
|
|
|
|
|
|
let filter = {
|
|
|
|
|
|
let q = q.trim();
|
|
|
|
|
|
if q.is_empty() {
|
|
|
|
|
|
Filter::None
|
|
|
|
|
|
} else if TokenID::from_hex(q).is_ok() {
|
|
|
|
|
|
where_sql.push_str("WHERE token_id = :id");
|
|
|
|
|
|
Filter::Id(q.to_lowercase())
|
|
|
|
|
|
} else {
|
|
|
|
|
|
where_sql.push_str(
|
|
|
|
|
|
"WHERE (display_name LIKE :pat COLLATE NOCASE OR display_symbol LIKE :pat COLLATE NOCASE)"
|
|
|
|
|
|
);
|
|
|
|
|
|
Filter::Pat(format!("%{}%", q))
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let sql = format!(
|
|
|
|
|
|
"SELECT
|
|
|
|
|
|
token_id,
|
|
|
|
|
|
trade_volume,
|
|
|
|
|
|
tvl_sats,
|
|
|
|
|
|
tvl_tokens,
|
|
|
|
|
|
score,
|
|
|
|
|
|
price_now,
|
|
|
|
|
|
price_24h,
|
|
|
|
|
|
price_7d,
|
|
|
|
|
|
change_24h_bp,
|
|
|
|
|
|
change_7d_bp,
|
|
|
|
|
|
display_name,
|
|
|
|
|
|
display_symbol,
|
|
|
|
|
|
price_now_usd,
|
|
|
|
|
|
price_24h_usd,
|
|
|
|
|
|
price_7d_usd,
|
|
|
|
|
|
change_24h_usd_bp,
|
|
|
|
|
|
change_7d_usd_bp,
|
|
|
|
|
|
apy_30d_bp,
|
|
|
|
|
|
score_rank
|
|
|
|
|
|
FROM cached_token_metrics
|
|
|
|
|
|
{where_sql}
|
|
|
|
|
|
{order_sql}
|
|
|
|
|
|
LIMIT :limit OFFSET :offset"
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
let mut stmt = cauldron_conn.prepare(&sql)?;
|
|
|
|
|
|
|
|
|
|
|
|
let limit_i64 = limit as i64;
|
|
|
|
|
|
let offset_i64 = offset as i64;
|
|
|
|
|
|
let mut params_vec: Vec<(&str, &dyn ToSql)> = Vec::with_capacity(3);
|
|
|
|
|
|
|
|
|
|
|
|
match &filter {
|
|
|
|
|
|
Filter::Id(s) => params_vec.push((":id", s as &dyn ToSql)),
|
|
|
|
|
|
Filter::Pat(s) => params_vec.push((":pat", s as &dyn ToSql)),
|
|
|
|
|
|
Filter::None => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
params_vec.push((":limit", &limit_i64));
|
|
|
|
|
|
params_vec.push((":offset", &offset_i64));
|
|
|
|
|
|
|
|
|
|
|
|
let mut rows = stmt.query(¶ms_vec[..])?;
|
|
|
|
|
|
let mut out = Vec::with_capacity(limit);
|
|
|
|
|
|
|
|
|
|
|
|
while let Some(row) = rows.next()? {
|
|
|
|
|
|
let token_id: String = row.get(0)?;
|
|
|
|
|
|
let trade_volume: u64 = row.get(1)?;
|
|
|
|
|
|
let tvl_sats: u64 = row.get::<_, i64>(2)? as u64;
|
|
|
|
|
|
let tvl_tokens: u64 = row.get::<_, i64>(3)? as u64;
|
|
|
|
|
|
let score: i64 = row.get(4)?;
|
|
|
|
|
|
let price_now: f64 = row.get(5)?;
|
|
|
|
|
|
let price_24h: f64 = row.get(6)?;
|
|
|
|
|
|
let price_7d: f64 = row.get(7)?;
|
|
|
|
|
|
let change_24h_bp: i64 = row.get(8)?;
|
|
|
|
|
|
let change_7d_bp: i64 = row.get(9)?;
|
|
|
|
|
|
let display_name: String = row.get(10)?;
|
|
|
|
|
|
let display_symbol: String = row.get(11)?;
|
|
|
|
|
|
let price_now_usd: f64 = row.get(12)?;
|
|
|
|
|
|
let price_24h_usd: f64 = row.get(13)?;
|
|
|
|
|
|
let price_7d_usd: f64 = row.get(14)?;
|
|
|
|
|
|
let change_24h_usd_bp: i64 = row.get(15)?;
|
|
|
|
|
|
let change_7d_usd_bp: i64 = row.get(16)?;
|
|
|
|
|
|
let apy_30d_bp: i64 = row.get(17)?;
|
|
|
|
|
|
let score_rank: i64 = row.get(18)?;
|
|
|
|
|
|
let bcmr = get_token_bcmr(bcmr_conn, &token_id)?;
|
|
|
|
|
|
let bcmr_well_known = get_well_known_bcmr(bcmr_conn, &token_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
out.push(TokenListItemCached {
|
|
|
|
|
|
token_id,
|
|
|
|
|
|
trade_volume,
|
|
|
|
|
|
tvl_sats,
|
|
|
|
|
|
tvl_tokens,
|
|
|
|
|
|
bcmr,
|
|
|
|
|
|
bcmr_well_known,
|
|
|
|
|
|
trade_count: 0,
|
|
|
|
|
|
score, // <-- NEW
|
|
|
|
|
|
score_rank,
|
|
|
|
|
|
price_now,
|
|
|
|
|
|
price_24h,
|
|
|
|
|
|
price_7d,
|
|
|
|
|
|
change_24h_bp,
|
|
|
|
|
|
change_7d_bp,
|
|
|
|
|
|
display_name,
|
|
|
|
|
|
display_symbol,
|
|
|
|
|
|
price_now_usd,
|
|
|
|
|
|
price_24h_usd,
|
|
|
|
|
|
price_7d_usd,
|
|
|
|
|
|
change_24h_usd_bp,
|
|
|
|
|
|
change_7d_usd_bp,
|
|
|
|
|
|
apy_30d_bp,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Ok(out)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-31 10:55:29 +00:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests {
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
use crate::bcmr::parsedbcmr::{FileMeta, ParsedBCMR, Token, Uris};
|
|
|
|
|
|
use crate::db::bcmr::{
|
|
|
|
|
|
insert_authheader, insert_bcmr_data, prepare_tables as bcmr_prepare_tables,
|
|
|
|
|
|
};
|
|
|
|
|
|
use crate::db::cauldron::pool::{dummy_init_seq, insert_new_pool, insert_pool_history_entry};
|
|
|
|
|
|
use crate::db::cauldron::prepare_tables as cauldron_prepare_tables;
|
2025-09-05 13:53:10 +00:00
|
|
|
|
use crate::db::cauldron::tokenlist::db_utils::create_cached_token_metrics_table;
|
2024-10-31 10:55:29 +00:00
|
|
|
|
use crate::db::cauldron::tx::{insert_block_tx, insert_mempool_tx};
|
|
|
|
|
|
use crate::db::cauldron::utxo_funding::insert_utxo_funding;
|
|
|
|
|
|
use crate::db::crc20::prepare_tables as crc20_prepare_tables;
|
2025-09-05 13:53:10 +00:00
|
|
|
|
use crate::timeutil::time_now;
|
2024-10-31 10:55:29 +00:00
|
|
|
|
use crate::utiltest::mock_db_pool;
|
|
|
|
|
|
use bitcoin_hashes::hex::{FromHex, ToHex};
|
|
|
|
|
|
use bitcoin_hashes::Hash;
|
|
|
|
|
|
use bitcoincash::{BlockHash, PubkeyHash, TokenID, Txid};
|
|
|
|
|
|
use riftenlabs_defi::cauldron::ParsedContract;
|
|
|
|
|
|
use riftenlabs_defi::chainutil::OutPointHash;
|
|
|
|
|
|
use rusqlite::{params, Connection};
|
|
|
|
|
|
|
|
|
|
|
|
fn setup_mock_db(conn: &Connection) {
|
|
|
|
|
|
cauldron_prepare_tables(conn);
|
|
|
|
|
|
bcmr_prepare_tables(conn);
|
|
|
|
|
|
crc20_prepare_tables(conn);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-05 13:53:10 +00:00
|
|
|
|
fn setup_cached_tables(conn: &Connection) {
|
|
|
|
|
|
// you already call the other prepare_* in setup_mock_db; add this:
|
|
|
|
|
|
create_cached_token_metrics_table(conn).expect("create cached_token_metrics");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn seed_cached_row_simple(
|
|
|
|
|
|
conn: &Connection,
|
|
|
|
|
|
token_hex: &str,
|
|
|
|
|
|
display_name: &str,
|
|
|
|
|
|
display_symbol: &str,
|
|
|
|
|
|
tvl_sats: i64,
|
|
|
|
|
|
tvl_tokens: i64,
|
|
|
|
|
|
trade_volume: i64,
|
|
|
|
|
|
score: i64,
|
|
|
|
|
|
price_now: f64,
|
|
|
|
|
|
price_24h: f64,
|
|
|
|
|
|
price_7d: f64,
|
|
|
|
|
|
ch24_bp: i64,
|
|
|
|
|
|
ch7d_bp: i64,
|
|
|
|
|
|
price_now_usd: f64,
|
|
|
|
|
|
price_24h_usd: f64,
|
|
|
|
|
|
price_7d_usd: f64,
|
|
|
|
|
|
ch24_usd_bp: i64,
|
|
|
|
|
|
ch7d_usd_bp: i64,
|
|
|
|
|
|
apy_bp: i64,
|
|
|
|
|
|
) {
|
|
|
|
|
|
conn.execute(
|
|
|
|
|
|
r#"
|
|
|
|
|
|
INSERT INTO cached_token_metrics
|
|
|
|
|
|
(token_id, trade_volume, tvl_sats, tvl_tokens, score,
|
|
|
|
|
|
display_name, display_symbol,
|
|
|
|
|
|
price_now, price_24h, price_7d, change_24h_bp, change_7d_bp,
|
|
|
|
|
|
price_now_usd, price_24h_usd, price_7d_usd, change_24h_usd_bp, change_7d_usd_bp,
|
|
|
|
|
|
apy_30d_bp, updated_at)
|
|
|
|
|
|
VALUES (?1, ?2, ?3, ?4, ?5,
|
|
|
|
|
|
?6, ?7,
|
|
|
|
|
|
?8, ?9, ?10, ?11, ?12,
|
|
|
|
|
|
?13, ?14, ?15, ?16, ?17,
|
|
|
|
|
|
?18, strftime('%s','now'))
|
|
|
|
|
|
"#,
|
|
|
|
|
|
params![
|
|
|
|
|
|
token_hex,
|
|
|
|
|
|
trade_volume,
|
|
|
|
|
|
tvl_sats,
|
|
|
|
|
|
tvl_tokens,
|
|
|
|
|
|
score,
|
|
|
|
|
|
display_name,
|
|
|
|
|
|
display_symbol,
|
|
|
|
|
|
price_now,
|
|
|
|
|
|
price_24h,
|
|
|
|
|
|
price_7d,
|
|
|
|
|
|
ch24_bp,
|
|
|
|
|
|
ch7d_bp,
|
|
|
|
|
|
price_now_usd,
|
|
|
|
|
|
price_24h_usd,
|
|
|
|
|
|
price_7d_usd,
|
|
|
|
|
|
ch24_usd_bp,
|
|
|
|
|
|
ch7d_usd_bp,
|
|
|
|
|
|
apy_bp
|
|
|
|
|
|
],
|
|
|
|
|
|
)
|
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_db_search_tokens_cached_fuzzy_and_exact() {
|
|
|
|
|
|
let mock = mock_db_pool(|conn| {
|
|
|
|
|
|
cauldron_prepare_tables(conn);
|
|
|
|
|
|
bcmr_prepare_tables(conn);
|
|
|
|
|
|
crc20_prepare_tables(conn);
|
|
|
|
|
|
setup_cached_tables(conn);
|
|
|
|
|
|
});
|
|
|
|
|
|
let r = mock.cauldron_r.get().unwrap();
|
|
|
|
|
|
let w = mock.cauldron_w.get().unwrap();
|
|
|
|
|
|
let bcmr = mock.bcmr_r.get().unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
// three tokens
|
|
|
|
|
|
let t_alpha = TokenID::from_inner([0xA1; 32]).to_hex();
|
|
|
|
|
|
let t_beta = TokenID::from_inner([0xB2; 32]).to_hex();
|
|
|
|
|
|
let t_gamma = TokenID::from_inner([0xC3; 32]).to_hex();
|
|
|
|
|
|
|
|
|
|
|
|
// tvl > 0 so they’re visible; make volumes/prices different so sorts are deterministic
|
|
|
|
|
|
seed_cached_row_simple(
|
|
|
|
|
|
&w, &t_alpha, "Alpha", "ALP", 1_000_000, 500_000, 5_000, 10, 10.0, 9.0, 8.0, 111, 222,
|
|
|
|
|
|
1.0, 0.9, 0.8, 100, 200, 1000,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
seed_cached_row_simple(
|
|
|
|
|
|
&w, &t_beta, "Beta", "BET", 2_000_000, 700_000, 50_000, 99, 20.0, 19.0, 18.0, 222, 111,
|
|
|
|
|
|
2.0, 1.9, 1.8, 200, 100, 500,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
seed_cached_row_simple(
|
|
|
|
|
|
&w, &t_gamma, "Gamma", "GAM", 1_500_000, 600_000, 10_000, 50, 30.0, 29.0, 28.0, 50,
|
|
|
|
|
|
-10, 3.0, 2.9, 2.8, 40, -20, 3000,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// fuzzy name (case-insensitive)
|
|
|
|
|
|
let v = db_search_tokens_cached(&r, &bcmr, "alp", CachedSort::NameAsc, 10, 0).unwrap();
|
|
|
|
|
|
assert_eq!(v.len(), 1);
|
|
|
|
|
|
assert_eq!(v[0].display_name, "Alpha");
|
|
|
|
|
|
|
|
|
|
|
|
// fuzzy symbol (case-insensitive)
|
|
|
|
|
|
let v = db_search_tokens_cached(&r, &bcmr, "BET", CachedSort::NameAsc, 10, 0).unwrap();
|
|
|
|
|
|
assert_eq!(v.len(), 1);
|
|
|
|
|
|
assert_eq!(v[0].display_name, "Beta");
|
|
|
|
|
|
|
|
|
|
|
|
// exact token id (full 64-hex)
|
|
|
|
|
|
let v = db_search_tokens_cached(&r, &bcmr, &t_gamma, CachedSort::NameAsc, 10, 0).unwrap();
|
|
|
|
|
|
assert_eq!(v.len(), 1);
|
|
|
|
|
|
assert_eq!(v[0].display_name, "Gamma");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_db_search_tokens_cached_sort_and_paging() {
|
|
|
|
|
|
let mock = mock_db_pool(|conn| {
|
|
|
|
|
|
cauldron_prepare_tables(conn);
|
|
|
|
|
|
bcmr_prepare_tables(conn);
|
|
|
|
|
|
crc20_prepare_tables(conn);
|
|
|
|
|
|
setup_cached_tables(conn);
|
|
|
|
|
|
});
|
|
|
|
|
|
let r = mock.cauldron_r.get().unwrap();
|
|
|
|
|
|
let w = mock.cauldron_w.get().unwrap();
|
|
|
|
|
|
let bcmr = mock.bcmr_r.get().unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
let t1 = TokenID::from_inner([0x11; 32]).to_hex(); // smaller volume
|
|
|
|
|
|
let t2 = TokenID::from_inner([0x22; 32]).to_hex(); // larger volume
|
|
|
|
|
|
|
|
|
|
|
|
seed_cached_row_simple(
|
|
|
|
|
|
&w, &t1, "Alpha", "ALP", 1_000_000, 500_000, 5_000, 10, 10.0, 9.0, 8.0, 111, 222, 1.0,
|
|
|
|
|
|
0.9, 0.8, 100, 200, 1000,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
seed_cached_row_simple(
|
|
|
|
|
|
&w, &t2, "Beta", "BET", 2_000_000, 700_000, 50_000, 999, 20.0, 19.0, 18.0, 222, 111,
|
|
|
|
|
|
2.0, 1.9, 1.8, 200, 100, 500,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// sort by volume desc → Beta then Alpha
|
|
|
|
|
|
let v = db_search_tokens_cached(&r, &bcmr, "", CachedSort::VolumeDesc, 10, 0).unwrap();
|
|
|
|
|
|
assert_eq!(v.len(), 2);
|
|
|
|
|
|
assert_eq!(v[0].display_name, "Beta");
|
|
|
|
|
|
assert_eq!(v[1].display_name, "Alpha");
|
|
|
|
|
|
|
|
|
|
|
|
// paging
|
|
|
|
|
|
let v = db_search_tokens_cached(&r, &bcmr, "", CachedSort::VolumeDesc, 1, 0).unwrap();
|
|
|
|
|
|
assert_eq!(v.len(), 1);
|
|
|
|
|
|
assert_eq!(v[0].display_name, "Beta");
|
|
|
|
|
|
|
|
|
|
|
|
let v = db_search_tokens_cached(&r, &bcmr, "", CachedSort::VolumeDesc, 1, 1).unwrap();
|
|
|
|
|
|
assert_eq!(v.len(), 1);
|
|
|
|
|
|
assert_eq!(v[0].display_name, "Alpha");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_db_search_tokens_cached_other_sorts() {
|
|
|
|
|
|
let mock = mock_db_pool(|conn| {
|
|
|
|
|
|
cauldron_prepare_tables(conn);
|
|
|
|
|
|
bcmr_prepare_tables(conn);
|
|
|
|
|
|
crc20_prepare_tables(conn);
|
|
|
|
|
|
setup_cached_tables(conn);
|
|
|
|
|
|
});
|
|
|
|
|
|
let r = mock.cauldron_r.get().unwrap();
|
|
|
|
|
|
let w = mock.cauldron_w.get().unwrap();
|
|
|
|
|
|
let bcmr = mock.bcmr_r.get().unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
let t_low = TokenID::from_inner([0x33; 32]).to_hex();
|
|
|
|
|
|
let t_mid = TokenID::from_inner([0x44; 32]).to_hex();
|
|
|
|
|
|
let t_high = TokenID::from_inner([0x55; 32]).to_hex();
|
|
|
|
|
|
|
|
|
|
|
|
// make USD price ascending: low < mid < high
|
|
|
|
|
|
seed_cached_row_simple(
|
|
|
|
|
|
&w, &t_low, "LowUSD", "LUS", 1_000, 100, 1, 1, 1.0, 1.0, 1.0, 0, 0, 0.5, 0.4, 0.3,
|
|
|
|
|
|
-100, -200, 100,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
seed_cached_row_simple(
|
|
|
|
|
|
&w, &t_mid, "MidUSD", "MUS", 2_000, 200, 1, 1, 2.0, 2.0, 2.0, 0, 0, 1.5, 1.4, 1.3, 100,
|
|
|
|
|
|
-50, 1000,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
seed_cached_row_simple(
|
|
|
|
|
|
&w, &t_high, "HighUSD", "HUS", 3_000, 300, 1, 1, 3.0, 3.0, 3.0, 0, 0, 2.5, 2.4, 2.3,
|
|
|
|
|
|
200, 150, 3000,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// price_usd desc → HighUSD, MidUSD, LowUSD
|
|
|
|
|
|
let v = db_search_tokens_cached(&r, &bcmr, "", CachedSort::PriceUsdDesc, 10, 0).unwrap();
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
|
v.iter().map(|x| x.display_name.clone()).collect::<Vec<_>>(),
|
|
|
|
|
|
vec!["HighUSD", "MidUSD", "LowUSD"]
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// change_24h_usd_bp asc → LowUSD (-100), MidUSD (+100), HighUSD (+200)
|
|
|
|
|
|
let v = db_search_tokens_cached(&r, &bcmr, "", CachedSort::Change24hUsdAsc, 10, 0).unwrap();
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
|
v.iter().map(|x| x.display_name.clone()).collect::<Vec<_>>(),
|
|
|
|
|
|
vec!["LowUSD", "MidUSD", "HighUSD"]
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// apy desc → HighUSD (3000) > MidUSD (1000) > LowUSD (100)
|
|
|
|
|
|
let v = db_search_tokens_cached(&r, &bcmr, "", CachedSort::Apy30dDesc, 10, 0).unwrap();
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
|
v.iter().map(|x| x.display_name.clone()).collect::<Vec<_>>(),
|
|
|
|
|
|
vec!["HighUSD", "MidUSD", "LowUSD"]
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-31 10:55:29 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
// Ensure that we are NOT looking for token_id when using incomplete hex.
|
|
|
|
|
|
fn test_search_token_by_inexact_hex_id() {
|
|
|
|
|
|
let mock_db = mock_db_pool(setup_mock_db);
|
|
|
|
|
|
|
|
|
|
|
|
let write_conn = mock_db.cauldron_w.get().unwrap();
|
|
|
|
|
|
match insert_mock_data(&write_conn) {
|
|
|
|
|
|
Ok(_) => println!("Mock data inserted successfully."),
|
2025-07-16 09:31:14 +02:00
|
|
|
|
Err(e) => println!("Failed to insert mock data: {e:?}"),
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let token_id_hex = "dadadadadadadadadadadadadadadadada";
|
|
|
|
|
|
|
|
|
|
|
|
let result = search_tokens_by_volume(
|
|
|
|
|
|
&mock_db.cauldron_r,
|
|
|
|
|
|
&mock_db.bcmr_r.get().unwrap(),
|
|
|
|
|
|
&mock_db.crc20_r.get().unwrap(),
|
|
|
|
|
|
token_id_hex,
|
|
|
|
|
|
)
|
|
|
|
|
|
.expect("Failed to search tokens by volume");
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(result.len(), 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_search_token_by_exact_hex_id() {
|
|
|
|
|
|
let mock_db = mock_db_pool(setup_mock_db);
|
|
|
|
|
|
|
|
|
|
|
|
let write_conn = mock_db.cauldron_w.get().unwrap();
|
|
|
|
|
|
match insert_mock_data(&write_conn) {
|
|
|
|
|
|
Ok(_) => println!("Mock data inserted successfully."),
|
2025-07-16 09:31:14 +02:00
|
|
|
|
Err(e) => println!("Failed to insert mock data: {e:?}"),
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let token_id_hex = "dadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadada";
|
|
|
|
|
|
|
|
|
|
|
|
let result = search_tokens_by_volume(
|
|
|
|
|
|
&mock_db.cauldron_r,
|
|
|
|
|
|
&mock_db.bcmr_r.get().unwrap(),
|
|
|
|
|
|
&mock_db.crc20_r.get().unwrap(),
|
|
|
|
|
|
token_id_hex,
|
|
|
|
|
|
)
|
|
|
|
|
|
.expect("Failed to search tokens by volume");
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(result.len(), 1);
|
|
|
|
|
|
assert_eq!(result[0].1, Some("TokenOne".to_string())); // Ensure it matches "TokenOne"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_search_tokens_by_volume_with_bcmr_and_crc20() {
|
|
|
|
|
|
let mock_db = mock_db_pool(setup_mock_db);
|
|
|
|
|
|
|
|
|
|
|
|
let write_conn = mock_db.cauldron_w.get().unwrap();
|
|
|
|
|
|
match insert_mock_data(&write_conn) {
|
|
|
|
|
|
Ok(_) => println!("Mock data inserted successfully."),
|
2025-07-16 09:31:14 +02:00
|
|
|
|
Err(e) => println!("Failed to insert mock data: {e:?}"),
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let result: Vec<(String, Option<String>, Option<String>, u64)> = search_tokens_by_volume(
|
|
|
|
|
|
&mock_db.cauldron_r,
|
|
|
|
|
|
&mock_db.bcmr_r.get().unwrap(),
|
|
|
|
|
|
&mock_db.crc20_r.get().unwrap(),
|
|
|
|
|
|
"",
|
|
|
|
|
|
)
|
|
|
|
|
|
.expect("Failed to search tokens by volume");
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Test Results: {result:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
|
|
|
|
|
|
assert_eq!(result.len(), 4); // We expect 4 tokens in total (3 BCMR + 1 CRC20)
|
|
|
|
|
|
|
2025-08-15 17:48:25 +02:00
|
|
|
|
// Assert ordering by volume (highest to lowest).
|
|
|
|
|
|
assert_eq!(result[0].1, Some("TokenFour".to_string())); // 5000 volume
|
|
|
|
|
|
assert_eq!(result[1].1, Some("TokenTwo".to_string())); // 3000 volume
|
|
|
|
|
|
assert_eq!(result[2].1, Some("TokenOne".to_string())); // 2000 volume
|
|
|
|
|
|
assert_eq!(result[3].1, Some("TokenThree".to_string())); // 0 volume
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_search_tokens_by_volume_no_results() {
|
|
|
|
|
|
let mock_db = mock_db_pool(setup_mock_db);
|
|
|
|
|
|
|
|
|
|
|
|
let write_conn = mock_db.cauldron_w.get().unwrap();
|
|
|
|
|
|
match insert_mock_data(&write_conn) {
|
|
|
|
|
|
Ok(_) => println!("Mock data inserted successfully."),
|
2025-07-16 09:31:14 +02:00
|
|
|
|
Err(e) => println!("Failed to insert mock data: {e:?}"),
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let result = search_tokens_by_volume(
|
|
|
|
|
|
&mock_db.cauldron_r,
|
|
|
|
|
|
&mock_db.bcmr_r.get().unwrap(),
|
|
|
|
|
|
&mock_db.crc20_r.get().unwrap(),
|
|
|
|
|
|
"doesNotExist",
|
|
|
|
|
|
)
|
|
|
|
|
|
.expect("Failed to search tokens by volume");
|
|
|
|
|
|
assert_eq!(result.len(), 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn dummy_cauldron(
|
|
|
|
|
|
txid: &Txid,
|
|
|
|
|
|
utxo: &OutPointHash,
|
|
|
|
|
|
token: &TokenID,
|
|
|
|
|
|
sats: u64,
|
|
|
|
|
|
tokens: i64,
|
|
|
|
|
|
pkh: &PubkeyHash,
|
|
|
|
|
|
) -> ParsedContract {
|
|
|
|
|
|
ParsedContract {
|
2025-05-20 16:17:32 +02:00
|
|
|
|
pkh: *pkh,
|
2024-10-31 10:55:29 +00:00
|
|
|
|
is_withdrawn: false,
|
|
|
|
|
|
spent_utxo_hash: OutPointHash::all_zeros(),
|
2025-05-20 16:17:32 +02:00
|
|
|
|
new_utxo_hash: Some(*utxo),
|
|
|
|
|
|
new_utxo_txid: Some(*txid),
|
2024-10-31 10:55:29 +00:00
|
|
|
|
new_utxo_n: Some(0),
|
2025-05-20 16:17:32 +02:00
|
|
|
|
token_id: Some(*token),
|
2024-10-31 10:55:29 +00:00
|
|
|
|
sats: Some(sats),
|
|
|
|
|
|
token_amount: Some(tokens),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_search_token_by_name_symbol_and_id() {
|
|
|
|
|
|
let mock_db = mock_db_pool(setup_mock_db);
|
|
|
|
|
|
let write_conn = mock_db.cauldron_w.get().unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
match insert_mock_data(&write_conn) {
|
|
|
|
|
|
Ok(_) => println!("Mock data inserted successfully."),
|
2025-07-16 09:31:14 +02:00
|
|
|
|
Err(e) => println!("Failed to insert mock data: {e:?}"),
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let token_tests = vec![
|
|
|
|
|
|
(
|
|
|
|
|
|
"TokenOne",
|
|
|
|
|
|
"TONE",
|
|
|
|
|
|
"dadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadada",
|
|
|
|
|
|
"TokenOne",
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
"TokenTwo",
|
|
|
|
|
|
"TWO",
|
|
|
|
|
|
"dbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdb",
|
|
|
|
|
|
"TokenTwo",
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
"TokenThree",
|
|
|
|
|
|
"TN3",
|
|
|
|
|
|
"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
|
|
|
|
|
|
"TokenThree",
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
"TokenFour",
|
|
|
|
|
|
"TFOUR",
|
|
|
|
|
|
"dcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdc",
|
|
|
|
|
|
"TokenFour",
|
|
|
|
|
|
),
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
for (name, symbol, token_id, expected_name) in token_tests {
|
|
|
|
|
|
let result = search_tokens_by_volume(
|
|
|
|
|
|
&mock_db.cauldron_r,
|
|
|
|
|
|
&mock_db.bcmr_r.get().unwrap(),
|
|
|
|
|
|
&mock_db.crc20_r.get().unwrap(),
|
|
|
|
|
|
name,
|
|
|
|
|
|
)
|
|
|
|
|
|
.expect("Failed to search tokens by volume");
|
|
|
|
|
|
assert_eq!(result.len(), 1);
|
|
|
|
|
|
assert_eq!(result[0].1, Some(expected_name.to_string()));
|
|
|
|
|
|
|
|
|
|
|
|
let result = search_tokens_by_volume(
|
|
|
|
|
|
&mock_db.cauldron_r,
|
|
|
|
|
|
&mock_db.bcmr_r.get().unwrap(),
|
|
|
|
|
|
&mock_db.crc20_r.get().unwrap(),
|
|
|
|
|
|
symbol,
|
|
|
|
|
|
)
|
|
|
|
|
|
.expect("Failed to search tokens by volume");
|
|
|
|
|
|
assert_eq!(result.len(), 1);
|
|
|
|
|
|
assert_eq!(result[0].1, Some(expected_name.to_string()));
|
|
|
|
|
|
|
|
|
|
|
|
let result = search_tokens_by_volume(
|
|
|
|
|
|
&mock_db.cauldron_r,
|
|
|
|
|
|
&mock_db.bcmr_r.get().unwrap(),
|
|
|
|
|
|
&mock_db.crc20_r.get().unwrap(),
|
|
|
|
|
|
token_id,
|
|
|
|
|
|
)
|
|
|
|
|
|
.expect("Failed to search tokens by volume");
|
|
|
|
|
|
assert_eq!(result.len(), 1);
|
|
|
|
|
|
assert_eq!(result[0].1, Some(expected_name.to_string()));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn insert_mock_data(conn: &Connection) -> Result<(), Box<dyn std::error::Error>> {
|
|
|
|
|
|
dummy_init_seq();
|
2025-09-05 13:53:10 +00:00
|
|
|
|
let current_timestamp = time_now();
|
2024-10-31 10:55:29 +00:00
|
|
|
|
let thirty_days_ago = current_timestamp - 30 * 24 * 60 * 60;
|
|
|
|
|
|
let owner_pkh = PubkeyHash::from_inner([0xca; 20]);
|
|
|
|
|
|
let block_zero = BlockHash::all_zeros();
|
|
|
|
|
|
|
|
|
|
|
|
// ================== TOKEN 1 (BCMR Token with Volume) ==================
|
|
|
|
|
|
let utxo1 = OutPointHash::from_hex(
|
|
|
|
|
|
"a3f1d42e2a5c9f2b5f1b9d7f7c2b19e7a3b1d2c3f4a5e6f2d1c3e4f5a1b2c3d4",
|
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Step 1: Prepare and insert BCMR data for token1 with non-null fields
|
|
|
|
|
|
let token1 = Token {
|
|
|
|
|
|
category: "asset_category".to_string(),
|
|
|
|
|
|
symbol: "TONE".to_string(),
|
|
|
|
|
|
decimals: 8,
|
|
|
|
|
|
};
|
|
|
|
|
|
let uris1 = Uris {
|
|
|
|
|
|
icon: Some("https://example.com/icon.png".to_string()),
|
|
|
|
|
|
web: Some("https://example.com".to_string()),
|
|
|
|
|
|
};
|
|
|
|
|
|
let filemeta1 = FileMeta {
|
|
|
|
|
|
expected_hash: Some("expected_hash1".to_string()),
|
|
|
|
|
|
actual_hash: Some("actual_hash1".to_string()),
|
|
|
|
|
|
source: "source_url".to_string(),
|
|
|
|
|
|
};
|
|
|
|
|
|
let parsed_bcmr1 = ParsedBCMR {
|
|
|
|
|
|
name: "TokenOne".to_string(),
|
|
|
|
|
|
description: "Test Token 1".to_string(),
|
|
|
|
|
|
token: token1,
|
|
|
|
|
|
uris: uris1,
|
|
|
|
|
|
filemeta: filemeta1,
|
|
|
|
|
|
};
|
|
|
|
|
|
if let Err(e) = insert_bcmr_data(conn, &utxo1, &parsed_bcmr1) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert BCMR data for token1: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Step 2: Insert Pool for token1
|
|
|
|
|
|
let txid1 = Txid::from_inner([0xf0; 32]);
|
|
|
|
|
|
let token_id1: TokenID = TokenID::from_inner([0xda; 32]);
|
|
|
|
|
|
let cauldron1 = dummy_cauldron(&txid1, &utxo1, &token_id1, 1000, 500, &owner_pkh);
|
|
|
|
|
|
if let Err(e) = insert_new_pool(conn, &cauldron1) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert pool for token1: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Step 3: Insert auth_chain_entry for token1
|
|
|
|
|
|
if let Err(e) = insert_authheader(
|
|
|
|
|
|
conn,
|
|
|
|
|
|
&utxo1,
|
|
|
|
|
|
&BlockHash::all_zeros(),
|
|
|
|
|
|
&txid1,
|
|
|
|
|
|
&token_id1,
|
|
|
|
|
|
10,
|
|
|
|
|
|
Some(Vec::from("bcmr_data1".as_bytes())),
|
|
|
|
|
|
) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert auth_chain_entry for token1: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Step 4: Insert the initial UTXO funding for token1
|
|
|
|
|
|
if let Err(e) = insert_utxo_funding(conn, &vec![cauldron1.clone()], &txid1, true) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert initial UTXO funding for token1: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Step 5: Insert the transaction for utxo1
|
|
|
|
|
|
if let Err(e) = insert_block_tx(conn, &txid1, &block_zero, thirty_days_ago) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert block transaction for utxo1: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
if let Err(e) = insert_mempool_tx(conn, &txid1, thirty_days_ago as u64) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert mempool transaction for utxo1: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Step 6: Simulate Volume - Insert second funding for token1
|
|
|
|
|
|
let txid2 = Txid::from_inner([0xf1; 32]);
|
|
|
|
|
|
let utxo2 = OutPointHash::from_inner([0xe1; 32]);
|
|
|
|
|
|
let cauldron2 = ParsedContract {
|
2025-05-20 16:17:32 +02:00
|
|
|
|
pkh: owner_pkh,
|
2024-10-31 10:55:29 +00:00
|
|
|
|
is_withdrawn: false,
|
2025-05-20 16:17:32 +02:00
|
|
|
|
spent_utxo_hash: utxo1,
|
|
|
|
|
|
new_utxo_hash: Some(utxo2),
|
|
|
|
|
|
new_utxo_txid: Some(txid2),
|
2024-10-31 10:55:29 +00:00
|
|
|
|
new_utxo_n: Some(0),
|
2025-05-20 16:17:32 +02:00
|
|
|
|
token_id: Some(token_id1),
|
2024-10-31 10:55:29 +00:00
|
|
|
|
sats: Some(2000),
|
|
|
|
|
|
token_amount: Some(1000),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if let Err(e) = insert_utxo_funding(conn, &vec![cauldron2.clone()], &txid2, true) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert second UTXO funding for token1: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Step 7: Insert the transaction for utxo2 (spending utxo1)
|
|
|
|
|
|
if let Err(e) = insert_block_tx(conn, &txid2, &block_zero, current_timestamp) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert block transaction for utxo2: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
if let Err(e) = insert_mempool_tx(conn, &txid2, current_timestamp as u64) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert mempool transaction for utxo2: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Step 8: Insert pool history entries for initial funding and spending
|
|
|
|
|
|
if let Err(e) = insert_pool_history_entry(
|
|
|
|
|
|
conn,
|
|
|
|
|
|
&utxo1,
|
|
|
|
|
|
&cauldron1.clone(),
|
|
|
|
|
|
Some(thirty_days_ago as u64),
|
|
|
|
|
|
Some(thirty_days_ago as u64),
|
2025-08-15 17:48:25 +02:00
|
|
|
|
0, // sats_delta for initial funding (no trading activity)
|
|
|
|
|
|
0, // token_delta for initial funding (no trading activity)
|
2024-10-31 10:55:29 +00:00
|
|
|
|
) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert pool history entry for initial funding of token1: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
if let Err(e) = insert_pool_history_entry(
|
|
|
|
|
|
conn,
|
|
|
|
|
|
&utxo1,
|
|
|
|
|
|
&cauldron2,
|
|
|
|
|
|
Some(current_timestamp as u64),
|
|
|
|
|
|
Some(current_timestamp as u64),
|
2025-08-15 17:48:25 +02:00
|
|
|
|
2000, // sats_delta for spending (actual trading activity)
|
|
|
|
|
|
1000, // token_delta for spending (actual trading activity)
|
2024-10-31 10:55:29 +00:00
|
|
|
|
) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert pool history entry for spending of token1: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ================== TOKEN 2 (BCMR Token) ==================
|
|
|
|
|
|
let utxo2 = OutPointHash::from_hex(
|
|
|
|
|
|
"c3d2e1f4b6a7c8d1e2f5d7c3b1a9e4f2b1d3f4e6c2b9f3a8d1e4f5b6c3d2e7a4",
|
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Step 1: Prepare and insert BCMR data for token2 with unique values
|
|
|
|
|
|
let token2 = Token {
|
|
|
|
|
|
category: "asset_category_2".to_string(),
|
|
|
|
|
|
symbol: "TWO".to_string(),
|
|
|
|
|
|
decimals: 8,
|
|
|
|
|
|
};
|
|
|
|
|
|
let uris2 = Uris {
|
|
|
|
|
|
icon: Some("https://example.com/icon2.png".to_string()),
|
|
|
|
|
|
web: Some("https://example.com/two".to_string()),
|
|
|
|
|
|
};
|
|
|
|
|
|
let filemeta2 = FileMeta {
|
|
|
|
|
|
expected_hash: Some("expected_hash2".to_string()),
|
|
|
|
|
|
actual_hash: Some("actual_hash2".to_string()),
|
|
|
|
|
|
source: "source_url_2".to_string(),
|
|
|
|
|
|
};
|
|
|
|
|
|
let parsed_bcmr2 = ParsedBCMR {
|
|
|
|
|
|
name: "TokenTwo".to_string(),
|
|
|
|
|
|
description: "Test Token 2".to_string(),
|
|
|
|
|
|
token: token2,
|
|
|
|
|
|
uris: uris2,
|
|
|
|
|
|
filemeta: filemeta2,
|
|
|
|
|
|
};
|
|
|
|
|
|
if let Err(e) = insert_bcmr_data(conn, &utxo2, &parsed_bcmr2) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert BCMR data for token2: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Step 2: Insert Pool for token2
|
|
|
|
|
|
let token_id2 = TokenID::from_inner([0xdb; 32]);
|
|
|
|
|
|
let cauldron2 = dummy_cauldron(
|
|
|
|
|
|
&Txid::from_inner([0xf2; 32]),
|
|
|
|
|
|
&utxo2,
|
|
|
|
|
|
&token_id2,
|
|
|
|
|
|
1500,
|
|
|
|
|
|
700,
|
|
|
|
|
|
&owner_pkh,
|
|
|
|
|
|
);
|
|
|
|
|
|
insert_new_pool(conn, &cauldron2)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Step 9: Insert auth_chain_entry for token2
|
|
|
|
|
|
if let Err(e) = insert_authheader(
|
|
|
|
|
|
conn,
|
|
|
|
|
|
&utxo2,
|
|
|
|
|
|
&BlockHash::all_zeros(),
|
|
|
|
|
|
&txid2,
|
|
|
|
|
|
&token_id2,
|
|
|
|
|
|
15,
|
|
|
|
|
|
Some(Vec::from("bcmr_data2".as_bytes())),
|
|
|
|
|
|
) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert auth_chain_entry for TOKEN 3: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Step 3: Insert the initial UTXO funding for token2
|
|
|
|
|
|
insert_utxo_funding(
|
|
|
|
|
|
conn,
|
|
|
|
|
|
&vec![cauldron2.clone()],
|
|
|
|
|
|
&Txid::from_inner([0xf2; 32]),
|
|
|
|
|
|
true,
|
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Step 4: Insert the transaction for utxo2
|
|
|
|
|
|
let txid2 = Txid::from_inner([0xf2; 32]);
|
|
|
|
|
|
insert_block_tx(conn, &txid2, &block_zero, thirty_days_ago)?;
|
|
|
|
|
|
insert_mempool_tx(conn, &txid2, thirty_days_ago as u64)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Step 5: Define cauldron3 for token2 with spent_utxo_hash referring to utxo2, then insert it
|
|
|
|
|
|
let txid3 = Txid::from_inner([0xf3; 32]);
|
|
|
|
|
|
let utxo3 = OutPointHash::from_inner([0xe2; 32]);
|
|
|
|
|
|
|
|
|
|
|
|
let cauldron3 = ParsedContract {
|
2025-05-20 16:17:32 +02:00
|
|
|
|
pkh: owner_pkh,
|
2024-10-31 10:55:29 +00:00
|
|
|
|
is_withdrawn: false,
|
2025-05-20 16:17:32 +02:00
|
|
|
|
spent_utxo_hash: utxo2,
|
|
|
|
|
|
new_utxo_hash: Some(utxo3),
|
|
|
|
|
|
new_utxo_txid: Some(txid3),
|
2024-10-31 10:55:29 +00:00
|
|
|
|
new_utxo_n: Some(0),
|
2025-05-20 16:17:32 +02:00
|
|
|
|
token_id: Some(token_id2),
|
2024-10-31 10:55:29 +00:00
|
|
|
|
sats: Some(3000),
|
|
|
|
|
|
token_amount: Some(1500),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Insert the second funding with `spent_utxo_hash` correctly set
|
|
|
|
|
|
insert_utxo_funding(conn, &vec![cauldron3.clone()], &txid3, true)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Step 6: Insert the transaction for utxo3 (the transaction that spent utxo2)
|
|
|
|
|
|
insert_block_tx(conn, &txid3, &block_zero, current_timestamp)?;
|
|
|
|
|
|
insert_mempool_tx(conn, &txid3, current_timestamp as u64)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Step 7: Insert pool history entry for the initial UTXO funding (utxo2)
|
|
|
|
|
|
insert_pool_history_entry(
|
|
|
|
|
|
conn,
|
|
|
|
|
|
&utxo2,
|
|
|
|
|
|
&cauldron2.clone(),
|
|
|
|
|
|
Some(thirty_days_ago as u64),
|
|
|
|
|
|
Some(thirty_days_ago as u64),
|
2025-08-15 17:48:25 +02:00
|
|
|
|
0, // sats_delta for initial funding (no trading activity)
|
|
|
|
|
|
0, // token_delta for initial funding (no trading activity)
|
2024-10-31 10:55:29 +00:00
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Step 8: Insert pool history entry for the spending of utxo2 (creation of utxo3)
|
|
|
|
|
|
insert_pool_history_entry(
|
|
|
|
|
|
conn,
|
|
|
|
|
|
&utxo2,
|
|
|
|
|
|
&cauldron3,
|
|
|
|
|
|
Some(current_timestamp as u64),
|
|
|
|
|
|
Some(current_timestamp as u64),
|
2025-08-15 17:48:25 +02:00
|
|
|
|
3000, // sats_delta for spending (actual trading activity)
|
|
|
|
|
|
1500, // token_delta for spending (actual trading activity)
|
2024-10-31 10:55:29 +00:00
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
|
|
// ================== TOKEN 3 (BCMR Token with No Volume) ==================
|
|
|
|
|
|
let utxo3 = OutPointHash::from_hex(
|
|
|
|
|
|
"d5e1f2a3b4c3d2f5b6a8e7d3c2f4b9a6d2e3f1c4b5a9e3d1b2c5f7a3d4b8e2c3",
|
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Prepare and insert BCMR data for TOKEN 3 with unique values
|
|
|
|
|
|
let token3 = Token {
|
|
|
|
|
|
category: "asset_category_3".to_string(),
|
|
|
|
|
|
symbol: "TN3".to_string(),
|
|
|
|
|
|
decimals: 8,
|
|
|
|
|
|
};
|
|
|
|
|
|
let uris3 = Uris {
|
|
|
|
|
|
icon: Some("https://example.com/icon3.png".to_string()),
|
|
|
|
|
|
web: Some("https://example.com/three".to_string()),
|
|
|
|
|
|
};
|
|
|
|
|
|
let filemeta3 = FileMeta {
|
|
|
|
|
|
expected_hash: Some("expected_hash3".to_string()),
|
|
|
|
|
|
actual_hash: Some("actual_hash3".to_string()),
|
|
|
|
|
|
source: "source_url_3".to_string(),
|
|
|
|
|
|
};
|
|
|
|
|
|
let parsed_bcmr3 = ParsedBCMR {
|
|
|
|
|
|
name: "TokenThree".to_string(),
|
|
|
|
|
|
description: "Test Token 3 without Volume".to_string(),
|
|
|
|
|
|
token: token3,
|
|
|
|
|
|
uris: uris3,
|
|
|
|
|
|
filemeta: filemeta3,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if let Err(e) = insert_bcmr_data(conn, &utxo3, &parsed_bcmr3) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert BCMR data for TOKEN 3: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Insert Pool for TOKEN 3
|
|
|
|
|
|
let txid3_initial = Txid::from_inner([0xf6; 32]);
|
|
|
|
|
|
let token_id3 = TokenID::from_inner([0xdd; 32]);
|
|
|
|
|
|
let cauldron3_initial =
|
|
|
|
|
|
dummy_cauldron(&txid3_initial, &utxo3, &token_id3, 1000, 500, &owner_pkh);
|
|
|
|
|
|
|
|
|
|
|
|
if let Err(e) = insert_new_pool(conn, &cauldron3_initial) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert pool for TOKEN 3: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Insert auth_chain_entry for TOKEN 3
|
|
|
|
|
|
if let Err(e) = insert_authheader(
|
|
|
|
|
|
conn,
|
|
|
|
|
|
&utxo3,
|
|
|
|
|
|
&BlockHash::all_zeros(),
|
|
|
|
|
|
&txid3,
|
|
|
|
|
|
&token_id3,
|
|
|
|
|
|
20,
|
|
|
|
|
|
Some(Vec::from("bcmr_data3".as_bytes())),
|
|
|
|
|
|
) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert auth_chain_entry for TOKEN 3: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Insert the initial UTXO funding for TOKEN 3 (No additional funding to keep volume at 0)
|
|
|
|
|
|
if let Err(e) =
|
|
|
|
|
|
insert_utxo_funding(conn, &vec![cauldron3_initial.clone()], &txid3_initial, true)
|
|
|
|
|
|
{
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert initial UTXO funding for TOKEN 3: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Insert transaction for the initial funding without spending
|
|
|
|
|
|
if let Err(e) = insert_block_tx(conn, &txid3_initial, &block_zero, thirty_days_ago) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert block transaction for TOKEN 3 initial funding: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
if let Err(e) = insert_mempool_tx(conn, &txid3_initial, thirty_days_ago as u64) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert mempool transaction for TOKEN 3 initial funding: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Insert pool history entry for the initial funding of TOKEN 3 (no spending history)
|
|
|
|
|
|
if let Err(e) = insert_pool_history_entry(
|
|
|
|
|
|
conn,
|
|
|
|
|
|
&utxo3,
|
|
|
|
|
|
&cauldron3_initial,
|
|
|
|
|
|
Some(thirty_days_ago as u64),
|
|
|
|
|
|
Some(thirty_days_ago as u64),
|
2025-08-15 17:48:25 +02:00
|
|
|
|
0, // sats_delta for initial funding (no trading activity)
|
|
|
|
|
|
0, // token_delta for initial funding (no trading activity)
|
2024-10-31 10:55:29 +00:00
|
|
|
|
) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert pool history entry for TOKEN 3 initial funding: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ================== TOKEN 4 (CRC20 Token with Volume) ==================
|
|
|
|
|
|
let utxo4_initial = OutPointHash::from_hex(
|
|
|
|
|
|
"f1d4e2a3b5c4d2f7b6a8e7d3c2f4b9a6d3e1f1c4b7a8e2d3b6c7f9a5d4b1e6c3",
|
|
|
|
|
|
)?;
|
|
|
|
|
|
let token_id4 = TokenID::from_inner([0xdc; 32]); // Unique ID for TOKEN 4
|
|
|
|
|
|
|
|
|
|
|
|
// Insert CRC20 data for TOKEN 4
|
|
|
|
|
|
conn.execute(
|
|
|
|
|
|
"INSERT INTO crc20 (token_id, name, symbol, decimals) VALUES (?, ?, ?, ?)",
|
|
|
|
|
|
params![&token_id4.to_hex(), "TokenFour", "TFOUR", 18],
|
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Step 1: Insert initial funding for TOKEN 4
|
|
|
|
|
|
let txid4_initial = Txid::from_inner([0xf4; 32]);
|
|
|
|
|
|
let cauldron4_initial = dummy_cauldron(
|
|
|
|
|
|
&txid4_initial,
|
|
|
|
|
|
&utxo4_initial,
|
|
|
|
|
|
&token_id4,
|
|
|
|
|
|
3600,
|
|
|
|
|
|
1200,
|
|
|
|
|
|
&owner_pkh,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-08-15 17:48:25 +02:00
|
|
|
|
// Insert pool for TOKEN 4
|
|
|
|
|
|
if let Err(e) = insert_new_pool(conn, &cauldron4_initial) {
|
|
|
|
|
|
println!("Failed to insert pool for TOKEN 4: {e:?}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-31 10:55:29 +00:00
|
|
|
|
if let Err(e) =
|
|
|
|
|
|
insert_utxo_funding(conn, &vec![cauldron4_initial.clone()], &txid4_initial, true)
|
|
|
|
|
|
{
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert initial UTXO funding for TOKEN 4: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Insert transaction for the initial funding
|
|
|
|
|
|
if let Err(e) = insert_block_tx(conn, &txid4_initial, &block_zero, thirty_days_ago) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert block transaction for TOKEN 4 initial funding: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
if let Err(e) = insert_mempool_tx(conn, &txid4_initial, thirty_days_ago as u64) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert mempool transaction for TOKEN 4 initial funding: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Step 2: Insert second funding entry to simulate volume
|
|
|
|
|
|
let txid4_spend = Txid::from_inner([0xf5; 32]);
|
|
|
|
|
|
let utxo4_spent = OutPointHash::from_inner([0xe4; 32]);
|
|
|
|
|
|
let cauldron4_spent = ParsedContract {
|
2025-05-20 16:17:32 +02:00
|
|
|
|
pkh: owner_pkh,
|
2024-10-31 10:55:29 +00:00
|
|
|
|
is_withdrawn: false,
|
2025-05-20 16:17:32 +02:00
|
|
|
|
spent_utxo_hash: utxo4_initial,
|
|
|
|
|
|
new_utxo_hash: Some(utxo4_spent),
|
|
|
|
|
|
new_utxo_txid: Some(txid4_spend),
|
2024-10-31 10:55:29 +00:00
|
|
|
|
new_utxo_n: Some(0),
|
2025-05-20 16:17:32 +02:00
|
|
|
|
token_id: Some(token_id4),
|
2024-10-31 10:55:29 +00:00
|
|
|
|
sats: Some(5000),
|
|
|
|
|
|
token_amount: Some(1500),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if let Err(e) =
|
|
|
|
|
|
insert_utxo_funding(conn, &vec![cauldron4_spent.clone()], &txid4_spend, true)
|
|
|
|
|
|
{
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert spent UTXO funding for TOKEN 4: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Insert transaction for the spending UTXO
|
|
|
|
|
|
if let Err(e) = insert_block_tx(conn, &txid4_spend, &block_zero, current_timestamp) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert block transaction for TOKEN 4 spending: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
if let Err(e) = insert_mempool_tx(conn, &txid4_spend, current_timestamp as u64) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert mempool transaction for TOKEN 4 spending: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Step 3: Insert pool history entries for funding and spending
|
|
|
|
|
|
if let Err(e) = insert_pool_history_entry(
|
|
|
|
|
|
conn,
|
|
|
|
|
|
&utxo4_initial,
|
|
|
|
|
|
&cauldron4_initial,
|
|
|
|
|
|
Some(thirty_days_ago as u64),
|
|
|
|
|
|
Some(thirty_days_ago as u64),
|
2025-08-15 17:48:25 +02:00
|
|
|
|
0, // sats_delta for initial funding (no trading activity)
|
|
|
|
|
|
0, // token_delta for initial funding (no trading activity)
|
2024-10-31 10:55:29 +00:00
|
|
|
|
) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert pool history entry for TOKEN 4 initial funding: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
if let Err(e) = insert_pool_history_entry(
|
|
|
|
|
|
conn,
|
|
|
|
|
|
&utxo4_initial,
|
|
|
|
|
|
&cauldron4_spent,
|
|
|
|
|
|
Some(current_timestamp as u64),
|
|
|
|
|
|
Some(current_timestamp as u64),
|
2025-08-15 17:48:25 +02:00
|
|
|
|
5000, // sats_delta for spending (actual trading activity)
|
|
|
|
|
|
1500, // token_delta for spending (actual trading activity)
|
2024-10-31 10:55:29 +00:00
|
|
|
|
) {
|
2025-07-16 09:31:14 +02:00
|
|
|
|
println!("Failed to insert pool history entry for TOKEN 4 spending: {e:?}");
|
2024-10-31 10:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|