This commit is contained in:
2026-06-01 19:05:46 +02:00
parent abb810d544
commit 27e25d5bf4
59 changed files with 5727 additions and 1706 deletions

View File

@@ -38,15 +38,10 @@ impl DexEventCoverageService {
};
}
/// 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(
async fn upsert_upstream_registry_rows(
&self,
decoder_code: std::option::Option<std::string::String>,
) -> Result<crate::DexEventCoverageSyncResult, crate::Error> {
) -> Result<(usize, usize), crate::Error> {
let request = crate::UpstreamRegistrySearchRequestDto {
decoder_code: decoder_code.clone(),
program_id: None,
@@ -70,6 +65,30 @@ impl DexEventCoverageService {
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 refreshed_entry_count = match &decoder_code {
Some(decoder_code) => {
let refresh_result =
@@ -103,7 +122,7 @@ impl DexEventCoverageService {
};
return Ok(crate::DexEventCoverageSyncResult {
decoder_code,
upstream_entry_count: search_result.entries.len(),
upstream_entry_count,
upserted_entry_count,
refreshed_entry_count,
summaries,
@@ -115,6 +134,11 @@ impl DexEventCoverageService {
&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 refreshed_entry_count = match &decoder_code {
Some(decoder_code) => {
let refresh_result =
@@ -148,8 +172,8 @@ impl DexEventCoverageService {
};
return Ok(crate::DexEventCoverageSyncResult {
decoder_code,
upstream_entry_count: 0,
upserted_entry_count: 0,
upstream_entry_count,
upserted_entry_count,
refreshed_entry_count,
summaries,
});
@@ -160,8 +184,12 @@ fn build_coverage_entry_from_upstream(
entry: &crate::UpstreamRegistryEntryDto,
) -> crate::DexEventCoverageEntryDto {
let event_family = infer_event_family(entry.entry_name.as_str(), entry.entry_kind.as_str());
let expected_db_target =
infer_expected_db_target(event_family.as_deref(), 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(
@@ -177,6 +205,18 @@ fn build_coverage_entry_from_upstream(
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 == "raydium_cpmm" && entry_name == "swap_event" {
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,
@@ -195,6 +235,7 @@ fn infer_expected_db_target(
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,
"position_open" => crate::DexEventCoverageEntryDto::DB_TARGET_POOL_LIFECYCLE_EVENTS,
@@ -235,6 +276,9 @@ fn infer_event_family(
return None;
}
let normalized = entry_name.to_ascii_lowercase();
if normalized == "lp_change_event" {
return Some("liquidity".to_string());
}
if contains_any(normalized.as_str(), &["swap", "buy", "sell", "trade"]) {
return Some("swap".to_string());
}
@@ -360,29 +404,58 @@ fn known_local_event_kind(
entry_name: &str,
) -> std::option::Option<std::string::String> {
match (decoder_code, entry_name) {
("raydium-cpmm", "swap_base_input") => {
("raydium_cpmm", "swap_base_input") => {
return Some("raydium_cpmm.swap_base_input".to_string());
},
("raydium-cpmm", "swap_base_output") => {
("raydium_cpmm", "swap_base_output") => {
return Some("raydium_cpmm.swap_base_output".to_string());
},
("raydium-cpmm", "collect_creator_fee") => {
("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", "withdraw") => return Some("raydium_cpmm.withdraw".to_string()),
("raydium-cpmm", "initialize") => return Some("raydium_cpmm.initialize".to_string()),
("raydium-clmm", "swap") => return Some("raydium_clmm.swap".to_string()),
("raydium-clmm", "swap_v2") => return Some("raydium_clmm.swap_v2".to_string()),
("raydium-clmm", "increase_liquidity_v2") => {
("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", "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", "swap") => return Some("raydium_clmm.swap".to_string()),
("raydium_clmm", "swap_v2") => return Some("raydium_clmm.swap_v2".to_string()),
("raydium_clmm", "increase_liquidity_v2") => {
return Some("raydium_clmm.increase_liquidity_v2".to_string());
},
("raydium-clmm", "decrease_liquidity_v2") => {
("raydium_clmm", "decrease_liquidity_v2") => {
return Some("raydium_clmm.decrease_liquidity_v2".to_string());
},
("raydium-clmm", "open_position_with_token22_nft") => {
("raydium_clmm", "open_position_with_token22_nft") => {
return Some("raydium_clmm.open_position_with_token22_nft".to_string());
},
("raydium-clmm", "close_position") => {
("raydium_clmm", "close_position") => {
return Some("raydium_clmm.close_position".to_string());
},
_ => return None,
@@ -442,7 +515,7 @@ mod tests {
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 = 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),
@@ -451,7 +524,7 @@ mod tests {
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",
"raydium_cpmm",
)
.await;
let rows = match rows_result {
@@ -467,7 +540,46 @@ mod tests {
assert!(rows.iter().any(|row| return {
row.entry_name == "deposit"
&& row.event_family == Some("liquidity_add".to_string())
&& row.local_event_kind.is_none()
&& 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 == "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());
}
}