1643 lines
70 KiB
Rust
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());
|
|
}
|
|
}
|