This commit is contained in:
2026-05-14 14:24:29 +02:00
parent edc8da02a3
commit 403f271083
12 changed files with 291 additions and 13 deletions

View File

@@ -71,6 +71,18 @@ pub struct LocalPipelineDiagnosticSummaryDto {
pub token_count: i64,
/// Total tokens missing symbol or name.
pub token_metadata_missing_count: i64,
/// Total tokens used by trade-materialized pairs that still miss symbol or name.
pub tradable_token_metadata_missing_count: i64,
/// Total quote-side tokens used by pairs that still miss symbol or name.
pub quote_token_metadata_missing_count: i64,
/// Total pairs whose display symbol is missing or still falls back to raw mints.
pub pair_symbol_fallback_count: i64,
/// Total pairs whose display symbol is present and does not include raw mints.
pub pair_symbol_resolved_count: i64,
/// Total pairs whose quote token is WSOL.
pub wsol_quote_pair_count: i64,
/// Total pairs whose quote token is a known stable quote.
pub stable_quote_pair_count: i64,
/// Total known pools.
pub pool_count: i64,
/// Total known pairs.
@@ -413,6 +425,18 @@ pub struct LocalPipelineDiagnosticCountersDto {
pub token_count: i64,
/// Total tokens missing metadata.
pub token_metadata_missing_count: i64,
/// Total tokens used by trade-materialized pairs that still miss metadata.
pub tradable_token_metadata_missing_count: i64,
/// Total quote-side tokens used by pairs that still miss metadata.
pub quote_token_metadata_missing_count: i64,
/// Total pairs whose display symbol is missing or falls back to raw mints.
pub pair_symbol_fallback_count: i64,
/// Total pairs whose display symbol is resolved without raw mints.
pub pair_symbol_resolved_count: i64,
/// Total pairs whose quote token is WSOL.
pub wsol_quote_pair_count: i64,
/// Total pairs whose quote token is a known stable quote.
pub stable_quote_pair_count: i64,
/// Total known pools.
pub pool_count: i64,
/// Total known pairs.
@@ -473,6 +497,12 @@ pub(crate) struct LocalPipelineDiagnosticCountersRow {
pub(crate) duplicate_candle_bucket_count: i64,
pub(crate) token_count: i64,
pub(crate) token_metadata_missing_count: i64,
pub(crate) tradable_token_metadata_missing_count: i64,
pub(crate) quote_token_metadata_missing_count: i64,
pub(crate) pair_symbol_fallback_count: i64,
pub(crate) pair_symbol_resolved_count: i64,
pub(crate) wsol_quote_pair_count: i64,
pub(crate) stable_quote_pair_count: i64,
pub(crate) pool_count: i64,
pub(crate) pair_count: i64,
pub(crate) literal_pair_without_trade_count: i64,

View File

@@ -187,10 +187,75 @@ SELECT
SELECT COUNT(*)
FROM k_sol_tokens
WHERE symbol IS NULL
OR symbol = ''
OR TRIM(symbol) = ''
OR name IS NULL
OR name = ''
OR TRIM(name) = ''
) AS token_metadata_missing_count,
(
SELECT COUNT(DISTINCT token.id)
FROM k_sol_tokens token
JOIN (
SELECT pair.base_token_id AS token_id
FROM k_sol_pairs pair
JOIN k_sol_trade_events te ON te.pair_id = pair.id
UNION
SELECT pair.quote_token_id AS token_id
FROM k_sol_pairs pair
JOIN k_sol_trade_events te ON te.pair_id = pair.id
) tradable_pair_token ON tradable_pair_token.token_id = token.id
WHERE token.symbol IS NULL
OR TRIM(token.symbol) = ''
OR token.name IS NULL
OR TRIM(token.name) = ''
) AS tradable_token_metadata_missing_count,
(
SELECT COUNT(DISTINCT quote_token.id)
FROM k_sol_pairs pair
JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
WHERE quote_token.symbol IS NULL
OR TRIM(quote_token.symbol) = ''
OR quote_token.name IS NULL
OR TRIM(quote_token.name) = ''
) AS quote_token_metadata_missing_count,
(
SELECT COUNT(*)
FROM k_sol_pairs pair
JOIN k_sol_tokens base_token ON base_token.id = pair.base_token_id
JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
WHERE pair.symbol IS NULL
OR TRIM(pair.symbol) = ''
OR pair.symbol = base_token.mint || '/' || quote_token.mint
OR instr(pair.symbol, base_token.mint) > 0
OR instr(pair.symbol, quote_token.mint) > 0
) AS pair_symbol_fallback_count,
(
SELECT COUNT(*)
FROM k_sol_pairs pair
JOIN k_sol_tokens base_token ON base_token.id = pair.base_token_id
JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
WHERE pair.symbol IS NOT NULL
AND TRIM(pair.symbol) != ''
AND pair.symbol != base_token.mint || '/' || quote_token.mint
AND instr(pair.symbol, base_token.mint) = 0
AND instr(pair.symbol, quote_token.mint) = 0
) AS pair_symbol_resolved_count,
(
SELECT COUNT(*)
FROM k_sol_pairs pair
JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
WHERE quote_token.mint = 'So11111111111111111111111111111111111111112'
) AS wsol_quote_pair_count,
(
SELECT COUNT(*)
FROM k_sol_pairs pair
JOIN k_sol_tokens quote_token ON quote_token.id = pair.quote_token_id
WHERE quote_token.mint IN (
'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
'USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB',
'JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD'
)
) AS stable_quote_pair_count,
(SELECT COUNT(*) FROM k_sol_pools) AS pool_count,
(SELECT COUNT(*) FROM k_sol_pairs) AS pair_count,
(
@@ -384,6 +449,12 @@ SELECT
duplicate_candle_bucket_count: row.duplicate_candle_bucket_count,
token_count: row.token_count,
token_metadata_missing_count: row.token_metadata_missing_count,
tradable_token_metadata_missing_count: row.tradable_token_metadata_missing_count,
quote_token_metadata_missing_count: row.quote_token_metadata_missing_count,
pair_symbol_fallback_count: row.pair_symbol_fallback_count,
pair_symbol_resolved_count: row.pair_symbol_resolved_count,
wsol_quote_pair_count: row.wsol_quote_pair_count,
stable_quote_pair_count: row.stable_quote_pair_count,
pool_count: row.pool_count,
pair_count: row.pair_count,
literal_pair_without_trade_count: row.literal_pair_without_trade_count,

View File

@@ -179,6 +179,12 @@ impl LocalPipelineDiagnosticsService {
duplicate_candle_bucket_count: counters.duplicate_candle_bucket_count,
token_count: counters.token_count,
token_metadata_missing_count: counters.token_metadata_missing_count,
tradable_token_metadata_missing_count: counters.tradable_token_metadata_missing_count,
quote_token_metadata_missing_count: counters.quote_token_metadata_missing_count,
pair_symbol_fallback_count: counters.pair_symbol_fallback_count,
pair_symbol_resolved_count: counters.pair_symbol_resolved_count,
wsol_quote_pair_count: counters.wsol_quote_pair_count,
stable_quote_pair_count: counters.stable_quote_pair_count,
pool_count: counters.pool_count,
pair_count: counters.pair_count,
pair_gap_counter_semantics: "blocking_actionable_pairs_only".to_string(),

View File

@@ -254,6 +254,18 @@ impl LocalPipelineValidationConfig {
config.allow_unexpected_dexes = true;
return config;
}
/// Builds the `0.7.37` token metadata/catalog enrichment validation config.
///
/// This profile keeps the `0.7.36` Meteora-family checks and exposes
/// token metadata and pair-symbol diagnostics. Missing metadata remains
/// informational in this profile; refresh/materialization invariants stay
/// governed by the existing trade and candle checks.
pub fn v0_7_37_token_metadata_catalog_enrichment() -> Self {
let mut config = Self::v0_7_36_meteora_family_consolidation();
config.profile_code = "0.7.37_token_metadata_catalog_enrichment".to_string();
return config;
}
}
/// A single local pipeline validation issue.
@@ -302,6 +314,22 @@ pub struct LocalPipelineValidationReportDto {
pub reward_event_count: i64,
/// Total persisted pool administration events.
pub pool_admin_event_count: i64,
/// Total known tokens.
pub token_count: i64,
/// Total tokens missing symbol or name.
pub token_metadata_missing_count: i64,
/// Total tokens used by trade-materialized pairs that still miss symbol or name.
pub tradable_token_metadata_missing_count: i64,
/// Total quote-side tokens used by pairs that still miss symbol or name.
pub quote_token_metadata_missing_count: i64,
/// Total pairs whose display symbol is missing or still falls back to raw mints.
pub pair_symbol_fallback_count: i64,
/// Total pairs whose display symbol is present and does not include raw mints.
pub pair_symbol_resolved_count: i64,
/// Total pairs whose quote token is WSOL.
pub wsol_quote_pair_count: i64,
/// Total pairs whose quote token is a known stable quote.
pub stable_quote_pair_count: i64,
/// Number of entries currently exposed by the DEX support matrix.
pub dex_support_matrix_entry_count: i64,
/// DEX support matrix snapshot exposed with the validation report.
@@ -448,6 +476,15 @@ impl LocalPipelineValidationService {
let config = crate::LocalPipelineValidationConfig::v0_7_36_meteora_family_consolidation();
return self.validate_current_database(&config).await;
}
/// Diagnoses the current database with the `0.7.37` metadata/catalog profile.
pub async fn validate_v0_7_37_current_database(
&self,
) -> Result<crate::LocalPipelineValidationRunDto, crate::Error> {
let config =
crate::LocalPipelineValidationConfig::v0_7_37_token_metadata_catalog_enrichment();
return self.validate_current_database(&config).await;
}
}
/// Validates a diagnostics summary without performing database access.
@@ -568,7 +605,8 @@ pub fn validate_local_pipeline_diagnostics_summary(
let missing_expected_dex_is_warning = config.profile_code
== "0.7.34_non_trade_liquidity_lifecycle"
|| config.profile_code == "0.7.35_non_trade_fee_reward_admin"
|| config.profile_code == "0.7.36_meteora_family_consolidation";
|| config.profile_code == "0.7.36_meteora_family_consolidation"
|| config.profile_code == "0.7.37_token_metadata_catalog_enrichment";
if config.require_all_expected_dexes || missing_expected_dex_is_warning {
for expected_dex_code in &expected_dex_codes {
if !observed_dex_codes.contains(expected_dex_code) {
@@ -643,6 +681,14 @@ pub fn validate_local_pipeline_diagnostics_summary(
fee_event_count: summary.fee_event_count,
reward_event_count: summary.reward_event_count,
pool_admin_event_count: summary.pool_admin_event_count,
token_count: summary.token_count,
token_metadata_missing_count: summary.token_metadata_missing_count,
tradable_token_metadata_missing_count: summary.tradable_token_metadata_missing_count,
quote_token_metadata_missing_count: summary.quote_token_metadata_missing_count,
pair_symbol_fallback_count: summary.pair_symbol_fallback_count,
pair_symbol_resolved_count: summary.pair_symbol_resolved_count,
wsol_quote_pair_count: summary.wsol_quote_pair_count,
stable_quote_pair_count: summary.stable_quote_pair_count,
dex_support_matrix_entry_count: crate::dex_support_matrix_entries().len() as i64,
dex_support_matrix: crate::dex_support_matrix_entry_dtos(),
issues,
@@ -873,6 +919,12 @@ mod tests {
duplicate_candle_bucket_count: 0,
token_count: 22,
token_metadata_missing_count: 0,
tradable_token_metadata_missing_count: 0,
quote_token_metadata_missing_count: 0,
pair_symbol_fallback_count: 4,
pair_symbol_resolved_count: 23,
wsol_quote_pair_count: 20,
stable_quote_pair_count: 3,
pool_count: 27,
pair_count: 27,
pair_gap_counter_semantics: "blocking_actionable_pairs_only".to_string(),
@@ -1221,6 +1273,33 @@ mod tests {
assert!(report.expected_dex_codes.contains(&"meteora_dlmm".to_string()));
}
#[test]
fn validation_accepts_0_7_37_metadata_catalog_summary() {
let mut summary = make_0_7_28_summary_with_meteora();
summary.token_count = 108;
summary.token_metadata_missing_count = 102;
summary.tradable_token_metadata_missing_count = 12;
summary.quote_token_metadata_missing_count = 4;
summary.pair_symbol_fallback_count = 16;
summary.pair_symbol_resolved_count = 5;
summary.wsol_quote_pair_count = 13;
summary.stable_quote_pair_count = 2;
let config =
crate::LocalPipelineValidationConfig::v0_7_37_token_metadata_catalog_enrichment();
let report = crate::validate_local_pipeline_diagnostics_summary(&summary, &config);
assert!(report.validation_passed);
assert_eq!(report.validation_profile_code, "0.7.37_token_metadata_catalog_enrichment");
assert_eq!(report.blocking_issue_count, 0);
assert_eq!(report.token_count, 108);
assert_eq!(report.token_metadata_missing_count, 102);
assert_eq!(report.tradable_token_metadata_missing_count, 12);
assert_eq!(report.quote_token_metadata_missing_count, 4);
assert_eq!(report.pair_symbol_fallback_count, 16);
assert_eq!(report.pair_symbol_resolved_count, 5);
assert_eq!(report.wsol_quote_pair_count, 13);
assert_eq!(report.stable_quote_pair_count, 2);
}
#[test]
fn validation_rejects_0_7_33_pair_trading_readiness_mismatch() {
let mut summary = make_0_7_28_summary_with_meteora();