Files
khadhroony-bobobot/kb_lib/src/dex_event_coverage.rs
2026-06-14 14:25:09 +02:00

1643 lines
70 KiB
Rust

// 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<std::string::String>,
/// 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<crate::DexEventCoverageSummaryDto>,
}
/// Service used to persist and refresh DEX event coverage rows.
#[derive(Debug, Clone)]
pub struct DexEventCoverageService {
database: std::sync::Arc<crate::Database>,
upstream_registry: crate::UpstreamRegistryService,
}
impl DexEventCoverageService {
/// Creates a new event coverage service.
pub fn new(database: std::sync::Arc<crate::Database>) -> Self {
return Self {
database,
upstream_registry: crate::UpstreamRegistryService::new(),
};
}
async fn upsert_upstream_registry_rows(
&self,
decoder_code: std::option::Option<std::string::String>,
) -> 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<std::string::String>,
) -> 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<std::string::String>,
) -> Result<crate::DexEventCoverageSyncResult, crate::Error> {
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<std::string::String>,
) -> Result<crate::DexEventCoverageSyncResult, crate::Error> {
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<std::string::String>,
) -> Result<u64, crate::Error> {
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<std::string::String>,
) -> Result<u64, crate::Error> {
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<std::string::String> {
if decoder_code == "pump_swap" {
return infer_pump_swap_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<std::string::String> {
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_swap_expected_db_target(
entry_name: &str,
entry_kind: &str,
) -> std::option::Option<std::string::String> {
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<std::string::String> {
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 pump_swap_local_event_kind(entry_name: &str) -> std::option::Option<std::string::String> {
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<std::string::String> {
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<std::string::String> {
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<std::string::String> {
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<std::string::String> {
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<std::string::String> {
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<std::string::String> {
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<std::string::String> {
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<std::string::String> {
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<std::string::String> {
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<std::string::String> {
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<crate::Database> {
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 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());
}
}