0.7.40
This commit is contained in:
@@ -31,6 +31,12 @@ pub use dtos::LaunchSurfaceDto;
|
||||
pub use dtos::LaunchSurfaceKeyDto;
|
||||
pub use dtos::LiquidityEventDto;
|
||||
pub use dtos::LocalDecodedEventDiagnosticSummaryDto;
|
||||
pub use dtos::LocalDexCorpusDecodedEventSampleDto;
|
||||
pub use dtos::LocalDexCorpusPoolPairSampleDto;
|
||||
pub use dtos::LocalDexCorpusSearchRequestDto;
|
||||
pub use dtos::LocalDexCorpusSearchResultDto;
|
||||
pub use dtos::LocalDexCorpusSearchSummaryDto;
|
||||
pub use dtos::LocalDexCorpusTransactionSampleDto;
|
||||
pub use dtos::LocalDexDiagnosticSummaryDto;
|
||||
pub use dtos::LocalDuplicateDecodedEventTradeDiagnosticSampleDto;
|
||||
pub use dtos::LocalEventClassificationDiagnosticSummaryDto;
|
||||
@@ -46,6 +52,8 @@ pub use dtos::LocalPairTradingReadinessDiagnosticSummaryDto;
|
||||
pub use dtos::LocalPipelineDiagnosticCountersDto;
|
||||
pub use dtos::LocalPipelineDiagnosticSummaryDto;
|
||||
pub use dtos::LocalPoolOriginDiagnosticSampleDto;
|
||||
pub use dtos::LocalRaydiumProgramInstructionDiagnosticSummaryDto;
|
||||
pub use dtos::LocalRaydiumSurfaceDiagnosticSummaryDto;
|
||||
pub use dtos::LocalTokenMetadataGapDiagnosticSampleDto;
|
||||
pub use dtos::ObservedTokenDto;
|
||||
pub use dtos::OnchainObservationDto;
|
||||
@@ -160,6 +168,10 @@ pub use queries::query_launch_surfaces_upsert;
|
||||
pub use queries::query_liquidity_events_list_recent;
|
||||
pub use queries::query_liquidity_events_upsert;
|
||||
pub use queries::query_local_decoded_event_diagnostic_list_summaries;
|
||||
pub use queries::query_local_dex_corpus_search_get_summary;
|
||||
pub use queries::query_local_dex_corpus_search_list_decoded_event_samples;
|
||||
pub use queries::query_local_dex_corpus_search_list_pool_pair_samples;
|
||||
pub use queries::query_local_dex_corpus_search_list_transaction_samples;
|
||||
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;
|
||||
@@ -175,6 +187,7 @@ 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_raydium_program_instruction_diagnostic_list_summaries;
|
||||
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;
|
||||
|
||||
@@ -17,6 +17,7 @@ mod launch_attribution;
|
||||
mod launch_surface;
|
||||
mod launch_surface_key;
|
||||
mod liquidity_event;
|
||||
mod local_dex_corpus_search;
|
||||
mod local_pipeline_diagnostics;
|
||||
mod observed_token;
|
||||
mod onchain_observation;
|
||||
@@ -45,6 +46,10 @@ mod wallet;
|
||||
mod wallet_holding;
|
||||
mod wallet_participation;
|
||||
|
||||
pub(crate) use local_dex_corpus_search::LocalDexCorpusDecodedEventSampleRow;
|
||||
pub(crate) use local_dex_corpus_search::LocalDexCorpusPoolPairSampleRow;
|
||||
pub(crate) use local_dex_corpus_search::LocalDexCorpusSearchSummaryRow;
|
||||
pub(crate) use local_dex_corpus_search::LocalDexCorpusTransactionSampleRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalDecodedEventDiagnosticSummaryRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalDexDiagnosticSummaryRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalDuplicateDecodedEventTradeDiagnosticSampleRow;
|
||||
@@ -60,6 +65,7 @@ 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::LocalRaydiumProgramInstructionDiagnosticSummaryRow;
|
||||
pub(crate) use local_pipeline_diagnostics::LocalTokenMetadataGapDiagnosticSampleRow;
|
||||
|
||||
pub use analysis_signal::AnalysisSignalDto;
|
||||
@@ -77,6 +83,12 @@ pub use launch_attribution::LaunchAttributionDto;
|
||||
pub use launch_surface::LaunchSurfaceDto;
|
||||
pub use launch_surface_key::LaunchSurfaceKeyDto;
|
||||
pub use liquidity_event::LiquidityEventDto;
|
||||
pub use local_dex_corpus_search::LocalDexCorpusDecodedEventSampleDto;
|
||||
pub use local_dex_corpus_search::LocalDexCorpusPoolPairSampleDto;
|
||||
pub use local_dex_corpus_search::LocalDexCorpusSearchRequestDto;
|
||||
pub use local_dex_corpus_search::LocalDexCorpusSearchResultDto;
|
||||
pub use local_dex_corpus_search::LocalDexCorpusSearchSummaryDto;
|
||||
pub use local_dex_corpus_search::LocalDexCorpusTransactionSampleDto;
|
||||
pub use local_pipeline_diagnostics::LocalDecodedEventDiagnosticSummaryDto;
|
||||
pub use local_pipeline_diagnostics::LocalDexDiagnosticSummaryDto;
|
||||
pub use local_pipeline_diagnostics::LocalDuplicateDecodedEventTradeDiagnosticSampleDto;
|
||||
@@ -93,6 +105,8 @@ pub use local_pipeline_diagnostics::LocalPairTradingReadinessDiagnosticSummaryDt
|
||||
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticCountersDto;
|
||||
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticSummaryDto;
|
||||
pub use local_pipeline_diagnostics::LocalPoolOriginDiagnosticSampleDto;
|
||||
pub use local_pipeline_diagnostics::LocalRaydiumProgramInstructionDiagnosticSummaryDto;
|
||||
pub use local_pipeline_diagnostics::LocalRaydiumSurfaceDiagnosticSummaryDto;
|
||||
pub use local_pipeline_diagnostics::LocalTokenMetadataGapDiagnosticSampleDto;
|
||||
pub use observed_token::ObservedTokenDto;
|
||||
pub use onchain_observation::OnchainObservationDto;
|
||||
|
||||
281
kb_lib/src/db/dtos/local_dex_corpus_search.rs
Normal file
281
kb_lib/src/db/dtos/local_dex_corpus_search.rs
Normal file
@@ -0,0 +1,281 @@
|
||||
// file: kb_lib/src/db/dtos/local_dex_corpus_search.rs
|
||||
|
||||
//! DTOs for local DEX corpus searches used by Demo3.
|
||||
|
||||
/// Search request for local DEX corpus discovery.
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LocalDexCorpusSearchRequestDto {
|
||||
/// Optional DEX code or decoded protocol name.
|
||||
pub dex_code: std::option::Option<std::string::String>,
|
||||
/// Optional Solana program id.
|
||||
pub program_id: std::option::Option<std::string::String>,
|
||||
/// Optional local pair id.
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Optional pool account/address.
|
||||
pub pool_address: std::option::Option<std::string::String>,
|
||||
/// Optional token mint to match as base, quote or decoded mint.
|
||||
pub token_mint: std::option::Option<std::string::String>,
|
||||
/// Optional transaction signature.
|
||||
pub signature: std::option::Option<std::string::String>,
|
||||
/// Maximum number of rows to return per sample category.
|
||||
pub limit: u32,
|
||||
}
|
||||
|
||||
/// Result of a local DEX corpus search.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LocalDexCorpusSearchResultDto {
|
||||
/// Normalized request used for the search.
|
||||
pub request: crate::LocalDexCorpusSearchRequestDto,
|
||||
/// Aggregate counts for matching local data.
|
||||
pub summary: crate::LocalDexCorpusSearchSummaryDto,
|
||||
/// Matching transaction samples.
|
||||
pub transaction_samples: std::vec::Vec<crate::LocalDexCorpusTransactionSampleDto>,
|
||||
/// Matching pool/pair samples.
|
||||
pub pool_pair_samples: std::vec::Vec<crate::LocalDexCorpusPoolPairSampleDto>,
|
||||
/// Matching decoded event samples.
|
||||
pub decoded_event_samples: std::vec::Vec<crate::LocalDexCorpusDecodedEventSampleDto>,
|
||||
}
|
||||
|
||||
/// Aggregate counts for a local DEX corpus search.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LocalDexCorpusSearchSummaryDto {
|
||||
/// Number of distinct matching transactions.
|
||||
pub transaction_count: i64,
|
||||
/// Number of distinct matching instructions.
|
||||
pub instruction_count: i64,
|
||||
/// Number of distinct matching decoded DEX events.
|
||||
pub decoded_event_count: i64,
|
||||
/// Number of distinct matching pools.
|
||||
pub pool_count: i64,
|
||||
/// Number of distinct matching pairs.
|
||||
pub pair_count: i64,
|
||||
/// Number of distinct matching trade events.
|
||||
pub trade_event_count: i64,
|
||||
/// Number of distinct matching candle rows.
|
||||
pub pair_candle_count: i64,
|
||||
/// Number of distinct matching protocol-candidate rows.
|
||||
pub protocol_candidate_count: i64,
|
||||
}
|
||||
|
||||
/// Matching transaction sample for corpus discovery.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LocalDexCorpusTransactionSampleDto {
|
||||
/// Transaction id.
|
||||
pub transaction_id: i64,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Optional Solana slot.
|
||||
pub slot: std::option::Option<i64>,
|
||||
/// Whether the transaction has a non-null error payload.
|
||||
pub failed: bool,
|
||||
/// Number of persisted instructions for the transaction.
|
||||
pub instruction_count: i64,
|
||||
/// Number of persisted decoded DEX events for the transaction.
|
||||
pub decoded_event_count: i64,
|
||||
/// Number of persisted trade events for the transaction.
|
||||
pub trade_event_count: i64,
|
||||
/// Comma-separated distinct program ids seen in the transaction.
|
||||
pub program_ids_csv: std::string::String,
|
||||
}
|
||||
|
||||
/// Matching pool/pair sample for corpus discovery.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LocalDexCorpusPoolPairSampleDto {
|
||||
/// Optional pool id.
|
||||
pub pool_id: std::option::Option<i64>,
|
||||
/// Optional pool address.
|
||||
pub pool_address: std::option::Option<std::string::String>,
|
||||
/// Optional pair id.
|
||||
pub pair_id: std::option::Option<i64>,
|
||||
/// Optional DEX code.
|
||||
pub dex_code: std::option::Option<std::string::String>,
|
||||
/// Optional pair symbol.
|
||||
pub pair_symbol: std::option::Option<std::string::String>,
|
||||
/// Optional base token mint.
|
||||
pub base_mint: std::option::Option<std::string::String>,
|
||||
/// Optional base token symbol.
|
||||
pub base_symbol: std::option::Option<std::string::String>,
|
||||
/// Optional quote token mint.
|
||||
pub quote_mint: std::option::Option<std::string::String>,
|
||||
/// Optional quote token symbol.
|
||||
pub quote_symbol: std::option::Option<std::string::String>,
|
||||
/// Number of decoded events attached to the pool.
|
||||
pub decoded_event_count: i64,
|
||||
/// Number of trade events attached to the pair.
|
||||
pub trade_event_count: i64,
|
||||
/// Number of candle rows attached to the pair.
|
||||
pub pair_candle_count: i64,
|
||||
/// Latest known founding or activity signature for the pool/pair.
|
||||
pub latest_signature: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
/// Matching decoded event sample for corpus discovery.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LocalDexCorpusDecodedEventSampleDto {
|
||||
/// Decoded event id.
|
||||
pub decoded_event_id: i64,
|
||||
/// Transaction id.
|
||||
pub transaction_id: i64,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Optional Solana slot.
|
||||
pub slot: std::option::Option<i64>,
|
||||
/// Protocol name stored on the decoded event.
|
||||
pub protocol_name: std::string::String,
|
||||
/// Program id stored on the decoded event.
|
||||
pub program_id: std::string::String,
|
||||
/// Event kind.
|
||||
pub event_kind: std::string::String,
|
||||
/// Optional pool account.
|
||||
pub pool_account: std::option::Option<std::string::String>,
|
||||
/// Optional token A mint.
|
||||
pub token_a_mint: std::option::Option<std::string::String>,
|
||||
/// Optional token B mint.
|
||||
pub token_b_mint: std::option::Option<std::string::String>,
|
||||
/// Decoded event category extracted from payload JSON.
|
||||
pub event_category: std::option::Option<std::string::String>,
|
||||
/// Decoded event lifecycle kind extracted from payload JSON.
|
||||
pub event_lifecycle_kind: std::option::Option<std::string::String>,
|
||||
/// Decoded event actionability extracted from payload JSON.
|
||||
pub event_actionability: std::option::Option<std::string::String>,
|
||||
/// Whether the decoded event is a trade candidate.
|
||||
pub trade_candidate: bool,
|
||||
/// Whether the decoded event is a candle candidate.
|
||||
pub candle_candidate: bool,
|
||||
}
|
||||
|
||||
/// SQL row for aggregate counts of a local DEX corpus search.
|
||||
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||
pub(crate) struct LocalDexCorpusSearchSummaryRow {
|
||||
pub(crate) transaction_count: i64,
|
||||
pub(crate) instruction_count: i64,
|
||||
pub(crate) decoded_event_count: i64,
|
||||
pub(crate) pool_count: i64,
|
||||
pub(crate) pair_count: i64,
|
||||
pub(crate) trade_event_count: i64,
|
||||
pub(crate) pair_candle_count: i64,
|
||||
pub(crate) protocol_candidate_count: i64,
|
||||
}
|
||||
|
||||
/// SQL row for a matching transaction sample.
|
||||
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||
pub(crate) struct LocalDexCorpusTransactionSampleRow {
|
||||
pub(crate) transaction_id: i64,
|
||||
pub(crate) signature: std::string::String,
|
||||
pub(crate) slot: std::option::Option<i64>,
|
||||
pub(crate) failed: i64,
|
||||
pub(crate) instruction_count: i64,
|
||||
pub(crate) decoded_event_count: i64,
|
||||
pub(crate) trade_event_count: i64,
|
||||
pub(crate) program_ids_csv: std::string::String,
|
||||
}
|
||||
|
||||
/// SQL row for a matching pool/pair sample.
|
||||
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||
pub(crate) struct LocalDexCorpusPoolPairSampleRow {
|
||||
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) dex_code: std::option::Option<std::string::String>,
|
||||
pub(crate) pair_symbol: std::option::Option<std::string::String>,
|
||||
pub(crate) base_mint: std::option::Option<std::string::String>,
|
||||
pub(crate) base_symbol: std::option::Option<std::string::String>,
|
||||
pub(crate) quote_mint: std::option::Option<std::string::String>,
|
||||
pub(crate) quote_symbol: std::option::Option<std::string::String>,
|
||||
pub(crate) decoded_event_count: i64,
|
||||
pub(crate) trade_event_count: i64,
|
||||
pub(crate) pair_candle_count: i64,
|
||||
pub(crate) latest_signature: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
/// SQL row for a matching decoded event sample.
|
||||
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||
pub(crate) struct LocalDexCorpusDecodedEventSampleRow {
|
||||
pub(crate) decoded_event_id: i64,
|
||||
pub(crate) transaction_id: i64,
|
||||
pub(crate) signature: std::string::String,
|
||||
pub(crate) slot: std::option::Option<i64>,
|
||||
pub(crate) protocol_name: std::string::String,
|
||||
pub(crate) program_id: std::string::String,
|
||||
pub(crate) event_kind: std::string::String,
|
||||
pub(crate) pool_account: std::option::Option<std::string::String>,
|
||||
pub(crate) token_a_mint: std::option::Option<std::string::String>,
|
||||
pub(crate) token_b_mint: std::option::Option<std::string::String>,
|
||||
pub(crate) event_category: std::option::Option<std::string::String>,
|
||||
pub(crate) event_lifecycle_kind: std::option::Option<std::string::String>,
|
||||
pub(crate) event_actionability: std::option::Option<std::string::String>,
|
||||
pub(crate) trade_candidate: std::option::Option<i64>,
|
||||
pub(crate) candle_candidate: std::option::Option<i64>,
|
||||
}
|
||||
|
||||
impl From<LocalDexCorpusSearchSummaryRow> for LocalDexCorpusSearchSummaryDto {
|
||||
fn from(row: LocalDexCorpusSearchSummaryRow) -> Self {
|
||||
return Self {
|
||||
transaction_count: row.transaction_count,
|
||||
instruction_count: row.instruction_count,
|
||||
decoded_event_count: row.decoded_event_count,
|
||||
pool_count: row.pool_count,
|
||||
pair_count: row.pair_count,
|
||||
trade_event_count: row.trade_event_count,
|
||||
pair_candle_count: row.pair_candle_count,
|
||||
protocol_candidate_count: row.protocol_candidate_count,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LocalDexCorpusTransactionSampleRow> for LocalDexCorpusTransactionSampleDto {
|
||||
fn from(row: LocalDexCorpusTransactionSampleRow) -> Self {
|
||||
return Self {
|
||||
transaction_id: row.transaction_id,
|
||||
signature: row.signature,
|
||||
slot: row.slot,
|
||||
failed: row.failed != 0,
|
||||
instruction_count: row.instruction_count,
|
||||
decoded_event_count: row.decoded_event_count,
|
||||
trade_event_count: row.trade_event_count,
|
||||
program_ids_csv: row.program_ids_csv,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LocalDexCorpusPoolPairSampleRow> for LocalDexCorpusPoolPairSampleDto {
|
||||
fn from(row: LocalDexCorpusPoolPairSampleRow) -> Self {
|
||||
return Self {
|
||||
pool_id: row.pool_id,
|
||||
pool_address: row.pool_address,
|
||||
pair_id: row.pair_id,
|
||||
dex_code: row.dex_code,
|
||||
pair_symbol: row.pair_symbol,
|
||||
base_mint: row.base_mint,
|
||||
base_symbol: row.base_symbol,
|
||||
quote_mint: row.quote_mint,
|
||||
quote_symbol: row.quote_symbol,
|
||||
decoded_event_count: row.decoded_event_count,
|
||||
trade_event_count: row.trade_event_count,
|
||||
pair_candle_count: row.pair_candle_count,
|
||||
latest_signature: row.latest_signature,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LocalDexCorpusDecodedEventSampleRow> for LocalDexCorpusDecodedEventSampleDto {
|
||||
fn from(row: LocalDexCorpusDecodedEventSampleRow) -> Self {
|
||||
return Self {
|
||||
decoded_event_id: row.decoded_event_id,
|
||||
transaction_id: row.transaction_id,
|
||||
signature: row.signature,
|
||||
slot: row.slot,
|
||||
protocol_name: row.protocol_name,
|
||||
program_id: row.program_id,
|
||||
event_kind: row.event_kind,
|
||||
pool_account: row.pool_account,
|
||||
token_a_mint: row.token_a_mint,
|
||||
token_b_mint: row.token_b_mint,
|
||||
event_category: row.event_category,
|
||||
event_lifecycle_kind: row.event_lifecycle_kind,
|
||||
event_actionability: row.event_actionability,
|
||||
trade_candidate: row.trade_candidate.unwrap_or(0) != 0,
|
||||
candle_candidate: row.candle_candidate.unwrap_or(0) != 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -122,6 +122,9 @@ pub struct LocalPipelineDiagnosticSummaryDto {
|
||||
pub pair_without_candle_count: i64,
|
||||
/// Diagnostics grouped by DEX.
|
||||
pub dex_summaries: std::vec::Vec<crate::LocalDexDiagnosticSummaryDto>,
|
||||
/// Raydium surface diagnostics derived from the matrix and observed instructions.
|
||||
pub raydium_surface_summaries:
|
||||
std::vec::Vec<crate::LocalRaydiumSurfaceDiagnosticSummaryDto>,
|
||||
/// Diagnostics grouped by pair.
|
||||
pub pair_summaries: std::vec::Vec<crate::LocalPairDiagnosticSummaryDto>,
|
||||
/// Diagnostics grouped by pair materialization/actionability class.
|
||||
@@ -185,6 +188,60 @@ pub struct LocalDexDiagnosticSummaryDto {
|
||||
pub pair_candle_count: i64,
|
||||
}
|
||||
|
||||
/// Local diagnostics for one Raydium surface from the DEX-first matrix.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LocalRaydiumSurfaceDiagnosticSummaryDto {
|
||||
/// DEX matrix code, such as `raydium_clmm` or `raydium_amm_v4`.
|
||||
pub dex_code: std::string::String,
|
||||
/// Human readable surface name.
|
||||
pub display_name: std::string::String,
|
||||
/// DEX-first role attached to this surface.
|
||||
pub surface_role: std::string::String,
|
||||
/// Program id from the support matrix, when known.
|
||||
pub program_id: std::option::Option<std::string::String>,
|
||||
/// Program id verification status from the support matrix.
|
||||
pub program_id_status: std::string::String,
|
||||
/// Support status from the support matrix.
|
||||
pub status: std::string::String,
|
||||
/// Whether this surface is enabled in the local DEX catalog.
|
||||
pub catalog_enabled: bool,
|
||||
/// Total projected instructions for this program in the current database.
|
||||
pub instruction_count: i64,
|
||||
/// Total distinct projected transactions for this program in the current database.
|
||||
pub transaction_count: i64,
|
||||
/// Total decoded events for this DEX code.
|
||||
pub decoded_event_count: i64,
|
||||
/// Total materialized trade events for this DEX code.
|
||||
pub trade_event_count: i64,
|
||||
/// Total candle buckets for this DEX code.
|
||||
pub pair_candle_count: i64,
|
||||
/// Latest slot where this program was observed, when present.
|
||||
pub latest_slot: std::option::Option<i64>,
|
||||
/// Latest signature where this program was observed, when present.
|
||||
pub latest_signature: std::option::Option<std::string::String>,
|
||||
/// Whether projected instructions prove this program exists in the current corpus.
|
||||
pub observed_in_current_corpus: bool,
|
||||
/// Whether this DEX code has decoded events in the current corpus.
|
||||
pub decoded_in_current_corpus: bool,
|
||||
/// Whether this DEX code has materialized trades in the current corpus.
|
||||
pub trade_materialized_in_current_corpus: bool,
|
||||
}
|
||||
|
||||
/// Projected instruction diagnostics grouped by Raydium program id.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LocalRaydiumProgramInstructionDiagnosticSummaryDto {
|
||||
/// Program id.
|
||||
pub program_id: std::string::String,
|
||||
/// Total projected instruction rows.
|
||||
pub instruction_count: i64,
|
||||
/// Total distinct transactions.
|
||||
pub transaction_count: i64,
|
||||
/// Latest slot where the program appears, when present.
|
||||
pub latest_slot: std::option::Option<i64>,
|
||||
/// Latest signature where the program appears, when present.
|
||||
pub latest_signature: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
/// Local pair diagnostics summary.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LocalPairDiagnosticSummaryDto {
|
||||
@@ -537,6 +594,16 @@ pub(crate) struct LocalDexDiagnosticSummaryRow {
|
||||
pub(crate) pair_candle_count: i64,
|
||||
}
|
||||
|
||||
/// SQL row for Raydium program instruction diagnostics.
|
||||
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||
pub(crate) struct LocalRaydiumProgramInstructionDiagnosticSummaryRow {
|
||||
pub(crate) program_id: std::string::String,
|
||||
pub(crate) instruction_count: i64,
|
||||
pub(crate) transaction_count: i64,
|
||||
pub(crate) latest_slot: std::option::Option<i64>,
|
||||
pub(crate) latest_signature: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
/// SQL row for local pair diagnostics.
|
||||
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||
pub(crate) struct LocalPairDiagnosticSummaryRow {
|
||||
|
||||
@@ -17,6 +17,7 @@ mod launch_attribution;
|
||||
mod launch_surface;
|
||||
mod launch_surface_key;
|
||||
mod liquidity_event;
|
||||
mod local_dex_corpus_search;
|
||||
mod local_pipeline_diagnostics;
|
||||
mod observed_token;
|
||||
mod onchain_observation;
|
||||
@@ -88,6 +89,10 @@ pub use launch_surface_key::query_launch_surface_keys_list_by_surface_id;
|
||||
pub use launch_surface_key::query_launch_surface_keys_upsert;
|
||||
pub use liquidity_event::query_liquidity_events_list_recent;
|
||||
pub use liquidity_event::query_liquidity_events_upsert;
|
||||
pub use local_dex_corpus_search::query_local_dex_corpus_search_get_summary;
|
||||
pub use local_dex_corpus_search::query_local_dex_corpus_search_list_decoded_event_samples;
|
||||
pub use local_dex_corpus_search::query_local_dex_corpus_search_list_pool_pair_samples;
|
||||
pub use local_dex_corpus_search::query_local_dex_corpus_search_list_transaction_samples;
|
||||
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;
|
||||
@@ -104,6 +109,7 @@ pub use local_pipeline_diagnostics::query_local_pair_without_trade_diagnostic_li
|
||||
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_raydium_program_instruction_diagnostic_list_summaries;
|
||||
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;
|
||||
|
||||
548
kb_lib/src/db/queries/local_dex_corpus_search.rs
Normal file
548
kb_lib/src/db/queries/local_dex_corpus_search.rs
Normal file
@@ -0,0 +1,548 @@
|
||||
// file: kb_lib/src/db/queries/local_dex_corpus_search.rs
|
||||
|
||||
//! Queries for local DEX corpus searches used by Demo3.
|
||||
|
||||
/// Returns aggregate counts for a local DEX corpus search.
|
||||
pub async fn query_local_dex_corpus_search_get_summary(
|
||||
database: &crate::Database,
|
||||
request: &crate::LocalDexCorpusSearchRequestDto,
|
||||
) -> Result<crate::LocalDexCorpusSearchSummaryDto, crate::Error> {
|
||||
match database.connection() {
|
||||
crate::DatabaseConnection::Sqlite(pool) => {
|
||||
let dex_code = normalized_filter_text(request.dex_code.as_deref());
|
||||
let program_id = normalized_filter_text(request.program_id.as_deref());
|
||||
let pool_address = normalized_filter_text(request.pool_address.as_deref());
|
||||
let token_mint = normalized_filter_text(request.token_mint.as_deref());
|
||||
let signature = normalized_filter_text(request.signature.as_deref());
|
||||
let pool_address_like = like_filter_text(pool_address.as_str());
|
||||
let token_mint_like = like_filter_text(token_mint.as_str());
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::db::dtos::LocalDexCorpusSearchSummaryRow>(
|
||||
r#"
|
||||
SELECT
|
||||
COUNT(DISTINCT tx.id) AS transaction_count,
|
||||
COUNT(DISTINCT ix.id) AS instruction_count,
|
||||
COUNT(DISTINCT dde.id) AS decoded_event_count,
|
||||
COUNT(DISTINCT pool.id) AS pool_count,
|
||||
COUNT(DISTINCT pair.id) AS pair_count,
|
||||
COUNT(DISTINCT te.id) AS trade_event_count,
|
||||
COUNT(DISTINCT candle.id) AS pair_candle_count,
|
||||
COUNT(DISTINCT pc.id) AS protocol_candidate_count
|
||||
FROM k_sol_chain_transactions tx
|
||||
LEFT JOIN k_sol_chain_instructions ix
|
||||
ON ix.transaction_id = tx.id
|
||||
LEFT JOIN k_sol_dex_decoded_events dde
|
||||
ON dde.transaction_id = tx.id
|
||||
LEFT JOIN k_sol_pools pool
|
||||
ON pool.address = dde.pool_account
|
||||
LEFT JOIN k_sol_pairs pair
|
||||
ON pair.pool_id = pool.id
|
||||
LEFT JOIN k_sol_dexes dex
|
||||
ON dex.id = pair.dex_id
|
||||
LEFT JOIN k_sol_tokens base_token
|
||||
ON base_token.id = pair.base_token_id
|
||||
LEFT JOIN k_sol_tokens quote_token
|
||||
ON quote_token.id = pair.quote_token_id
|
||||
LEFT JOIN k_sol_trade_events te
|
||||
ON te.transaction_id = tx.id
|
||||
LEFT JOIN k_sol_pair_candles candle
|
||||
ON candle.pair_id = pair.id
|
||||
LEFT JOIN k_sol_protocol_candidates pc
|
||||
ON pc.transaction_id = tx.id
|
||||
WHERE
|
||||
(NULLIF(TRIM(?), '') IS NULL
|
||||
OR dex.code = ?
|
||||
OR dde.protocol_name = ?
|
||||
OR pc.candidate_protocol = ?
|
||||
OR pc.candidate_surface = ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL
|
||||
OR ix.program_id = ?
|
||||
OR dde.program_id = ?
|
||||
OR pc.program_id = ?)
|
||||
AND (? IS NULL OR pair.id = ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL
|
||||
OR pool.address = ?
|
||||
OR dde.pool_account = ?
|
||||
OR ix.accounts_json LIKE ?
|
||||
OR pc.evidence_json LIKE ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL
|
||||
OR base_token.mint = ?
|
||||
OR quote_token.mint = ?
|
||||
OR dde.token_a_mint = ?
|
||||
OR dde.token_b_mint = ?
|
||||
OR ix.accounts_json LIKE ?
|
||||
OR tx.transaction_json LIKE ?
|
||||
OR pc.evidence_json LIKE ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL OR tx.signature = ?)
|
||||
"#,
|
||||
)
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(request.pair_id)
|
||||
.bind(request.pair_id)
|
||||
.bind(pool_address.clone())
|
||||
.bind(pool_address.clone())
|
||||
.bind(pool_address.clone())
|
||||
.bind(pool_address_like.clone())
|
||||
.bind(pool_address_like.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint_like.clone())
|
||||
.bind(token_mint_like.clone())
|
||||
.bind(token_mint_like.clone())
|
||||
.bind(signature.clone())
|
||||
.bind(signature.clone())
|
||||
.fetch_one(pool)
|
||||
.await;
|
||||
let row = match query_result {
|
||||
Ok(row) => row,
|
||||
Err(error) => {
|
||||
return Err(crate::Error::Db(format!(
|
||||
"cannot query local DEX corpus search summary on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
};
|
||||
return Ok(crate::LocalDexCorpusSearchSummaryDto::from(row));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Lists matching transaction samples for a local DEX corpus search.
|
||||
pub async fn query_local_dex_corpus_search_list_transaction_samples(
|
||||
database: &crate::Database,
|
||||
request: &crate::LocalDexCorpusSearchRequestDto,
|
||||
) -> Result<std::vec::Vec<crate::LocalDexCorpusTransactionSampleDto>, crate::Error> {
|
||||
let limit = normalized_limit(request.limit);
|
||||
if limit == 0 {
|
||||
return Ok(std::vec::Vec::new());
|
||||
}
|
||||
match database.connection() {
|
||||
crate::DatabaseConnection::Sqlite(pool) => {
|
||||
let dex_code = normalized_filter_text(request.dex_code.as_deref());
|
||||
let program_id = normalized_filter_text(request.program_id.as_deref());
|
||||
let pool_address = normalized_filter_text(request.pool_address.as_deref());
|
||||
let token_mint = normalized_filter_text(request.token_mint.as_deref());
|
||||
let signature = normalized_filter_text(request.signature.as_deref());
|
||||
let pool_address_like = like_filter_text(pool_address.as_str());
|
||||
let token_mint_like = like_filter_text(token_mint.as_str());
|
||||
let query_result = sqlx::query_as::<
|
||||
sqlx::Sqlite,
|
||||
crate::db::dtos::LocalDexCorpusTransactionSampleRow,
|
||||
>(
|
||||
r#"
|
||||
SELECT
|
||||
tx.id AS transaction_id,
|
||||
tx.signature AS signature,
|
||||
tx.slot AS slot,
|
||||
CASE WHEN tx.err_json IS NULL THEN 0 ELSE 1 END AS failed,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM k_sol_chain_instructions count_ix
|
||||
WHERE count_ix.transaction_id = tx.id
|
||||
) AS instruction_count,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM k_sol_dex_decoded_events count_dde
|
||||
WHERE count_dde.transaction_id = tx.id
|
||||
) AS decoded_event_count,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM k_sol_trade_events count_te
|
||||
WHERE count_te.transaction_id = tx.id
|
||||
) AS trade_event_count,
|
||||
COALESCE((
|
||||
SELECT GROUP_CONCAT(DISTINCT program_ix.program_id)
|
||||
FROM k_sol_chain_instructions program_ix
|
||||
WHERE program_ix.transaction_id = tx.id
|
||||
AND program_ix.program_id IS NOT NULL
|
||||
), '') AS program_ids_csv
|
||||
FROM k_sol_chain_transactions tx
|
||||
LEFT JOIN k_sol_chain_instructions ix
|
||||
ON ix.transaction_id = tx.id
|
||||
LEFT JOIN k_sol_dex_decoded_events dde
|
||||
ON dde.transaction_id = tx.id
|
||||
LEFT JOIN k_sol_pools pool
|
||||
ON pool.address = dde.pool_account
|
||||
LEFT JOIN k_sol_pairs pair
|
||||
ON pair.pool_id = pool.id
|
||||
LEFT JOIN k_sol_dexes dex
|
||||
ON dex.id = pair.dex_id
|
||||
LEFT JOIN k_sol_tokens base_token
|
||||
ON base_token.id = pair.base_token_id
|
||||
LEFT JOIN k_sol_tokens quote_token
|
||||
ON quote_token.id = pair.quote_token_id
|
||||
LEFT JOIN k_sol_protocol_candidates pc
|
||||
ON pc.transaction_id = tx.id
|
||||
WHERE
|
||||
(NULLIF(TRIM(?), '') IS NULL
|
||||
OR dex.code = ?
|
||||
OR dde.protocol_name = ?
|
||||
OR pc.candidate_protocol = ?
|
||||
OR pc.candidate_surface = ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL
|
||||
OR ix.program_id = ?
|
||||
OR dde.program_id = ?
|
||||
OR pc.program_id = ?)
|
||||
AND (? IS NULL OR pair.id = ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL
|
||||
OR pool.address = ?
|
||||
OR dde.pool_account = ?
|
||||
OR ix.accounts_json LIKE ?
|
||||
OR pc.evidence_json LIKE ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL
|
||||
OR base_token.mint = ?
|
||||
OR quote_token.mint = ?
|
||||
OR dde.token_a_mint = ?
|
||||
OR dde.token_b_mint = ?
|
||||
OR ix.accounts_json LIKE ?
|
||||
OR tx.transaction_json LIKE ?
|
||||
OR pc.evidence_json LIKE ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL OR tx.signature = ?)
|
||||
GROUP BY tx.id
|
||||
ORDER BY tx.slot DESC, tx.id DESC
|
||||
LIMIT ?
|
||||
"#,
|
||||
)
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(request.pair_id)
|
||||
.bind(request.pair_id)
|
||||
.bind(pool_address.clone())
|
||||
.bind(pool_address.clone())
|
||||
.bind(pool_address.clone())
|
||||
.bind(pool_address_like.clone())
|
||||
.bind(pool_address_like.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint_like.clone())
|
||||
.bind(token_mint_like.clone())
|
||||
.bind(token_mint_like.clone())
|
||||
.bind(signature.clone())
|
||||
.bind(signature.clone())
|
||||
.bind(i64::from(limit))
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
let rows = match query_result {
|
||||
Ok(rows) => rows,
|
||||
Err(error) => {
|
||||
return Err(crate::Error::Db(format!(
|
||||
"cannot list local DEX corpus transaction samples on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for row in rows {
|
||||
dtos.push(crate::LocalDexCorpusTransactionSampleDto::from(row));
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Lists matching pool/pair samples for a local DEX corpus search.
|
||||
pub async fn query_local_dex_corpus_search_list_pool_pair_samples(
|
||||
database: &crate::Database,
|
||||
request: &crate::LocalDexCorpusSearchRequestDto,
|
||||
) -> Result<std::vec::Vec<crate::LocalDexCorpusPoolPairSampleDto>, crate::Error> {
|
||||
let limit = normalized_limit(request.limit);
|
||||
if limit == 0 {
|
||||
return Ok(std::vec::Vec::new());
|
||||
}
|
||||
match database.connection() {
|
||||
crate::DatabaseConnection::Sqlite(pool) => {
|
||||
let dex_code = normalized_filter_text(request.dex_code.as_deref());
|
||||
let program_id = normalized_filter_text(request.program_id.as_deref());
|
||||
let pool_address = normalized_filter_text(request.pool_address.as_deref());
|
||||
let token_mint = normalized_filter_text(request.token_mint.as_deref());
|
||||
let signature = normalized_filter_text(request.signature.as_deref());
|
||||
let pool_address_like = like_filter_text(pool_address.as_str());
|
||||
let token_mint_like = like_filter_text(token_mint.as_str());
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::db::dtos::LocalDexCorpusPoolPairSampleRow>(
|
||||
r#"
|
||||
SELECT
|
||||
pool.id AS pool_id,
|
||||
pool.address AS pool_address,
|
||||
pair.id AS pair_id,
|
||||
dex.code AS dex_code,
|
||||
pair.symbol AS pair_symbol,
|
||||
base_token.mint AS base_mint,
|
||||
base_token.symbol AS base_symbol,
|
||||
quote_token.mint AS quote_mint,
|
||||
quote_token.symbol AS quote_symbol,
|
||||
COUNT(DISTINCT dde.id) AS decoded_event_count,
|
||||
COUNT(DISTINCT te.id) AS trade_event_count,
|
||||
COUNT(DISTINCT candle.id) AS pair_candle_count,
|
||||
MAX(COALESCE(te.signature, tx.signature, pc.signature)) AS latest_signature
|
||||
FROM k_sol_pools pool
|
||||
LEFT JOIN k_sol_pairs pair
|
||||
ON pair.pool_id = pool.id
|
||||
LEFT JOIN k_sol_dexes dex
|
||||
ON dex.id = pool.dex_id
|
||||
LEFT JOIN k_sol_tokens base_token
|
||||
ON base_token.id = pair.base_token_id
|
||||
LEFT JOIN k_sol_tokens quote_token
|
||||
ON quote_token.id = pair.quote_token_id
|
||||
LEFT JOIN k_sol_dex_decoded_events dde
|
||||
ON dde.pool_account = pool.address
|
||||
LEFT JOIN k_sol_chain_transactions tx
|
||||
ON tx.id = dde.transaction_id
|
||||
LEFT JOIN k_sol_chain_instructions ix
|
||||
ON ix.transaction_id = tx.id
|
||||
LEFT JOIN k_sol_trade_events te
|
||||
ON te.pair_id = pair.id
|
||||
LEFT JOIN k_sol_pair_candles candle
|
||||
ON candle.pair_id = pair.id
|
||||
LEFT JOIN k_sol_protocol_candidates pc
|
||||
ON pc.transaction_id = tx.id
|
||||
WHERE
|
||||
(NULLIF(TRIM(?), '') IS NULL
|
||||
OR dex.code = ?
|
||||
OR dde.protocol_name = ?
|
||||
OR pc.candidate_protocol = ?
|
||||
OR pc.candidate_surface = ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL
|
||||
OR ix.program_id = ?
|
||||
OR dde.program_id = ?
|
||||
OR pc.program_id = ?)
|
||||
AND (? IS NULL OR pair.id = ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL
|
||||
OR pool.address = ?
|
||||
OR dde.pool_account = ?
|
||||
OR ix.accounts_json LIKE ?
|
||||
OR pc.evidence_json LIKE ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL
|
||||
OR base_token.mint = ?
|
||||
OR quote_token.mint = ?
|
||||
OR dde.token_a_mint = ?
|
||||
OR dde.token_b_mint = ?
|
||||
OR ix.accounts_json LIKE ?
|
||||
OR tx.transaction_json LIKE ?
|
||||
OR pc.evidence_json LIKE ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL OR tx.signature = ? OR te.signature = ? OR pc.signature = ?)
|
||||
GROUP BY pool.id, pair.id
|
||||
ORDER BY MAX(COALESCE(tx.slot, te.slot, pc.slot)) DESC, pool.id DESC
|
||||
LIMIT ?
|
||||
"#,
|
||||
)
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(request.pair_id)
|
||||
.bind(request.pair_id)
|
||||
.bind(pool_address.clone())
|
||||
.bind(pool_address.clone())
|
||||
.bind(pool_address.clone())
|
||||
.bind(pool_address_like.clone())
|
||||
.bind(pool_address_like.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint_like.clone())
|
||||
.bind(token_mint_like.clone())
|
||||
.bind(token_mint_like.clone())
|
||||
.bind(signature.clone())
|
||||
.bind(signature.clone())
|
||||
.bind(signature.clone())
|
||||
.bind(signature.clone())
|
||||
.bind(i64::from(limit))
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
let rows = match query_result {
|
||||
Ok(rows) => rows,
|
||||
Err(error) => {
|
||||
return Err(crate::Error::Db(format!(
|
||||
"cannot list local DEX corpus pool/pair samples on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for row in rows {
|
||||
dtos.push(crate::LocalDexCorpusPoolPairSampleDto::from(row));
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Lists matching decoded-event samples for a local DEX corpus search.
|
||||
pub async fn query_local_dex_corpus_search_list_decoded_event_samples(
|
||||
database: &crate::Database,
|
||||
request: &crate::LocalDexCorpusSearchRequestDto,
|
||||
) -> Result<std::vec::Vec<crate::LocalDexCorpusDecodedEventSampleDto>, crate::Error> {
|
||||
let limit = normalized_limit(request.limit);
|
||||
if limit == 0 {
|
||||
return Ok(std::vec::Vec::new());
|
||||
}
|
||||
match database.connection() {
|
||||
crate::DatabaseConnection::Sqlite(pool) => {
|
||||
let dex_code = normalized_filter_text(request.dex_code.as_deref());
|
||||
let program_id = normalized_filter_text(request.program_id.as_deref());
|
||||
let pool_address = normalized_filter_text(request.pool_address.as_deref());
|
||||
let token_mint = normalized_filter_text(request.token_mint.as_deref());
|
||||
let signature = normalized_filter_text(request.signature.as_deref());
|
||||
let pool_address_like = like_filter_text(pool_address.as_str());
|
||||
let token_mint_like = like_filter_text(token_mint.as_str());
|
||||
let query_result = sqlx::query_as::<sqlx::Sqlite, crate::db::dtos::LocalDexCorpusDecodedEventSampleRow>(
|
||||
r#"
|
||||
SELECT
|
||||
dde.id AS decoded_event_id,
|
||||
tx.id AS transaction_id,
|
||||
tx.signature AS signature,
|
||||
tx.slot AS slot,
|
||||
dde.protocol_name AS protocol_name,
|
||||
dde.program_id AS program_id,
|
||||
dde.event_kind AS event_kind,
|
||||
dde.pool_account AS pool_account,
|
||||
dde.token_a_mint AS token_a_mint,
|
||||
dde.token_b_mint AS token_b_mint,
|
||||
json_extract(dde.payload_json, '$.eventCategory') AS event_category,
|
||||
json_extract(dde.payload_json, '$.eventLifecycleKind') AS event_lifecycle_kind,
|
||||
json_extract(dde.payload_json, '$.eventActionability') AS event_actionability,
|
||||
json_extract(dde.payload_json, '$.tradeCandidate') AS trade_candidate,
|
||||
json_extract(dde.payload_json, '$.candleCandidate') AS candle_candidate
|
||||
FROM k_sol_dex_decoded_events dde
|
||||
JOIN k_sol_chain_transactions tx
|
||||
ON tx.id = dde.transaction_id
|
||||
LEFT JOIN k_sol_chain_instructions ix
|
||||
ON ix.transaction_id = tx.id
|
||||
LEFT JOIN k_sol_pools pool
|
||||
ON pool.address = dde.pool_account
|
||||
LEFT JOIN k_sol_pairs pair
|
||||
ON pair.pool_id = pool.id
|
||||
LEFT JOIN k_sol_dexes dex
|
||||
ON dex.id = pair.dex_id
|
||||
LEFT JOIN k_sol_tokens base_token
|
||||
ON base_token.id = pair.base_token_id
|
||||
LEFT JOIN k_sol_tokens quote_token
|
||||
ON quote_token.id = pair.quote_token_id
|
||||
LEFT JOIN k_sol_protocol_candidates pc
|
||||
ON pc.transaction_id = tx.id
|
||||
WHERE
|
||||
(NULLIF(TRIM(?), '') IS NULL
|
||||
OR dex.code = ?
|
||||
OR dde.protocol_name = ?
|
||||
OR pc.candidate_protocol = ?
|
||||
OR pc.candidate_surface = ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL
|
||||
OR ix.program_id = ?
|
||||
OR dde.program_id = ?
|
||||
OR pc.program_id = ?)
|
||||
AND (? IS NULL OR pair.id = ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL
|
||||
OR pool.address = ?
|
||||
OR dde.pool_account = ?
|
||||
OR ix.accounts_json LIKE ?
|
||||
OR pc.evidence_json LIKE ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL
|
||||
OR base_token.mint = ?
|
||||
OR quote_token.mint = ?
|
||||
OR dde.token_a_mint = ?
|
||||
OR dde.token_b_mint = ?
|
||||
OR ix.accounts_json LIKE ?
|
||||
OR tx.transaction_json LIKE ?
|
||||
OR pc.evidence_json LIKE ?)
|
||||
AND (NULLIF(TRIM(?), '') IS NULL OR tx.signature = ? OR pc.signature = ?)
|
||||
GROUP BY dde.id
|
||||
ORDER BY tx.slot DESC, dde.id DESC
|
||||
LIMIT ?
|
||||
"#,
|
||||
)
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(dex_code.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(program_id.clone())
|
||||
.bind(request.pair_id)
|
||||
.bind(request.pair_id)
|
||||
.bind(pool_address.clone())
|
||||
.bind(pool_address.clone())
|
||||
.bind(pool_address.clone())
|
||||
.bind(pool_address_like.clone())
|
||||
.bind(pool_address_like.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint.clone())
|
||||
.bind(token_mint_like.clone())
|
||||
.bind(token_mint_like.clone())
|
||||
.bind(token_mint_like.clone())
|
||||
.bind(signature.clone())
|
||||
.bind(signature.clone())
|
||||
.bind(signature.clone())
|
||||
.bind(i64::from(limit))
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
let rows = match query_result {
|
||||
Ok(rows) => rows,
|
||||
Err(error) => {
|
||||
return Err(crate::Error::Db(format!(
|
||||
"cannot list local DEX corpus decoded-event samples on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
};
|
||||
let mut dtos = std::vec::Vec::new();
|
||||
for row in rows {
|
||||
dtos.push(crate::LocalDexCorpusDecodedEventSampleDto::from(row));
|
||||
}
|
||||
return Ok(dtos);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn normalized_limit(limit: u32) -> u32 {
|
||||
if limit > 200 {
|
||||
return 200;
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
|
||||
fn normalized_filter_text(value: std::option::Option<&str>) -> std::string::String {
|
||||
let value = match value {
|
||||
Some(value) => value.trim(),
|
||||
None => return std::string::String::new(),
|
||||
};
|
||||
return value.to_string();
|
||||
}
|
||||
|
||||
fn like_filter_text(value: &str) -> std::string::String {
|
||||
if value.is_empty() {
|
||||
return std::string::String::new();
|
||||
}
|
||||
return format!("%{}%", value);
|
||||
}
|
||||
@@ -584,6 +584,80 @@ ORDER BY dex_code
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Lists observed Raydium program instruction diagnostics.
|
||||
pub async fn query_local_raydium_program_instruction_diagnostic_list_summaries(
|
||||
database: &crate::Database,
|
||||
) -> Result<
|
||||
std::vec::Vec<crate::LocalRaydiumProgramInstructionDiagnosticSummaryDto>,
|
||||
crate::Error,
|
||||
> {
|
||||
match database.connection() {
|
||||
crate::DatabaseConnection::Sqlite(pool) => {
|
||||
let rows_result = sqlx::query_as::<
|
||||
sqlx::Sqlite,
|
||||
crate::db::dtos::LocalRaydiumProgramInstructionDiagnosticSummaryRow,
|
||||
>(
|
||||
r#"
|
||||
SELECT
|
||||
ix.program_id AS program_id,
|
||||
COUNT(ix.id) AS instruction_count,
|
||||
COUNT(DISTINCT tx.signature) AS transaction_count,
|
||||
MAX(tx.slot) AS latest_slot,
|
||||
(
|
||||
SELECT tx_latest.signature
|
||||
FROM k_sol_chain_instructions ix_latest
|
||||
JOIN k_sol_chain_transactions tx_latest
|
||||
ON tx_latest.id = ix_latest.transaction_id
|
||||
WHERE ix_latest.program_id = ix.program_id
|
||||
ORDER BY
|
||||
tx_latest.slot DESC,
|
||||
tx_latest.id DESC,
|
||||
ix_latest.instruction_index DESC,
|
||||
COALESCE(ix_latest.inner_instruction_index, -1) DESC
|
||||
LIMIT 1
|
||||
) AS latest_signature
|
||||
FROM k_sol_chain_instructions ix
|
||||
JOIN k_sol_chain_transactions tx
|
||||
ON tx.id = ix.transaction_id
|
||||
WHERE ix.program_id IN (
|
||||
'CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C',
|
||||
'CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK',
|
||||
'675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8',
|
||||
'5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h',
|
||||
'routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS',
|
||||
'LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj'
|
||||
)
|
||||
GROUP BY ix.program_id
|
||||
ORDER BY transaction_count DESC, instruction_count DESC, program_id
|
||||
"#,
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await;
|
||||
let rows = match rows_result {
|
||||
Ok(rows) => rows,
|
||||
Err(error) => {
|
||||
return Err(crate::Error::Db(format!(
|
||||
"cannot list local Raydium program instruction diagnostics on sqlite: {}",
|
||||
error
|
||||
)));
|
||||
},
|
||||
};
|
||||
let mut summaries = std::vec::Vec::new();
|
||||
for row in rows {
|
||||
summaries.push(crate::LocalRaydiumProgramInstructionDiagnosticSummaryDto {
|
||||
program_id: row.program_id,
|
||||
instruction_count: row.instruction_count,
|
||||
transaction_count: row.transaction_count,
|
||||
latest_slot: row.latest_slot,
|
||||
latest_signature: row.latest_signature,
|
||||
});
|
||||
}
|
||||
return Ok(summaries);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Lists local pair diagnostic summaries.
|
||||
pub async fn query_local_pair_diagnostic_list_summaries(
|
||||
database: &crate::Database,
|
||||
|
||||
@@ -53,6 +53,8 @@ mod http_pool;
|
||||
mod json_rpc_ws;
|
||||
/// Launch surface attribution service.
|
||||
mod launch_origin;
|
||||
/// Local DEX corpus search service used by Demo3.
|
||||
mod local_dex_corpus_search;
|
||||
/// Local pipeline diagnostics service.
|
||||
mod local_pipeline_diagnostics;
|
||||
/// Local pipeline replay from already persisted raw transaction data.
|
||||
@@ -61,6 +63,8 @@ mod local_pipeline_replay;
|
||||
mod local_pipeline_validation;
|
||||
/// Useful non-trade DEX event materialization service.
|
||||
mod non_trade_event_materialization;
|
||||
/// On-chain DEX pair/pool discovery helpers used by Demo3.
|
||||
mod onchain_dex_pair_discovery;
|
||||
/// Pair analytic signal service.
|
||||
mod pair_analytic_signal;
|
||||
/// Pair-candle aggregation service.
|
||||
@@ -359,6 +363,18 @@ pub use db::LiquidityEventEntity;
|
||||
pub use db::LiquidityEventKind;
|
||||
/// Local decoded-event diagnostics summary.
|
||||
pub use db::LocalDecodedEventDiagnosticSummaryDto;
|
||||
/// Local DEX corpus decoded-event sample for Demo3.
|
||||
pub use db::LocalDexCorpusDecodedEventSampleDto;
|
||||
/// Local DEX corpus pool/pair sample for Demo3.
|
||||
pub use db::LocalDexCorpusPoolPairSampleDto;
|
||||
/// Local DEX corpus search request for Demo3.
|
||||
pub use db::LocalDexCorpusSearchRequestDto;
|
||||
/// Local DEX corpus search result for Demo3.
|
||||
pub use db::LocalDexCorpusSearchResultDto;
|
||||
/// Local DEX corpus search summary for Demo3.
|
||||
pub use db::LocalDexCorpusSearchSummaryDto;
|
||||
/// Local DEX corpus transaction sample for Demo3.
|
||||
pub use db::LocalDexCorpusTransactionSampleDto;
|
||||
/// Local DEX diagnostics summary.
|
||||
pub use db::LocalDexDiagnosticSummaryDto;
|
||||
/// Sample of duplicated trade rows grouped by decoded event id.
|
||||
@@ -389,6 +405,10 @@ pub use db::LocalPipelineDiagnosticCountersDto;
|
||||
pub use db::LocalPipelineDiagnosticSummaryDto;
|
||||
/// Sample of a pool-origin row and optional launch linkage.
|
||||
pub use db::LocalPoolOriginDiagnosticSampleDto;
|
||||
/// Projected instruction diagnostics grouped by Raydium program id.
|
||||
pub use db::LocalRaydiumProgramInstructionDiagnosticSummaryDto;
|
||||
/// Raydium DEX-first surface diagnostics.
|
||||
pub use db::LocalRaydiumSurfaceDiagnosticSummaryDto;
|
||||
/// Prioritized sample of an incomplete token metadata row.
|
||||
pub use db::LocalTokenMetadataGapDiagnosticSampleDto;
|
||||
/// Source family for one on-chain observation.
|
||||
@@ -603,6 +623,14 @@ pub use db::query_liquidity_events_list_recent;
|
||||
pub use db::query_liquidity_events_upsert;
|
||||
/// Lists local decoded-event diagnostic summaries.
|
||||
pub use db::query_local_decoded_event_diagnostic_list_summaries;
|
||||
/// Returns aggregate counts for a local DEX corpus search.
|
||||
pub use db::query_local_dex_corpus_search_get_summary;
|
||||
/// Lists matching decoded-event samples for a local DEX corpus search.
|
||||
pub use db::query_local_dex_corpus_search_list_decoded_event_samples;
|
||||
/// Lists matching pool/pair samples for a local DEX corpus search.
|
||||
pub use db::query_local_dex_corpus_search_list_pool_pair_samples;
|
||||
/// Lists matching transaction samples for a local DEX corpus search.
|
||||
pub use db::query_local_dex_corpus_search_list_transaction_samples;
|
||||
/// Lists samples of duplicated trade rows by decoded event id.
|
||||
pub use db::query_local_duplicate_decoded_event_trade_diagnostic_list_samples;
|
||||
/// Lists local decoded-event classification diagnostic summaries.
|
||||
@@ -633,6 +661,8 @@ pub use db::query_local_pipeline_diagnostic_get_counters;
|
||||
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 observed Raydium program instruction diagnostics.
|
||||
pub use db::query_local_raydium_program_instruction_diagnostic_list_summaries;
|
||||
/// Lists prioritized token metadata gap diagnostic samples.
|
||||
pub use db::query_local_token_metadata_gap_diagnostic_list_samples;
|
||||
/// Reads one observed token by mint.
|
||||
@@ -1049,6 +1079,8 @@ pub use json_rpc_ws::parse_json_rpc_ws_incoming_value;
|
||||
pub use launch_origin::LaunchAttributionResult;
|
||||
/// Launch surface attribution service.
|
||||
pub use launch_origin::LaunchOriginService;
|
||||
/// Local DEX corpus search service used by Demo3.
|
||||
pub use local_dex_corpus_search::LocalDexCorpusSearchService;
|
||||
/// Local pipeline diagnostics service.
|
||||
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticsService;
|
||||
/// Configuration for a local pipeline replay pass.
|
||||
@@ -1071,6 +1103,18 @@ pub use local_pipeline_validation::LocalPipelineValidationRunDto;
|
||||
pub use local_pipeline_validation::LocalPipelineValidationService;
|
||||
/// Validates a diagnostics summary without performing database access.
|
||||
pub use local_pipeline_validation::validate_local_pipeline_diagnostics_summary;
|
||||
/// Candidate account inferred from generic transaction evidence.
|
||||
pub use onchain_dex_pair_discovery::OnchainDexCandidateAccountDto;
|
||||
/// Candidate transaction/instruction observed on-chain for one DEX program id.
|
||||
pub use onchain_dex_pair_discovery::OnchainDexPairCandidateDto;
|
||||
/// Request for on-chain DEX pair/pool discovery.
|
||||
pub use onchain_dex_pair_discovery::OnchainDexPairDiscoveryRequestDto;
|
||||
/// Result of one on-chain DEX pair/pool discovery run.
|
||||
pub use onchain_dex_pair_discovery::OnchainDexPairDiscoveryResultDto;
|
||||
/// On-chain pair/pool discovery service.
|
||||
pub use onchain_dex_pair_discovery::OnchainDexPairDiscoveryService;
|
||||
/// Token-balance delta observed in one transaction through Solana transaction metadata.
|
||||
pub use onchain_dex_pair_discovery::OnchainDexTokenBalanceDeltaDto;
|
||||
/// One pair-analytic-signal recording result.
|
||||
pub use pair_analytic_signal::PairAnalyticSignalResult;
|
||||
/// Pair analytic signal service.
|
||||
@@ -1112,6 +1156,8 @@ pub use solana_pubsub_ws::parse_solana_ws_typed_notification;
|
||||
pub use solana_pubsub_ws::parse_solana_ws_typed_notification_from_event;
|
||||
/// One pool-backfill result summary.
|
||||
pub use token_backfill::PoolBackfillResult;
|
||||
/// One signature-backfill result summary.
|
||||
pub use token_backfill::SignatureBackfillResult;
|
||||
/// One token-backfill result summary.
|
||||
pub use token_backfill::TokenBackfillResult;
|
||||
/// Historical token backfill service.
|
||||
|
||||
103
kb_lib/src/local_dex_corpus_search.rs
Normal file
103
kb_lib/src/local_dex_corpus_search.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
// file: kb_lib/src/local_dex_corpus_search.rs
|
||||
|
||||
//! Local DEX corpus search service used by Demo3.
|
||||
|
||||
/// Local DEX corpus search service.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LocalDexCorpusSearchService {
|
||||
database: std::sync::Arc<crate::Database>,
|
||||
}
|
||||
|
||||
impl LocalDexCorpusSearchService {
|
||||
/// Creates a new local DEX corpus search service.
|
||||
pub fn new(database: std::sync::Arc<crate::Database>) -> Self {
|
||||
return Self { database };
|
||||
}
|
||||
|
||||
/// Searches the local persisted pipeline for DEX corpus candidates.
|
||||
pub async fn search(
|
||||
&self,
|
||||
request: crate::LocalDexCorpusSearchRequestDto,
|
||||
) -> Result<crate::LocalDexCorpusSearchResultDto, crate::Error> {
|
||||
let normalized_request = normalize_request(request);
|
||||
let summary_result = crate::query_local_dex_corpus_search_get_summary(
|
||||
self.database.as_ref(),
|
||||
&normalized_request,
|
||||
)
|
||||
.await;
|
||||
let summary = match summary_result {
|
||||
Ok(summary) => summary,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let transaction_samples_result =
|
||||
crate::query_local_dex_corpus_search_list_transaction_samples(
|
||||
self.database.as_ref(),
|
||||
&normalized_request,
|
||||
)
|
||||
.await;
|
||||
let transaction_samples = match transaction_samples_result {
|
||||
Ok(transaction_samples) => transaction_samples,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let pool_pair_samples_result = crate::query_local_dex_corpus_search_list_pool_pair_samples(
|
||||
self.database.as_ref(),
|
||||
&normalized_request,
|
||||
)
|
||||
.await;
|
||||
let pool_pair_samples = match pool_pair_samples_result {
|
||||
Ok(pool_pair_samples) => pool_pair_samples,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let decoded_event_samples_result =
|
||||
crate::query_local_dex_corpus_search_list_decoded_event_samples(
|
||||
self.database.as_ref(),
|
||||
&normalized_request,
|
||||
)
|
||||
.await;
|
||||
let decoded_event_samples = match decoded_event_samples_result {
|
||||
Ok(decoded_event_samples) => decoded_event_samples,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
return Ok(crate::LocalDexCorpusSearchResultDto {
|
||||
request: normalized_request,
|
||||
summary,
|
||||
transaction_samples,
|
||||
pool_pair_samples,
|
||||
decoded_event_samples,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_request(
|
||||
request: crate::LocalDexCorpusSearchRequestDto,
|
||||
) -> crate::LocalDexCorpusSearchRequestDto {
|
||||
let limit = if request.limit == 0 {
|
||||
50
|
||||
} else if request.limit > 200 {
|
||||
200
|
||||
} else {
|
||||
request.limit
|
||||
};
|
||||
return crate::LocalDexCorpusSearchRequestDto {
|
||||
dex_code: normalize_optional_text(request.dex_code),
|
||||
program_id: normalize_optional_text(request.program_id),
|
||||
pair_id: request.pair_id,
|
||||
pool_address: normalize_optional_text(request.pool_address),
|
||||
token_mint: normalize_optional_text(request.token_mint),
|
||||
signature: normalize_optional_text(request.signature),
|
||||
limit,
|
||||
};
|
||||
}
|
||||
|
||||
fn normalize_optional_text(
|
||||
value: std::option::Option<std::string::String>,
|
||||
) -> std::option::Option<std::string::String> {
|
||||
let value = match value {
|
||||
Some(value) => value.trim().to_string(),
|
||||
None => return None,
|
||||
};
|
||||
if value.is_empty() {
|
||||
return None;
|
||||
}
|
||||
return Some(value);
|
||||
}
|
||||
@@ -29,6 +29,20 @@ impl LocalPipelineDiagnosticsService {
|
||||
Ok(dex_summaries) => dex_summaries,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let raydium_program_instruction_summaries_result =
|
||||
crate::query_local_raydium_program_instruction_diagnostic_list_summaries(
|
||||
self.database.as_ref(),
|
||||
)
|
||||
.await;
|
||||
let raydium_program_instruction_summaries =
|
||||
match raydium_program_instruction_summaries_result {
|
||||
Ok(summaries) => summaries,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let raydium_surface_summaries = build_raydium_surface_summaries(
|
||||
&dex_summaries,
|
||||
&raydium_program_instruction_summaries,
|
||||
);
|
||||
let pair_summaries_result =
|
||||
crate::query_local_pair_diagnostic_list_summaries(self.database.as_ref()).await;
|
||||
let pair_summaries = match pair_summaries_result {
|
||||
@@ -229,6 +243,7 @@ impl LocalPipelineDiagnosticsService {
|
||||
pair_without_trade_count: counters.pair_without_trade_count,
|
||||
pair_without_candle_count: counters.pair_without_candle_count,
|
||||
dex_summaries,
|
||||
raydium_surface_summaries,
|
||||
pair_summaries,
|
||||
pair_actionability_summaries,
|
||||
pair_trading_readiness_summaries,
|
||||
@@ -248,3 +263,99 @@ impl LocalPipelineDiagnosticsService {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn build_raydium_surface_summaries(
|
||||
dex_summaries: &[crate::LocalDexDiagnosticSummaryDto],
|
||||
program_instruction_summaries: &[crate::LocalRaydiumProgramInstructionDiagnosticSummaryDto],
|
||||
) -> std::vec::Vec<crate::LocalRaydiumSurfaceDiagnosticSummaryDto> {
|
||||
let mut summaries = std::vec::Vec::new();
|
||||
for entry in crate::dex_support_matrix_entries() {
|
||||
if entry.family != "raydium" {
|
||||
continue;
|
||||
}
|
||||
let dex_summary = find_dex_summary(dex_summaries, entry.code);
|
||||
let program_summary = match entry.program_id {
|
||||
Some(program_id) => {
|
||||
find_raydium_program_summary(program_instruction_summaries, program_id)
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
let instruction_count = match program_summary {
|
||||
Some(program_summary) => program_summary.instruction_count,
|
||||
None => 0,
|
||||
};
|
||||
let transaction_count = match program_summary {
|
||||
Some(program_summary) => program_summary.transaction_count,
|
||||
None => 0,
|
||||
};
|
||||
let latest_slot = match program_summary {
|
||||
Some(program_summary) => program_summary.latest_slot,
|
||||
None => None,
|
||||
};
|
||||
let latest_signature = match program_summary {
|
||||
Some(program_summary) => program_summary.latest_signature.clone(),
|
||||
None => None,
|
||||
};
|
||||
let decoded_event_count = match dex_summary {
|
||||
Some(dex_summary) => dex_summary.decoded_event_count,
|
||||
None => 0,
|
||||
};
|
||||
let trade_event_count = match dex_summary {
|
||||
Some(dex_summary) => dex_summary.trade_event_count,
|
||||
None => 0,
|
||||
};
|
||||
let pair_candle_count = match dex_summary {
|
||||
Some(dex_summary) => dex_summary.pair_candle_count,
|
||||
None => 0,
|
||||
};
|
||||
summaries.push(crate::LocalRaydiumSurfaceDiagnosticSummaryDto {
|
||||
dex_code: entry.code.to_string(),
|
||||
display_name: entry.display_name.to_string(),
|
||||
surface_role: entry.surface_role.to_string(),
|
||||
program_id: match entry.program_id {
|
||||
Some(program_id) => Some(program_id.to_string()),
|
||||
None => None,
|
||||
},
|
||||
program_id_status: entry.program_id_status.to_string(),
|
||||
status: entry.status.to_string(),
|
||||
catalog_enabled: entry.catalog_enabled,
|
||||
instruction_count,
|
||||
transaction_count,
|
||||
decoded_event_count,
|
||||
trade_event_count,
|
||||
pair_candle_count,
|
||||
latest_slot,
|
||||
latest_signature,
|
||||
observed_in_current_corpus: instruction_count > 0,
|
||||
decoded_in_current_corpus: decoded_event_count > 0,
|
||||
trade_materialized_in_current_corpus: trade_event_count > 0,
|
||||
});
|
||||
}
|
||||
summaries.sort_by(|left, right| return left.dex_code.cmp(&right.dex_code));
|
||||
return summaries;
|
||||
}
|
||||
|
||||
fn find_dex_summary<'a>(
|
||||
dex_summaries: &'a [crate::LocalDexDiagnosticSummaryDto],
|
||||
dex_code: &str,
|
||||
) -> std::option::Option<&'a crate::LocalDexDiagnosticSummaryDto> {
|
||||
for summary in dex_summaries {
|
||||
if summary.dex_code == dex_code {
|
||||
return Some(summary);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
fn find_raydium_program_summary<'a>(
|
||||
program_instruction_summaries: &'a [crate::LocalRaydiumProgramInstructionDiagnosticSummaryDto],
|
||||
program_id: &str,
|
||||
) -> std::option::Option<&'a crate::LocalRaydiumProgramInstructionDiagnosticSummaryDto> {
|
||||
for summary in program_instruction_summaries {
|
||||
if summary.program_id.as_str() == program_id {
|
||||
return Some(summary);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
@@ -290,6 +290,26 @@ impl LocalPipelineValidationConfig {
|
||||
return config;
|
||||
}
|
||||
|
||||
/// Builds the `0.7.40` Raydium effective surfaces validation config.
|
||||
///
|
||||
/// This profile keeps the DEX-first matrix invariants and focuses expected
|
||||
/// corpus warnings on Raydium effective swap surfaces. Missing Raydium
|
||||
/// variants are intentionally warnings so AMM v4 or Stable Swap are not
|
||||
/// promoted without transaction evidence.
|
||||
pub fn v0_7_40_raydium_effective_surfaces() -> Self {
|
||||
let mut config = Self::v0_7_39_dex_first_effective_swap_surfaces();
|
||||
config.profile_code = "0.7.40_raydium_effective_surfaces".to_string();
|
||||
config.expected_dex_codes = vec![
|
||||
"raydium_cpmm".to_string(),
|
||||
"raydium_clmm".to_string(),
|
||||
"raydium_amm_v4".to_string(),
|
||||
"raydium_stable_swap".to_string(),
|
||||
];
|
||||
config.require_all_expected_dexes = false;
|
||||
config.allow_unexpected_dexes = true;
|
||||
return config;
|
||||
}
|
||||
|
||||
/// Builds the legacy `0.7.39` launch-surface validation alias.
|
||||
///
|
||||
/// The implementation now delegates to the DEX-first profile so callers that
|
||||
@@ -534,6 +554,14 @@ impl LocalPipelineValidationService {
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_39_dex_first_effective_swap_surfaces();
|
||||
return self.validate_current_database(&config).await;
|
||||
}
|
||||
|
||||
/// Diagnoses the current database with the `0.7.40` Raydium profile.
|
||||
pub async fn validate_v0_7_40_current_database(
|
||||
&self,
|
||||
) -> Result<crate::LocalPipelineValidationRunDto, crate::Error> {
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_40_raydium_effective_surfaces();
|
||||
return self.validate_current_database(&config).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates a diagnostics summary without performing database access.
|
||||
@@ -658,7 +686,8 @@ pub fn validate_local_pipeline_diagnostics_summary(
|
||||
|| 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.39_dex_first_effective_swap_surfaces"
|
||||
|| config.profile_code == "0.7.39_launch_surface_origin_baseline";
|
||||
|| config.profile_code == "0.7.39_launch_surface_origin_baseline"
|
||||
|| config.profile_code == "0.7.40_raydium_effective_surfaces";
|
||||
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) {
|
||||
@@ -1083,6 +1112,27 @@ mod tests {
|
||||
pair_candle_count: 131,
|
||||
},
|
||||
],
|
||||
raydium_surface_summaries: vec![
|
||||
crate::LocalRaydiumSurfaceDiagnosticSummaryDto {
|
||||
dex_code: "raydium_clmm".to_string(),
|
||||
display_name: "Raydium CLMM".to_string(),
|
||||
surface_role: "dex_effective".to_string(),
|
||||
program_id: Some(crate::RAYDIUM_CLMM_PROGRAM_ID.to_string()),
|
||||
program_id_status: "known".to_string(),
|
||||
status: "supported".to_string(),
|
||||
catalog_enabled: true,
|
||||
instruction_count: 106,
|
||||
transaction_count: 101,
|
||||
decoded_event_count: 106,
|
||||
trade_event_count: 101,
|
||||
pair_candle_count: 131,
|
||||
latest_slot: Some(1),
|
||||
latest_signature: Some("raydium_clmm_fixture".to_string()),
|
||||
observed_in_current_corpus: true,
|
||||
decoded_in_current_corpus: true,
|
||||
trade_materialized_in_current_corpus: true,
|
||||
},
|
||||
],
|
||||
pair_summaries: vec![],
|
||||
pair_actionability_summaries: vec![
|
||||
crate::LocalPairActionabilityDiagnosticSummaryDto {
|
||||
@@ -1447,6 +1497,18 @@ mod tests {
|
||||
assert_eq!(report.blocking_issue_count, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validation_accepts_0_7_40_raydium_effective_surfaces_with_missing_variants_as_warnings() {
|
||||
let summary = make_0_7_28_summary_with_meteora();
|
||||
let config = crate::LocalPipelineValidationConfig::v0_7_40_raydium_effective_surfaces();
|
||||
let report = crate::validate_local_pipeline_diagnostics_summary(&summary, &config);
|
||||
assert!(report.validation_passed);
|
||||
assert_eq!(report.validation_profile_code, "0.7.40_raydium_effective_surfaces");
|
||||
assert_eq!(report.blocking_issue_count, 0);
|
||||
assert!(report.warning_count >= 1);
|
||||
assert!(report.expected_dex_codes.contains(&"raydium_amm_v4".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validation_rejects_0_7_33_pair_trading_readiness_mismatch() {
|
||||
let mut summary = make_0_7_28_summary_with_meteora();
|
||||
|
||||
1550
kb_lib/src/onchain_dex_pair_discovery.rs
Normal file
1550
kb_lib/src/onchain_dex_pair_discovery.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -84,6 +84,41 @@ pub struct PoolBackfillResult {
|
||||
pub pair_candle_count: usize,
|
||||
}
|
||||
|
||||
/// One signature-backfill result summary.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct SignatureBackfillResult {
|
||||
/// Input transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Number of transactions resolved through HTTP during this run.
|
||||
pub resolved_transaction_count: usize,
|
||||
/// Number of signatures whose `getTransaction` lookup returned `null`.
|
||||
pub missing_transaction_count: usize,
|
||||
/// Total number of decoded DEX events replayed during this run.
|
||||
pub decoded_event_count: usize,
|
||||
/// Total number of DEX detection results produced during this run.
|
||||
pub detection_count: usize,
|
||||
/// Total number of launch-attribution results produced during this run.
|
||||
pub launch_attribution_count: usize,
|
||||
/// Total number of pool-origin results produced during this run.
|
||||
pub pool_origin_count: usize,
|
||||
/// Total number of wallet-participation observations produced during this run.
|
||||
pub wallet_participation_count: usize,
|
||||
/// Total number of trade-aggregation results produced during this run.
|
||||
pub trade_event_count: usize,
|
||||
/// Total number of liquidity event materialization results produced during this run.
|
||||
pub liquidity_event_count: usize,
|
||||
/// Total number of pool lifecycle event materialization results produced during this run.
|
||||
pub pool_lifecycle_event_count: usize,
|
||||
/// Total number of fee event materialization results produced during this run.
|
||||
pub fee_event_count: usize,
|
||||
/// Total number of reward event materialization results produced during this run.
|
||||
pub reward_event_count: usize,
|
||||
/// Total number of pool administration event materialization results produced during this run.
|
||||
pub pool_admin_event_count: usize,
|
||||
/// Total number of pair-candle aggregation results produced during this run.
|
||||
pub pair_candle_count: usize,
|
||||
}
|
||||
|
||||
/// Historical token backfill service.
|
||||
///
|
||||
/// This service reuses the existing transaction projection and downstream
|
||||
@@ -681,6 +716,87 @@ impl TokenBackfillService {
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// Replays one known transaction signature through the existing pipeline.
|
||||
pub async fn backfill_signature(
|
||||
&self,
|
||||
signature: &str,
|
||||
) -> Result<crate::SignatureBackfillResult, crate::Error> {
|
||||
let trimmed_signature = signature.trim().to_string();
|
||||
if trimmed_signature.is_empty() {
|
||||
return Err(crate::Error::Config("signature must not be empty".to_string()));
|
||||
}
|
||||
let replay_result = self.replay_signature(trimmed_signature.clone()).await;
|
||||
let replay = match replay_result {
|
||||
Ok(replay) => replay,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
self.backfill_missing_token_metadata_best_effort(100).await;
|
||||
let result = crate::SignatureBackfillResult {
|
||||
signature: trimmed_signature.clone(),
|
||||
resolved_transaction_count: replay.resolved_transaction_count,
|
||||
missing_transaction_count: replay.missing_transaction_count,
|
||||
decoded_event_count: replay.decoded_event_count,
|
||||
detection_count: replay.detection_count,
|
||||
launch_attribution_count: replay.launch_attribution_count,
|
||||
pool_origin_count: replay.pool_origin_count,
|
||||
wallet_participation_count: replay.wallet_participation_count,
|
||||
trade_event_count: replay.trade_event_count,
|
||||
liquidity_event_count: replay.liquidity_event_count,
|
||||
pool_lifecycle_event_count: replay.pool_lifecycle_event_count,
|
||||
fee_event_count: replay.fee_event_count,
|
||||
reward_event_count: replay.reward_event_count,
|
||||
pool_admin_event_count: replay.pool_admin_event_count,
|
||||
pair_candle_count: replay.pair_candle_count,
|
||||
};
|
||||
let summary_payload = serde_json::json!({
|
||||
"signature": result.signature.clone(),
|
||||
"resolvedTransactionCount": result.resolved_transaction_count,
|
||||
"missingTransactionCount": result.missing_transaction_count,
|
||||
"decodedEventCount": result.decoded_event_count,
|
||||
"detectionCount": result.detection_count,
|
||||
"launchAttributionCount": result.launch_attribution_count,
|
||||
"poolOriginCount": result.pool_origin_count,
|
||||
"walletParticipationCount": result.wallet_participation_count,
|
||||
"tradeEventCount": result.trade_event_count,
|
||||
"liquidityEventCount": result.liquidity_event_count,
|
||||
"poolLifecycleEventCount": result.pool_lifecycle_event_count,
|
||||
"feeEventCount": result.fee_event_count,
|
||||
"rewardEventCount": result.reward_event_count,
|
||||
"poolAdminEventCount": result.pool_admin_event_count,
|
||||
"pairCandleCount": result.pair_candle_count
|
||||
});
|
||||
let observation_result = self
|
||||
.persistence
|
||||
.record_observation(&crate::DetectionObservationInput::new(
|
||||
"signature.backfill.completed".to_string(),
|
||||
crate::ObservationSourceKind::HttpRpc,
|
||||
Some(format!("backfill:{}", self.http_role)),
|
||||
trimmed_signature.clone(),
|
||||
None,
|
||||
summary_payload.clone(),
|
||||
))
|
||||
.await;
|
||||
let observation_id = match observation_result {
|
||||
Ok(observation_id) => observation_id,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let signal_result = self
|
||||
.persistence
|
||||
.record_signal(&crate::DetectionSignalInput::new(
|
||||
"signal.signature.backfill.completed".to_string(),
|
||||
crate::AnalysisSignalSeverity::Low,
|
||||
trimmed_signature,
|
||||
Some(observation_id),
|
||||
None,
|
||||
summary_payload,
|
||||
))
|
||||
.await;
|
||||
if let Err(error) = signal_result {
|
||||
return Err(error);
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
async fn backfill_missing_token_metadata_best_effort(&self, limit: i64) {
|
||||
let metadata_result =
|
||||
self.token_metadata_service.backfill_missing_token_metadata(Some(limit)).await;
|
||||
|
||||
Reference in New Issue
Block a user