0.7.38-B
This commit is contained in:
@@ -34,6 +34,7 @@ pub use dtos::LocalDecodedEventDiagnosticSummaryDto;
|
||||
pub use dtos::LocalDexDiagnosticSummaryDto;
|
||||
pub use dtos::LocalDuplicateDecodedEventTradeDiagnosticSampleDto;
|
||||
pub use dtos::LocalEventClassificationDiagnosticSummaryDto;
|
||||
pub use dtos::LocalLaunchOriginDiagnosticSampleDto;
|
||||
pub use dtos::LocalMissingTradeEventDiagnosticSampleDto;
|
||||
pub use dtos::LocalMissingTradeEventReasonSummaryDto;
|
||||
pub use dtos::LocalMultiTradeSignaturePairDiagnosticSampleDto;
|
||||
@@ -44,6 +45,7 @@ pub use dtos::LocalPairGapDiagnosticSampleDto;
|
||||
pub use dtos::LocalPairTradingReadinessDiagnosticSummaryDto;
|
||||
pub use dtos::LocalPipelineDiagnosticCountersDto;
|
||||
pub use dtos::LocalPipelineDiagnosticSummaryDto;
|
||||
pub use dtos::LocalPoolOriginDiagnosticSampleDto;
|
||||
pub use dtos::LocalTokenMetadataGapDiagnosticSampleDto;
|
||||
pub use dtos::ObservedTokenDto;
|
||||
pub use dtos::OnchainObservationDto;
|
||||
@@ -160,6 +162,7 @@ pub use queries::query_liquidity_events_upsert;
|
||||
pub use queries::query_local_decoded_event_diagnostic_list_summaries;
|
||||
pub use queries::query_local_duplicate_decoded_event_trade_diagnostic_list_samples;
|
||||
pub use queries::query_local_event_classification_diagnostic_list_summaries;
|
||||
pub use queries::query_local_launch_origin_diagnostic_list_samples;
|
||||
pub use queries::query_local_missing_trade_event_diagnostic_list_samples;
|
||||
pub use queries::query_local_missing_trade_event_reason_list_summaries;
|
||||
pub use queries::query_local_multi_trade_signature_pair_diagnostic_list_samples;
|
||||
@@ -171,6 +174,7 @@ pub use queries::query_local_pair_without_candle_diagnostic_list_samples;
|
||||
pub use queries::query_local_pair_without_trade_diagnostic_list_samples;
|
||||
pub use queries::query_local_pipeline_diagnostic_get_counters;
|
||||
pub use queries::query_local_pipeline_diagnostic_list_summaries;
|
||||
pub use queries::query_local_pool_origin_diagnostic_list_samples;
|
||||
pub use queries::query_local_token_metadata_gap_diagnostic_list_samples;
|
||||
pub use queries::query_observed_tokens_get_by_mint;
|
||||
pub use queries::query_observed_tokens_list;
|
||||
|
||||
@@ -49,6 +49,7 @@ pub(crate) use local_pipeline_diagnostics::LocalDecodedEventDiagnosticSummaryRow
|
||||
pub(crate) use local_pipeline_diagnostics::LocalDexDiagnosticSummaryRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalDuplicateDecodedEventTradeDiagnosticSampleRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalEventClassificationDiagnosticSummaryRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalLaunchOriginDiagnosticSampleRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalMissingTradeEventDiagnosticSampleRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalMissingTradeEventReasonSummaryRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalMultiTradeSignaturePairDiagnosticSampleRow;
|
||||
@@ -58,6 +59,7 @@ pub(crate) use local_pipeline_diagnostics::LocalPairDiagnosticSummaryRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalPairGapDiagnosticSampleRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalPairTradingReadinessDiagnosticSummaryRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalPipelineDiagnosticCountersRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalPoolOriginDiagnosticSampleRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalTokenMetadataGapDiagnosticSampleRow;
|
||||
|
||||
pub use analysis_signal::AnalysisSignalDto;
|
||||
@@ -79,6 +81,7 @@ pub use local_pipeline_diagnostics::LocalDecodedEventDiagnosticSummaryDto;
|
||||
pub use local_pipeline_diagnostics::LocalDexDiagnosticSummaryDto;
|
||||
pub use local_pipeline_diagnostics::LocalDuplicateDecodedEventTradeDiagnosticSampleDto;
|
||||
pub use local_pipeline_diagnostics::LocalEventClassificationDiagnosticSummaryDto;
|
||||
pub use local_pipeline_diagnostics::LocalLaunchOriginDiagnosticSampleDto;
|
||||
pub use local_pipeline_diagnostics::LocalMissingTradeEventDiagnosticSampleDto;
|
||||
pub use local_pipeline_diagnostics::LocalMissingTradeEventReasonSummaryDto;
|
||||
pub use local_pipeline_diagnostics::LocalMultiTradeSignaturePairDiagnosticSampleDto;
|
||||
@@ -89,6 +92,7 @@ pub use local_pipeline_diagnostics::LocalPairGapDiagnosticSampleDto;
|
||||
pub use local_pipeline_diagnostics::LocalPairTradingReadinessDiagnosticSummaryDto;
|
||||
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticCountersDto;
|
||||
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticSummaryDto;
|
||||
pub use local_pipeline_diagnostics::LocalPoolOriginDiagnosticSampleDto;
|
||||
pub use local_pipeline_diagnostics::LocalTokenMetadataGapDiagnosticSampleDto;
|
||||
pub use observed_token::ObservedTokenDto;
|
||||
pub use onchain_observation::OnchainObservationDto;
|
||||
|
||||
@@ -138,6 +138,10 @@ pub struct LocalPipelineDiagnosticSummaryDto {
|
||||
/// Missing trade events grouped by diagnostic reason.
|
||||
pub missing_trade_event_reason_summaries:
|
||||
std::vec::Vec<crate::LocalMissingTradeEventReasonSummaryDto>,
|
||||
/// Samples of launch-origin attributions.
|
||||
pub launch_origin_samples: std::vec::Vec<crate::LocalLaunchOriginDiagnosticSampleDto>,
|
||||
/// Samples of pool-origin rows and their optional launch linkage.
|
||||
pub pool_origin_samples: std::vec::Vec<crate::LocalPoolOriginDiagnosticSampleDto>,
|
||||
/// Prioritized samples of tokens whose display metadata is still incomplete.
|
||||
pub token_metadata_gap_samples: std::vec::Vec<crate::LocalTokenMetadataGapDiagnosticSampleDto>,
|
||||
/// Total pairs with only non-actionable missing trade events.
|
||||
@@ -714,6 +718,60 @@ pub struct LocalPairGapDiagnosticSampleDto {
|
||||
pub pair_candle_count: i64,
|
||||
}
|
||||
|
||||
/// Sample of a launch-origin attribution.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LocalLaunchOriginDiagnosticSampleDto {
|
||||
/// Launch attribution id.
|
||||
pub launch_attribution_id: i64,
|
||||
/// Launch surface code.
|
||||
pub launch_surface_code: std::string::String,
|
||||
/// Launch surface display name.
|
||||
pub launch_surface_name: std::string::String,
|
||||
/// Transaction signature.
|
||||
pub transaction_signature: std::string::String,
|
||||
/// Decoded event id.
|
||||
pub decoded_event_id: i64,
|
||||
/// Protocol that materialized the decoded event.
|
||||
pub protocol_name: std::string::String,
|
||||
/// Match kind used to attribute the launch surface.
|
||||
pub match_kind: std::string::String,
|
||||
/// Matched key value used to attribute the launch surface.
|
||||
pub matched_value: std::string::String,
|
||||
/// Optional related pool id.
|
||||
pub pool_id: std::option::Option<i64>,
|
||||
/// Optional related pool address.
|
||||
pub pool_address: std::option::Option<std::string::String>,
|
||||
/// Optional related pair id.
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Optional related pair symbol.
|
||||
pub pair_symbol: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
/// Sample of a pool-origin row.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LocalPoolOriginDiagnosticSampleDto {
|
||||
/// Pool-origin id.
|
||||
pub pool_origin_id: i64,
|
||||
/// Effective DEX code attached to the pool.
|
||||
pub dex_code: std::string::String,
|
||||
/// Pool id.
|
||||
pub pool_id: i64,
|
||||
/// Pool address.
|
||||
pub pool_address: std::string::String,
|
||||
/// Optional pair id.
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Optional pair symbol.
|
||||
pub pair_symbol: std::option::Option<std::string::String>,
|
||||
/// Optional launch surface code linked through launch attribution.
|
||||
pub launch_surface_code: std::option::Option<std::string::String>,
|
||||
/// Founding transaction signature.
|
||||
pub founding_signature: std::string::String,
|
||||
/// Founding effective DEX/protocol name.
|
||||
pub founding_protocol_name: std::string::String,
|
||||
/// Founding decoded event kind.
|
||||
pub founding_event_kind: std::string::String,
|
||||
}
|
||||
|
||||
/// Prioritized sample of an incomplete token metadata row.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LocalTokenMetadataGapDiagnosticSampleDto {
|
||||
@@ -835,6 +893,38 @@ pub(crate) struct LocalPairGapDiagnosticSampleRow {
|
||||
pub(crate) pair_candle_count: i64,
|
||||
}
|
||||
|
||||
/// SQL row for launch-origin diagnostic samples.
|
||||
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||
pub(crate) struct LocalLaunchOriginDiagnosticSampleRow {
|
||||
pub(crate) launch_attribution_id: i64,
|
||||
pub(crate) launch_surface_code: std::string::String,
|
||||
pub(crate) launch_surface_name: std::string::String,
|
||||
pub(crate) transaction_signature: std::string::String,
|
||||
pub(crate) decoded_event_id: i64,
|
||||
pub(crate) protocol_name: std::string::String,
|
||||
pub(crate) match_kind: std::string::String,
|
||||
pub(crate) matched_value: std::string::String,
|
||||
pub(crate) pool_id: std::option::Option<i64>,
|
||||
pub(crate) pool_address: std::option::Option<std::string::String>,
|
||||
pub(crate) pair_id: std::option::Option<i64>,
|
||||
pub(crate) pair_symbol: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
/// SQL row for pool-origin diagnostic samples.
|
||||
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||
pub(crate) struct LocalPoolOriginDiagnosticSampleRow {
|
||||
pub(crate) pool_origin_id: i64,
|
||||
pub(crate) dex_code: std::string::String,
|
||||
pub(crate) pool_id: i64,
|
||||
pub(crate) pool_address: std::string::String,
|
||||
pub(crate) pair_id: std::option::Option<i64>,
|
||||
pub(crate) pair_symbol: std::option::Option<std::string::String>,
|
||||
pub(crate) launch_surface_code: std::option::Option<std::string::String>,
|
||||
pub(crate) founding_signature: std::string::String,
|
||||
pub(crate) founding_protocol_name: std::string::String,
|
||||
pub(crate) founding_event_kind: std::string::String,
|
||||
}
|
||||
|
||||
/// SQL row for incomplete token metadata samples.
|
||||
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||
pub(crate) struct LocalTokenMetadataGapDiagnosticSampleRow {
|
||||
|
||||
@@ -91,6 +91,7 @@ pub use liquidity_event::query_liquidity_events_upsert;
|
||||
pub use local_pipeline_diagnostics::query_local_decoded_event_diagnostic_list_summaries;
|
||||
pub use local_pipeline_diagnostics::query_local_duplicate_decoded_event_trade_diagnostic_list_samples;
|
||||
pub use local_pipeline_diagnostics::query_local_event_classification_diagnostic_list_summaries;
|
||||
pub use local_pipeline_diagnostics::query_local_launch_origin_diagnostic_list_samples;
|
||||
pub use local_pipeline_diagnostics::query_local_missing_trade_event_diagnostic_list_samples;
|
||||
pub use local_pipeline_diagnostics::query_local_missing_trade_event_reason_list_summaries;
|
||||
pub use local_pipeline_diagnostics::query_local_multi_trade_signature_pair_diagnostic_list_samples;
|
||||
@@ -102,6 +103,7 @@ pub use local_pipeline_diagnostics::query_local_pair_without_candle_diagnostic_l
|
||||
pub use local_pipeline_diagnostics::query_local_pair_without_trade_diagnostic_list_samples;
|
||||
pub use local_pipeline_diagnostics::query_local_pipeline_diagnostic_get_counters;
|
||||
pub use local_pipeline_diagnostics::query_local_pipeline_diagnostic_list_summaries;
|
||||
pub use local_pipeline_diagnostics::query_local_pool_origin_diagnostic_list_samples;
|
||||
pub use local_pipeline_diagnostics::query_local_token_metadata_gap_diagnostic_list_samples;
|
||||
pub use observed_token::query_observed_tokens_get_by_mint;
|
||||
pub use observed_token::query_observed_tokens_list;
|
||||
|
||||
@@ -1678,6 +1678,139 @@ LIMIT ?
|
||||
}
|
||||
}
|
||||
|
||||
/// Lists samples of launch-origin attributions.
|
||||
pub async fn query_local_launch_origin_diagnostic_list_samples(
|
||||
database: &crate::Database,
|
||||
limit: i64,
|
||||
) -> Result<std::vec::Vec<crate::LocalLaunchOriginDiagnosticSampleDto>, crate::Error> {
|
||||
match database.connection() {
|
||||
crate::DatabaseConnection::Sqlite(pool) => {
|
||||
let rows_result = sqlx::query_as::<
|
||||
sqlx::Sqlite,
|
||||
crate::db::dtos::LocalLaunchOriginDiagnosticSampleRow,
|
||||
>(
|
||||
r#"
|
||||
SELECT
|
||||
la.id AS launch_attribution_id,
|
||||
ls.code AS launch_surface_code,
|
||||
ls.name AS launch_surface_name,
|
||||
ct.signature AS transaction_signature,
|
||||
la.decoded_event_id AS decoded_event_id,
|
||||
la.protocol_name AS protocol_name,
|
||||
la.match_kind AS match_kind,
|
||||
la.matched_value AS matched_value,
|
||||
la.pool_id AS pool_id,
|
||||
p.address AS pool_address,
|
||||
la.pair_id AS pair_id,
|
||||
pair.symbol AS pair_symbol
|
||||
FROM k_sol_launch_attributions la
|
||||
JOIN k_sol_launch_surfaces ls ON ls.id = la.launch_surface_id
|
||||
JOIN k_sol_chain_transactions ct ON ct.id = la.transaction_id
|
||||
LEFT JOIN k_sol_pools p ON p.id = la.pool_id
|
||||
LEFT JOIN k_sol_pairs pair ON pair.id = la.pair_id
|
||||
ORDER BY la.id DESC
|
||||
LIMIT ?
|
||||
"#,
|
||||
)
|
||||
.bind(limit)
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
let rows = match rows_result {
|
||||
Ok(rows) => rows,
|
||||
Err(error) => {
|
||||
return Err(crate::Error::Db(format!(
|
||||
"cannot list launch-origin diagnostic samples on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
};
|
||||
let mut samples = std::vec::Vec::new();
|
||||
for row in rows {
|
||||
samples.push(crate::LocalLaunchOriginDiagnosticSampleDto {
|
||||
launch_attribution_id: row.launch_attribution_id,
|
||||
launch_surface_code: row.launch_surface_code,
|
||||
launch_surface_name: row.launch_surface_name,
|
||||
transaction_signature: row.transaction_signature,
|
||||
decoded_event_id: row.decoded_event_id,
|
||||
protocol_name: row.protocol_name,
|
||||
match_kind: row.match_kind,
|
||||
matched_value: row.matched_value,
|
||||
pool_id: row.pool_id,
|
||||
pool_address: row.pool_address,
|
||||
pair_id: row.pair_id,
|
||||
pair_symbol: row.pair_symbol,
|
||||
});
|
||||
}
|
||||
return Ok(samples);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Lists samples of pool-origin rows and their optional launch linkage.
|
||||
pub async fn query_local_pool_origin_diagnostic_list_samples(
|
||||
database: &crate::Database,
|
||||
limit: i64,
|
||||
) -> Result<std::vec::Vec<crate::LocalPoolOriginDiagnosticSampleDto>, crate::Error> {
|
||||
match database.connection() {
|
||||
crate::DatabaseConnection::Sqlite(pool) => {
|
||||
let rows_result = sqlx::query_as::<
|
||||
sqlx::Sqlite,
|
||||
crate::db::dtos::LocalPoolOriginDiagnosticSampleRow,
|
||||
>(
|
||||
r#"
|
||||
SELECT
|
||||
po.id AS pool_origin_id,
|
||||
d.code AS dex_code,
|
||||
po.pool_id AS pool_id,
|
||||
p.address AS pool_address,
|
||||
po.pair_id AS pair_id,
|
||||
pair.symbol AS pair_symbol,
|
||||
ls.code AS launch_surface_code,
|
||||
po.founding_signature AS founding_signature,
|
||||
po.founding_protocol_name AS founding_protocol_name,
|
||||
po.founding_event_kind AS founding_event_kind
|
||||
FROM k_sol_pool_origins po
|
||||
JOIN k_sol_dexes d ON d.id = po.dex_id
|
||||
JOIN k_sol_pools p ON p.id = po.pool_id
|
||||
LEFT JOIN k_sol_pairs pair ON pair.id = po.pair_id
|
||||
LEFT JOIN k_sol_launch_attributions la ON la.id = po.launch_attribution_id
|
||||
LEFT JOIN k_sol_launch_surfaces ls ON ls.id = la.launch_surface_id
|
||||
ORDER BY po.id DESC
|
||||
LIMIT ?
|
||||
"#,
|
||||
)
|
||||
.bind(limit)
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
let rows = match rows_result {
|
||||
Ok(rows) => rows,
|
||||
Err(error) => {
|
||||
return Err(crate::Error::Db(format!(
|
||||
"cannot list pool-origin diagnostic samples on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
};
|
||||
let mut samples = std::vec::Vec::new();
|
||||
for row in rows {
|
||||
samples.push(crate::LocalPoolOriginDiagnosticSampleDto {
|
||||
pool_origin_id: row.pool_origin_id,
|
||||
dex_code: row.dex_code,
|
||||
pool_id: row.pool_id,
|
||||
pool_address: row.pool_address,
|
||||
pair_id: row.pair_id,
|
||||
pair_symbol: row.pair_symbol,
|
||||
launch_surface_code: row.launch_surface_code,
|
||||
founding_signature: row.founding_signature,
|
||||
founding_protocol_name: row.founding_protocol_name,
|
||||
founding_event_kind: row.founding_event_kind,
|
||||
});
|
||||
}
|
||||
return Ok(samples);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Lists prioritized samples of tokens whose metadata is still incomplete.
|
||||
pub async fn query_local_token_metadata_gap_diagnostic_list_samples(
|
||||
database: &crate::Database,
|
||||
|
||||
@@ -573,6 +573,27 @@ const DEX_SUPPORT_MATRIX_ENTRIES: &[DexSupportMatrixEntry] = &[
|
||||
skip_reason: Some("program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "bonk_fun",
|
||||
display_name: "Bonk.fun",
|
||||
family: "bonk",
|
||||
version: "unknown",
|
||||
surface_type: "launch",
|
||||
program_id: None,
|
||||
router_program_id: None,
|
||||
program_id_status: "unknown",
|
||||
observed: false,
|
||||
decoded: false,
|
||||
materialized: false,
|
||||
trade_candidate: false,
|
||||
candle_candidate: false,
|
||||
pair_candidate: true,
|
||||
pool_candidate: true,
|
||||
status: "planned",
|
||||
confidence: "low",
|
||||
skip_reason: Some("program_id_to_verify"),
|
||||
catalog_enabled: false,
|
||||
},
|
||||
DexSupportMatrixEntry {
|
||||
code: "okx_dex",
|
||||
display_name: "OKX DEX",
|
||||
@@ -806,6 +827,7 @@ mod tests {
|
||||
"meteora_damm_v2",
|
||||
"bags",
|
||||
"bonk",
|
||||
"bonk_fun",
|
||||
"okx_dex",
|
||||
"boop_fun",
|
||||
"moonshot",
|
||||
@@ -825,8 +847,11 @@ mod tests {
|
||||
#[test]
|
||||
fn matrix_does_not_invent_program_ids_for_unverified_planned_surfaces() {
|
||||
let codes = [
|
||||
"raydium_launchpad",
|
||||
"bags",
|
||||
"letsbonk",
|
||||
"bonk",
|
||||
"bonk_fun",
|
||||
"okx_dex",
|
||||
"boop_fun",
|
||||
"moonshot",
|
||||
@@ -879,7 +904,19 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn matrix_marks_launch_surfaces_as_launch_or_bonding_curve() {
|
||||
let codes = ["pump_fun", "raydium_launchlab", "bags", "moonshot", "moonit"];
|
||||
let codes = [
|
||||
"pump_fun",
|
||||
"raydium_launchlab",
|
||||
"raydium_launchpad",
|
||||
"letsbonk",
|
||||
"bonk_fun",
|
||||
"bags",
|
||||
"moonshot",
|
||||
"moonit",
|
||||
"boop_fun",
|
||||
"believe",
|
||||
"heaven",
|
||||
];
|
||||
for code in codes {
|
||||
let entry = match crate::dex_support_matrix_entry_by_code(code) {
|
||||
Some(entry) => entry,
|
||||
@@ -894,6 +931,33 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_keeps_0_7_39_launch_surfaces_non_trade_materialized() {
|
||||
let codes = [
|
||||
"raydium_launchlab",
|
||||
"raydium_launchpad",
|
||||
"letsbonk",
|
||||
"bonk_fun",
|
||||
"bags",
|
||||
"moonshot",
|
||||
"moonit",
|
||||
"boop_fun",
|
||||
"believe",
|
||||
"heaven",
|
||||
];
|
||||
for code in codes {
|
||||
let entry = match crate::dex_support_matrix_entry_by_code(code) {
|
||||
Some(entry) => entry,
|
||||
None => panic!("missing matrix entry for {}", code),
|
||||
};
|
||||
assert!(!entry.decoded, "{} must not be decoded without corpus", code);
|
||||
assert!(!entry.materialized, "{} must not materialize launch rows as trades", code);
|
||||
assert!(!entry.trade_candidate, "{} must not create trade candidates", code);
|
||||
assert!(!entry.candle_candidate, "{} must not create candle candidates", code);
|
||||
assert!(!entry.catalog_enabled, "{} must not be inserted as enabled DEX", code);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_dto_preserves_core_fields() {
|
||||
let entry = match crate::dex_support_matrix_entry_by_code("pump_swap") {
|
||||
|
||||
@@ -2,6 +2,77 @@
|
||||
|
||||
//! Launch surface attribution service.
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct BuiltinLaunchSurfaceSpec {
|
||||
code: &'static str,
|
||||
name: &'static str,
|
||||
protocol_family: &'static str,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
const BUILTIN_LAUNCH_SURFACE_SPECS: &[BuiltinLaunchSurfaceSpec] = &[
|
||||
BuiltinLaunchSurfaceSpec {
|
||||
code: "raydium_launchlab",
|
||||
name: "Raydium LaunchLab",
|
||||
protocol_family: "raydium",
|
||||
enabled: true,
|
||||
},
|
||||
BuiltinLaunchSurfaceSpec {
|
||||
code: "raydium_launchpad",
|
||||
name: "Raydium Launchpad",
|
||||
protocol_family: "raydium",
|
||||
enabled: true,
|
||||
},
|
||||
BuiltinLaunchSurfaceSpec {
|
||||
code: "letsbonk",
|
||||
name: "LetsBonk / Bonk.fun",
|
||||
protocol_family: "bonk",
|
||||
enabled: true,
|
||||
},
|
||||
BuiltinLaunchSurfaceSpec {
|
||||
code: "bonk_fun",
|
||||
name: "Bonk.fun",
|
||||
protocol_family: "bonk",
|
||||
enabled: true,
|
||||
},
|
||||
BuiltinLaunchSurfaceSpec {
|
||||
code: "bags",
|
||||
name: "Bags",
|
||||
protocol_family: "bags",
|
||||
enabled: true,
|
||||
},
|
||||
BuiltinLaunchSurfaceSpec {
|
||||
code: "moonshot",
|
||||
name: "Moonshot",
|
||||
protocol_family: "moonshot",
|
||||
enabled: true,
|
||||
},
|
||||
BuiltinLaunchSurfaceSpec {
|
||||
code: "moonit",
|
||||
name: "Moonit",
|
||||
protocol_family: "moonit",
|
||||
enabled: true,
|
||||
},
|
||||
BuiltinLaunchSurfaceSpec {
|
||||
code: "boop_fun",
|
||||
name: "Boop.fun",
|
||||
protocol_family: "boop",
|
||||
enabled: true,
|
||||
},
|
||||
BuiltinLaunchSurfaceSpec {
|
||||
code: "believe",
|
||||
name: "Believe",
|
||||
protocol_family: "believe",
|
||||
enabled: true,
|
||||
},
|
||||
BuiltinLaunchSurfaceSpec {
|
||||
code: "heaven",
|
||||
name: "Heaven",
|
||||
protocol_family: "heaven",
|
||||
enabled: false,
|
||||
},
|
||||
];
|
||||
|
||||
/// Result of one launch surface attribution.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LaunchAttributionResult {
|
||||
@@ -33,57 +104,121 @@ impl LaunchOriginService {
|
||||
return Self { database, persistence };
|
||||
}
|
||||
|
||||
/// Ensures that the built-in `moonit` launch surface exists and returns its id.
|
||||
pub async fn ensure_moonit_surface(&self) -> Result<i64, crate::Error> {
|
||||
let existing_result =
|
||||
crate::query_launch_surfaces_get_by_code(self.database.as_ref(), "moonit").await;
|
||||
let existing_option = match existing_result {
|
||||
Ok(existing_option) => existing_option,
|
||||
/// Ensures that every built-in launch surface tracked by `0.7.39` exists.
|
||||
pub async fn ensure_target_launch_surfaces(&self) -> Result<std::vec::Vec<i64>, crate::Error> {
|
||||
let mut surface_ids = std::vec::Vec::new();
|
||||
for spec in BUILTIN_LAUNCH_SURFACE_SPECS {
|
||||
let surface_id_result = self.ensure_builtin_launch_surface(spec.code).await;
|
||||
let surface_id = match surface_id_result {
|
||||
Ok(surface_id) => surface_id,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
surface_ids.push(surface_id);
|
||||
}
|
||||
return Ok(surface_ids);
|
||||
}
|
||||
|
||||
/// Ensures that one built-in launch surface exists and returns its id.
|
||||
pub async fn ensure_builtin_launch_surface(&self, code: &str) -> Result<i64, crate::Error> {
|
||||
let spec = match find_builtin_launch_surface_spec(code) {
|
||||
Some(spec) => spec,
|
||||
None => {
|
||||
return Err(crate::Error::InvalidState(format!(
|
||||
"unknown built-in launch surface '{}'",
|
||||
code
|
||||
)));
|
||||
},
|
||||
};
|
||||
let surface_id_result = self.ensure_launch_surface_from_spec(spec).await;
|
||||
let surface_id = match surface_id_result {
|
||||
Ok(surface_id) => surface_id,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let surface_id = match existing_option {
|
||||
Some(existing) => match existing.id {
|
||||
Some(surface_id) => surface_id,
|
||||
None => {
|
||||
return Err(crate::Error::InvalidState(
|
||||
"moonit launch surface has no internal id".to_string(),
|
||||
));
|
||||
},
|
||||
},
|
||||
None => {
|
||||
let dto = crate::LaunchSurfaceDto::new(
|
||||
"moonit".to_string(),
|
||||
"Moonit".to_string(),
|
||||
Some("launchpad".to_string()),
|
||||
true,
|
||||
);
|
||||
let insert_result =
|
||||
crate::query_launch_surfaces_upsert(self.database.as_ref(), &dto).await;
|
||||
match insert_result {
|
||||
Ok(surface_id) => surface_id,
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
},
|
||||
};
|
||||
let suffix_key_result = crate::query_launch_surface_keys_upsert(
|
||||
self.database.as_ref(),
|
||||
&crate::LaunchSurfaceKeyDto::new(
|
||||
surface_id,
|
||||
"token_mint_suffix".to_string(),
|
||||
"moon".to_string(),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
if let Err(error) = suffix_key_result {
|
||||
return Err(error);
|
||||
if spec.code == "moonit" {
|
||||
let suffix_key_result = crate::query_launch_surface_keys_upsert(
|
||||
self.database.as_ref(),
|
||||
&crate::LaunchSurfaceKeyDto::new(
|
||||
surface_id,
|
||||
"token_mint_suffix".to_string(),
|
||||
"moon".to_string(),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
if let Err(error) = suffix_key_result {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
return Ok(surface_id);
|
||||
}
|
||||
|
||||
/// Ensures that the built-in `moonit` launch surface exists and returns its id.
|
||||
pub async fn ensure_moonit_surface(&self) -> Result<i64, crate::Error> {
|
||||
return self.ensure_builtin_launch_surface("moonit").await;
|
||||
}
|
||||
|
||||
/// Ensures that the built-in `bags` launch surface exists and returns its id.
|
||||
pub async fn ensure_bags_surface(&self) -> Result<i64, crate::Error> {
|
||||
return self.ensure_builtin_launch_surface("bags").await;
|
||||
}
|
||||
|
||||
/// Registers one generic launch-surface mapping from verified corpus/API evidence.
|
||||
pub async fn register_launch_surface_mapping(
|
||||
&self,
|
||||
surface_code: &str,
|
||||
token_mint: std::option::Option<&str>,
|
||||
config_account: std::option::Option<&str>,
|
||||
pool_account: std::option::Option<&str>,
|
||||
creator: std::option::Option<&str>,
|
||||
) -> Result<i64, crate::Error> {
|
||||
let surface_id_result = self.ensure_builtin_launch_surface(surface_code).await;
|
||||
let surface_id = match surface_id_result {
|
||||
Ok(surface_id) => surface_id,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let key_specs = [
|
||||
("token_mint", token_mint),
|
||||
("config_account", config_account),
|
||||
("pool_account", pool_account),
|
||||
("creator", creator),
|
||||
];
|
||||
let mut inserted_key_count = 0_u32;
|
||||
for (match_kind, match_value) in key_specs {
|
||||
let match_value = match match_value {
|
||||
Some(match_value) => match_value,
|
||||
None => continue,
|
||||
};
|
||||
if match_value.trim().is_empty() {
|
||||
continue;
|
||||
}
|
||||
let key_result = crate::query_launch_surface_keys_upsert(
|
||||
self.database.as_ref(),
|
||||
&crate::LaunchSurfaceKeyDto::new(
|
||||
surface_id,
|
||||
match_kind.to_string(),
|
||||
match_value.to_string(),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
if let Err(error) = key_result {
|
||||
return Err(error);
|
||||
}
|
||||
inserted_key_count += 1;
|
||||
}
|
||||
if inserted_key_count == 0 {
|
||||
return Err(crate::Error::InvalidState(format!(
|
||||
"launch surface '{}' mapping must include at least one non-empty key",
|
||||
surface_code
|
||||
)));
|
||||
}
|
||||
return Ok(surface_id);
|
||||
}
|
||||
|
||||
async fn ensure_launch_surface_from_spec(
|
||||
&self,
|
||||
spec: BuiltinLaunchSurfaceSpec,
|
||||
) -> Result<i64, crate::Error> {
|
||||
let existing_result =
|
||||
crate::query_launch_surfaces_get_by_code(self.database.as_ref(), "bags").await;
|
||||
crate::query_launch_surfaces_get_by_code(self.database.as_ref(), spec.code).await;
|
||||
let existing_option = match existing_result {
|
||||
Ok(existing_option) => existing_option,
|
||||
Err(error) => return Err(error),
|
||||
@@ -92,17 +227,18 @@ impl LaunchOriginService {
|
||||
Some(existing) => match existing.id {
|
||||
Some(surface_id) => return Ok(surface_id),
|
||||
None => {
|
||||
return Err(crate::Error::InvalidState(
|
||||
"bags launch surface has no internal id".to_string(),
|
||||
));
|
||||
return Err(crate::Error::InvalidState(format!(
|
||||
"{} launch surface has no internal id",
|
||||
spec.code
|
||||
)));
|
||||
},
|
||||
},
|
||||
None => {
|
||||
let dto = crate::LaunchSurfaceDto::new(
|
||||
"bags".to_string(),
|
||||
"Bags".to_string(),
|
||||
Some("launchpad".to_string()),
|
||||
true,
|
||||
spec.code.to_string(),
|
||||
spec.name.to_string(),
|
||||
Some(spec.protocol_family.to_string()),
|
||||
spec.enabled,
|
||||
);
|
||||
return crate::query_launch_surfaces_upsert(self.database.as_ref(), &dto).await;
|
||||
},
|
||||
@@ -117,51 +253,19 @@ impl LaunchOriginService {
|
||||
dbc_pool_key: std::option::Option<std::string::String>,
|
||||
damm_v2_pool_key: std::option::Option<std::string::String>,
|
||||
) -> Result<i64, crate::Error> {
|
||||
let surface_id_result = self.ensure_bags_surface().await;
|
||||
let surface_id_result = self
|
||||
.register_launch_surface_mapping(
|
||||
"bags",
|
||||
Some(token_mint),
|
||||
dbc_config_key.as_deref(),
|
||||
dbc_pool_key.as_deref(),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
let surface_id = match surface_id_result {
|
||||
Ok(surface_id) => surface_id,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let token_key_result = crate::query_launch_surface_keys_upsert(
|
||||
self.database.as_ref(),
|
||||
&crate::LaunchSurfaceKeyDto::new(
|
||||
surface_id,
|
||||
"token_mint".to_string(),
|
||||
token_mint.to_string(),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
if let Err(error) = token_key_result {
|
||||
return Err(error);
|
||||
}
|
||||
if let Some(dbc_config_key) = dbc_config_key {
|
||||
let key_result = crate::query_launch_surface_keys_upsert(
|
||||
self.database.as_ref(),
|
||||
&crate::LaunchSurfaceKeyDto::new(
|
||||
surface_id,
|
||||
"config_account".to_string(),
|
||||
dbc_config_key,
|
||||
),
|
||||
)
|
||||
.await;
|
||||
if let Err(error) = key_result {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
if let Some(dbc_pool_key) = dbc_pool_key {
|
||||
let key_result = crate::query_launch_surface_keys_upsert(
|
||||
self.database.as_ref(),
|
||||
&crate::LaunchSurfaceKeyDto::new(
|
||||
surface_id,
|
||||
"pool_account".to_string(),
|
||||
dbc_pool_key,
|
||||
),
|
||||
)
|
||||
.await;
|
||||
if let Err(error) = key_result {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
if let Some(damm_v2_pool_key) = damm_v2_pool_key {
|
||||
let key_result = crate::query_launch_surface_keys_upsert(
|
||||
self.database.as_ref(),
|
||||
@@ -209,8 +313,8 @@ impl LaunchOriginService {
|
||||
)));
|
||||
},
|
||||
};
|
||||
let ensure_moonit_result = self.ensure_moonit_surface().await;
|
||||
if let Err(error) = ensure_moonit_result {
|
||||
let ensure_surfaces_result = self.ensure_target_launch_surfaces().await;
|
||||
if let Err(error) = ensure_surfaces_result {
|
||||
return Err(error);
|
||||
}
|
||||
let decoded_events_result = crate::query_dex_decoded_events_list_by_transaction_id(
|
||||
@@ -512,6 +616,15 @@ impl LaunchOriginService {
|
||||
}
|
||||
}
|
||||
|
||||
fn find_builtin_launch_surface_spec(code: &str) -> std::option::Option<BuiltinLaunchSurfaceSpec> {
|
||||
for spec in BUILTIN_LAUNCH_SURFACE_SPECS {
|
||||
if spec.code == code {
|
||||
return Some(*spec);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct MatchedLaunchSurface {
|
||||
launch_surface_id: i64,
|
||||
@@ -866,4 +979,79 @@ mod tests {
|
||||
assert_eq!(listed[0].match_kind, "token_mint_suffix".to_string());
|
||||
assert_eq!(listed[0].matched_value, "ExampleTokenmoon".to_string());
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn ensure_target_launch_surfaces_seeds_0_7_39_surfaces_without_program_ids() {
|
||||
let database = make_database().await;
|
||||
let service = crate::LaunchOriginService::new(database.clone());
|
||||
let result = service.ensure_target_launch_surfaces().await;
|
||||
let surface_ids = match result {
|
||||
Ok(surface_ids) => surface_ids,
|
||||
Err(error) => panic!("target launch surfaces must seed: {}", error),
|
||||
};
|
||||
assert_eq!(surface_ids.len(), 10);
|
||||
let required_codes = [
|
||||
"raydium_launchlab",
|
||||
"raydium_launchpad",
|
||||
"letsbonk",
|
||||
"bonk_fun",
|
||||
"bags",
|
||||
"moonshot",
|
||||
"moonit",
|
||||
"boop_fun",
|
||||
"believe",
|
||||
"heaven",
|
||||
];
|
||||
for code in required_codes {
|
||||
let surface_result =
|
||||
crate::query_launch_surfaces_get_by_code(database.as_ref(), code).await;
|
||||
let surface_option = match surface_result {
|
||||
Ok(surface_option) => surface_option,
|
||||
Err(error) => panic!("surface fetch must succeed: {}", error),
|
||||
};
|
||||
assert!(surface_option.is_some(), "missing launch surface {}", code);
|
||||
}
|
||||
let heaven_surface =
|
||||
match crate::query_launch_surfaces_get_by_code(database.as_ref(), "heaven").await {
|
||||
Ok(Some(surface)) => surface,
|
||||
Ok(None) => panic!("heaven surface must exist"),
|
||||
Err(error) => panic!("heaven surface fetch must succeed: {}", error),
|
||||
};
|
||||
assert!(!heaven_surface.is_enabled);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn attribute_transaction_by_signature_detects_generic_launch_surface_mapping() {
|
||||
let database = make_database().await;
|
||||
let service = crate::LaunchOriginService::new(database.clone());
|
||||
let register_result = service
|
||||
.register_launch_surface_mapping(
|
||||
"raydium_launchlab",
|
||||
Some("DbcDetectTokenA111"),
|
||||
Some("DbcDetectConfig111"),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
if let Err(error) = register_result {
|
||||
panic!("launchlab mapping registration must succeed: {}", error);
|
||||
}
|
||||
seed_decoded_meteora_dbc_event(database.clone(), "sig-launch-origin-launchlab-1").await;
|
||||
let result = service
|
||||
.attribute_transaction_by_signature("sig-launch-origin-launchlab-1")
|
||||
.await;
|
||||
let results = match result {
|
||||
Ok(results) => results,
|
||||
Err(error) => panic!("launchlab attribution must succeed: {}", error),
|
||||
};
|
||||
assert_eq!(results.len(), 1);
|
||||
assert!(results[0].created_attribution);
|
||||
let surface_result =
|
||||
crate::query_launch_surfaces_get_by_code(database.as_ref(), "raydium_launchlab").await;
|
||||
let surface = match surface_result {
|
||||
Ok(Some(surface)) => surface,
|
||||
Ok(None) => panic!("raydium_launchlab surface must exist"),
|
||||
Err(error) => panic!("surface fetch must succeed: {}", error),
|
||||
};
|
||||
assert_eq!(surface.id, Some(results[0].launch_surface_id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,8 @@ pub use constants::ARBITRAGE_BOT_6MWVT_PROGRAM_ID;
|
||||
/// Associated Token Account program identifier. ("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL").
|
||||
/// @see solana_sdk::pubkey::Pubkey = spl_associated_token_account_interface::program::ID
|
||||
pub use constants::ASSOCIATED_TOKEN_PROGRAM_ID;
|
||||
/// Canonical Bonk token mint identifier.
|
||||
pub use constants::BONK_MINT_ID;
|
||||
/// BPF Loader program identifier. ("BPFLoader1111111111111111111111111111111111").
|
||||
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::bpf_loader_deprecated::ID
|
||||
pub use constants::BPF_LOADER_DEPRECATED_PROGRAM_ID;
|
||||
@@ -171,6 +173,8 @@ pub use constants::FLUXBEAM_PROGRAM_ID;
|
||||
/// Incinerator program identifier. ("1nc1nerator11111111111111111111111111111111").
|
||||
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::incinerator::ID
|
||||
pub use constants::INCINERATOR_PROGRAM_ID;
|
||||
/// Canonical Jupiter governance token mint identifier.
|
||||
pub use constants::JUP_MINT_ID;
|
||||
/// Loader V4 program identifier. ("LoaderV411111111111111111111111111111111111").
|
||||
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::loader_v4::ID
|
||||
pub use constants::LOADER_V4_PROGRAM_ID;
|
||||
@@ -191,6 +195,8 @@ pub use constants::ORCA_WHIRLPOOLS_PROGRAM_ID;
|
||||
pub use constants::PUMP_FUN_PROGRAM_ID;
|
||||
/// PumpSwap / PumpAMM program id. ("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA").
|
||||
pub use constants::PUMP_SWAP_PROGRAM_ID;
|
||||
/// Canonical Raydium token mint identifier.
|
||||
pub use constants::RAY_MINT_ID;
|
||||
/// Raydium AMM routing program id. ("routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS").
|
||||
pub use constants::RAYDIUM_AMM_ROUTING_PROGRAM_ID;
|
||||
/// Raydium AmmV4 program id. ("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8").
|
||||
@@ -263,19 +269,13 @@ pub use constants::SYSVAR_SLOT_HISTORY_PROGRAM_ID;
|
||||
/// Sysvar Stake History program identifier. ("SysvarStakeHistory1111111111111111111111111").
|
||||
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::sysvar::stake_history::ID
|
||||
pub use constants::SYSVAR_STAKE_HISTORY_PROGRAM_ID;
|
||||
/// Vote program identifier. ("Vote111111111111111111111111111111111111111").
|
||||
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::vote::ID
|
||||
pub use constants::VOTE_PROGRAM_ID;
|
||||
/// Canonical Bonk token mint identifier.
|
||||
pub use constants::BONK_MINT_ID;
|
||||
/// Canonical Jupiter governance token mint identifier.
|
||||
pub use constants::JUP_MINT_ID;
|
||||
/// Canonical Raydium token mint identifier.
|
||||
pub use constants::RAY_MINT_ID;
|
||||
/// Canonical Solana USDC mint identifier.
|
||||
pub use constants::USDC_MINT_ID;
|
||||
/// Canonical Solana USDT mint identifier.
|
||||
pub use constants::USDT_MINT_ID;
|
||||
/// Vote program identifier. ("Vote111111111111111111111111111111111111111").
|
||||
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::vote::ID
|
||||
pub use constants::VOTE_PROGRAM_ID;
|
||||
/// Wrapped SOL mint identifier. ("So11111111111111111111111111111111111111112").
|
||||
/// @see solana_sdk::pubkey::Pubkey = spl_token_interface::native_mint::ID
|
||||
pub use constants::WSOL_MINT_ID;
|
||||
@@ -365,6 +365,8 @@ pub use db::LocalDexDiagnosticSummaryDto;
|
||||
pub use db::LocalDuplicateDecodedEventTradeDiagnosticSampleDto;
|
||||
/// Local decoded-event classification diagnostics summary.
|
||||
pub use db::LocalEventClassificationDiagnosticSummaryDto;
|
||||
/// Sample of a launch-origin attribution.
|
||||
pub use db::LocalLaunchOriginDiagnosticSampleDto;
|
||||
/// Sample of a decoded trade candidate without linked trade event.
|
||||
pub use db::LocalMissingTradeEventDiagnosticSampleDto;
|
||||
/// Missing trade event diagnostics grouped by reason.
|
||||
@@ -385,6 +387,8 @@ pub use db::LocalPairTradingReadinessDiagnosticSummaryDto;
|
||||
pub use db::LocalPipelineDiagnosticCountersDto;
|
||||
/// Local pipeline diagnostics summary.
|
||||
pub use db::LocalPipelineDiagnosticSummaryDto;
|
||||
/// Sample of a pool-origin row and optional launch linkage.
|
||||
pub use db::LocalPoolOriginDiagnosticSampleDto;
|
||||
/// Prioritized sample of an incomplete token metadata row.
|
||||
pub use db::LocalTokenMetadataGapDiagnosticSampleDto;
|
||||
/// Source family for one on-chain observation.
|
||||
@@ -603,6 +607,8 @@ pub use db::query_local_decoded_event_diagnostic_list_summaries;
|
||||
pub use db::query_local_duplicate_decoded_event_trade_diagnostic_list_samples;
|
||||
/// Lists local decoded-event classification diagnostic summaries.
|
||||
pub use db::query_local_event_classification_diagnostic_list_summaries;
|
||||
/// Lists launch-origin diagnostic samples.
|
||||
pub use db::query_local_launch_origin_diagnostic_list_samples;
|
||||
/// Lists samples of decoded trade candidates without linked trade event.
|
||||
pub use db::query_local_missing_trade_event_diagnostic_list_samples;
|
||||
/// Lists missing trade events grouped by diagnostic reason.
|
||||
@@ -625,6 +631,8 @@ pub use db::query_local_pair_without_trade_diagnostic_list_samples;
|
||||
pub use db::query_local_pipeline_diagnostic_get_counters;
|
||||
/// Lists local DEX diagnostic summaries.
|
||||
pub use db::query_local_pipeline_diagnostic_list_summaries;
|
||||
/// Lists pool-origin diagnostic samples.
|
||||
pub use db::query_local_pool_origin_diagnostic_list_samples;
|
||||
/// Lists prioritized token metadata gap diagnostic samples.
|
||||
pub use db::query_local_token_metadata_gap_diagnostic_list_samples;
|
||||
/// Reads one observed token by mint.
|
||||
|
||||
@@ -75,6 +75,25 @@ impl LocalPipelineDiagnosticsService {
|
||||
Ok(summaries) => summaries,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let launch_origin_samples_result =
|
||||
crate::query_local_launch_origin_diagnostic_list_samples(
|
||||
self.database.as_ref(),
|
||||
sample_limit,
|
||||
)
|
||||
.await;
|
||||
let launch_origin_samples = match launch_origin_samples_result {
|
||||
Ok(samples) => samples,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let pool_origin_samples_result = crate::query_local_pool_origin_diagnostic_list_samples(
|
||||
self.database.as_ref(),
|
||||
sample_limit,
|
||||
)
|
||||
.await;
|
||||
let pool_origin_samples = match pool_origin_samples_result {
|
||||
Ok(samples) => samples,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let token_metadata_gap_samples_result =
|
||||
crate::query_local_token_metadata_gap_diagnostic_list_samples(
|
||||
self.database.as_ref(),
|
||||
@@ -216,6 +235,8 @@ impl LocalPipelineDiagnosticsService {
|
||||
decoded_event_summaries,
|
||||
event_classification_summaries,
|
||||
missing_trade_event_reason_summaries,
|
||||
launch_origin_samples,
|
||||
pool_origin_samples,
|
||||
token_metadata_gap_samples,
|
||||
non_actionable_pair_count: counters.non_actionable_pair_count,
|
||||
non_actionable_pair_summaries,
|
||||
|
||||
@@ -277,6 +277,19 @@ impl LocalPipelineValidationConfig {
|
||||
config.profile_code = "0.7.38_token_metadata_gap_prioritization".to_string();
|
||||
return config;
|
||||
}
|
||||
|
||||
/// Builds the `0.7.39` launch surface origin baseline validation config.
|
||||
///
|
||||
/// This profile keeps the `0.7.38` metadata-gap semantics and requires the
|
||||
/// static DEX matrix invariants that prevent planned launch surfaces from
|
||||
/// being treated as priced trade/candle producers before corpus-backed
|
||||
/// decoders are available.
|
||||
pub fn v0_7_39_launch_surface_origin_baseline() -> Self {
|
||||
let mut config = Self::v0_7_38_token_metadata_gap_prioritization();
|
||||
config.profile_code = "0.7.39_launch_surface_origin_baseline".to_string();
|
||||
config.require_dex_support_matrix_semantics = true;
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
/// A single local pipeline validation issue.
|
||||
@@ -505,6 +518,14 @@ impl LocalPipelineValidationService {
|
||||
crate::LocalPipelineValidationConfig::v0_7_38_token_metadata_gap_prioritization();
|
||||
return self.validate_current_database(&config).await;
|
||||
}
|
||||
|
||||
/// Diagnoses the current database with the `0.7.39` launch-origin baseline profile.
|
||||
pub async fn validate_v0_7_39_current_database(
|
||||
&self,
|
||||
) -> Result<crate::LocalPipelineValidationRunDto, crate::Error> {
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_39_launch_surface_origin_baseline();
|
||||
return self.validate_current_database(&config).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates a diagnostics summary without performing database access.
|
||||
@@ -627,7 +648,8 @@ pub fn validate_local_pipeline_diagnostics_summary(
|
||||
|| 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.37_token_metadata_catalog_enrichment"
|
||||
|| config.profile_code == "0.7.38_token_metadata_gap_prioritization";
|
||||
|| config.profile_code == "0.7.38_token_metadata_gap_prioritization"
|
||||
|| config.profile_code == "0.7.39_launch_surface_origin_baseline";
|
||||
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) {
|
||||
@@ -875,6 +897,23 @@ fn validate_dex_support_matrix_semantics(
|
||||
blocking: true,
|
||||
});
|
||||
}
|
||||
if (entry.status == "planned" || entry.status == "to_verify" || entry.status == "unknown")
|
||||
&& entry.surface_type == "launch"
|
||||
&& (entry.decoded
|
||||
|| entry.materialized
|
||||
|| entry.trade_candidate
|
||||
|| entry.candle_candidate)
|
||||
{
|
||||
issues.push(crate::LocalPipelineValidationIssueDto {
|
||||
code: "inactive_launch_surface_matrix_entry_actionable".to_string(),
|
||||
message: format!(
|
||||
"inactive launch surface '{}' must not be decoded, materialized, or priced as trade/candle candidate",
|
||||
entry_code
|
||||
),
|
||||
subject: Some(entry_code.clone()),
|
||||
blocking: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1061,6 +1100,8 @@ mod tests {
|
||||
decoded_event_summaries: vec![],
|
||||
event_classification_summaries: vec![],
|
||||
missing_trade_event_reason_summaries: vec![],
|
||||
launch_origin_samples: vec![],
|
||||
pool_origin_samples: vec![],
|
||||
token_metadata_gap_samples: vec![],
|
||||
non_actionable_pair_summaries: vec![],
|
||||
missing_trade_event_samples: vec![],
|
||||
@@ -1355,6 +1396,16 @@ mod tests {
|
||||
assert_eq!(summary.token_metadata_gap_samples.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validation_accepts_0_7_39_launch_surface_origin_baseline() {
|
||||
let summary = make_0_7_28_summary_with_meteora();
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_39_launch_surface_origin_baseline();
|
||||
let report = crate::validate_local_pipeline_diagnostics_summary(&summary, &config);
|
||||
assert!(report.validation_passed);
|
||||
assert_eq!(report.validation_profile_code, "0.7.39_launch_surface_origin_baseline");
|
||||
assert_eq!(report.blocking_issue_count, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validation_rejects_0_7_33_pair_trading_readiness_mismatch() {
|
||||
let mut summary = make_0_7_28_summary_with_meteora();
|
||||
|
||||
Reference in New Issue
Block a user