1953 lines
77 KiB
Rust
1953 lines
77 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,
|
|
/// Total persisted liquidity events.
|
|
#[ts(type = "number")]
|
|
pub liquidity_event_count: i64,
|
|
/// Total persisted pool lifecycle events.
|
|
#[ts(type = "number")]
|
|
pub pool_lifecycle_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,
|
|
/// Total persisted liquidity events.
|
|
#[ts(type = "number")]
|
|
pub liquidity_event_count: i64,
|
|
/// Total persisted pool lifecycle events.
|
|
#[ts(type = "number")]
|
|
pub pool_lifecycle_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,
|
|
/// Stable explanation for legacy pair gap counters.
|
|
pub pair_gap_counter_semantics: std::string::String,
|
|
/// Total pairs without any persisted trade event.
|
|
#[ts(type = "number")]
|
|
pub literal_pair_without_trade_count: i64,
|
|
/// Total pairs without any persisted candle.
|
|
#[ts(type = "number")]
|
|
pub literal_pair_without_candle_count: i64,
|
|
/// Total pairs that have at least one persisted trade event.
|
|
#[ts(type = "number")]
|
|
pub trade_materialized_pair_count: i64,
|
|
/// Total pairs that have at least one persisted candle bucket.
|
|
#[ts(type = "number")]
|
|
pub candle_materialized_pair_count: i64,
|
|
/// Total pairs with at least one successful decoded trade candidate.
|
|
#[ts(type = "number")]
|
|
pub actionable_pair_count: i64,
|
|
/// Total distinct candle timeframes currently materialized.
|
|
#[ts(type = "number")]
|
|
pub candle_bucket_timeframe_count: i64,
|
|
/// Whether candle rows are bucketed aggregates rather than one row per trade.
|
|
pub candles_are_bucketed: bool,
|
|
/// Total pairs without trade among actionable successful trade candidates.
|
|
#[ts(type = "number")]
|
|
pub blocking_pair_without_trade_count: i64,
|
|
/// Total pairs without candle among actionable successful candle candidates.
|
|
#[ts(type = "number")]
|
|
pub blocking_pair_without_candle_count: i64,
|
|
/// Total pairs without trade. Legacy alias for blocking/actionable gaps.
|
|
#[ts(type = "number")]
|
|
pub pair_without_trade_count: i64,
|
|
/// Total pairs without candle. Legacy alias for blocking/actionable gaps.
|
|
#[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 pair materialization/actionability class.
|
|
pub pair_actionability_summaries:
|
|
std::vec::Vec<DemoPipeline2LocalPairActionabilityDiagnosticSummary>,
|
|
/// Diagnostics grouped by pair trading-readiness class.
|
|
pub pair_trading_readiness_summaries:
|
|
std::vec::Vec<DemoPipeline2LocalPairTradingReadinessDiagnosticSummary>,
|
|
/// 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>,
|
|
/// Pair trading-readiness class derived from base/quote orientation.
|
|
pub pair_trading_readiness: std::string::String,
|
|
/// Quote asset class used by the readiness classifier.
|
|
pub quote_asset_class: std::string::String,
|
|
/// Whether the pair likely requires a router or aggregator before direct execution.
|
|
pub trading_route_required: bool,
|
|
}
|
|
|
|
/// Local pair actionability diagnostics summary for the UI.
|
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
|
#[ts(
|
|
export,
|
|
export_to = "../frontend/ts/bindings/DemoPipeline2LocalPairActionabilityDiagnosticSummary.ts"
|
|
)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub(crate) struct DemoPipeline2LocalPairActionabilityDiagnosticSummary {
|
|
/// Pair actionability or materialization class.
|
|
pub pair_actionability: std::string::String,
|
|
/// 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,
|
|
/// Actionable trade candidate count.
|
|
#[ts(type = "number")]
|
|
pub actionable_trade_candidate_count: i64,
|
|
/// Failed trade candidate count.
|
|
#[ts(type = "number")]
|
|
pub failed_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,
|
|
}
|
|
|
|
/// Local pair trading-readiness diagnostics summary for the UI.
|
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
|
#[ts(
|
|
export,
|
|
export_to = "../frontend/ts/bindings/DemoPipeline2LocalPairTradingReadinessDiagnosticSummary.ts"
|
|
)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub(crate) struct DemoPipeline2LocalPairTradingReadinessDiagnosticSummary {
|
|
/// Pair trading-readiness class.
|
|
pub pair_trading_readiness: std::string::String,
|
|
/// Quote asset class.
|
|
pub quote_asset_class: std::string::String,
|
|
/// Whether a router or aggregator is required before direct execution.
|
|
pub trading_route_required: bool,
|
|
/// 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,
|
|
/// Trade event count.
|
|
#[ts(type = "number")]
|
|
pub trade_event_count: i64,
|
|
/// Pair candle count.
|
|
#[ts(type = "number")]
|
|
pub pair_candle_count: i64,
|
|
}
|
|
|
|
/// 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.36_meteora_family_consolidation".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
|
|
},
|
|
"0.7.32" | "0.7.32_validation_report_semantics" => {
|
|
service.validate_v0_7_32_current_database().await
|
|
},
|
|
"0.7.33" | "0.7.33_pair_trading_readiness" => {
|
|
service.validate_v0_7_33_current_database().await
|
|
},
|
|
"0.7.34" | "0.7.34_non_trade_liquidity_lifecycle" => {
|
|
service.validate_v0_7_34_current_database().await
|
|
},
|
|
"0.7.35" | "0.7.35_non_trade_fee_reward_admin" => {
|
|
service.validate_v0_7_35_current_database().await
|
|
},
|
|
"0.7.36" | "0.7.36_meteora_family_consolidation" => {
|
|
service.validate_v0_7_36_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,
|
|
liquidity_event_count: report.liquidity_event_count,
|
|
pool_lifecycle_event_count: report.pool_lifecycle_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 pair_actionability_summaries = std::vec::Vec::new();
|
|
for pair_actionability_summary in summary.pair_actionability_summaries {
|
|
pair_actionability_summaries.push(
|
|
demo_pipeline2_map_local_pair_actionability_diagnostic_summary(
|
|
pair_actionability_summary,
|
|
),
|
|
);
|
|
}
|
|
let mut pair_trading_readiness_summaries = std::vec::Vec::new();
|
|
for pair_trading_readiness_summary in summary.pair_trading_readiness_summaries {
|
|
pair_trading_readiness_summaries.push(
|
|
demo_pipeline2_map_local_pair_trading_readiness_diagnostic_summary(
|
|
pair_trading_readiness_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,
|
|
liquidity_event_count: summary.liquidity_event_count,
|
|
pool_lifecycle_event_count: summary.pool_lifecycle_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_gap_counter_semantics: summary.pair_gap_counter_semantics,
|
|
literal_pair_without_trade_count: summary.literal_pair_without_trade_count,
|
|
literal_pair_without_candle_count: summary.literal_pair_without_candle_count,
|
|
trade_materialized_pair_count: summary.trade_materialized_pair_count,
|
|
candle_materialized_pair_count: summary.candle_materialized_pair_count,
|
|
actionable_pair_count: summary.actionable_pair_count,
|
|
candle_bucket_timeframe_count: summary.candle_bucket_timeframe_count,
|
|
candles_are_bucketed: summary.candles_are_bucketed,
|
|
blocking_pair_without_trade_count: summary.blocking_pair_without_trade_count,
|
|
blocking_pair_without_candle_count: summary.blocking_pair_without_candle_count,
|
|
pair_without_trade_count: summary.pair_without_trade_count,
|
|
pair_without_candle_count: summary.pair_without_candle_count,
|
|
dex_summaries,
|
|
pair_summaries,
|
|
pair_actionability_summaries,
|
|
pair_trading_readiness_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,
|
|
pair_trading_readiness: summary.pair_trading_readiness,
|
|
quote_asset_class: summary.quote_asset_class,
|
|
trading_route_required: summary.trading_route_required,
|
|
}
|
|
}
|
|
|
|
fn demo_pipeline2_map_local_pair_actionability_diagnostic_summary(
|
|
summary: kb_lib::LocalPairActionabilityDiagnosticSummaryDto,
|
|
) -> DemoPipeline2LocalPairActionabilityDiagnosticSummary {
|
|
DemoPipeline2LocalPairActionabilityDiagnosticSummary {
|
|
pair_actionability: summary.pair_actionability,
|
|
pair_count: summary.pair_count,
|
|
decoded_event_count: summary.decoded_event_count,
|
|
decoded_trade_candidate_count: summary.decoded_trade_candidate_count,
|
|
actionable_trade_candidate_count: summary.actionable_trade_candidate_count,
|
|
failed_trade_candidate_count: summary.failed_trade_candidate_count,
|
|
trade_event_count: summary.trade_event_count,
|
|
pair_candle_count: summary.pair_candle_count,
|
|
}
|
|
}
|
|
|
|
fn demo_pipeline2_map_local_pair_trading_readiness_diagnostic_summary(
|
|
summary: kb_lib::LocalPairTradingReadinessDiagnosticSummaryDto,
|
|
) -> DemoPipeline2LocalPairTradingReadinessDiagnosticSummary {
|
|
DemoPipeline2LocalPairTradingReadinessDiagnosticSummary {
|
|
pair_trading_readiness: summary.pair_trading_readiness,
|
|
quote_asset_class: summary.quote_asset_class,
|
|
trading_route_required: summary.trading_route_required,
|
|
pair_count: summary.pair_count,
|
|
decoded_event_count: summary.decoded_event_count,
|
|
decoded_trade_candidate_count: summary.decoded_trade_candidate_count,
|
|
trade_event_count: summary.trade_event_count,
|
|
pair_candle_count: summary.pair_candle_count,
|
|
}
|
|
}
|
|
|
|
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(),
|
|
}
|
|
}
|