rpc: list_tokens update

Improve volume calculation; add additional fields
This commit is contained in:
Dagur Valberg Johannsson 2024-01-19 14:03:23 +01:00
parent 2387888017
commit b4305a07de
No known key found for this signature in database
GPG key ID: FD701804AEE88107
2 changed files with 102 additions and 40 deletions

112
src/db.rs
View file

@ -7,6 +7,7 @@ pub fn prepare_tables(conn: &Connection) {
conn.execute(
"CREATE TABLE utxo_funding (
new_utxo_hash TEXT PRIMARY KEY,
spent_utxo_hash TEXT,
timestamp BIGINT,
new_utxo_txid TEXT,
new_utxo_n INT,
@ -48,6 +49,14 @@ pub fn prepare_tables(conn: &Connection) {
[],
)
.unwrap();
// Indexes for list_by_volume
conn.execute(
"CREATE INDEX idx_utxo_funding_join ON utxo_funding(new_utxo_hash, sats, token_id);",
[],
)
.unwrap();
conn.execute("CREATE INDEX idx_utxo_funding_tvl_highest ON utxo_funding(token_id, new_utxo_hash, sats, token_amount);", []).unwrap();
}
pub fn config_set(conn: &Connection, key: &str, value: i64) {
@ -70,7 +79,7 @@ pub fn insert_utxo_funding(
cauldrons: &Vec<ParsedContract>,
) -> Result<()> {
let mut statement = con
.prepare("INSERT OR IGNORE INTO utxo_funding (new_utxo_hash, timestamp, new_utxo_txid, new_utxo_n, sats, token_amount, token_id) VALUES (?, ?, ?, ?, ?, ?, ?)")?;
.prepare("INSERT OR IGNORE INTO utxo_funding (new_utxo_hash, spent_utxo_hash, timestamp, new_utxo_txid, new_utxo_n, sats, token_amount, token_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")?;
for c in cauldrons {
if c.new_utxo_hash.is_none() {
@ -78,6 +87,7 @@ pub fn insert_utxo_funding(
}
statement.execute(params![
c.new_utxo_hash.unwrap().to_hex(),
c.spent_utxo_hash.to_hex(),
timestamp,
c.new_utxo_txid.unwrap().to_hex(),
c.new_utxo_n.unwrap(),
@ -143,49 +153,87 @@ pub fn list_tokens_by_volume(
connection: &Connection,
seconds: usize,
limit: usize,
) -> Result<Vec<(String, u64, u64)>> {
) -> Result<Vec<(String, u64, u64, u64, u64, u64, u64)>> {
// List token ID's by volume last n seconds
let mut statement = connection
.prepare(
"
WITH FundingVolume AS (
SELECT token_id, SUM(sats) as total_volume
FROM utxo_funding
WHERE timestamp >= (strftime('%s', 'now') - ?)
GROUP BY token_id
),
Liquidity AS (
SELECT f.token_id, SUM(f.sats) as total_funded_sats,
COALESCE((SELECT SUM(sp.sats)
FROM utxo_funding sp
INNER JOIN utxo_spending s ON sp.new_utxo_hash = s.spent_utxo_hash
WHERE sp.token_id = f.token_id), 0) as spent_sats
FROM utxo_funding f
GROUP BY f.token_id
)
SELECT
fv.token_id,
fv.total_volume,
(SELECT SUM(l.total_funded_sats - l.spent_sats)
FROM Liquidity l
WHERE l.token_id = fv.token_id) as TVL
FROM FundingVolume fv
JOIN Liquidity l ON fv.token_id = l.token_id
ORDER BY fv.total_volume DESC, (l.total_funded_sats - l.spent_sats) DESC
LIMIT ?;
WITH TradeData AS (
SELECT
uf1.token_id,
ABS(uf1.sats - COALESCE(uf2.sats, 0)) as trade_volume
FROM utxo_funding uf1
LEFT JOIN utxo_funding uf2 ON uf1.spent_utxo_hash = uf2.new_utxo_hash
WHERE uf1.timestamp >= (strftime('%s', 'now') - ?)
),
TVLData AS (
SELECT
token_id,
SUM(sats) as tvl_sats,
SUM(token_amount) as tvl_tokens
FROM utxo_funding
WHERE new_utxo_hash NOT IN (SELECT spent_utxo_hash FROM utxo_spending)
GROUP BY token_id
),
HighestUnspentUTXO AS (
SELECT
token_id,
MAX(sats) as highest_sats,
token_amount
FROM utxo_funding
WHERE new_utxo_hash NOT IN (SELECT spent_utxo_hash FROM utxo_spending)
GROUP BY token_id
),
AggregateTradeData AS (
SELECT
td.token_id,
SUM(td.trade_volume) as total_trade_volume,
COUNT(*) as number_of_trades,
tvl.tvl_sats,
tvl.tvl_tokens,
hu.highest_sats,
hu.token_amount
FROM TradeData td
LEFT JOIN TVLData tvl ON td.token_id = tvl.token_id
LEFT JOIN HighestUnspentUTXO hu ON td.token_id = hu.token_id
GROUP BY td.token_id
)
SELECT
token_id,
total_trade_volume,
number_of_trades,
tvl_sats,
tvl_tokens,
highest_sats,
token_amount
FROM AggregateTradeData
ORDER BY total_trade_volume DESC, number_of_trades DESC
LIMIT ?
",
)
.unwrap();
let tokens: Vec<(String, u64, u64)> = statement
let tokens: Vec<(String, u64, u64, u64, u64, u64, u64)> = statement
.query_and_then([seconds, limit], |row| {
let token_id: String = row.get(0)?;
let token_volume: u64 = row.get(1)?;
let token_tv: u64 = row.get(2)?;
Ok((token_id, token_volume, token_tv))
let trade_volume: u64 = row.get(1)?;
let trade_count: u64 = row.get(2)?;
let tvl_sats: u64 = row.get(3)?;
let tvl_token: u64 = row.get(4)?;
let best_contract_sats: u64 = row.get(5)?;
let best_contracts_token: u64 = row.get(6)?;
Ok((
token_id,
trade_volume,
trade_count,
tvl_sats,
tvl_token,
best_contract_sats,
best_contracts_token,
))
})
.unwrap()
.map(|row: Result<(String, u64, u64)>| row.unwrap())
.map(|row: Result<(String, u64, u64, u64, u64, u64, u64)>| row.unwrap())
.collect();
Ok(tokens)

View file

@ -174,19 +174,33 @@ fn list_by_volume(
let limit = limit.unwrap_or(50);
let limit = std::cmp::max(limit, 1000);
let list: Vec<(String, u64, u64)> =
let list: Vec<(String, u64, u64, u64, u64, u64, u64)> =
list_tokens_by_volume(&conn.lock().unwrap(), duration, limit)
.map_err(|e| Custom(Status::InternalServerError, format!("Error: {}", e)))?;
let result: Vec<Value> = list
.into_par_iter()
.map(|(token, volume, sats)| {
json!({
"token_id": token,
"volume": volume,
"tvl": sats
})
})
.map(
|(
token_id,
trade_volume,
trade_count,
tvl_sats,
tvl_token,
best_contract_sats,
best_contract_tokens,
)| {
json!({
"token_id": token_id,
"trade_volume": trade_volume,
"trade_count": trade_count,
"tvl_sats": tvl_sats,
"tvl_tokens": tvl_token,
"best_contract_sats": best_contract_sats,
"best_contract_tokens": best_contract_tokens
})
},
)
.collect();
Ok(Json(result))