// file: kb_lib/src/db/queries/token_burn_event.rs //! Queries for `k_sol_token_burn_events`. /// Inserts or updates one normalized token burn event row. pub async fn query_token_burn_events_upsert( database: &crate::Database, dto: &crate::TokenBurnEventDto, ) -> Result { let slot_i64 = match dto.slot { Some(slot) => { let slot_result = i64::try_from(slot); match slot_result { Ok(slot) => Some(slot), Err(error) => { return Err(crate::Error::Db(format!( "cannot convert token burn event slot '{}' to i64: {}", slot, error ))); }, } }, None => None, }; match database.connection() { crate::DatabaseConnection::Sqlite(pool) => { let query_result = sqlx::query( r#" INSERT INTO k_sol_token_burn_events ( token_id, signature, instruction_index, slot, authority_wallet, source_wallet, amount, supply_after, executed_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(signature, instruction_index) DO UPDATE SET token_id = excluded.token_id, slot = excluded.slot, authority_wallet = excluded.authority_wallet, source_wallet = excluded.source_wallet, amount = excluded.amount, supply_after = excluded.supply_after, executed_at = excluded.executed_at "#, ) .bind(dto.token_id) .bind(dto.signature.clone()) .bind(dto.instruction_index) .bind(slot_i64) .bind(dto.authority_wallet.clone()) .bind(dto.source_wallet.clone()) .bind(dto.amount.clone()) .bind(dto.supply_after.clone()) .bind(dto.executed_at.to_rfc3339()) .execute(pool) .await; if let Err(error) = query_result { return Err(crate::Error::Db(format!( "cannot upsert k_sol_token_burn_events on sqlite: {}", error ))); } let id_result = sqlx::query_scalar::( r#" SELECT id FROM k_sol_token_burn_events WHERE signature = ? AND instruction_index = ? LIMIT 1 "#, ) .bind(dto.signature.clone()) .bind(dto.instruction_index) .fetch_one(pool) .await; match id_result { Ok(id) => return Ok(id), Err(error) => { return Err(crate::Error::Db(format!( "cannot fetch k_sol_token_burn_events id for signature '{}' and instruction_index '{}' on sqlite: {}", dto.signature, dto.instruction_index, error ))); }, } }, } } /// Lists recent token burn events ordered from newest to oldest. pub async fn query_token_burn_events_list_recent( database: &crate::Database, limit: u32, ) -> Result, crate::Error> { if limit == 0 { return Ok(std::vec::Vec::new()); } match database.connection() { crate::DatabaseConnection::Sqlite(pool) => { let query_result = sqlx::query_as::( r#" SELECT id, token_id, signature, instruction_index, slot, authority_wallet, source_wallet, amount, supply_after, executed_at FROM k_sol_token_burn_events ORDER BY id DESC LIMIT ? "#, ) .bind(i64::from(limit)) .fetch_all(pool) .await; let entities = match query_result { Ok(entities) => entities, Err(error) => { return Err(crate::Error::Db(format!( "cannot list k_sol_token_burn_events on sqlite: {}", error ))); }, }; let mut dtos = std::vec::Vec::new(); for entity in entities { let dto_result = crate::TokenBurnEventDto::try_from(entity); let dto = match dto_result { Ok(dto) => dto, Err(error) => return Err(error), }; dtos.push(dto); } return Ok(dtos); }, } } #[cfg(test)] mod tests { #[tokio::test] async fn normalized_activity_roundtrip_works() { let tempdir = tempfile::tempdir().expect("tempdir must succeed"); let database_path = tempdir.path().join("normalized_activity.sqlite3"); let config = crate::DatabaseConfig { enabled: true, backend: crate::DatabaseBackend::Sqlite, sqlite: crate::SqliteDatabaseConfig { path: database_path.to_string_lossy().to_string(), create_if_missing: true, busy_timeout_ms: 5000, max_connections: 1, auto_initialize_schema: true, use_wal: true, }, }; let database = crate::Database::connect_and_initialize(&config) .await .expect("database init must succeed"); let dex_id = crate::query_dexs_upsert( &database, &crate::DexDto::new("raydium".to_string(), "Raydium".to_string(), None, None, true), ) .await .expect("dex upsert must succeed"); let base_token_id = crate::query_tokens_upsert( &database, &crate::TokenDto::new( "Base111111111111111111111111111111111111111".to_string(), Some("BASE".to_string()), Some("Base Token".to_string()), Some(6), crate::SPL_TOKEN_PROGRAM_ID.to_string(), false, ), ) .await .expect("base token upsert must succeed"); let quote_token_id = crate::query_tokens_upsert( &database, &crate::TokenDto::new( crate::WSOL_MINT_ID.to_string(), Some("WSOL".to_string()), Some("Wrapped SOL".to_string()), Some(9), crate::SPL_TOKEN_PROGRAM_ID.to_string(), true, ), ) .await .expect("quote token upsert must succeed"); let pool_id = crate::query_pools_upsert( &database, &crate::PoolDto::new( dex_id, "Pool111111111111111111111111111111111111111".to_string(), crate::PoolKind::Amm, crate::PoolStatus::Active, ), ) .await .expect("pool upsert must succeed"); let pair_id = crate::query_pairs_upsert( &database, &crate::PairDto::new( dex_id, pool_id, base_token_id, quote_token_id, Some("BASE/WSOL".to_string()), ), ) .await .expect("pair upsert must succeed"); let swap_id = crate::query_swaps_upsert( &database, &crate::SwapDto::new( dex_id, pool_id, Some(pair_id), "swap-signature-1".to_string(), 0, Some(1000), Some("Trader11111111111111111111111111111111111".to_string()), base_token_id, quote_token_id, "1000.50".to_string(), "2.75".to_string(), Some("0.002748625687156422".to_string()), crate::SwapTradeSide::BuyBase, ), ) .await .expect("swap upsert must succeed"); let liquidity_id = crate::query_liquidity_events_upsert( &database, &crate::LiquidityEventDto::new( dex_id, pool_id, Some(pair_id), "liq-signature-1".to_string(), 1, Some(1001), crate::LiquidityEventKind::Add, Some("LpUser1111111111111111111111111111111111".to_string()), base_token_id, quote_token_id, None, "5000".to_string(), "15".to_string(), None, ), ) .await .expect("liquidity event upsert must succeed"); let mint_id = crate::query_token_mint_events_upsert( &database, &crate::TokenMintEventDto::new( base_token_id, "mint-signature-1".to_string(), 2, Some(1002), Some("MintAuthority111111111111111111111111111".to_string()), Some("Dest1111111111111111111111111111111111111".to_string()), "1000000".to_string(), Some("5000000".to_string()), ), ) .await .expect("token mint event upsert must succeed"); let burn_id = crate::query_token_burn_events_upsert( &database, &crate::TokenBurnEventDto::new( base_token_id, "burn-signature-1".to_string(), 3, Some(1003), Some("BurnAuthority111111111111111111111111111".to_string()), Some("Source11111111111111111111111111111111111".to_string()), "25000".to_string(), Some("4975000".to_string()), ), ) .await .expect("token burn event upsert must succeed"); assert!(swap_id > 0); assert!(liquidity_id > 0); assert!(mint_id > 0); assert!(burn_id > 0); let swaps = crate::query_swaps_list_recent(&database, 10) .await .expect("swaps list must succeed"); let liquidity_events = crate::query_liquidity_events_list_recent(&database, 10) .await .expect("liquidity list must succeed"); let mint_events = crate::query_token_mint_events_list_recent(&database, 10) .await .expect("mint events list must succeed"); let burn_events = crate::query_token_burn_events_list_recent(&database, 10) .await .expect("burn events list must succeed"); assert_eq!(swaps.len(), 1); assert_eq!(liquidity_events.len(), 1); assert_eq!(mint_events.len(), 1); assert_eq!(burn_events.len(), 1); } }