// 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, /// Observed DEX codes found in diagnostics. pub observed_dex_codes: std::vec::Vec, /// 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, /// Issues produced by validation. pub issues: std::vec::Vec, } /// 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, /// Optional router program id, when this entry uses a distinct router. pub router_program_id: std::option::Option, /// 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, /// 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, /// 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, /// Diagnostics grouped by pair. pub pair_summaries: std::vec::Vec, /// Diagnostics grouped by decoded event kind. pub decoded_event_summaries: std::vec::Vec, /// Diagnostics grouped by decoded event classification. pub event_classification_summaries: std::vec::Vec, /// Missing trade events grouped by diagnostic reason. pub missing_trade_event_reason_summaries: std::vec::Vec, /// 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, /// Samples of decoded trade candidates without linked trade event. pub missing_trade_event_samples: std::vec::Vec, /// Samples of duplicated trade rows by decoded event id. pub duplicate_decoded_event_trade_samples: std::vec::Vec, /// Samples of multi-trade signature/pair groups. pub multi_trade_signature_pair_samples: std::vec::Vec, /// Samples of pairs without trade. pub pair_without_trade_samples: std::vec::Vec, /// Samples of pairs without candle. pub pair_without_candle_samples: std::vec::Vec, } /// 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, /// Quote mint. pub quote_mint: std::string::String, /// Quote symbol. pub quote_symbol: std::option::Option, /// Pair symbol. pub pair_symbol: std::option::Option, /// 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, } /// 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, /// Event lifecycle kind. pub event_lifecycle_kind: std::option::Option, /// Event actionability class. pub event_actionability: std::option::Option, /// Whether the event is useful but not trade/candle materialized. pub non_trade_useful: std::option::Option, /// Trade candidate flag. pub trade_candidate: std::option::Option, /// Candle candidate flag. pub candle_candidate: std::option::Option, /// 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, /// Quote token mint. pub quote_mint: std::string::String, /// Quote token symbol. pub quote_symbol: std::option::Option, /// Pair symbol. pub pair_symbol: std::option::Option, /// 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, /// 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, /// Transaction signature. pub signature: std::option::Option, /// Protocol name. pub protocol_name: std::string::String, /// Event kind. pub event_kind: std::string::String, /// Pool account. pub pool_account: std::option::Option, /// 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, /// Event kind. pub event_kind: std::option::Option, /// Pool account. pub pool_account: std::option::Option, /// Trade event count. #[ts(type = "number")] pub trade_event_count: i64, /// Trade event ids. pub trade_event_ids: std::option::Option, /// Signatures. pub signatures: std::option::Option, } /// 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, /// DEX code. pub dex_code: std::option::Option, /// 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, /// Decoded event ids. pub decoded_event_ids: std::option::Option, } /// 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, /// Quote mint. pub quote_mint: std::string::String, /// Quote symbol. pub quote_symbol: std::option::Option, /// Pair symbol. pub pair_symbol: std::option::Option, /// 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, /// Optional token name. pub name: std::option::Option, } /// 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, /// Optional DEX code. pub dex_code: std::option::Option, } /// 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, /// Optional DEX code. pub dex_code: std::option::Option, /// Optional local trade count. #[ts(type = "number | null")] pub trade_count: std::option::Option, /// Optional local last price. #[ts(type = "number | null")] pub last_price_quote_per_base: std::option::Option, } /// 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, /// Known pool list. pub pools: std::vec::Vec, /// Known pair list. pub pairs: std::vec::Vec, } /// 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, /// 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, /// 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 { 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 { 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 { 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, ) -> Result { 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 { 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 { 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 { 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 { 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, ) -> Result { 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::::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::::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::::new(); for pair in &pairs { pair_by_pool_id.insert(pair.pool_id, pair.clone()); } let mut pair_items = std::vec::Vec::::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::::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, refresh_missing_token_metadata: bool, token_metadata_limit: std::option::Option, ) -> Result { 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 { 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(), } }