// file: kb_lib/src/dex_event_coverage.rs //! Event coverage synchronization and reporting service. //! //! This service bridges the read-only upstream registry and the persisted //! coverage table. It does not decode transactions and never materializes //! trades, metrics or candles. /// Result of one event coverage synchronization pass. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename_all = "camelCase")] pub struct DexEventCoverageSyncResult { /// Optional decoder filter used for this synchronization pass. pub decoder_code: std::option::Option, /// Number of upstream registry entries selected by the filter. pub upstream_entry_count: usize, /// Number of coverage rows upserted from the upstream registry. pub upserted_entry_count: usize, /// Number of coverage rows touched by the local observation refresh. pub refreshed_entry_count: u64, /// Aggregated coverage summaries after synchronization. pub summaries: std::vec::Vec, } /// Service used to persist and refresh DEX event coverage rows. #[derive(Debug, Clone)] pub struct DexEventCoverageService { database: std::sync::Arc, upstream_registry: crate::UpstreamRegistryService, } impl DexEventCoverageService { /// Creates a new event coverage service. pub fn new(database: std::sync::Arc) -> Self { return Self { database, upstream_registry: crate::UpstreamRegistryService::new(), }; } async fn upsert_upstream_registry_rows( &self, decoder_code: std::option::Option, ) -> Result<(usize, usize), crate::Error> { let request = crate::UpstreamRegistrySearchRequestDto { decoder_code: decoder_code.clone(), program_id: None, program_family: None, surface_kind: None, entry_kind: None, proof_status: None, limit: None, }; let search_result = self.upstream_registry.search(&request); let mut upserted_entry_count = 0_usize; for entry in &search_result.entries { let coverage_entry = build_coverage_entry_from_upstream(entry); let upsert_result = crate::query_dex_event_coverage_entries_upsert( self.database.as_ref(), &coverage_entry, ) .await; match upsert_result { Ok(_) => upserted_entry_count += 1, Err(error) => return Err(error), } } return Ok((search_result.entries.len(), upserted_entry_count)); } async fn ensure_upstream_registry_rows_if_needed( &self, decoder_code: std::option::Option, ) -> Result<(usize, usize), crate::Error> { return self.upsert_upstream_registry_rows(decoder_code).await; } /// Synchronizes static upstream registry entries into SQLite coverage rows. /// /// The resulting rows are still discovery/audit metadata. A row can become /// observed or materialized only through local corpus replay and explicit /// count refreshes. pub async fn sync_upstream_registry( &self, decoder_code: std::option::Option, ) -> Result { let sync_counts = self.upsert_upstream_registry_rows(decoder_code.clone()).await; let (upstream_entry_count, upserted_entry_count) = match sync_counts { Ok(sync_counts) => sync_counts, Err(error) => return Err(error), }; let cleanup_result = self.cleanup_deprecated_pump_swap_observed_unknown_rows(&decoder_code).await; if let Err(error) = cleanup_result { return Err(error); } let duplicate_cleanup_result = self.cleanup_duplicate_pump_swap_logical_coverage_rows(&decoder_code).await; if let Err(error) = duplicate_cleanup_result { return Err(error); } let refreshed_entry_count = match &decoder_code { Some(decoder_code) => { let refresh_result = crate::query_dex_event_coverage_entries_refresh_local_counts_by_decoder( self.database.as_ref(), decoder_code.as_str(), ) .await; match refresh_result { Ok(refreshed_entry_count) => refreshed_entry_count, Err(error) => return Err(error), } }, None => { let refresh_result = crate::query_dex_event_coverage_entries_refresh_local_counts( self.database.as_ref(), ) .await; match refresh_result { Ok(refreshed_entry_count) => refreshed_entry_count, Err(error) => return Err(error), } }, }; let summaries_result = crate::query_dex_event_coverage_entries_list_summary_by_decoder(self.database.as_ref()) .await; let summaries = match summaries_result { Ok(summaries) => summaries, Err(error) => return Err(error), }; return Ok(crate::DexEventCoverageSyncResult { decoder_code, upstream_entry_count, upserted_entry_count, refreshed_entry_count, summaries, }); } /// Refreshes observed, materialized and proof-status counters from local DB rows. pub async fn refresh_local_counts( &self, decoder_code: std::option::Option, ) -> Result { let sync_counts = self.ensure_upstream_registry_rows_if_needed(decoder_code.clone()).await; let (upstream_entry_count, upserted_entry_count) = match sync_counts { Ok(sync_counts) => sync_counts, Err(error) => return Err(error), }; let cleanup_result = self.cleanup_deprecated_pump_swap_observed_unknown_rows(&decoder_code).await; if let Err(error) = cleanup_result { return Err(error); } let duplicate_cleanup_result = self.cleanup_duplicate_pump_swap_logical_coverage_rows(&decoder_code).await; if let Err(error) = duplicate_cleanup_result { return Err(error); } let refreshed_entry_count = match &decoder_code { Some(decoder_code) => { let refresh_result = crate::query_dex_event_coverage_entries_refresh_local_counts_by_decoder( self.database.as_ref(), decoder_code.as_str(), ) .await; match refresh_result { Ok(refreshed_entry_count) => refreshed_entry_count, Err(error) => return Err(error), } }, None => { let refresh_result = crate::query_dex_event_coverage_entries_refresh_local_counts( self.database.as_ref(), ) .await; match refresh_result { Ok(refreshed_entry_count) => refreshed_entry_count, Err(error) => return Err(error), } }, }; let summaries_result = crate::query_dex_event_coverage_entries_list_summary_by_decoder(self.database.as_ref()) .await; let summaries = match summaries_result { Ok(summaries) => summaries, Err(error) => return Err(error), }; return Ok(crate::DexEventCoverageSyncResult { decoder_code, upstream_entry_count, upserted_entry_count, refreshed_entry_count, summaries, }); } async fn cleanup_deprecated_pump_swap_observed_unknown_rows( &self, decoder_code: &std::option::Option, ) -> Result { if let Some(decoder_code) = decoder_code { if decoder_code != "pump_swap" { return Ok(0); } } match self.database.connection() { crate::DatabaseConnection::Sqlite(pool) => { let query_result = sqlx::query( r#" DELETE FROM k_sol_dex_event_coverage_entries WHERE decoder_code = 'pump_swap' AND ( entry_name LIKE 'observed_unknown_%' OR local_event_kind LIKE 'pump_swap.observed_unknown_%' ) "#, ) .execute(pool) .await; match query_result { Ok(query_result) => return Ok(query_result.rows_affected()), Err(error) => { return Err(crate::Error::Db(format!( "cannot delete deprecated PumpSwap observed_unknown coverage rows on sqlite: {}", error ))); }, } }, } } async fn cleanup_duplicate_pump_swap_logical_coverage_rows( &self, decoder_code: &std::option::Option, ) -> Result { if let Some(decoder_code) = decoder_code { if decoder_code != "pump_swap" { return Ok(0); } } match self.database.connection() { crate::DatabaseConnection::Sqlite(pool) => { let query_result = sqlx::query( r#" DELETE FROM k_sol_dex_event_coverage_entries WHERE decoder_code = 'pump_swap' AND id NOT IN ( SELECT MIN(id) FROM k_sol_dex_event_coverage_entries WHERE decoder_code = 'pump_swap' GROUP BY decoder_code, COALESCE(program_id, ''), entry_kind, entry_name, COALESCE(discriminator_hex, ''), COALESCE(local_event_kind, '') ) "#, ) .execute(pool) .await; match query_result { Ok(query_result) => return Ok(query_result.rows_affected()), Err(error) => { return Err(crate::Error::Db(format!( "cannot delete duplicate PumpSwap logical coverage rows on sqlite: {}", error ))); }, } }, } } } fn build_coverage_entry_from_upstream( entry: &crate::UpstreamRegistryEntryDto, ) -> crate::DexEventCoverageEntryDto { let event_family = infer_event_family_for_entry( entry.decoder_code.as_str(), entry.entry_name.as_str(), entry.entry_kind.as_str(), ); let expected_db_target = infer_expected_db_target_for_entry( entry.decoder_code.as_str(), entry.entry_name.as_str(), event_family.as_deref(), entry.entry_kind.as_str(), ); let local_event_kind = known_local_event_kind(entry.decoder_code.as_str(), entry.entry_name.as_str()); let mut coverage_entry = crate::DexEventCoverageEntryDto::from_upstream_registry_entry( entry, event_family, expected_db_target, local_event_kind.clone(), ); if local_event_kind.is_some() && coverage_entry.observed_count == 0 { coverage_entry.proof_status = crate::PROOF_STATUS_UPSTREAM_GIT_MAPPED_UNVERIFIED.to_string(); } return coverage_entry; } fn infer_expected_db_target_for_entry( decoder_code: &str, entry_name: &str, event_family: std::option::Option<&str>, entry_kind: &str, ) -> std::option::Option { if decoder_code == "pump_swap" { return infer_pump_swap_expected_db_target(entry_name, entry_kind); } if decoder_code == "pump_fun" { return infer_pump_fun_expected_db_target(entry_name, entry_kind); } if decoder_code == "raydium_cpmm" && (entry_name == "swap_event" || entry_name == "anchor_idl_instruction") { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string()); } if decoder_code == "raydium_amm_v4" { if entry_name == "swap_base_in" || entry_name == "swap_base_out" || entry_name == "swap_base_in_v2" || entry_name == "swap_base_out_v2" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_TRADE_EVENTS.to_string()); } if entry_name == "initialize" || entry_name == "initialize2" || entry_name == "pre_initialize" { return Some( crate::DexEventCoverageEntryDto::DB_TARGET_POOL_LIFECYCLE_EVENTS.to_string(), ); } if entry_name == "deposit" || entry_name == "withdraw" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_EVENTS.to_string()); } if entry_name == "withdraw_pnl" || entry_name == "withdraw_srm" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_FEE_EVENTS.to_string()); } if entry_name == "admin_cancel_orders" || entry_name == "migrate_to_open_book" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_ORDERBOOK_EVENTS.to_string()); } if entry_name == "monitor_step" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_ORDERBOOK_EVENTS.to_string()); } if entry_name == "create_config_account" || entry_name == "update_config_account" || entry_name == "set_params" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_POOL_ADMIN_EVENTS.to_string()); } if entry_name == "simulate_info" { return Some( crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string(), ); } } if decoder_code == "raydium_stable_swap" { if entry_name == "initialize" || entry_name == "pre_initialize" { return Some( crate::DexEventCoverageEntryDto::DB_TARGET_POOL_LIFECYCLE_EVENTS.to_string(), ); } if entry_name == "deposit" || entry_name == "withdraw" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_EVENTS.to_string()); } if entry_name == "swap_base_in" || entry_name == "swap_base_out" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_TRADE_EVENTS.to_string()); } if entry_name == "withdraw_pnl" || entry_name == "withdraw_srm" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_FEE_EVENTS.to_string()); } if entry_name == "set_params" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_POOL_ADMIN_EVENTS.to_string()); } if entry_name == "monitor_step" || entry_name == "admin_cancel_orders" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_ORDERBOOK_EVENTS.to_string()); } if entry_name == "update_model_data" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_POOL_ADMIN_EVENTS.to_string()); } if entry_name == "init_model_data" || entry_name == "simulate_info" || entry_name == "swap_event" { return Some( crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string(), ); } } if decoder_code == "raydium_clmm" { if entry_name == "initialize_reward" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_REWARD_EVENTS.to_string()); } if entry_name == "swap_event" || entry_name == "swap_router_base_in" { return Some( crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string(), ); } if entry_name == "open_position" || entry_name == "close_position" || entry_name == "close_protocol_position" { return Some( crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string(), ); } } if decoder_code == "raydium_launchpad" { if entry_name == "trade_event" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_TRADE_EVENTS.to_string()); } if entry_name == "buy_exact_in" || entry_name == "buy_exact_out" || entry_name == "sell_exact_in" || entry_name == "sell_exact_out" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_LAUNCH_EVENTS.to_string()); } if entry_name == "initialize" || entry_name == "initialize_v2" || entry_name == "initialize_with_token_2022" || entry_name == "pool_create_event" { return Some( crate::DexEventCoverageEntryDto::DB_TARGET_POOL_LIFECYCLE_EVENTS.to_string(), ); } if entry_name == "claim_creator_fee" || entry_name == "claim_platform_fee" || entry_name == "claim_platform_fee_from_vault" || entry_name == "collect_fee" || entry_name == "collect_migrate_fee" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_FEE_EVENTS.to_string()); } if entry_name == "close_platform_global_access" || entry_name == "create_config" || entry_name == "create_platform_config" || entry_name == "create_platform_global_access" || entry_name == "remove_platform_curve_param" || entry_name == "update_config" || entry_name == "update_platform_config" || entry_name == "update_platform_curve_param" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_POOL_ADMIN_EVENTS.to_string()); } if entry_name == "claim_vested_event" || entry_name == "claim_vested_token" || entry_name == "create_platform_vesting_account" || entry_name == "create_vesting_account" || entry_name == "create_vesting_event" || entry_name == "migrate_to_amm" || entry_name == "migrate_to_cpswap" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_LAUNCH_EVENTS.to_string()); } return Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string()); } return infer_expected_db_target(event_family, entry_kind); } fn infer_expected_db_target( event_family: std::option::Option<&str>, entry_kind: &str, ) -> std::option::Option { if entry_kind == crate::ENTRY_KIND_PROGRAM || entry_kind == crate::ENTRY_KIND_ACCOUNT { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string()); } let family = match event_family { Some(family) => family, None => { return Some( crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string(), ); }, }; let target = match family { "swap" => crate::DexEventCoverageEntryDto::DB_TARGET_TRADE_EVENTS, "pool_create" => crate::DexEventCoverageEntryDto::DB_TARGET_POOL_LIFECYCLE_EVENTS, "liquidity" => crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_EVENTS, "liquidity_add" => crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_EVENTS, "liquidity_remove" => crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_EVENTS, "liquidity_change" => crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_EVENTS, "position_open" => crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_EVENTS, "position_close" => crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_EVENTS, "fee" => crate::DexEventCoverageEntryDto::DB_TARGET_FEE_EVENTS, "reward" => crate::DexEventCoverageEntryDto::DB_TARGET_REWARD_EVENTS, "admin_config" => crate::DexEventCoverageEntryDto::DB_TARGET_POOL_ADMIN_EVENTS, "mint" => crate::DexEventCoverageEntryDto::DB_TARGET_TOKEN_MINT_EVENTS, "burn" => crate::DexEventCoverageEntryDto::DB_TARGET_TOKEN_BURN_EVENTS, "transfer" => crate::DexEventCoverageEntryDto::DB_TARGET_TOKEN_TRANSFER_EVENTS, "account_create" => crate::DexEventCoverageEntryDto::DB_TARGET_TOKEN_ACCOUNT_EVENTS, "account_close" => crate::DexEventCoverageEntryDto::DB_TARGET_TOKEN_ACCOUNT_EVENTS, "wrap_sol" => crate::DexEventCoverageEntryDto::DB_TARGET_TOKEN_ACCOUNT_EVENTS, "unwrap_sol" => crate::DexEventCoverageEntryDto::DB_TARGET_TOKEN_ACCOUNT_EVENTS, "order_place" => crate::DexEventCoverageEntryDto::DB_TARGET_ORDERBOOK_EVENTS, "order_cancel" => crate::DexEventCoverageEntryDto::DB_TARGET_ORDERBOOK_EVENTS, "order_fill" => crate::DexEventCoverageEntryDto::DB_TARGET_ORDERBOOK_EVENTS, "consume_events" => crate::DexEventCoverageEntryDto::DB_TARGET_ORDERBOOK_EVENTS, "settle_funds" => crate::DexEventCoverageEntryDto::DB_TARGET_ORDERBOOK_EVENTS, "vault_deposit" => crate::DexEventCoverageEntryDto::DB_TARGET_VAULT_EVENTS, "vault_withdraw" => crate::DexEventCoverageEntryDto::DB_TARGET_VAULT_EVENTS, "lock" => crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_LOCK_EVENTS, "unlock" => crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_LOCK_EVENTS, "launch" => crate::DexEventCoverageEntryDto::DB_TARGET_LAUNCH_EVENTS, "migration" => crate::DexEventCoverageEntryDto::DB_TARGET_LAUNCH_EVENTS, "vesting" => crate::DexEventCoverageEntryDto::DB_TARGET_LAUNCH_EVENTS, "liquidity_calculation" => crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY, "cpi_transport" => crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY, "idl_management" => crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY, "stake" => crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY, "unstake" => crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY, _ => crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY, }; return Some(target.to_string()); } fn infer_pump_fun_expected_db_target( entry_name: &str, entry_kind: &str, ) -> std::option::Option { if entry_kind == crate::ENTRY_KIND_PROGRAM { return None; } if entry_name == "buy" || entry_name == "sell" || entry_name == "buy_v2" || entry_name == "sell_v2" || entry_name == "buy_exact_sol_in" || entry_name == "buy_exact_quote_in_v2" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_TRADE_EVENTS.to_string()); } if entry_name == "trade_event" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_TRADE_EVENTS.to_string()); } if entry_name == "create" || entry_name == "create_event" || entry_name == "create_v2" || entry_name == "create_v2_token" || entry_name == "complete_event" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_LAUNCH_EVENTS.to_string()); } if entry_name == "initialize" { return Some( crate::DexEventCoverageEntryDto::DB_TARGET_POOL_LIFECYCLE_EVENTS.to_string(), ); } if entry_name == "migrate" || entry_name == "migrate_v2" || entry_name == "migrate_bonding_curve_creator" || entry_name == "migrate_bonding_curve_creator_event" || entry_name == "complete_pump_amm_migration_event" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_LAUNCH_EVENTS.to_string()); } if entry_name == "collect_creator_fee" || entry_name == "collect_creator_fee_v2" || entry_name == "collect_creator_fee_event" || entry_name == "distribute_creator_fees" || entry_name == "distribute_creator_fees_v2" || entry_name == "distribute_creator_fees_event" || entry_name == "get_minimum_distributable_fee" || entry_name == "minimum_distributable_fee_event" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_FEE_EVENTS.to_string()); } if entry_name == "claim_cashback" || entry_name == "claim_cashback_v2" || entry_name == "claim_cashback_event" || entry_name == "claim_token_incentives" || entry_name == "claim_token_incentives_event" || entry_name == "admin_update_token_incentives" || entry_name == "admin_update_token_incentives_event" || entry_name == "init_user_volume_accumulator" || entry_name == "init_user_volume_accumulator_event" || entry_name == "sync_user_volume_accumulator" || entry_name == "sync_user_volume_accumulator_event" || entry_name == "close_user_volume_accumulator" || entry_name == "close_user_volume_accumulator_event" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_REWARD_EVENTS.to_string()); } if entry_name == "admin_set_creator" || entry_name == "admin_set_creator_event" || entry_name == "admin_set_idl_authority" || entry_name == "admin_set_idl_authority_event" || entry_name == "add_quote_mint" || entry_name == "remove_quote_mint" || entry_name == "extend_account" || entry_name == "extend_account_event" || entry_name == "set_creator" || entry_name == "set_creator_event" || entry_name == "set_mayhem_virtual_params" || entry_name == "update_mayhem_virtual_params_event" || entry_name == "set_metaplex_creator" || entry_name == "set_metaplex_creator_event" || entry_name == "set_params" || entry_name == "set_params_event" || entry_name == "set_reserved_fee_recipients" || entry_name == "reserved_fee_recipients_event" || entry_name == "set_virtual_quote_reserves" || entry_name == "toggle_cashback_enabled" || entry_name == "toggle_create_v2" || entry_name == "toggle_mayhem_mode" || entry_name == "update_buyback_config" || entry_name == "update_global_authority" || entry_name == "update_global_authority_event" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_POOL_ADMIN_EVENTS.to_string()); } return Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string()); } fn infer_pump_swap_expected_db_target( entry_name: &str, entry_kind: &str, ) -> std::option::Option { if entry_kind == crate::ENTRY_KIND_PROGRAM { return None; } if entry_name == "buy" || entry_name == "sell" || entry_name == "buy_exact_quote_in" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_TRADE_EVENTS.to_string()); } if entry_name.starts_with("observed_unknown_") { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string()); } if entry_name.ends_with("_event") && entry_name != "claim_token_incentives_event" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string()); } if entry_name == "deposit" || entry_name == "deposit_event" || entry_name == "withdraw" || entry_name == "withdraw_event" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_EVENTS.to_string()); } if entry_name == "create_pool" || entry_name == "create_pool_event" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_POOL_LIFECYCLE_EVENTS.to_string()); } if entry_name == "collect_coin_creator_fee" || entry_name == "collect_coin_creator_fee_event" || entry_name == "transfer_creator_fees_to_pump" || entry_name == "transfer_creator_fees_to_pump_v2" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_FEE_EVENTS.to_string()); } if entry_name == "claim_cashback" || entry_name == "claim_cashback_event" || entry_name == "claim_token_incentives" || entry_name == "claim_token_incentives_event" || entry_name == "admin_update_token_incentives" || entry_name == "admin_update_token_incentives_event" || entry_name == "init_user_volume_accumulator" || entry_name == "init_user_volume_accumulator_event" || entry_name == "sync_user_volume_accumulator" || entry_name == "sync_user_volume_accumulator_event" || entry_name == "close_user_volume_accumulator" || entry_name == "close_user_volume_accumulator_event" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_REWARD_EVENTS.to_string()); } if entry_name == "admin_set_coin_creator" || entry_name == "admin_set_coin_creator_event" || entry_name == "create_config" || entry_name == "create_config_event" || entry_name == "disable" || entry_name == "disable_event" || entry_name == "extend_account" || entry_name == "extend_account_event" || entry_name == "migrate_pool_coin_creator" || entry_name == "migrate_pool_coin_creator_event" || entry_name == "reserved_fee_recipients_event" || entry_name == "set_bonding_curve_coin_creator_event" || entry_name == "set_coin_creator" || entry_name == "set_metaplex_coin_creator_event" || entry_name == "set_reserved_fee_recipient" || entry_name == "set_reserved_fee_recipients" || entry_name == "toggle_cashback_enabled" || entry_name == "toggle_mayhem_mode" || entry_name == "update_admin" || entry_name == "update_buyback_config" || entry_name == "update_admin_event" || entry_name == "update_fee_config" || entry_name == "update_fee_config_event" { return Some(crate::DexEventCoverageEntryDto::DB_TARGET_POOL_ADMIN_EVENTS.to_string()); } return Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string()); } fn infer_pump_swap_event_family( entry_name: &str, entry_kind: &str, ) -> std::option::Option { if entry_kind == crate::ENTRY_KIND_PROGRAM { return None; } if entry_name == "buy" || entry_name == "sell" || entry_name == "buy_exact_quote_in" { return Some("swap".to_string()); } if entry_name == "buy_event" || entry_name == "sell_event" { return Some("swap_event_audit".to_string()); } if entry_name == "deposit" || entry_name == "deposit_event" { return Some("liquidity_add".to_string()); } if entry_name == "withdraw" || entry_name == "withdraw_event" { return Some("liquidity_remove".to_string()); } if entry_name == "create_pool" || entry_name == "create_pool_event" { return Some("pool_create".to_string()); } if entry_name.starts_with("observed_unknown_") { return Some("observed_unknown_instruction".to_string()); } if entry_name == "toggle_cashback_enabled" || entry_name == "set_reserved_fee_recipient" || entry_name == "set_reserved_fee_recipients" || entry_name == "reserved_fee_recipients_event" { return Some("admin_config".to_string()); } if entry_name.contains("creator_fee") || entry_name.contains("fee_recipient") { return Some("fee".to_string()); } if entry_name.contains("cashback") || entry_name.contains("incentive") || entry_name.contains("volume_accumulator") { return Some("reward".to_string()); } if entry_name.contains("config") || entry_name.contains("admin") || entry_name.contains("disable") || entry_name.contains("toggle") || entry_name.contains("coin_creator") || entry_name.contains("extend_account") { return Some("admin_config".to_string()); } return infer_event_family(entry_name, entry_kind); } fn infer_pump_fun_event_family( entry_name: &str, entry_kind: &str, ) -> std::option::Option { if entry_kind == crate::ENTRY_KIND_PROGRAM { return None; } match entry_name { "buy" | "sell" | "buy_v2" | "sell_v2" | "buy_exact_quote_in_v2" | "buy_exact_sol_in" | "trade_event" => return Some("swap".to_string()), "create" | "create_event" | "create_v2" | "create_v2_token" | "complete_event" => { return Some("launch".to_string()); }, "migrate" | "migrate_v2" | "migrate_bonding_curve_creator" | "migrate_bonding_curve_creator_event" | "complete_pump_amm_migration_event" => return Some("migration".to_string()), "claim_cashback" | "claim_cashback_v2" | "claim_cashback_event" | "claim_token_incentives" | "claim_token_incentives_event" | "admin_update_token_incentives" | "admin_update_token_incentives_event" | "init_user_volume_accumulator" | "init_user_volume_accumulator_event" | "sync_user_volume_accumulator" | "sync_user_volume_accumulator_event" | "close_user_volume_accumulator" | "close_user_volume_accumulator_event" => return Some("reward".to_string()), "collect_creator_fee" | "collect_creator_fee_v2" | "collect_creator_fee_event" | "distribute_creator_fees" | "distribute_creator_fees_v2" | "distribute_creator_fees_event" | "get_minimum_distributable_fee" | "minimum_distributable_fee_event" => return Some("fee".to_string()), "add_quote_mint" | "remove_quote_mint" | "admin_set_creator" | "admin_set_creator_event" | "admin_set_idl_authority" | "admin_set_idl_authority_event" | "extend_account" | "extend_account_event" | "set_creator" | "set_creator_event" | "set_mayhem_virtual_params" | "update_mayhem_virtual_params_event" | "set_metaplex_creator" | "set_metaplex_creator_event" | "set_params" | "set_params_event" | "set_reserved_fee_recipients" | "reserved_fee_recipients_event" | "set_virtual_quote_reserves" | "toggle_cashback_enabled" | "toggle_create_v2" | "toggle_mayhem_mode" | "update_buyback_config" | "update_global_authority" | "update_global_authority_event" => return Some("admin_config".to_string()), "initialize" => return Some("pool_create".to_string()), _ => return infer_event_family(entry_name, entry_kind), } } fn pump_fun_local_event_kind(entry_name: &str) -> std::option::Option { if entry_name.ends_with("_event") { return Some(format!("pump_fun.{}", entry_name)); } match entry_name { "buy" => return Some("pump_fun.buy".to_string()), "sell" => return Some("pump_fun.sell".to_string()), "create_v2" => return Some("pump_fun.create_v2_token".to_string()), "add_quote_mint" => return Some("pump_fun.add_quote_mint".to_string()), "admin_set_creator" => return Some("pump_fun.admin_set_creator".to_string()), "admin_set_idl_authority" => { return Some("pump_fun.admin_set_idl_authority".to_string()); }, "admin_update_token_incentives" => { return Some("pump_fun.admin_update_token_incentives".to_string()); }, "buy_exact_quote_in_v2" => { return Some("pump_fun.buy_exact_quote_in_v2".to_string()); }, "buy_exact_sol_in" => return Some("pump_fun.buy_exact_sol_in".to_string()), "buy_v2" => return Some("pump_fun.buy_v2".to_string()), "claim_cashback" => return Some("pump_fun.claim_cashback".to_string()), "claim_cashback_v2" => return Some("pump_fun.claim_cashback_v2".to_string()), "claim_token_incentives" => { return Some("pump_fun.claim_token_incentives".to_string()); }, "close_user_volume_accumulator" => { return Some("pump_fun.close_user_volume_accumulator".to_string()); }, "collect_creator_fee" => return Some("pump_fun.collect_creator_fee".to_string()), "collect_creator_fee_v2" => return Some("pump_fun.collect_creator_fee_v2".to_string()), "create" => return Some("pump_fun.create".to_string()), "distribute_creator_fees" => { return Some("pump_fun.distribute_creator_fees".to_string()); }, "distribute_creator_fees_v2" => { return Some("pump_fun.distribute_creator_fees_v2".to_string()); }, "extend_account" => return Some("pump_fun.extend_account".to_string()), "get_minimum_distributable_fee" => { return Some("pump_fun.get_minimum_distributable_fee".to_string()); }, "init_user_volume_accumulator" => { return Some("pump_fun.init_user_volume_accumulator".to_string()); }, "initialize" => return Some("pump_fun.initialize".to_string()), "migrate" => return Some("pump_fun.migrate".to_string()), "migrate_bonding_curve_creator" => { return Some("pump_fun.migrate_bonding_curve_creator".to_string()); }, "migrate_v2" => return Some("pump_fun.migrate_v2".to_string()), "remove_quote_mint" => return Some("pump_fun.remove_quote_mint".to_string()), "sell_v2" => return Some("pump_fun.sell_v2".to_string()), "set_creator" => return Some("pump_fun.set_creator".to_string()), "set_mayhem_virtual_params" => { return Some("pump_fun.set_mayhem_virtual_params".to_string()); }, "set_metaplex_creator" => return Some("pump_fun.set_metaplex_creator".to_string()), "set_params" => return Some("pump_fun.set_params".to_string()), "set_reserved_fee_recipients" => { return Some("pump_fun.set_reserved_fee_recipients".to_string()); }, "set_virtual_quote_reserves" => { return Some("pump_fun.set_virtual_quote_reserves".to_string()); }, "sync_user_volume_accumulator" => { return Some("pump_fun.sync_user_volume_accumulator".to_string()); }, "toggle_cashback_enabled" => { return Some("pump_fun.toggle_cashback_enabled".to_string()); }, "toggle_create_v2" => return Some("pump_fun.toggle_create_v2".to_string()), "toggle_mayhem_mode" => return Some("pump_fun.toggle_mayhem_mode".to_string()), "update_buyback_config" => return Some("pump_fun.update_buyback_config".to_string()), "update_global_authority" => { return Some("pump_fun.update_global_authority".to_string()); }, _ => return None, } } fn pump_swap_local_event_kind(entry_name: &str) -> std::option::Option { if entry_name.ends_with("_event") { return Some(format!("pump_swap.{}", entry_name)); } match entry_name { "admin_set_coin_creator" => return Some("pump_swap.admin_set_coin_creator".to_string()), "admin_update_token_incentives" => { return Some("pump_swap.admin_update_token_incentives".to_string()); }, "buy" => return Some("pump_swap.buy".to_string()), "buy_exact_quote_in" => return Some("pump_swap.buy_exact_quote_in".to_string()), "claim_cashback" => return Some("pump_swap.claim_cashback".to_string()), "claim_token_incentives" => return Some("pump_swap.claim_token_incentives".to_string()), "close_user_volume_accumulator" => { return Some("pump_swap.close_user_volume_accumulator".to_string()); }, "collect_coin_creator_fee" => { return Some("pump_swap.collect_coin_creator_fee".to_string()); }, "create_config" => return Some("pump_swap.create_config".to_string()), "create_pool" => return Some("pump_swap.create_pool".to_string()), "deposit" => return Some("pump_swap.deposit".to_string()), "disable" => return Some("pump_swap.disable".to_string()), "extend_account" => return Some("pump_swap.extend_account".to_string()), "init_user_volume_accumulator" => { return Some("pump_swap.init_user_volume_accumulator".to_string()); }, "migrate_pool_coin_creator" => { return Some("pump_swap.migrate_pool_coin_creator".to_string()); }, "sell" => return Some("pump_swap.sell".to_string()), "set_coin_creator" => return Some("pump_swap.set_coin_creator".to_string()), "set_reserved_fee_recipients" => { return Some("pump_swap.set_reserved_fee_recipients".to_string()); }, "sync_user_volume_accumulator" => { return Some("pump_swap.sync_user_volume_accumulator".to_string()); }, "toggle_cashback_enabled" => return Some("pump_swap.toggle_cashback_enabled".to_string()), "toggle_mayhem_mode" => return Some("pump_swap.toggle_mayhem_mode".to_string()), "transfer_creator_fees_to_pump" => { return Some("pump_swap.transfer_creator_fees_to_pump".to_string()); }, "transfer_creator_fees_to_pump_v2" => { return Some("pump_swap.transfer_creator_fees_to_pump_v2".to_string()); }, "update_admin" => return Some("pump_swap.update_admin".to_string()), "update_buyback_config" => return Some("pump_swap.update_buyback_config".to_string()), "update_fee_config" => return Some("pump_swap.update_fee_config".to_string()), "withdraw" => return Some("pump_swap.withdraw".to_string()), "set_reserved_fee_recipient" => { return Some("pump_swap.set_reserved_fee_recipient".to_string()); }, _ => return None, } } fn infer_event_family_for_entry( decoder_code: &str, entry_name: &str, entry_kind: &str, ) -> std::option::Option { if decoder_code == "pump_fun" { return infer_pump_fun_event_family(entry_name, entry_kind); } if decoder_code == "pump_swap" { return infer_pump_swap_event_family(entry_name, entry_kind); } if decoder_code == "raydium_launchpad" { return infer_raydium_launchpad_event_family(entry_name, entry_kind); } if decoder_code == "raydium_amm_v4" { return infer_raydium_amm_v4_event_family(entry_name, entry_kind); } if decoder_code == "raydium_clmm" { return infer_raydium_clmm_event_family(entry_name, entry_kind); } if decoder_code == "raydium_cpmm" { return infer_raydium_cpmm_event_family(entry_name, entry_kind); } if decoder_code == "raydium_stable_swap" { return infer_raydium_stable_swap_event_family(entry_name, entry_kind); } return infer_event_family(entry_name, entry_kind); } fn infer_raydium_amm_v4_event_family( entry_name: &str, entry_kind: &str, ) -> std::option::Option { if entry_kind == crate::ENTRY_KIND_PROGRAM { return None; } match entry_name { "swap_base_in" => return Some("swap".to_string()), "swap_base_out" => return Some("swap".to_string()), "swap_base_in_v2" => return Some("swap".to_string()), "swap_base_out_v2" => return Some("swap".to_string()), "initialize" => return Some("pool_create".to_string()), "initialize2" => return Some("pool_create".to_string()), "pre_initialize" => return Some("pool_create".to_string()), "deposit" => return Some("liquidity_add".to_string()), "withdraw" => return Some("liquidity_remove".to_string()), "withdraw_pnl" => return Some("fee".to_string()), "withdraw_srm" => return Some("fee".to_string()), "admin_cancel_orders" => return Some("order_cancel".to_string()), "migrate_to_open_book" => return Some("order_place".to_string()), "create_config_account" => return Some("admin_config".to_string()), "update_config_account" => return Some("admin_config".to_string()), "set_params" => return Some("admin_config".to_string()), "monitor_step" => return Some("order_place".to_string()), "simulate_info" => return Some("cpi_transport".to_string()), _ => return infer_event_family(entry_name, entry_kind), } } fn infer_raydium_cpmm_event_family( entry_name: &str, entry_kind: &str, ) -> std::option::Option { if entry_kind == crate::ENTRY_KIND_PROGRAM { return None; } match entry_name { "anchor_idl_instruction" => return Some("idl_management".to_string()), "cpi_event" => return Some("cpi_transport".to_string()), _ => return infer_event_family(entry_name, entry_kind), } } fn infer_raydium_stable_swap_event_family( entry_name: &str, entry_kind: &str, ) -> std::option::Option { if entry_kind == crate::ENTRY_KIND_PROGRAM { return None; } match entry_name { "initialize" => return Some("pool_create".to_string()), "pre_initialize" => return Some("pool_create".to_string()), "init_model_data" => return Some("model_setup".to_string()), "update_model_data" => return Some("admin_config".to_string()), "deposit" => return Some("liquidity_add".to_string()), "withdraw" => return Some("liquidity_remove".to_string()), "monitor_step" => return Some("order_place".to_string()), "set_params" => return Some("admin_config".to_string()), "withdraw_pnl" => return Some("fee".to_string()), "withdraw_srm" => return Some("fee".to_string()), "swap_base_in" => return Some("swap".to_string()), "swap_base_out" => return Some("swap".to_string()), "simulate_info" => return Some("cpi_transport".to_string()), "admin_cancel_orders" => return Some("orderbook_admin".to_string()), "swap_event" => return Some("cpi_transport".to_string()), _ => return infer_event_family(entry_name, entry_kind), } } fn infer_raydium_clmm_event_family( entry_name: &str, entry_kind: &str, ) -> std::option::Option { if entry_kind == crate::ENTRY_KIND_PROGRAM { return None; } match entry_name { "cpi_event" => return Some("cpi_transport".to_string()), "create_personal_position_event" => return Some("position_open".to_string()), "liquidity_calculate_event" => return Some("liquidity_calculation".to_string()), "liquidity_change_event" => return Some("liquidity_change".to_string()), "pool_created_event" => return Some("pool_create".to_string()), "create_operation_account" => return Some("admin_config".to_string()), "update_operation_account" => return Some("admin_config".to_string()), "update_dynamic_fee_config" => return Some("admin_config".to_string()), _ => return infer_event_family(entry_name, entry_kind), } } fn infer_raydium_launchpad_event_family( entry_name: &str, entry_kind: &str, ) -> std::option::Option { if entry_kind == crate::ENTRY_KIND_PROGRAM { return None; } match entry_name { "buy_exact_in" => return Some("swap".to_string()), "buy_exact_out" => return Some("swap".to_string()), "sell_exact_in" => return Some("swap".to_string()), "sell_exact_out" => return Some("swap".to_string()), "trade_event" => return Some("swap".to_string()), "pool_create_event" => return Some("pool_create".to_string()), "initialize" => return Some("pool_create".to_string()), "initialize_v2" => return Some("pool_create".to_string()), "initialize_with_token_2022" => return Some("pool_create".to_string()), "claim_creator_fee" => return Some("fee".to_string()), "claim_platform_fee" => return Some("fee".to_string()), "claim_platform_fee_from_vault" => return Some("fee".to_string()), "collect_fee" => return Some("fee".to_string()), "collect_migrate_fee" => return Some("fee".to_string()), "claim_vested_event" => return Some("vesting".to_string()), "claim_vested_token" => return Some("vesting".to_string()), "create_platform_vesting_account" => return Some("vesting".to_string()), "create_vesting_account" => return Some("vesting".to_string()), "create_vesting_event" => return Some("vesting".to_string()), "migrate_to_amm" => return Some("migration".to_string()), "migrate_to_cpswap" => return Some("migration".to_string()), "close_platform_global_access" => return Some("account_close".to_string()), "create_config" => return Some("admin_config".to_string()), "create_platform_config" => return Some("admin_config".to_string()), "create_platform_global_access" => return Some("admin_config".to_string()), "remove_platform_curve_param" => return Some("admin_config".to_string()), "update_config" => return Some("admin_config".to_string()), "update_platform_config" => return Some("admin_config".to_string()), "update_platform_curve_param" => return Some("admin_config".to_string()), "cpi_event" => return Some("cpi_transport".to_string()), _ => return infer_event_family(entry_name, entry_kind), } } fn infer_event_family( entry_name: &str, entry_kind: &str, ) -> std::option::Option { if entry_kind == crate::ENTRY_KIND_PROGRAM { return None; } let normalized = entry_name.to_ascii_lowercase(); if normalized == "lp_change_event" { return Some("liquidity".to_string()); } if normalized == "cpi_event" { return Some("cpi_transport".to_string()); } if contains_any(normalized.as_str(), &["migrate", "migration", "graduate"]) { return Some("migration".to_string()); } if contains_any(normalized.as_str(), &["swap", "buy", "sell", "trade"]) { return Some("swap".to_string()); } if contains_any(normalized.as_str(), &["create_pool", "initialize_pool", "initialize2"]) || normalized == "create_customizable_pool" || normalized == "initialize" || normalized.starts_with("initialize_") { return Some("pool_create".to_string()); } if contains_any(normalized.as_str(), &["add_liquidity", "increase_liquidity", "deposit"]) || normalized.contains("bootstrap_liquidity") { return Some("liquidity_add".to_string()); } if contains_any(normalized.as_str(), &["remove_liquidity", "decrease_liquidity", "withdraw"]) && !normalized.contains("funds") { return Some("liquidity_remove".to_string()); } if contains_any( normalized.as_str(), &["open_position", "initialize_position", "position_create"], ) { return Some("position_open".to_string()); } if contains_any(normalized.as_str(), &["close_position", "position_close"]) || normalized.contains("close_position_if_empty") || normalized == "close_protocol_position" { return Some("position_close".to_string()); } if contains_any(normalized.as_str(), &["fee", "collect", "claim_fee"]) && !normalized.contains("reward") && !normalized.contains("config") { return Some("fee".to_string()); } if normalized.contains("reward") { return Some("reward".to_string()); } if contains_any( normalized.as_str(), &["config", "admin", "authority", "permission", "pause", "status", "update_pool"], ) { return Some("admin_config".to_string()); } if normalized == "create_support_mint_associated" { return Some("account_create".to_string()); } if normalized.contains("mint") { return Some("mint".to_string()); } if normalized.contains("burn") { return Some("burn".to_string()); } if normalized.contains("transfer") { return Some("transfer".to_string()); } if contains_any(normalized.as_str(), &["create_ata", "init_account", "open_orders_create"]) { return Some("account_create".to_string()); } if normalized.contains("wrap_sol") { return Some("wrap_sol".to_string()); } if normalized.contains("unwrap_sol") { return Some("unwrap_sol".to_string()); } if normalized.contains("place_order") || normalized.contains("post_order") || normalized == "open_limit_order" || normalized == "increase_limit_order" { return Some("order_place".to_string()); } if normalized.contains("cancel_order") || normalized.contains("cancel_all") || normalized == "close_limit_order" || normalized == "decrease_limit_order" { return Some("order_cancel".to_string()); } if normalized.contains("fill") { return Some("order_fill".to_string()); } if normalized.contains("consume_events") { return Some("consume_events".to_string()); } if normalized.contains("settle_funds") || normalized == "settle_limit_order" { return Some("settle_funds".to_string()); } if contains_any(normalized.as_str(), &["close_account", "close_open_orders"]) || normalized.starts_with("close_") { return Some("account_close".to_string()); } if normalized.contains("vault") && normalized.contains("deposit") { return Some("vault_deposit".to_string()); } if normalized.contains("vault") && normalized.contains("withdraw") { return Some("vault_withdraw".to_string()); } if contains_any(normalized.as_str(), &["lock_liquidity", "create_lock", "lock"]) && !normalized.contains("unlock") { return Some("lock".to_string()); } if normalized.contains("unlock") { return Some("unlock".to_string()); } if contains_any(normalized.as_str(), &["launch", "create_bonding", "bonding_curve"]) { return Some("launch".to_string()); } if contains_any(normalized.as_str(), &["migrate", "migration", "graduate"]) { return Some("migration".to_string()); } if normalized.contains("unstake") { return Some("unstake".to_string()); } if normalized.contains("stake") { return Some("stake".to_string()); } return Some("unknown".to_string()); } fn contains_any(value: &str, needles: &[&str]) -> bool { for needle in needles { if value.contains(needle) { return true; } } return false; } fn raydium_launchpad_local_entry_is_known(entry_name: &str) -> bool { match entry_name { "buy_exact_in" => return true, "buy_exact_out" => return true, "close_platform_global_access" => return true, "claim_creator_fee" => return true, "claim_platform_fee" => return true, "claim_platform_fee_from_vault" => return true, "claim_vested_event" => return true, "claim_vested_token" => return true, "collect_fee" => return true, "collect_migrate_fee" => return true, "create_config" => return true, "create_platform_config" => return true, "cpi_event" => return true, "create_platform_vesting_account" => return true, "create_platform_global_access" => return true, "create_vesting_account" => return true, "create_vesting_event" => return true, "initialize" => return true, "initialize_v2" => return true, "initialize_with_token_2022" => return true, "migrate_to_amm" => return true, "migrate_to_cpswap" => return true, "pool_create_event" => return true, "remove_platform_curve_param" => return true, "sell_exact_in" => return true, "sell_exact_out" => return true, "trade_event" => return true, "update_config" => return true, "update_platform_config" => return true, "update_platform_curve_param" => return true, _ => return false, } } fn raydium_amm_v4_local_event_kind(entry_name: &str) -> std::option::Option { match entry_name { "swap_base_in" => return Some("raydium_amm_v4.swap_base_in".to_string()), "swap_base_out" => return Some("raydium_amm_v4.swap_base_out".to_string()), "swap_base_in_v2" => return Some("raydium_amm_v4.swap_base_in_v2".to_string()), "swap_base_out_v2" => return Some("raydium_amm_v4.swap_base_out_v2".to_string()), "initialize" => return Some("raydium_amm_v4.initialize".to_string()), "initialize2" => return Some("raydium_amm_v4.initialize2_pool".to_string()), "pre_initialize" => return Some("raydium_amm_v4.pre_initialize".to_string()), "deposit" => return Some("raydium_amm_v4.deposit".to_string()), "withdraw" => return Some("raydium_amm_v4.withdraw".to_string()), "withdraw_pnl" => return Some("raydium_amm_v4.withdraw_pnl".to_string()), "withdraw_srm" => return Some("raydium_amm_v4.withdraw_srm".to_string()), "admin_cancel_orders" => return Some("raydium_amm_v4.admin_cancel_orders".to_string()), "migrate_to_open_book" => return Some("raydium_amm_v4.migrate_to_open_book".to_string()), "create_config_account" => return Some("raydium_amm_v4.create_config_account".to_string()), "update_config_account" => return Some("raydium_amm_v4.update_config_account".to_string()), "set_params" => return Some("raydium_amm_v4.set_params".to_string()), "monitor_step" => return Some("raydium_amm_v4.monitor_step".to_string()), "simulate_info" => return Some("raydium_amm_v4.simulate_info".to_string()), _ => return None, } } fn raydium_stable_swap_local_event_kind( entry_name: &str, ) -> std::option::Option { match entry_name { "initialize" => return Some("raydium_stable_swap.initialize".to_string()), "init_model_data" => return Some("raydium_stable_swap.init_model_data".to_string()), "update_model_data" => return Some("raydium_stable_swap.update_model_data".to_string()), "pre_initialize" => return Some("raydium_stable_swap.pre_initialize".to_string()), "deposit" => return Some("raydium_stable_swap.deposit".to_string()), "withdraw" => return Some("raydium_stable_swap.withdraw".to_string()), "monitor_step" => return Some("raydium_stable_swap.monitor_step".to_string()), "set_params" => return Some("raydium_stable_swap.set_params".to_string()), "withdraw_pnl" => return Some("raydium_stable_swap.withdraw_pnl".to_string()), "withdraw_srm" => return Some("raydium_stable_swap.withdraw_srm".to_string()), "swap_base_in" => return Some("raydium_stable_swap.swap_base_in".to_string()), "swap_base_out" => return Some("raydium_stable_swap.swap_base_out".to_string()), "simulate_info" => return Some("raydium_stable_swap.simulate_info".to_string()), "admin_cancel_orders" => { return Some("raydium_stable_swap.admin_cancel_orders".to_string()); }, "swap_event" => return Some("raydium_stable_swap.swap_event".to_string()), _ => return None, } } pub(crate) fn known_local_event_kind( decoder_code: &str, entry_name: &str, ) -> std::option::Option { if decoder_code == "pump_fun" { return pump_fun_local_event_kind(entry_name); } if decoder_code == "pump_swap" { return pump_swap_local_event_kind(entry_name); } if decoder_code == "raydium_amm_v4" { return raydium_amm_v4_local_event_kind(entry_name); } if decoder_code == "raydium_stable_swap" { return raydium_stable_swap_local_event_kind(entry_name); } if decoder_code == "raydium_launchpad" && raydium_launchpad_local_entry_is_known(entry_name) { return Some(format!("raydium_launchpad.{}", entry_name)); } match (decoder_code, entry_name) { ("raydium_cpmm", "swap_base_input") => { return Some("raydium_cpmm.swap_base_input".to_string()); }, ("raydium_cpmm", "swap_base_output") => { return Some("raydium_cpmm.swap_base_output".to_string()); }, ("raydium_cpmm", "close_permission_pda") => { return Some("raydium_cpmm.close_permission_pda".to_string()); }, ("raydium_cpmm", "collect_creator_fee") => { return Some("raydium_cpmm.collect_creator_fee".to_string()); }, ("raydium_cpmm", "collect_fund_fee") => { return Some("raydium_cpmm.collect_fund_fee".to_string()); }, ("raydium_cpmm", "collect_protocol_fee") => { return Some("raydium_cpmm.collect_protocol_fee".to_string()); }, ("raydium_cpmm", "create_amm_config") => { return Some("raydium_cpmm.create_amm_config".to_string()); }, ("raydium_cpmm", "create_permission_pda") => { return Some("raydium_cpmm.create_permission_pda".to_string()); }, ("raydium_cpmm", "deposit") => return Some("raydium_cpmm.deposit".to_string()), ("raydium_cpmm", "initialize") => return Some("raydium_cpmm.initialize".to_string()), ("raydium_cpmm", "initialize_with_permission") => { return Some("raydium_cpmm.initialize_with_permission".to_string()); }, ("raydium_cpmm", "lp_change_event") => { return Some("raydium_cpmm.lp_change_event".to_string()); }, ("raydium_cpmm", "cpi_event") => return Some("raydium_cpmm.cpi_event".to_string()), ("raydium_cpmm", "anchor_idl_instruction") => { return Some("raydium_cpmm.anchor_idl_instruction".to_string()); }, ("raydium_cpmm", "swap_event") => return Some("raydium_cpmm.swap_event".to_string()), ("raydium_cpmm", "update_amm_config") => { return Some("raydium_cpmm.update_amm_config".to_string()); }, ("raydium_cpmm", "update_pool_status") => { return Some("raydium_cpmm.update_pool_status".to_string()); }, ("raydium_cpmm", "withdraw") => return Some("raydium_cpmm.withdraw".to_string()), ("raydium_clmm", "cpi_event") => return Some("raydium_clmm.cpi_event".to_string()), ("raydium_clmm", "collect_personal_fee_event") => { return Some("raydium_clmm.collect_personal_fee_event".to_string()); }, ("raydium_clmm", "collect_protocol_fee_event") => { return Some("raydium_clmm.collect_protocol_fee_event".to_string()); }, ("raydium_clmm", "config_change_event") => { return Some("raydium_clmm.config_change_event".to_string()); }, ("raydium_clmm", "create_personal_position_event") => { return Some("raydium_clmm.create_personal_position_event".to_string()); }, ("raydium_clmm", "decrease_liquidity_event") => { return Some("raydium_clmm.decrease_liquidity_event".to_string()); }, ("raydium_clmm", "increase_liquidity_event") => { return Some("raydium_clmm.increase_liquidity_event".to_string()); }, ("raydium_clmm", "liquidity_calculate_event") => { return Some("raydium_clmm.liquidity_calculate_event".to_string()); }, ("raydium_clmm", "liquidity_change_event") => { return Some("raydium_clmm.liquidity_change_event".to_string()); }, ("raydium_clmm", "pool_created_event") => { return Some("raydium_clmm.pool_created_event".to_string()); }, ("raydium_clmm", "swap_event") => return Some("raydium_clmm.swap_event".to_string()), ("raydium_clmm", "update_reward_infos_event") => { return Some("raydium_clmm.update_reward_infos_event".to_string()); }, ("raydium_clmm", "close_limit_order") => { return Some("raydium_clmm.close_limit_order".to_string()); }, ("raydium_clmm", "open_limit_order") => { return Some("raydium_clmm.open_limit_order".to_string()); }, ("raydium_clmm", "increase_limit_order") => { return Some("raydium_clmm.increase_limit_order".to_string()); }, ("raydium_clmm", "decrease_limit_order") => { return Some("raydium_clmm.decrease_limit_order".to_string()); }, ("raydium_clmm", "close_position") => { return Some("raydium_clmm.close_position".to_string()); }, ("raydium_clmm", "close_protocol_position") => { return Some("raydium_clmm.close_protocol_position".to_string()); }, ("raydium_clmm", "collect_fund_fee") => { return Some("raydium_clmm.collect_fund_fee".to_string()); }, ("raydium_clmm", "collect_protocol_fee") => { return Some("raydium_clmm.collect_protocol_fee".to_string()); }, ("raydium_clmm", "collect_remaining_rewards") => { return Some("raydium_clmm.collect_remaining_rewards".to_string()); }, ("raydium_clmm", "create_amm_config") => { return Some("raydium_clmm.create_amm_config".to_string()); }, ("raydium_clmm", "create_customizable_pool") => { return Some("raydium_clmm.create_customizable_pool".to_string()); }, ("raydium_clmm", "create_dynamic_fee_config") => { return Some("raydium_clmm.create_dynamic_fee_config".to_string()); }, ("raydium_clmm", "create_operation_account") => { return Some("raydium_clmm.create_operation_account".to_string()); }, ("raydium_clmm", "create_pool") => return Some("raydium_clmm.create_pool".to_string()), ("raydium_clmm", "create_support_mint_associated") => { return Some("raydium_clmm.create_support_mint_associated".to_string()); }, ("raydium_clmm", "decrease_liquidity") => { return Some("raydium_clmm.decrease_liquidity".to_string()); }, ("raydium_clmm", "decrease_liquidity_v2") => { return Some("raydium_clmm.decrease_liquidity_v2".to_string()); }, ("raydium_clmm", "increase_liquidity") => { return Some("raydium_clmm.increase_liquidity".to_string()); }, ("raydium_clmm", "increase_liquidity_v2") => { return Some("raydium_clmm.increase_liquidity_v2".to_string()); }, ("raydium_clmm", "initialize_reward") => { return Some("raydium_clmm.initialize_reward".to_string()); }, ("raydium_clmm", "open_position") => { return Some("raydium_clmm.open_position".to_string()); }, ("raydium_clmm", "open_position_v2") => { return Some("raydium_clmm.open_position_v2".to_string()); }, ("raydium_clmm", "open_position_with_token22_nft") => { return Some("raydium_clmm.open_position_with_token22_nft".to_string()); }, ("raydium_clmm", "set_reward_params") => { return Some("raydium_clmm.set_reward_params".to_string()); }, ("raydium_clmm", "settle_limit_order") => { return Some("raydium_clmm.settle_limit_order".to_string()); }, ("raydium_clmm", "swap") => return Some("raydium_clmm.swap".to_string()), ("raydium_clmm", "swap_router_base_in") => { return Some("raydium_clmm.swap_router_base_in".to_string()); }, ("raydium_clmm", "swap_v2") => return Some("raydium_clmm.swap_v2".to_string()), ("raydium_clmm", "transfer_reward_owner") => { return Some("raydium_clmm.transfer_reward_owner".to_string()); }, ("raydium_clmm", "update_amm_config") => { return Some("raydium_clmm.update_amm_config".to_string()); }, ("raydium_clmm", "update_dynamic_fee_config") => { return Some("raydium_clmm.update_dynamic_fee_config".to_string()); }, ("raydium_clmm", "update_operation_account") => { return Some("raydium_clmm.update_operation_account".to_string()); }, ("raydium_clmm", "update_pool_status") => { return Some("raydium_clmm.update_pool_status".to_string()); }, ("raydium_clmm", "update_reward_infos") => { return Some("raydium_clmm.update_reward_infos".to_string()); }, _ => return None, } } #[cfg(test)] mod tests { async fn make_database() -> std::sync::Arc { let tempdir_result = tempfile::tempdir(); let tempdir = match tempdir_result { Ok(tempdir) => tempdir, Err(error) => panic!("tempdir must succeed: {}", error), }; let database_path = tempdir.path().join("dex_event_coverage.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_result = crate::Database::connect_and_initialize(&config).await; let database = match database_result { Ok(database) => database, Err(error) => panic!("database init must succeed: {}", error), }; return std::sync::Arc::new(database); } #[test] fn event_family_inference_covers_raydium_cpmm_core_entries() { assert_eq!( super::infer_event_family("swap_base_input", crate::ENTRY_KIND_INSTRUCTION), Some("swap".to_string()) ); assert_eq!( super::infer_event_family("initialize", crate::ENTRY_KIND_INSTRUCTION), Some("pool_create".to_string()) ); assert_eq!( super::infer_event_family("withdraw", crate::ENTRY_KIND_INSTRUCTION), Some("liquidity_remove".to_string()) ); assert_eq!( super::infer_event_family("collect_creator_fee", crate::ENTRY_KIND_INSTRUCTION), Some("fee".to_string()) ); assert_eq!( super::infer_event_family_for_entry( "raydium_cpmm", "anchor_idl_instruction", crate::ENTRY_KIND_INSTRUCTION, ), Some("idl_management".to_string()) ); assert_eq!( super::infer_expected_db_target_for_entry( "raydium_cpmm", "anchor_idl_instruction", Some("idl_management"), crate::ENTRY_KIND_INSTRUCTION, ), Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string()) ); } #[test] fn event_family_inference_covers_raydium_clmm_idl_entries() { assert_eq!( super::infer_event_family("create_customizable_pool", crate::ENTRY_KIND_INSTRUCTION), Some("pool_create".to_string()) ); assert_eq!( super::infer_event_family("create_dynamic_fee_config", crate::ENTRY_KIND_INSTRUCTION), Some("admin_config".to_string()) ); assert_eq!( super::infer_event_family_for_entry( "raydium_clmm", "update_dynamic_fee_config", crate::ENTRY_KIND_INSTRUCTION, ), Some("admin_config".to_string()) ); assert_eq!( super::infer_event_family_for_entry( "raydium_clmm", "create_personal_position_event", crate::ENTRY_KIND_EVENT, ), Some("position_open".to_string()) ); assert_eq!( super::infer_event_family_for_entry( "raydium_clmm", "liquidity_calculate_event", crate::ENTRY_KIND_EVENT, ), Some("liquidity_calculation".to_string()) ); assert_eq!( super::infer_event_family_for_entry( "raydium_clmm", "liquidity_change_event", crate::ENTRY_KIND_EVENT, ), Some("liquidity_change".to_string()) ); assert_eq!( super::infer_event_family( "create_support_mint_associated", crate::ENTRY_KIND_INSTRUCTION, ), Some("account_create".to_string()) ); assert_eq!( super::infer_event_family("close_limit_order", crate::ENTRY_KIND_INSTRUCTION), Some("order_cancel".to_string()) ); assert_eq!( super::infer_event_family("open_limit_order", crate::ENTRY_KIND_INSTRUCTION), Some("order_place".to_string()) ); assert_eq!( super::infer_event_family("increase_limit_order", crate::ENTRY_KIND_INSTRUCTION), Some("order_place".to_string()) ); assert_eq!( super::infer_event_family("decrease_limit_order", crate::ENTRY_KIND_INSTRUCTION), Some("order_cancel".to_string()) ); assert_eq!( super::infer_event_family("close_protocol_position", crate::ENTRY_KIND_INSTRUCTION), Some("position_close".to_string()) ); assert_eq!( super::infer_event_family("settle_limit_order", crate::ENTRY_KIND_INSTRUCTION), Some("settle_funds".to_string()) ); assert_eq!( super::infer_expected_db_target(Some("position_open"), crate::ENTRY_KIND_INSTRUCTION), Some(crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_EVENTS.to_string()) ); assert_eq!( super::infer_expected_db_target_for_entry( "raydium_clmm", "open_position", Some("position_open"), crate::ENTRY_KIND_INSTRUCTION, ), Some(crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string()) ); assert_eq!( super::infer_expected_db_target(Some("position_close"), crate::ENTRY_KIND_INSTRUCTION), Some(crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_EVENTS.to_string()) ); assert_eq!( super::known_local_event_kind("raydium_clmm", "create_pool"), Some("raydium_clmm.create_pool".to_string()) ); assert_eq!( super::known_local_event_kind("raydium_clmm", "collect_protocol_fee"), Some("raydium_clmm.collect_protocol_fee".to_string()) ); assert_eq!( super::known_local_event_kind("raydium_clmm", "open_limit_order"), Some("raydium_clmm.open_limit_order".to_string()) ); assert_eq!( super::known_local_event_kind("raydium_clmm", "increase_limit_order"), Some("raydium_clmm.increase_limit_order".to_string()) ); assert_eq!( super::known_local_event_kind("raydium_clmm", "decrease_limit_order"), Some("raydium_clmm.decrease_limit_order".to_string()) ); assert_eq!( super::known_local_event_kind("raydium_clmm", "update_dynamic_fee_config"), Some("raydium_clmm.update_dynamic_fee_config".to_string()) ); assert_eq!( super::known_local_event_kind("raydium_clmm", "cpi_event"), Some("raydium_clmm.cpi_event".to_string()) ); assert_eq!( super::known_local_event_kind("raydium_clmm", "pool_created_event"), Some("raydium_clmm.pool_created_event".to_string()) ); } #[test] fn pump_fun_coverage_maps_local_idl_and_audit_entries() { assert_eq!( super::known_local_event_kind("pump_fun", "buy"), Some("pump_fun.buy".to_string()) ); assert_eq!( super::known_local_event_kind("pump_fun", "create_v2"), Some("pump_fun.create_v2_token".to_string()) ); assert_eq!( super::known_local_event_kind("pump_fun", "buy_v2"), Some("pump_fun.buy_v2".to_string()) ); assert_eq!( super::known_local_event_kind("pump_fun", "collect_creator_fee_v2"), Some("pump_fun.collect_creator_fee_v2".to_string()) ); assert_eq!( super::known_local_event_kind("pump_fun", "trade_event"), Some("pump_fun.trade_event".to_string()) ); assert_eq!( super::known_local_event_kind("pump_fun", "claim_cashback_event"), Some("pump_fun.claim_cashback_event".to_string()) ); assert_eq!( super::infer_event_family_for_entry("pump_fun", "create_event", crate::ENTRY_KIND_EVENT), Some("launch".to_string()) ); assert_eq!( super::infer_event_family_for_entry( "pump_fun", "set_metaplex_creator_event", crate::ENTRY_KIND_EVENT, ), Some("admin_config".to_string()) ); assert_eq!( super::infer_event_family_for_entry( "pump_fun", "claim_token_incentives_event", crate::ENTRY_KIND_EVENT, ), Some("reward".to_string()) ); assert_eq!( super::infer_event_family_for_entry("pump_fun", "buy_v2", crate::ENTRY_KIND_INSTRUCTION), Some("swap".to_string()) ); assert_eq!( super::infer_expected_db_target_for_entry( "pump_fun", "buy", Some("swap"), crate::ENTRY_KIND_INSTRUCTION, ), Some(crate::DexEventCoverageEntryDto::DB_TARGET_TRADE_EVENTS.to_string()) ); assert_eq!( super::infer_expected_db_target_for_entry( "pump_fun", "buy_v2", Some("swap"), crate::ENTRY_KIND_INSTRUCTION, ), Some(crate::DexEventCoverageEntryDto::DB_TARGET_TRADE_EVENTS.to_string()) ); assert_eq!( super::infer_expected_db_target_for_entry( "pump_fun", "create_v2", Some("launch"), crate::ENTRY_KIND_INSTRUCTION, ), Some(crate::DexEventCoverageEntryDto::DB_TARGET_LAUNCH_EVENTS.to_string()) ); } #[test] fn launchpad_swap_instructions_materialize_as_launch_events_without_duplicate_trades() { assert_eq!( super::known_local_event_kind("raydium_launchpad", "buy_exact_in"), Some("raydium_launchpad.buy_exact_in".to_string()) ); assert_eq!( super::known_local_event_kind("raydium_launchpad", "trade_event"), Some("raydium_launchpad.trade_event".to_string()) ); assert_eq!(super::known_local_event_kind("raydium_launchpad", "program"), None); assert_eq!( super::infer_event_family_for_entry( "raydium_launchpad", "pool_create_event", crate::ENTRY_KIND_EVENT, ), Some("pool_create".to_string()) ); assert_eq!( super::infer_event_family_for_entry( "raydium_launchpad", "claim_vested_event", crate::ENTRY_KIND_EVENT, ), Some("vesting".to_string()) ); assert_eq!( super::infer_event_family_for_entry( "raydium_launchpad", "create_platform_vesting_account", crate::ENTRY_KIND_INSTRUCTION, ), Some("vesting".to_string()) ); assert_eq!( super::infer_event_family_for_entry( "raydium_launchpad", "remove_platform_curve_param", crate::ENTRY_KIND_INSTRUCTION, ), Some("admin_config".to_string()) ); assert_eq!( super::infer_event_family_for_entry( "raydium_launchpad", "cpi_event", crate::ENTRY_KIND_INSTRUCTION, ), Some("cpi_transport".to_string()) ); assert_eq!( super::infer_expected_db_target_for_entry( "raydium_launchpad", "buy_exact_in", Some("swap"), crate::ENTRY_KIND_INSTRUCTION, ), Some(crate::DexEventCoverageEntryDto::DB_TARGET_LAUNCH_EVENTS.to_string()) ); } #[tokio::test] async fn sync_upstream_registry_persists_raydium_cpmm_coverage_rows() { let database = make_database().await; let service = crate::DexEventCoverageService::new(database.clone()); let result = service.sync_upstream_registry(Some("raydium_cpmm".to_string())).await; let result = match result { Ok(result) => result, Err(error) => panic!("coverage sync must succeed: {}", error), }; assert!(result.upstream_entry_count > 0); assert_eq!(result.upstream_entry_count, result.upserted_entry_count); let rows_result = crate::query_dex_event_coverage_entries_list_by_decoder( database.as_ref(), "raydium_cpmm", ) .await; let rows = match rows_result { Ok(rows) => rows, Err(error) => panic!("coverage rows must load: {}", error), }; assert_eq!(rows.len(), result.upstream_entry_count); assert!(rows.iter().any(|row| return { row.entry_name == "swap_base_input" && row.event_family == Some("swap".to_string()) && row.local_event_kind == Some("raydium_cpmm.swap_base_input".to_string()) })); assert!(rows.iter().any(|row| return { row.entry_name == "deposit" && row.event_family == Some("liquidity_add".to_string()) && row.local_event_kind == Some("raydium_cpmm.deposit".to_string()) })); assert!(rows.iter().any(|row| return { row.entry_name == "lp_change_event" && row.event_family == Some("liquidity".to_string()) && row.expected_db_target == Some(crate::DexEventCoverageEntryDto::DB_TARGET_LIQUIDITY_EVENTS.to_string()) && row.local_event_kind == Some("raydium_cpmm.lp_change_event".to_string()) })); assert!(rows.iter().any(|row| return { row.entry_name == "cpi_event" && row.event_family == Some("cpi_transport".to_string()) && row.expected_db_target == Some( crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string(), ) && row.local_event_kind == Some("raydium_cpmm.cpi_event".to_string()) })); assert!(rows.iter().any(|row| return { row.entry_name == "anchor_idl_instruction" && row.event_family == Some("idl_management".to_string()) && row.expected_db_target == Some( crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string(), ) && row.local_event_kind == Some("raydium_cpmm.anchor_idl_instruction".to_string()) })); assert!(rows.iter().any(|row| return { row.entry_name == "swap_event" && row.event_family == Some("swap".to_string()) && row.expected_db_target == Some( crate::DexEventCoverageEntryDto::DB_TARGET_DECODED_EVENTS_ONLY.to_string(), ) && row.local_event_kind == Some("raydium_cpmm.swap_event".to_string()) })); } #[tokio::test] async fn refresh_local_counts_auto_syncs_empty_coverage_table() { let database = make_database().await; let service = crate::DexEventCoverageService::new(database.clone()); let result = service.refresh_local_counts(Some("raydium_cpmm".to_string())).await; let result = match result { Ok(result) => result, Err(error) => panic!("coverage refresh must succeed: {}", error), }; assert!(result.upstream_entry_count > 0); assert_eq!(result.upstream_entry_count, result.upserted_entry_count); let rows_result = crate::query_dex_event_coverage_entries_list_by_decoder( database.as_ref(), "raydium_cpmm", ) .await; let rows = match rows_result { Ok(rows) => rows, Err(error) => panic!("coverage rows must load: {}", error), }; assert!(!rows.is_empty()); } }