riftenlabs-indexer/src/db/cauldron/tokenlist/db_utils.rs

192 lines
7.1 KiB
Rust
Raw Normal View History

2025-09-05 13:53:10 +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::Result;
use rusqlite::Connection;
pub enum CachedSort {
ScoreDesc,
ScoreAsc,
NameAsc,
NameDesc,
SymbolAsc,
SymbolDesc,
TvlDesc,
TvlAsc,
VolumeDesc,
VolumeAsc,
Change24hDesc,
Change24hAsc,
Change7dDesc,
Change7dAsc,
PriceUsdDesc,
PriceUsdAsc,
Change24hUsdDesc,
Change24hUsdAsc,
Change7dUsdDesc,
Change7dUsdAsc,
Apy30dDesc,
Apy30dAsc,
}
pub fn order_clause(sort: CachedSort) -> &'static str {
match sort {
CachedSort::ScoreDesc => "ORDER BY score DESC, trade_volume DESC, token_id ASC",
CachedSort::ScoreAsc => "ORDER BY score ASC, trade_volume ASC, token_id ASC",
CachedSort::NameAsc => "ORDER BY display_name COLLATE NOCASE ASC, token_id ASC",
CachedSort::NameDesc => "ORDER BY display_name COLLATE NOCASE DESC, token_id ASC",
CachedSort::SymbolAsc => "ORDER BY display_symbol COLLATE NOCASE ASC, token_id ASC",
CachedSort::SymbolDesc => "ORDER BY display_symbol COLLATE NOCASE DESC, token_id ASC",
CachedSort::TvlDesc => "ORDER BY tvl_sats DESC, token_id ASC",
CachedSort::TvlAsc => "ORDER BY tvl_sats ASC, token_id ASC",
CachedSort::VolumeDesc => "ORDER BY trade_volume DESC, token_id ASC",
CachedSort::VolumeAsc => "ORDER BY trade_volume ASC, token_id ASC",
CachedSort::Change24hDesc => "ORDER BY change_24h_bp DESC, token_id ASC",
CachedSort::Change24hAsc => "ORDER BY change_24h_bp ASC, token_id ASC",
CachedSort::Change7dDesc => "ORDER BY change_7d_bp DESC, token_id ASC",
CachedSort::Change7dAsc => "ORDER BY change_7d_bp ASC, token_id ASC",
CachedSort::PriceUsdDesc => "ORDER BY price_now_usd DESC, token_id ASC",
CachedSort::PriceUsdAsc => "ORDER BY price_now_usd ASC, token_id ASC",
CachedSort::Change24hUsdDesc => "ORDER BY change_24h_usd_bp DESC, token_id ASC",
CachedSort::Change24hUsdAsc => "ORDER BY change_24h_usd_bp ASC, token_id ASC",
CachedSort::Change7dUsdDesc => "ORDER BY change_7d_usd_bp DESC, token_id ASC",
CachedSort::Change7dUsdAsc => "ORDER BY change_7d_usd_bp ASC, token_id ASC",
CachedSort::Apy30dDesc => "ORDER BY apy_30d_bp DESC, token_id ASC",
CachedSort::Apy30dAsc => "ORDER BY apy_30d_bp ASC, token_id ASC",
}
}
pub fn create_cached_token_metrics_indexes(conn: &Connection) -> Result<()> {
conn.execute_batch(
r#"
-- ====== cached_token_metrics: list/sort indexes ======
-- Score (matches: ORDER BY score DESC, trade_volume DESC, token_id ASC)
CREATE INDEX IF NOT EXISTS idx_ctm_score_desc
ON cached_token_metrics(score DESC, trade_volume DESC, token_id ASC);
-- Score ASC
CREATE INDEX IF NOT EXISTS idx_ctm_score_asc
ON cached_token_metrics(score ASC, trade_volume ASC, token_id ASC);
-- Price USD
CREATE INDEX IF NOT EXISTS idx_ctm_price_usd_desc
ON cached_token_metrics(price_now_usd DESC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_price_usd_asc
ON cached_token_metrics(price_now_usd ASC, token_id ASC);
-- APY 30d
CREATE INDEX IF NOT EXISTS idx_ctm_apy_desc
ON cached_token_metrics(apy_30d_bp DESC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_apy_asc
ON cached_token_metrics(apy_30d_bp ASC, token_id ASC);
-- Name/Symbol sorts (note the collation!)
CREATE INDEX IF NOT EXISTS idx_ctm_name_asc
ON cached_token_metrics(display_name COLLATE NOCASE ASC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_name_desc
ON cached_token_metrics(display_name COLLATE NOCASE DESC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_symbol_asc
ON cached_token_metrics(display_symbol COLLATE NOCASE ASC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_symbol_desc
ON cached_token_metrics(display_symbol COLLATE NOCASE DESC, token_id ASC);
-- TVL & Volume
CREATE INDEX IF NOT EXISTS idx_ctm_tvl_desc
ON cached_token_metrics(tvl_sats DESC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_tvl_asc
ON cached_token_metrics(tvl_sats ASC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_volume_desc
ON cached_token_metrics(trade_volume DESC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_volume_asc
ON cached_token_metrics(trade_volume ASC, token_id ASC);
-- % change (sats)
CREATE INDEX IF NOT EXISTS idx_ctm_ch24_desc
ON cached_token_metrics(change_24h_bp DESC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_ch24_asc
ON cached_token_metrics(change_24h_bp ASC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_ch7d_desc
ON cached_token_metrics(change_7d_bp DESC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_ch7d_asc
ON cached_token_metrics(change_7d_bp ASC, token_id ASC);
-- % change (USD)
CREATE INDEX IF NOT EXISTS idx_ctm_ch24_usd_desc
ON cached_token_metrics(change_24h_usd_bp DESC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_ch24_usd_asc
ON cached_token_metrics(change_24h_usd_bp ASC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_ch7d_usd_desc
ON cached_token_metrics(change_7d_usd_bp DESC, token_id ASC);
CREATE INDEX IF NOT EXISTS idx_ctm_ch7d_usd_asc
ON cached_token_metrics(change_7d_usd_bp ASC, token_id ASC);
-- ====== aggregation path (update job) ======
-- Speeds WHERE tx.effective_timestamp >= ? and join by txid
CREATE INDEX IF NOT EXISTS idx_tx_effective_ts
ON tx(effective_timestamp, txid);
-- Speeds JOIN pool_history_entry(pool) → pool(creation_utxo)
CREATE INDEX IF NOT EXISTS idx_pool_creation_utxo
ON pool(creation_utxo, token_id);
-- Speeds SUM(...) GROUP BY p.token_id over phe JOINs
CREATE INDEX IF NOT EXISTS idx_phe_pool_txid
ON pool_history_entry(pool, txid);
-- Helpful for other token lookups
CREATE INDEX IF NOT EXISTS idx_pool_token_id
ON pool(token_id, creation_utxo);
-- Analyze stats for the new indexes
ANALYZE;
PRAGMA optimize;
"#,
)?;
Ok(())
}
pub fn create_cached_token_metrics_table(conn: &Connection) -> Result<()> {
conn.execute_batch(
r#"
CREATE TABLE IF NOT EXISTS cached_token_metrics (
token_id TEXT PRIMARY KEY,
trade_volume INTEGER NOT NULL DEFAULT 0,
tvl_sats INTEGER NOT NULL DEFAULT 0,
tvl_tokens INTEGER NOT NULL DEFAULT 0,
score INTEGER NOT NULL DEFAULT 0,
score_rank INTEGER NOT NULL DEFAULT 0,
display_name TEXT NOT NULL DEFAULT '',
display_symbol TEXT NOT NULL DEFAULT '',
-- sats per human token (already cached)
price_now REAL NOT NULL DEFAULT 0,
price_24h REAL NOT NULL DEFAULT 0,
price_7d REAL NOT NULL DEFAULT 0,
change_24h_bp INTEGER NOT NULL DEFAULT 0,
change_7d_bp INTEGER NOT NULL DEFAULT 0,
-- NEW: USD per human token (computed with time-matched BCH/USD oracle)
price_now_usd REAL NOT NULL DEFAULT 0,
price_24h_usd REAL NOT NULL DEFAULT 0,
price_7d_usd REAL NOT NULL DEFAULT 0,
change_24h_usd_bp INTEGER NOT NULL DEFAULT 0,
change_7d_usd_bp INTEGER NOT NULL DEFAULT 0,
apy_30d_bp INTEGER NOT NULL DEFAULT 0,
last_trade_ts INTEGER,
updated_at INTEGER NOT NULL
);
"#,
)?;
Ok(())
}