Files
khadhroony-bobobot/kb_demo_app/src/demo_pipeline2.rs
2026-05-13 09:39:50 +02:00

1757 lines
69 KiB
Rust

// file: kb_demo_app/src/demo_pipeline2.rs
//! Tauri commands for the focused pipeline demo window.
//!
//! This demo is intentionally narrower than `Demo Pipeline`:
//! - read the local catalog of tokens / pools / pairs,
//! - trigger targeted backfills from the chain,
//! - load candles for one selected pair and timeframe.
use tauri::Manager;
use ts_rs::TS;
/// Request payload for program instruction discriminator summaries.
#[derive(Clone, Debug, serde::Deserialize, ts_rs::TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2ProgramInstructionDiscriminatorSummaryRequest.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2ProgramInstructionDiscriminatorSummaryRequest {
/// Program id to inspect.
pub program_id: std::string::String,
/// Maximum number of instruction rows to inspect before grouping.
pub limit: u32,
}
/// Response payload for program instruction discriminator summaries.
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2ProgramInstructionDiscriminatorSummaryPayload.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2ProgramInstructionDiscriminatorSummaryPayload {
/// Pretty JSON summary rows.
pub summaries_json: std::string::String,
}
/// Request payload for protocol candidate summary diagnostics.
#[derive(Clone, Debug, serde::Deserialize, ts_rs::TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2ProtocolCandidateSummaryRequest.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2ProtocolCandidateSummaryRequest {
/// Maximum number of summary rows to return.
pub limit: u32,
}
/// Response payload for protocol candidate summary diagnostics.
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2ProtocolCandidateSummaryPayload.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2ProtocolCandidateSummaryPayload {
/// Pretty JSON summary rows.
pub summaries_json: std::string::String,
}
/// Local diagnostics payload returned to the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalDiagnosticsPayload.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalDiagnosticsPayload {
/// Open database URL.
pub database_url: std::string::String,
/// Pretty JSON diagnostics summary.
pub summary_json: std::string::String,
/// Structured diagnostics summary.
pub summary: DemoPipeline2LocalPipelineDiagnosticSummary,
}
/// Request payload for local pipeline validation.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalValidationRequest.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalValidationRequest {
/// Stable validation profile code to execute.
pub profile_code: std::string::String,
}
/// Local validation payload returned to the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalValidationPayload.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalValidationPayload {
/// Open database URL.
pub database_url: std::string::String,
/// Pretty JSON diagnostics summary used by validation.
pub summary_json: std::string::String,
/// Pretty JSON validation run.
pub validation_json: std::string::String,
/// Structured validation run.
pub run: DemoPipeline2LocalPipelineValidationRun,
}
/// Local pipeline validation run for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalPipelineValidationRun.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalPipelineValidationRun {
/// Stable validation profile code used to produce the run.
pub validation_profile_code: std::string::String,
/// Whether the validation passed without blocking issues.
pub validation_passed: bool,
/// Number of blocking issues found by validation.
#[ts(type = "number")]
pub blocking_issue_count: i64,
/// Number of non-blocking warnings found by validation.
#[ts(type = "number")]
pub warning_count: i64,
/// Diagnostics summary that was validated.
pub summary: DemoPipeline2LocalPipelineDiagnosticSummary,
/// Detailed validation report produced from the diagnostics summary.
pub report: DemoPipeline2LocalPipelineValidationReport,
}
/// Local pipeline validation report for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalPipelineValidationReport.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalPipelineValidationReport {
/// Stable validation profile code used to produce the report.
pub validation_profile_code: std::string::String,
/// Whether the validation passed without blocking issues.
pub validation_passed: bool,
/// Number of blocking issues found by validation.
#[ts(type = "number")]
pub blocking_issue_count: i64,
/// Number of non-blocking warnings found by validation.
#[ts(type = "number")]
pub warning_count: i64,
/// Expected DEX codes used by the validation run.
pub expected_dex_codes: std::vec::Vec<std::string::String>,
/// Observed DEX codes found in diagnostics.
pub observed_dex_codes: std::vec::Vec<std::string::String>,
/// Total decoded useful non-trade events.
#[ts(type = "number")]
pub decoded_non_trade_useful_event_count: i64,
/// Total decoded swap-like events that are intentionally non-actionable.
#[ts(type = "number")]
pub decoded_non_actionable_trade_event_count: i64,
/// Total decoded events with unknown classification.
#[ts(type = "number")]
pub decoded_unknown_event_count: i64,
/// Number of entries currently exposed by the DEX support matrix.
#[ts(type = "number")]
pub dex_support_matrix_entry_count: i64,
/// DEX support matrix snapshot exposed with the validation report.
pub dex_support_matrix: std::vec::Vec<DemoPipeline2DexSupportMatrixEntry>,
/// Issues produced by validation.
pub issues: std::vec::Vec<DemoPipeline2LocalPipelineValidationIssue>,
}
/// One DEX support matrix entry for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2DexSupportMatrixEntry.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2DexSupportMatrixEntry {
/// Stable internal protocol or surface code.
pub code: std::string::String,
/// Human-readable protocol or surface name.
pub display_name: std::string::String,
/// Protocol family.
pub family: std::string::String,
/// Protocol version or `unknown` when not verified locally.
pub version: std::string::String,
/// Surface type: launch, bonding curve, AMM, CLMM, DLMM, router, aggregator or unknown.
pub surface_type: std::string::String,
/// Primary Solana program id, when verified in local constants or docs.
pub program_id: std::option::Option<std::string::String>,
/// Optional router program id, when this entry uses a distinct router.
pub router_program_id: std::option::Option<std::string::String>,
/// Program id confidence: known, to_verify or unknown.
pub program_id_status: std::string::String,
/// Whether this protocol has been observed in the local replay corpus.
pub observed: bool,
/// Whether the code currently contains a decoder for this protocol.
pub decoded: bool,
/// Whether decoded events are currently materialized beyond raw decoded rows.
pub materialized: bool,
/// Whether this protocol can currently produce trade candidates.
pub trade_candidate: bool,
/// Whether this protocol can currently produce candle candidates.
pub candle_candidate: bool,
/// Whether this protocol can currently produce pair candidates.
pub pair_candidate: bool,
/// Whether this protocol can currently produce pool candidates.
pub pool_candidate: bool,
/// Operational support status.
pub status: std::string::String,
/// Confidence level attached to this matrix entry.
pub confidence: std::string::String,
/// Optional explicit skip reason for partial or ignored entries.
pub skip_reason: std::option::Option<std::string::String>,
/// Whether the entry should be inserted as an enabled DEX in the storage catalog.
pub catalog_enabled: bool,
}
/// One local pipeline validation issue for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalPipelineValidationIssue.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalPipelineValidationIssue {
/// Stable machine-readable issue code.
pub code: std::string::String,
/// Human-readable issue message.
pub message: std::string::String,
/// Optional subject associated with the issue.
pub subject: std::option::Option<std::string::String>,
/// Whether the issue blocks the validation report.
pub blocking: bool,
}
/// Local pipeline diagnostics summary for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalPipelineDiagnosticSummary.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalPipelineDiagnosticSummary {
/// Total persisted chain transactions.
#[ts(type = "number")]
pub transaction_count: i64,
/// Total successful chain transactions.
#[ts(type = "number")]
pub ok_transaction_count: i64,
/// Total failed chain transactions.
#[ts(type = "number")]
pub failed_transaction_count: i64,
/// Total decoded DEX events.
#[ts(type = "number")]
pub decoded_event_count: i64,
/// Total decoded DEX trade candidates.
#[ts(type = "number")]
pub decoded_trade_candidate_count: i64,
/// Total decoded DEX candle candidates.
#[ts(type = "number")]
pub decoded_candle_candidate_count: i64,
/// Total decoded useful non-trade events.
#[ts(type = "number")]
pub decoded_non_trade_useful_event_count: i64,
/// Total decoded swap-like events that are intentionally non-actionable.
#[ts(type = "number")]
pub decoded_non_actionable_trade_event_count: i64,
/// Total decoded events with unknown classification.
#[ts(type = "number")]
pub decoded_unknown_event_count: i64,
/// Whether the local persisted pipeline has no blocking diagnostic issue.
pub diagnostics_clean: bool,
/// Number of blocking diagnostic issues.
#[ts(type = "number")]
pub blocking_issue_count: i64,
/// Total trade candidates without trade event, including ignored failed transactions.
#[ts(type = "number")]
pub missing_trade_event_count: i64,
/// Explicit alias for decoded trade candidates without linked trade event.
#[ts(type = "number")]
pub decoded_trade_candidate_without_trade_event_count: i64,
/// Trade candidates without linked trade event on successful transactions.
#[ts(type = "number")]
pub decoded_trade_candidate_without_trade_event_on_ok_transaction_count: i64,
/// Trade candidates without linked trade event on failed transactions.
#[ts(type = "number")]
pub decoded_trade_candidate_without_trade_event_on_failed_transaction_count: i64,
/// Trade candidates without linked trade event and without explicit base/quote payload amounts.
/// Actionable missing trade events on successful transactions.
#[ts(type = "number")]
pub actionable_missing_trade_event_count: i64,
/// Ignored missing trade events caused by failed transactions.
#[ts(type = "number")]
pub ignored_failed_transaction_trade_candidate_count: i64,
#[ts(type = "number")]
pub decoded_trade_candidate_without_amount_payload_count: i64,
/// Total persisted trade events.
#[ts(type = "number")]
pub trade_event_count: i64,
/// Total invalid trade events.
#[ts(type = "number")]
pub invalid_trade_event_count: i64,
/// Total persisted pair candles.
#[ts(type = "number")]
pub pair_candle_count: i64,
/// Real duplicate trade rows grouped by decoded event id.
#[ts(type = "number")]
pub duplicate_decoded_event_trade_count: i64,
/// Multi-trade groups sharing the same signature and pair id.
#[ts(type = "number")]
pub multi_trade_signature_pair_count: i64,
/// Total duplicate candle buckets.
#[ts(type = "number")]
pub duplicate_candle_bucket_count: i64,
/// Total known tokens.
#[ts(type = "number")]
pub token_count: i64,
/// Total tokens missing symbol or name.
#[ts(type = "number")]
pub token_metadata_missing_count: i64,
/// Total known pools.
#[ts(type = "number")]
pub pool_count: i64,
/// Total known pairs.
#[ts(type = "number")]
pub pair_count: i64,
/// Total pairs without trade.
#[ts(type = "number")]
pub pair_without_trade_count: i64,
/// Total pairs without candle.
#[ts(type = "number")]
pub pair_without_candle_count: i64,
/// Diagnostics grouped by DEX.
pub dex_summaries: std::vec::Vec<DemoPipeline2LocalDexDiagnosticSummary>,
/// Diagnostics grouped by pair.
pub pair_summaries: std::vec::Vec<DemoPipeline2LocalPairDiagnosticSummary>,
/// Diagnostics grouped by decoded event kind.
pub decoded_event_summaries: std::vec::Vec<DemoPipeline2LocalDecodedEventDiagnosticSummary>,
/// Diagnostics grouped by decoded event classification.
pub event_classification_summaries:
std::vec::Vec<DemoPipeline2LocalEventClassificationDiagnosticSummary>,
/// Missing trade events grouped by diagnostic reason.
pub missing_trade_event_reason_summaries:
std::vec::Vec<DemoPipeline2LocalMissingTradeEventReasonSummary>,
/// Total pairs with only non-actionable missing trade events.
#[ts(type = "number")]
pub non_actionable_pair_count: i64,
/// Pair summaries for non-actionable missing trade events.
pub non_actionable_pair_summaries:
std::vec::Vec<DemoPipeline2LocalNonActionablePairDiagnosticSummary>,
/// Samples of decoded trade candidates without linked trade event.
pub missing_trade_event_samples:
std::vec::Vec<DemoPipeline2LocalMissingTradeEventDiagnosticSample>,
/// Samples of duplicated trade rows by decoded event id.
pub duplicate_decoded_event_trade_samples:
std::vec::Vec<DemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample>,
/// Samples of multi-trade signature/pair groups.
pub multi_trade_signature_pair_samples:
std::vec::Vec<DemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample>,
/// Samples of pairs without trade.
pub pair_without_trade_samples: std::vec::Vec<DemoPipeline2LocalPairGapDiagnosticSample>,
/// Samples of pairs without candle.
pub pair_without_candle_samples: std::vec::Vec<DemoPipeline2LocalPairGapDiagnosticSample>,
}
/// Local DEX diagnostics summary for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalDexDiagnosticSummary.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalDexDiagnosticSummary {
/// DEX code.
pub dex_code: std::string::String,
/// Pool count.
#[ts(type = "number")]
pub pool_count: i64,
/// Pair count.
#[ts(type = "number")]
pub pair_count: i64,
/// Decoded event count.
#[ts(type = "number")]
pub decoded_event_count: i64,
/// Decoded trade candidate count.
#[ts(type = "number")]
pub decoded_trade_candidate_count: i64,
/// Decoded candle candidate count.
#[ts(type = "number")]
pub decoded_candle_candidate_count: i64,
/// Trade event count.
#[ts(type = "number")]
pub trade_event_count: i64,
/// Pair candle count.
#[ts(type = "number")]
pub pair_candle_count: i64,
}
/// Local pair diagnostics summary for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalPairDiagnosticSummary.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalPairDiagnosticSummary {
/// Pair id.
#[ts(type = "number")]
pub pair_id: i64,
/// Pool address.
pub pool_address: std::string::String,
/// DEX code.
pub dex_code: std::string::String,
/// Base mint.
pub base_mint: std::string::String,
/// Base symbol.
pub base_symbol: std::option::Option<std::string::String>,
/// Quote mint.
pub quote_mint: std::string::String,
/// Quote symbol.
pub quote_symbol: std::option::Option<std::string::String>,
/// Pair symbol.
pub pair_symbol: std::option::Option<std::string::String>,
/// Decoded event count.
#[ts(type = "number")]
pub decoded_event_count: i64,
/// Decoded trade candidate count.
#[ts(type = "number")]
pub decoded_trade_candidate_count: i64,
/// Decoded candle candidate count.
#[ts(type = "number")]
pub decoded_candle_candidate_count: i64,
/// Trade event count.
#[ts(type = "number")]
pub trade_event_count: i64,
/// Invalid trade event count.
#[ts(type = "number")]
pub invalid_trade_event_count: i64,
/// Pair candle count.
#[ts(type = "number")]
pub pair_candle_count: i64,
/// Last known price.
#[ts(type = "number | null")]
pub last_price_quote_per_base: std::option::Option<f64>,
}
/// Local decoded-event diagnostics summary for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalDecodedEventDiagnosticSummary.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalDecodedEventDiagnosticSummary {
/// Protocol name.
pub protocol_name: std::string::String,
/// Event kind.
pub event_kind: std::string::String,
/// Event category.
pub event_category: std::option::Option<std::string::String>,
/// Event lifecycle kind.
pub event_lifecycle_kind: std::option::Option<std::string::String>,
/// Event actionability class.
pub event_actionability: std::option::Option<std::string::String>,
/// Whether the event is useful but not trade/candle materialized.
pub non_trade_useful: std::option::Option<bool>,
/// Trade candidate flag.
pub trade_candidate: std::option::Option<bool>,
/// Candle candidate flag.
pub candle_candidate: std::option::Option<bool>,
/// Event count.
#[ts(type = "number")]
pub event_count: i64,
/// Linked trade-event count.
#[ts(type = "number")]
pub trade_event_count: i64,
}
/// Local decoded-event classification summary for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalEventClassificationDiagnosticSummary.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalEventClassificationDiagnosticSummary {
/// Event category.
pub event_category: std::string::String,
/// Event lifecycle kind.
pub event_lifecycle_kind: std::string::String,
/// Event actionability class.
pub event_actionability: std::string::String,
/// Whether the event is useful but not trade/candle materialized.
pub non_trade_useful: bool,
/// Event count.
#[ts(type = "number")]
pub event_count: i64,
/// Decoded trade candidate count.
#[ts(type = "number")]
pub decoded_trade_candidate_count: i64,
/// Decoded candle candidate count.
#[ts(type = "number")]
pub decoded_candle_candidate_count: i64,
/// Linked trade-event count.
#[ts(type = "number")]
pub trade_event_count: i64,
}
/// Local missing-trade-event reason summary for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalMissingTradeEventReasonSummary.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalMissingTradeEventReasonSummary {
/// Diagnostic reason.
pub reason: std::string::String,
/// Whether grouped source transactions failed.
pub transaction_failed: bool,
/// Whether grouped missing trade events are actionable.
pub actionable: bool,
/// Total missing trade events in this group.
#[ts(type = "number")]
pub event_count: i64,
/// Total events in this group with an explicit base amount payload.
#[ts(type = "number")]
pub has_base_amount_payload_count: i64,
/// Total events in this group with an explicit quote amount payload.
#[ts(type = "number")]
pub has_quote_amount_payload_count: i64,
/// Total events in this group with an explicit price payload.
#[ts(type = "number")]
pub has_price_payload_count: i64,
}
/// Local non-actionable pair diagnostic summary for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalNonActionablePairDiagnosticSummary.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalNonActionablePairDiagnosticSummary {
/// Pair id.
#[ts(type = "number")]
pub pair_id: i64,
/// Pool address.
pub pool_address: std::string::String,
/// DEX code.
pub dex_code: std::string::String,
/// Base token mint.
pub base_mint: std::string::String,
/// Base token symbol.
pub base_symbol: std::option::Option<std::string::String>,
/// Quote token mint.
pub quote_mint: std::string::String,
/// Quote token symbol.
pub quote_symbol: std::option::Option<std::string::String>,
/// Pair symbol.
pub pair_symbol: std::option::Option<std::string::String>,
/// Total decoded trade candidates attached to the pool.
#[ts(type = "number")]
pub decoded_trade_candidate_count: i64,
/// Total non-actionable missing trade events attached to the pair.
#[ts(type = "number")]
pub non_actionable_missing_trade_event_count: i64,
/// Total missing trade candidates caused by failed transactions.
#[ts(type = "number")]
pub failed_transaction_candidate_count: i64,
/// Total successful missing trade candidates without token mints.
#[ts(type = "number")]
pub ok_transaction_without_token_mints_count: i64,
/// Total successful missing trade candidates without amount payload.
#[ts(type = "number")]
pub ok_transaction_without_amount_payload_count: i64,
/// Distinct non-actionable diagnostic reasons seen for this pair.
pub reason_summary: std::option::Option<std::string::String>,
/// Total trade events attached to the pair.
#[ts(type = "number")]
pub trade_event_count: i64,
/// Total candle buckets attached to the pair.
#[ts(type = "number")]
pub pair_candle_count: i64,
}
/// Local missing-trade-event diagnostic sample for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalMissingTradeEventDiagnosticSample.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalMissingTradeEventDiagnosticSample {
/// Decoded event id.
#[ts(type = "number")]
pub decoded_event_id: i64,
/// Chain transaction id.
#[ts(type = "number | null")]
pub transaction_id: std::option::Option<i64>,
/// Transaction signature.
pub signature: std::option::Option<std::string::String>,
/// Protocol name.
pub protocol_name: std::string::String,
/// Event kind.
pub event_kind: std::string::String,
/// Pool account.
pub pool_account: std::option::Option<std::string::String>,
/// Whether the source transaction failed.
pub transaction_failed: bool,
/// Whether this missing trade event is actionable for validation.
pub actionable: bool,
/// Diagnostic reason explaining why no trade event was linked.
pub reason: std::string::String,
/// Whether payload has an explicit base amount.
pub has_base_amount_payload: bool,
/// Whether payload has an explicit quote amount.
pub has_quote_amount_payload: bool,
/// Whether payload has an explicit price.
pub has_price_payload: bool,
}
/// Local duplicate decoded-event trade diagnostic sample for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample {
/// Decoded event id.
#[ts(type = "number")]
pub decoded_event_id: i64,
/// Protocol name.
pub protocol_name: std::option::Option<std::string::String>,
/// Event kind.
pub event_kind: std::option::Option<std::string::String>,
/// Pool account.
pub pool_account: std::option::Option<std::string::String>,
/// Trade event count.
#[ts(type = "number")]
pub trade_event_count: i64,
/// Trade event ids.
pub trade_event_ids: std::option::Option<std::string::String>,
/// Signatures.
pub signatures: std::option::Option<std::string::String>,
}
/// Local multi-trade signature/pair diagnostic sample for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample {
/// Transaction signature.
pub signature: std::string::String,
/// Pair id.
#[ts(type = "number")]
pub pair_id: i64,
/// Pool address.
pub pool_address: std::option::Option<std::string::String>,
/// DEX code.
pub dex_code: std::option::Option<std::string::String>,
/// Trade event count.
#[ts(type = "number")]
pub trade_event_count: i64,
/// Distinct decoded event count.
#[ts(type = "number")]
pub decoded_event_count: i64,
/// Trade event ids.
pub trade_event_ids: std::option::Option<std::string::String>,
/// Decoded event ids.
pub decoded_event_ids: std::option::Option<std::string::String>,
}
/// Local pair gap diagnostic sample for the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2LocalPairGapDiagnosticSample.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2LocalPairGapDiagnosticSample {
/// Pair id.
#[ts(type = "number")]
pub pair_id: i64,
/// Pool address.
pub pool_address: std::string::String,
/// DEX code.
pub dex_code: std::string::String,
/// Base mint.
pub base_mint: std::string::String,
/// Base symbol.
pub base_symbol: std::option::Option<std::string::String>,
/// Quote mint.
pub quote_mint: std::string::String,
/// Quote symbol.
pub quote_symbol: std::option::Option<std::string::String>,
/// Pair symbol.
pub pair_symbol: std::option::Option<std::string::String>,
/// Decoded event count.
#[ts(type = "number")]
pub decoded_event_count: i64,
/// Decoded trade candidate count.
#[ts(type = "number")]
pub decoded_trade_candidate_count: i64,
/// Trade event count.
#[ts(type = "number")]
pub trade_event_count: i64,
/// Pair candle count.
#[ts(type = "number")]
pub pair_candle_count: i64,
}
/// One token item for the local catalog.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/DemoPipeline2TokenItem.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2TokenItem {
/// Token mint.
pub mint: std::string::String,
/// Optional token symbol.
pub symbol: std::option::Option<std::string::String>,
/// Optional token name.
pub name: std::option::Option<std::string::String>,
}
/// One pool item for the local catalog.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/DemoPipeline2PoolItem.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2PoolItem {
/// Pool address.
pub pool_address: std::string::String,
/// Optional internal pair id when known.
#[ts(type = "number | null")]
pub pair_id: std::option::Option<i64>,
/// Optional DEX code.
pub dex_code: std::option::Option<std::string::String>,
}
/// One pair item for the local catalog.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/DemoPipeline2PairItem.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2PairItem {
/// Internal pair id.
#[ts(type = "number")]
pub pair_id: i64,
/// Related pool address.
pub pool_address: std::string::String,
/// Optional pair symbol.
pub symbol: std::option::Option<std::string::String>,
/// Optional DEX code.
pub dex_code: std::option::Option<std::string::String>,
/// Optional local trade count.
#[ts(type = "number | null")]
pub trade_count: std::option::Option<i64>,
/// Optional local last price.
#[ts(type = "number | null")]
pub last_price_quote_per_base: std::option::Option<f64>,
}
/// Full local catalog payload.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/DemoPipeline2CatalogPayload.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2CatalogPayload {
/// Open database URL.
pub database_url: std::string::String,
/// Observed token list.
pub tokens: std::vec::Vec<DemoPipeline2TokenItem>,
/// Known pool list.
pub pools: std::vec::Vec<DemoPipeline2PoolItem>,
/// Known pair list.
pub pairs: std::vec::Vec<DemoPipeline2PairItem>,
}
/// Request payload for token backfill.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2BackfillTokenRequest.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2BackfillTokenRequest {
/// Token mint to backfill.
pub token_mint: std::string::String,
/// Optional HTTP role.
pub http_role: std::option::Option<std::string::String>,
/// Limit for signatures fetched from the mint.
pub mint_signature_limit: u32,
/// Limit for signatures fetched from each discovered pool.
pub pool_signature_limit: u32,
}
/// Request payload for pool backfill.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/DemoPipeline2BackfillPoolRequest.ts"
)]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2BackfillPoolRequest {
/// Pool address to backfill.
pub pool_address: std::string::String,
/// Optional HTTP role.
pub http_role: std::option::Option<std::string::String>,
/// Limit for signatures fetched from the pool.
pub pool_signature_limit: u32,
}
/// Shared backfill response payload.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/DemoPipeline2BackfillPayload.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2BackfillPayload {
/// Object key used by the backfill.
pub object_key: std::string::String,
/// Mode: `tokenMint` or `poolAddress`.
pub mode: std::string::String,
/// HTTP role used.
pub http_role: std::string::String,
/// Pretty JSON summary.
pub summary_json: std::string::String,
/// Refreshed local catalog after backfill.
pub catalog: DemoPipeline2CatalogPayload,
}
/// Request payload for pair candles.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/DemoPipeline2PairCandlesRequest.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2PairCandlesRequest {
/// Pair id to load.
#[ts(type = "number")]
pub pair_id: i64,
/// Timeframe in seconds.
#[ts(type = "number")]
pub timeframe_seconds: i64,
/// Whether materialized candles should be preferred when available.
pub prefer_materialized: bool,
}
/// Candle payload returned to the UI.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/DemoPipeline2PairCandlesPayload.ts")]
#[serde(rename_all = "camelCase")]
pub(crate) struct DemoPipeline2PairCandlesPayload {
/// Pair id.
#[ts(type = "number")]
pub pair_id: i64,
/// Timeframe in seconds.
#[ts(type = "number")]
pub timeframe_seconds: i64,
/// Pretty JSON array of candles.
pub candles_json: std::string::String,
}
/// Lists program instruction discriminator summaries for one program id.
#[tauri::command]
pub(crate) async fn demo_pipeline2_get_program_instruction_discriminator_summaries(
state: tauri::State<'_, crate::AppState>,
request: DemoPipeline2ProgramInstructionDiscriminatorSummaryRequest,
) -> Result<DemoPipeline2ProgramInstructionDiscriminatorSummaryPayload, std::string::String> {
if request.program_id.trim().is_empty() {
return Err("program id must not be empty".to_string());
}
if request.limit == 0 {
return Err("instruction discriminator summary limit must be > 0".to_string());
}
let rows_result = kb_lib::query_program_instruction_discriminator_summaries_list_by_program_id(
state.database.as_ref(),
request.program_id.as_str(),
request.limit,
)
.await;
let rows = match rows_result {
Ok(rows) => rows,
Err(error) => {
return Err(format!(
"cannot list instruction discriminator summaries for program_id '{}': {}",
request.program_id, error
));
},
};
let summaries_json_result = serde_json::to_string_pretty(&rows);
let summaries_json = match summaries_json_result {
Ok(summaries_json) => summaries_json,
Err(error) => {
return Err(format!("cannot serialize instruction discriminator summaries: {}", error));
},
};
return Ok(DemoPipeline2ProgramInstructionDiscriminatorSummaryPayload { summaries_json });
}
/// Lists protocol candidate summaries ordered by investigation priority.
#[tauri::command]
pub(crate) async fn demo_pipeline2_get_protocol_candidate_summaries(
state: tauri::State<'_, crate::AppState>,
request: DemoPipeline2ProtocolCandidateSummaryRequest,
) -> Result<DemoPipeline2ProtocolCandidateSummaryPayload, std::string::String> {
if request.limit == 0 {
return Err("protocol candidate summary limit must be > 0".to_string());
}
let summaries_result = kb_lib::query_protocol_candidate_summaries_list_by_priority(
state.database.as_ref(),
request.limit,
)
.await;
let summaries = match summaries_result {
Ok(summaries) => summaries,
Err(error) => {
return Err(format!(
"cannot list protocol candidate summaries with limit '{}': {}",
request.limit, error
));
},
};
let summaries_json_result = serde_json::to_string_pretty(&summaries);
let summaries_json = match summaries_json_result {
Ok(summaries_json) => summaries_json,
Err(error) => {
return Err(format!("cannot serialize protocol candidate summaries: {}", error));
},
};
return Ok(DemoPipeline2ProtocolCandidateSummaryPayload { summaries_json });
}
/// Runs local pipeline diagnostics from persisted data only.
#[tauri::command]
pub(crate) async fn demo_pipeline2_diagnose_local_pipeline(
state: tauri::State<'_, crate::AppState>,
) -> Result<DemoPipeline2LocalDiagnosticsPayload, std::string::String> {
let database = state.database.clone();
let service = kb_lib::LocalPipelineDiagnosticsService::new(database.clone());
let summary_result = service.diagnose().await;
let summary = match summary_result {
Ok(summary) => summary,
Err(error) => {
return Err(format!("local pipeline diagnostics failed: {}", error));
},
};
let ui_summary = demo_pipeline2_map_local_diagnostics_summary(summary);
let summary_json_result = serde_json::to_string_pretty(&ui_summary);
let summary_json = match summary_json_result {
Ok(summary_json) => summary_json,
Err(error) => {
return Err(format!("cannot serialize local pipeline diagnostics: {}", error));
},
};
Ok(DemoPipeline2LocalDiagnosticsPayload {
database_url: database.database_url().to_string(),
summary_json,
summary: ui_summary,
})
}
/// Validates the local pipeline with a selected multi-DEX validation profile.
#[tauri::command]
pub(crate) async fn demo_pipeline2_validate_local_pipeline(
state: tauri::State<'_, crate::AppState>,
request: std::option::Option<DemoPipeline2LocalValidationRequest>,
) -> Result<DemoPipeline2LocalValidationPayload, std::string::String> {
let database = state.database.clone();
let service = kb_lib::LocalPipelineValidationService::new(database.clone());
let profile_code = match request {
Some(request) => request.profile_code,
None => "0.7.31_trade_event_actionability_policy".to_string(),
};
let run_result = match profile_code.as_str() {
"0.7.27" | "0.7.27_dexes_non_regression" => {
service.validate_v0_7_27_current_database().await
},
"0.7.28" | "0.7.28_multi_dex_non_regression" => {
service.validate_v0_7_28_current_database().await
},
"0.7.29" | "0.7.29_multi_dex_matrix_baseline" => {
service.validate_v0_7_29_current_database().await
},
"0.7.30" | "0.7.30_non_trade_event_classification" => {
service.validate_v0_7_30_current_database().await
},
"0.7.31" | "0.7.31_trade_event_actionability_policy" => {
service.validate_v0_7_31_current_database().await
},
other => Err(kb_lib::Error::InvalidState(format!(
"unsupported local pipeline validation profile: {other}"
))),
};
let run = match run_result {
Ok(run) => run,
Err(error) => {
return Err(format!("local pipeline validation failed: {}", error));
},
};
let ui_run = demo_pipeline2_map_local_validation_run(run);
let summary_json_result = serde_json::to_string_pretty(&ui_run.summary);
let summary_json = match summary_json_result {
Ok(summary_json) => summary_json,
Err(error) => {
return Err(format!("cannot serialize local pipeline validation summary: {}", error));
},
};
let validation_json_result = serde_json::to_string_pretty(&ui_run);
let validation_json = match validation_json_result {
Ok(validation_json) => validation_json,
Err(error) => {
return Err(format!("cannot serialize local pipeline validation run: {}", error));
},
};
Ok(DemoPipeline2LocalValidationPayload {
database_url: database.database_url().to_string(),
summary_json,
validation_json,
run: ui_run,
})
}
/// Opens the `Demo Pipeline 2` window.
#[tauri::command]
pub(crate) fn open_demo_pipeline2_window(
app_handle: tauri::AppHandle,
) -> Result<(), std::string::String> {
let existing_window_option = app_handle.get_webview_window("demo_pipeline2");
let demo_window = match existing_window_option {
Some(demo_window) => demo_window,
None => {
let builder = tauri::WebviewWindowBuilder::new(
&app_handle,
"demo_pipeline2",
tauri::WebviewUrl::App("demo_pipeline2.html".into()),
)
.title("Demo Pipeline 2")
.inner_size(1480.0, 920.0)
.min_inner_size(1100.0, 720.0)
.center()
.visible(true)
.transparent(false)
.decorations(true);
let build_result = builder.build();
match build_result {
Ok(window) => window,
Err(error) => {
return Err(format!("cannot create demo_pipeline2 window: {error:?}"));
},
}
},
};
let show_result = demo_window.show();
if let Err(error) = show_result {
return Err(format!("cannot show demo_pipeline2 window: {error:?}"));
}
let focus_result = demo_window.set_focus();
if let Err(error) = focus_result {
return Err(format!("cannot focus demo_pipeline2 window: {error:?}"));
}
Ok(())
}
/// Returns the local catalog of observed tokens, pools and pairs.
#[tauri::command]
pub(crate) async fn demo_pipeline2_get_catalog(
state: tauri::State<'_, crate::AppState>,
) -> Result<DemoPipeline2CatalogPayload, std::string::String> {
demo_pipeline2_build_catalog(state.database.clone()).await
}
/// Runs a targeted token backfill then returns the refreshed catalog.
#[tauri::command]
pub(crate) async fn demo_pipeline2_backfill_token_mint(
state: tauri::State<'_, crate::AppState>,
request: DemoPipeline2BackfillTokenRequest,
) -> Result<DemoPipeline2BackfillPayload, std::string::String> {
let token_mint = request.token_mint.trim().to_string();
if token_mint.is_empty() {
return Err("token mint must not be empty".to_string());
}
if request.mint_signature_limit == 0 {
return Err("mintSignatureLimit must be > 0".to_string());
}
if request.pool_signature_limit == 0 {
return Err("poolSignatureLimit must be > 0".to_string());
}
let http_role = demo_pipeline2_normalize_http_role(request.http_role);
let database = state.database.clone();
let http_pool = std::sync::Arc::new(state.http_pool.clone());
let service = kb_lib::TokenBackfillService::new(http_pool, database.clone(), http_role.clone());
let result = service
.backfill_token_by_mint(
token_mint.as_str(),
request.mint_signature_limit as usize,
request.pool_signature_limit as usize,
)
.await;
let backfill = match result {
Ok(backfill) => backfill,
Err(error) => {
return Err(format!(
"cannot backfill token mint '{}' with role '{}': {}",
token_mint, http_role, error
));
},
};
let summary_json_result = serde_json::to_string_pretty(&backfill);
let summary_json = match summary_json_result {
Ok(summary_json) => summary_json,
Err(error) => {
return Err(format!(
"cannot serialize token backfill result for '{}': {}",
token_mint, error
));
},
};
let catalog = demo_pipeline2_build_catalog(database).await?;
Ok(DemoPipeline2BackfillPayload {
object_key: token_mint,
mode: "tokenMint".to_string(),
http_role,
summary_json,
catalog,
})
}
/// Runs a targeted pool backfill then returns the refreshed catalog.
#[tauri::command]
pub(crate) async fn demo_pipeline2_backfill_pool_address(
state: tauri::State<'_, crate::AppState>,
request: DemoPipeline2BackfillPoolRequest,
) -> Result<DemoPipeline2BackfillPayload, std::string::String> {
let pool_address = request.pool_address.trim().to_string();
if pool_address.is_empty() {
return Err("pool address must not be empty".to_string());
}
if request.pool_signature_limit == 0 {
return Err("poolSignatureLimit must be > 0".to_string());
}
let http_role = demo_pipeline2_normalize_http_role(request.http_role);
let database = state.database.clone();
let http_pool = std::sync::Arc::new(state.http_pool.clone());
let service = kb_lib::TokenBackfillService::new(http_pool, database.clone(), http_role.clone());
let result = service
.backfill_pool_by_address(pool_address.as_str(), request.pool_signature_limit as usize)
.await;
let backfill = match result {
Ok(backfill) => backfill,
Err(error) => {
return Err(format!(
"cannot backfill pool address '{}' with role '{}': {}",
pool_address, http_role, error
));
},
};
let summary_json_result = serde_json::to_string_pretty(&backfill);
let summary_json = match summary_json_result {
Ok(summary_json) => summary_json,
Err(error) => {
return Err(format!(
"cannot serialize pool backfill result for '{}': {}",
pool_address, error
));
},
};
let catalog = demo_pipeline2_build_catalog(database).await?;
Ok(DemoPipeline2BackfillPayload {
object_key: pool_address,
mode: "poolAddress".to_string(),
http_role,
summary_json,
catalog,
})
}
/// Loads candles for one pair and one timeframe.
#[tauri::command]
pub(crate) async fn demo_pipeline2_get_pair_candles(
state: tauri::State<'_, crate::AppState>,
request: DemoPipeline2PairCandlesRequest,
) -> Result<DemoPipeline2PairCandlesPayload, std::string::String> {
if request.pair_id <= 0 {
return Err("pairId must be > 0".to_string());
}
if request.timeframe_seconds <= 0 {
return Err("timeframeSeconds must be > 0".to_string());
}
let query_service = kb_lib::PairCandleQueryService::new(state.database.clone());
let candles_result = query_service
.list_pair_candles(
request.pair_id,
request.timeframe_seconds,
None,
None,
request.prefer_materialized,
)
.await;
let candles = match candles_result {
Ok(candles) => candles,
Err(error) => {
return Err(format!(
"cannot load candles for pair '{}' timeframe '{}': {}",
request.pair_id, request.timeframe_seconds, error
));
},
};
let candles_json_result = serde_json::to_string_pretty(&candles);
let candles_json = match candles_json_result {
Ok(candles_json) => candles_json,
Err(error) => {
return Err(format!(
"cannot serialize candles for pair '{}' timeframe '{}': {}",
request.pair_id, request.timeframe_seconds, error
));
},
};
Ok(DemoPipeline2PairCandlesPayload {
pair_id: request.pair_id,
timeframe_seconds: request.timeframe_seconds,
candles_json,
})
}
async fn demo_pipeline2_build_catalog(
database: std::sync::Arc<kb_lib::Database>,
) -> Result<DemoPipeline2CatalogPayload, std::string::String> {
let dexes_result = kb_lib::query_dexs_list(database.as_ref()).await;
let dexes = match dexes_result {
Ok(dexes) => dexes,
Err(error) => {
return Err(format!("cannot list DEXes: {}", error));
},
};
let mut dex_code_by_id = std::collections::BTreeMap::<i64, std::string::String>::new();
for dex in dexes {
if let Some(dex_id) = dex.id {
dex_code_by_id.insert(dex_id, dex.code);
}
}
let tokens_result = kb_lib::query_tokens_list(database.as_ref()).await;
let db_tokens = match tokens_result {
Ok(db_tokens) => db_tokens,
Err(error) => {
return Err(format!("cannot list tokens: {}", error));
},
};
let mut tokens = std::vec::Vec::<DemoPipeline2TokenItem>::new();
for token in db_tokens {
tokens.push(DemoPipeline2TokenItem {
mint: token.mint,
symbol: token.symbol,
name: token.name,
});
}
let pools_result = kb_lib::query_pools_list(database.as_ref()).await;
let pools = match pools_result {
Ok(pools) => pools,
Err(error) => {
return Err(format!("cannot list pools: {}", error));
},
};
let pairs_result = kb_lib::query_pairs_list(database.as_ref()).await;
let pairs = match pairs_result {
Ok(pairs) => pairs,
Err(error) => {
return Err(format!("cannot list pairs: {}", error));
},
};
let mut pair_by_pool_id = std::collections::BTreeMap::<i64, kb_lib::PairDto>::new();
for pair in &pairs {
pair_by_pool_id.insert(pair.pool_id, pair.clone());
}
let mut pair_items = std::vec::Vec::<DemoPipeline2PairItem>::new();
for pair in pairs {
let pair_id = match pair.id {
Some(pair_id) => pair_id,
None => continue,
};
let pool_result = kb_lib::query_pools_get_by_address(database.as_ref(), "").await;
let _ = pool_result;
let pool_address = {
let all_pools_result = kb_lib::query_pools_list(database.as_ref()).await;
let all_pools = match all_pools_result {
Ok(all_pools) => all_pools,
Err(error) => {
return Err(format!("cannot reload pools for pair catalog: {}", error));
},
};
let mut found_address = std::string::String::new();
for pool in all_pools {
let pool_id = match pool.id {
Some(pool_id) => pool_id,
None => continue,
};
if pool_id == pair.pool_id {
found_address = pool.address;
break;
}
}
found_address
};
let pair_metric_result =
kb_lib::query_pair_metrics_get_by_pair_id(database.as_ref(), pair_id).await;
let pair_metric_option = match pair_metric_result {
Ok(pair_metric_option) => pair_metric_option,
Err(error) => {
return Err(format!("cannot fetch pair metric for pair '{}': {}", pair_id, error));
},
};
let trade_count = pair_metric_option.as_ref().map(|metric| metric.trade_count);
let last_price_quote_per_base =
pair_metric_option.and_then(|metric| metric.last_price_quote_per_base);
pair_items.push(DemoPipeline2PairItem {
pair_id,
pool_address,
symbol: pair.symbol,
dex_code: dex_code_by_id.get(&pair.dex_id).cloned(),
trade_count,
last_price_quote_per_base,
});
}
let mut pool_items = std::vec::Vec::<DemoPipeline2PoolItem>::new();
for pool in pools {
let pool_id = match pool.id {
Some(pool_id) => pool_id,
None => continue,
};
let pair_id = pair_by_pool_id.get(&pool_id).and_then(|pair| pair.id);
pool_items.push(DemoPipeline2PoolItem {
pool_address: pool.address,
pair_id,
dex_code: dex_code_by_id.get(&pool.dex_id).cloned(),
});
}
tokens.sort_by(|left, right| left.mint.cmp(&right.mint));
pool_items.sort_by(|left, right| left.pool_address.cmp(&right.pool_address));
pair_items.sort_by(|left, right| left.pair_id.cmp(&right.pair_id));
Ok(DemoPipeline2CatalogPayload {
database_url: database.database_url().to_string(),
tokens,
pools: pool_items,
pairs: pair_items,
})
}
/// Replays the local pipeline from persisted raw transactions.
#[tauri::command]
pub(crate) async fn demo_pipeline2_replay_local_pipeline(
state: tauri::State<'_, crate::AppState>,
limit: std::option::Option<i64>,
refresh_missing_token_metadata: bool,
token_metadata_limit: std::option::Option<i64>,
) -> Result<kb_lib::LocalPipelineReplayResult, std::string::String> {
let config = kb_lib::LocalPipelineReplayConfig {
limit,
refresh_missing_token_metadata,
token_metadata_limit,
reset_market_materialization_before_replay: true,
};
let database = state.database.clone();
let service = if refresh_missing_token_metadata {
kb_lib::LocalPipelineReplayService::new_with_http_pool(
std::sync::Arc::new(state.http_pool.clone()),
database,
"history_backfill".to_string(),
)
} else {
kb_lib::LocalPipelineReplayService::new(database)
};
let replay_result = service.replay_local_pipeline(&config).await;
match replay_result {
Ok(result) => Ok(result),
Err(error) => Err(format!("local pipeline replay failed: {}", error)),
}
}
fn demo_pipeline2_map_local_validation_run(
run: kb_lib::LocalPipelineValidationRunDto,
) -> DemoPipeline2LocalPipelineValidationRun {
let validation_profile_code = run.validation_profile_code;
let validation_passed = run.validation_passed;
let blocking_issue_count = run.blocking_issue_count;
let warning_count = run.warning_count;
let summary = demo_pipeline2_map_local_diagnostics_summary(run.summary);
let report = demo_pipeline2_map_local_validation_report(run.report);
return DemoPipeline2LocalPipelineValidationRun {
validation_profile_code,
validation_passed,
blocking_issue_count,
warning_count,
summary,
report,
};
}
fn demo_pipeline2_map_local_validation_report(
report: kb_lib::LocalPipelineValidationReportDto,
) -> DemoPipeline2LocalPipelineValidationReport {
let mut issues = std::vec::Vec::new();
for issue in report.issues {
issues.push(demo_pipeline2_map_local_validation_issue(issue));
}
let mut dex_support_matrix = std::vec::Vec::new();
for entry in report.dex_support_matrix {
dex_support_matrix.push(demo_pipeline2_map_dex_support_matrix_entry(entry));
}
return DemoPipeline2LocalPipelineValidationReport {
validation_profile_code: report.validation_profile_code,
validation_passed: report.validation_passed,
blocking_issue_count: report.blocking_issue_count,
warning_count: report.warning_count,
expected_dex_codes: report.expected_dex_codes,
observed_dex_codes: report.observed_dex_codes,
decoded_non_trade_useful_event_count: report.decoded_non_trade_useful_event_count,
decoded_non_actionable_trade_event_count: report.decoded_non_actionable_trade_event_count,
decoded_unknown_event_count: report.decoded_unknown_event_count,
dex_support_matrix_entry_count: report.dex_support_matrix_entry_count,
dex_support_matrix,
issues,
};
}
fn demo_pipeline2_map_dex_support_matrix_entry(
entry: kb_lib::DexSupportMatrixEntryDto,
) -> DemoPipeline2DexSupportMatrixEntry {
return DemoPipeline2DexSupportMatrixEntry {
code: entry.code,
display_name: entry.display_name,
family: entry.family,
version: entry.version,
surface_type: entry.surface_type,
program_id: entry.program_id,
router_program_id: entry.router_program_id,
program_id_status: entry.program_id_status,
observed: entry.observed,
decoded: entry.decoded,
materialized: entry.materialized,
trade_candidate: entry.trade_candidate,
candle_candidate: entry.candle_candidate,
pair_candidate: entry.pair_candidate,
pool_candidate: entry.pool_candidate,
status: entry.status,
confidence: entry.confidence,
skip_reason: entry.skip_reason,
catalog_enabled: entry.catalog_enabled,
};
}
fn demo_pipeline2_map_local_validation_issue(
issue: kb_lib::LocalPipelineValidationIssueDto,
) -> DemoPipeline2LocalPipelineValidationIssue {
return DemoPipeline2LocalPipelineValidationIssue {
code: issue.code,
message: issue.message,
subject: issue.subject,
blocking: issue.blocking,
};
}
fn demo_pipeline2_map_local_diagnostics_summary(
summary: kb_lib::LocalPipelineDiagnosticSummaryDto,
) -> DemoPipeline2LocalPipelineDiagnosticSummary {
let mut dex_summaries = std::vec::Vec::new();
for dex_summary in summary.dex_summaries {
dex_summaries.push(demo_pipeline2_map_local_dex_diagnostic_summary(dex_summary));
}
let mut pair_summaries = std::vec::Vec::new();
for pair_summary in summary.pair_summaries {
pair_summaries.push(demo_pipeline2_map_local_pair_diagnostic_summary(pair_summary));
}
let mut decoded_event_summaries = std::vec::Vec::new();
for decoded_event_summary in summary.decoded_event_summaries {
decoded_event_summaries
.push(demo_pipeline2_map_local_decoded_event_diagnostic_summary(decoded_event_summary));
}
let mut event_classification_summaries = std::vec::Vec::new();
for classification_summary in summary.event_classification_summaries {
event_classification_summaries.push(
demo_pipeline2_map_local_event_classification_diagnostic_summary(
classification_summary,
),
);
}
let mut missing_trade_event_reason_summaries = std::vec::Vec::new();
for reason_summary in summary.missing_trade_event_reason_summaries {
missing_trade_event_reason_summaries
.push(demo_pipeline2_map_missing_trade_event_reason_summary(reason_summary));
}
let mut non_actionable_pair_summaries = std::vec::Vec::new();
for pair_summary in summary.non_actionable_pair_summaries {
non_actionable_pair_summaries
.push(demo_pipeline2_map_non_actionable_pair_diagnostic_summary(pair_summary));
}
let mut missing_trade_event_samples = std::vec::Vec::new();
for sample in summary.missing_trade_event_samples {
missing_trade_event_samples.push(demo_pipeline2_map_missing_trade_event_sample(sample));
}
let mut duplicate_decoded_event_trade_samples = std::vec::Vec::new();
for sample in summary.duplicate_decoded_event_trade_samples {
duplicate_decoded_event_trade_samples
.push(demo_pipeline2_map_duplicate_decoded_event_trade_sample(sample));
}
let mut multi_trade_signature_pair_samples = std::vec::Vec::new();
for sample in summary.multi_trade_signature_pair_samples {
multi_trade_signature_pair_samples
.push(demo_pipeline2_map_multi_trade_signature_pair_sample(sample));
}
let mut pair_without_trade_samples = std::vec::Vec::new();
for sample in summary.pair_without_trade_samples {
pair_without_trade_samples.push(demo_pipeline2_map_pair_gap_sample(sample));
}
let mut pair_without_candle_samples = std::vec::Vec::new();
for sample in summary.pair_without_candle_samples {
pair_without_candle_samples.push(demo_pipeline2_map_pair_gap_sample(sample));
}
DemoPipeline2LocalPipelineDiagnosticSummary {
transaction_count: summary.transaction_count,
ok_transaction_count: summary.ok_transaction_count,
failed_transaction_count: summary.failed_transaction_count,
decoded_event_count: summary.decoded_event_count,
decoded_trade_candidate_count: summary.decoded_trade_candidate_count,
decoded_candle_candidate_count: summary.decoded_candle_candidate_count,
decoded_non_trade_useful_event_count: summary.decoded_non_trade_useful_event_count,
decoded_non_actionable_trade_event_count: summary.decoded_non_actionable_trade_event_count,
decoded_unknown_event_count: summary.decoded_unknown_event_count,
diagnostics_clean: summary.diagnostics_clean,
blocking_issue_count: summary.blocking_issue_count,
missing_trade_event_count: summary.missing_trade_event_count,
decoded_trade_candidate_without_trade_event_count: summary
.decoded_trade_candidate_without_trade_event_count,
decoded_trade_candidate_without_trade_event_on_ok_transaction_count: summary
.decoded_trade_candidate_without_trade_event_on_ok_transaction_count,
decoded_trade_candidate_without_trade_event_on_failed_transaction_count: summary
.decoded_trade_candidate_without_trade_event_on_failed_transaction_count,
actionable_missing_trade_event_count: summary.actionable_missing_trade_event_count,
ignored_failed_transaction_trade_candidate_count: summary
.ignored_failed_transaction_trade_candidate_count,
decoded_trade_candidate_without_amount_payload_count: summary
.decoded_trade_candidate_without_amount_payload_count,
trade_event_count: summary.trade_event_count,
invalid_trade_event_count: summary.invalid_trade_event_count,
pair_candle_count: summary.pair_candle_count,
duplicate_decoded_event_trade_count: summary.duplicate_decoded_event_trade_count,
multi_trade_signature_pair_count: summary.multi_trade_signature_pair_count,
duplicate_candle_bucket_count: summary.duplicate_candle_bucket_count,
token_count: summary.token_count,
token_metadata_missing_count: summary.token_metadata_missing_count,
pool_count: summary.pool_count,
pair_count: summary.pair_count,
pair_without_trade_count: summary.pair_without_trade_count,
pair_without_candle_count: summary.pair_without_candle_count,
dex_summaries,
pair_summaries,
decoded_event_summaries,
event_classification_summaries,
missing_trade_event_reason_summaries,
non_actionable_pair_count: summary.non_actionable_pair_count,
non_actionable_pair_summaries,
missing_trade_event_samples,
duplicate_decoded_event_trade_samples,
multi_trade_signature_pair_samples,
pair_without_trade_samples,
pair_without_candle_samples,
}
}
fn demo_pipeline2_map_local_dex_diagnostic_summary(
summary: kb_lib::LocalDexDiagnosticSummaryDto,
) -> DemoPipeline2LocalDexDiagnosticSummary {
DemoPipeline2LocalDexDiagnosticSummary {
dex_code: summary.dex_code,
pool_count: summary.pool_count,
pair_count: summary.pair_count,
decoded_event_count: summary.decoded_event_count,
decoded_trade_candidate_count: summary.decoded_trade_candidate_count,
decoded_candle_candidate_count: summary.decoded_candle_candidate_count,
trade_event_count: summary.trade_event_count,
pair_candle_count: summary.pair_candle_count,
}
}
fn demo_pipeline2_map_local_pair_diagnostic_summary(
summary: kb_lib::LocalPairDiagnosticSummaryDto,
) -> DemoPipeline2LocalPairDiagnosticSummary {
DemoPipeline2LocalPairDiagnosticSummary {
pair_id: summary.pair_id,
pool_address: summary.pool_address,
dex_code: summary.dex_code,
base_mint: summary.base_mint,
base_symbol: summary.base_symbol,
quote_mint: summary.quote_mint,
quote_symbol: summary.quote_symbol,
pair_symbol: summary.pair_symbol,
decoded_event_count: summary.decoded_event_count,
decoded_trade_candidate_count: summary.decoded_trade_candidate_count,
decoded_candle_candidate_count: summary.decoded_candle_candidate_count,
trade_event_count: summary.trade_event_count,
invalid_trade_event_count: summary.invalid_trade_event_count,
pair_candle_count: summary.pair_candle_count,
last_price_quote_per_base: summary.last_price_quote_per_base,
}
}
fn demo_pipeline2_map_local_decoded_event_diagnostic_summary(
summary: kb_lib::LocalDecodedEventDiagnosticSummaryDto,
) -> DemoPipeline2LocalDecodedEventDiagnosticSummary {
DemoPipeline2LocalDecodedEventDiagnosticSummary {
protocol_name: summary.protocol_name,
event_kind: summary.event_kind,
event_category: summary.event_category,
event_lifecycle_kind: summary.event_lifecycle_kind,
event_actionability: summary.event_actionability,
non_trade_useful: summary.non_trade_useful,
trade_candidate: summary.trade_candidate,
candle_candidate: summary.candle_candidate,
event_count: summary.event_count,
trade_event_count: summary.trade_event_count,
}
}
fn demo_pipeline2_map_local_event_classification_diagnostic_summary(
summary: kb_lib::LocalEventClassificationDiagnosticSummaryDto,
) -> DemoPipeline2LocalEventClassificationDiagnosticSummary {
DemoPipeline2LocalEventClassificationDiagnosticSummary {
event_category: summary.event_category,
event_lifecycle_kind: summary.event_lifecycle_kind,
event_actionability: summary.event_actionability,
non_trade_useful: summary.non_trade_useful,
event_count: summary.event_count,
decoded_trade_candidate_count: summary.decoded_trade_candidate_count,
decoded_candle_candidate_count: summary.decoded_candle_candidate_count,
trade_event_count: summary.trade_event_count,
}
}
fn demo_pipeline2_map_missing_trade_event_reason_summary(
summary: kb_lib::LocalMissingTradeEventReasonSummaryDto,
) -> DemoPipeline2LocalMissingTradeEventReasonSummary {
return DemoPipeline2LocalMissingTradeEventReasonSummary {
reason: summary.reason,
transaction_failed: summary.transaction_failed,
actionable: summary.actionable,
event_count: summary.event_count,
has_base_amount_payload_count: summary.has_base_amount_payload_count,
has_quote_amount_payload_count: summary.has_quote_amount_payload_count,
has_price_payload_count: summary.has_price_payload_count,
};
}
fn demo_pipeline2_map_non_actionable_pair_diagnostic_summary(
summary: kb_lib::LocalNonActionablePairDiagnosticSummaryDto,
) -> DemoPipeline2LocalNonActionablePairDiagnosticSummary {
return DemoPipeline2LocalNonActionablePairDiagnosticSummary {
pair_id: summary.pair_id,
pool_address: summary.pool_address,
dex_code: summary.dex_code,
base_mint: summary.base_mint,
base_symbol: summary.base_symbol,
quote_mint: summary.quote_mint,
quote_symbol: summary.quote_symbol,
pair_symbol: summary.pair_symbol,
decoded_trade_candidate_count: summary.decoded_trade_candidate_count,
non_actionable_missing_trade_event_count: summary.non_actionable_missing_trade_event_count,
failed_transaction_candidate_count: summary.failed_transaction_candidate_count,
ok_transaction_without_token_mints_count: summary.ok_transaction_without_token_mints_count,
ok_transaction_without_amount_payload_count: summary
.ok_transaction_without_amount_payload_count,
reason_summary: summary.reason_summary,
trade_event_count: summary.trade_event_count,
pair_candle_count: summary.pair_candle_count,
};
}
fn demo_pipeline2_map_missing_trade_event_sample(
sample: kb_lib::LocalMissingTradeEventDiagnosticSampleDto,
) -> DemoPipeline2LocalMissingTradeEventDiagnosticSample {
return DemoPipeline2LocalMissingTradeEventDiagnosticSample {
decoded_event_id: sample.decoded_event_id,
transaction_id: sample.transaction_id,
signature: sample.signature,
protocol_name: sample.protocol_name,
event_kind: sample.event_kind,
pool_account: sample.pool_account,
transaction_failed: sample.transaction_failed,
actionable: sample.actionable,
reason: sample.reason,
has_base_amount_payload: sample.has_base_amount_payload,
has_quote_amount_payload: sample.has_quote_amount_payload,
has_price_payload: sample.has_price_payload,
};
}
fn demo_pipeline2_map_duplicate_decoded_event_trade_sample(
sample: kb_lib::LocalDuplicateDecodedEventTradeDiagnosticSampleDto,
) -> DemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample {
return DemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample {
decoded_event_id: sample.decoded_event_id,
protocol_name: sample.protocol_name,
event_kind: sample.event_kind,
pool_account: sample.pool_account,
trade_event_count: sample.trade_event_count,
trade_event_ids: sample.trade_event_ids,
signatures: sample.signatures,
};
}
fn demo_pipeline2_map_multi_trade_signature_pair_sample(
sample: kb_lib::LocalMultiTradeSignaturePairDiagnosticSampleDto,
) -> DemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample {
return DemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample {
signature: sample.signature,
pair_id: sample.pair_id,
pool_address: sample.pool_address,
dex_code: sample.dex_code,
trade_event_count: sample.trade_event_count,
decoded_event_count: sample.decoded_event_count,
trade_event_ids: sample.trade_event_ids,
decoded_event_ids: sample.decoded_event_ids,
};
}
fn demo_pipeline2_map_pair_gap_sample(
sample: kb_lib::LocalPairGapDiagnosticSampleDto,
) -> DemoPipeline2LocalPairGapDiagnosticSample {
return DemoPipeline2LocalPairGapDiagnosticSample {
pair_id: sample.pair_id,
pool_address: sample.pool_address,
dex_code: sample.dex_code,
base_mint: sample.base_mint,
base_symbol: sample.base_symbol,
quote_mint: sample.quote_mint,
quote_symbol: sample.quote_symbol,
pair_symbol: sample.pair_symbol,
decoded_event_count: sample.decoded_event_count,
decoded_trade_candidate_count: sample.decoded_trade_candidate_count,
trade_event_count: sample.trade_event_count,
pair_candle_count: sample.pair_candle_count,
};
}
fn demo_pipeline2_normalize_http_role(
role: std::option::Option<std::string::String>,
) -> std::string::String {
match role {
Some(role) => {
let trimmed = role.trim().to_string();
if trimmed.is_empty() { "history_backfill".to_string() } else { trimmed }
},
None => "history_backfill".to_string(),
}
}