// file: kb_app_demo/src/demo_spl_token.rs // version: 2 //! Thin Tauri adapter for classic SPL Token execution and materialized journals. use ts_rs::TS; // rust-rules: derive-import /// Request for one checked classic SPL Token transfer on Devnet. #[derive(Clone, Debug, serde::Deserialize, TS)] #[serde(rename_all = "camelCase")] #[ts( export, export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_token/DemoExecutionSplTokenRequest.ts" )] pub(crate) struct DemoExecutionSplTokenRequest { /// Selected Devnet profile. pub(crate) profile_name: std::string::String, /// Source token account. pub(crate) source: std::string::String, /// Exact mint account carried by `TransferChecked`. pub(crate) mint: std::string::String, /// Destination token account. pub(crate) destination: std::string::String, /// Simple authority resolved by the selected profile wallet. pub(crate) authority: std::string::String, /// Exact raw amount represented as a decimal string. pub(crate) amount_raw: std::string::String, /// Expected mint decimals carried by the wire. pub(crate) decimals: u8, /// Whether the transaction should be signed and submitted after simulation. pub(crate) submit: bool, /// Explicit operator confirmation for signed submission. pub(crate) operator_confirmed: bool, /// Whether post-execution core and decode outputs should be force-replayed. pub(crate) force_post_validation_replay: bool, } /// UI-safe result of one checked SPL Token transfer orchestration. #[derive(Clone, Debug, serde::Serialize, TS)] #[serde(rename_all = "camelCase")] #[ts( export, export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_token/DemoExecutionSplTokenSummaryPayload.ts" )] pub(crate) struct DemoExecutionSplTokenSummaryPayload { /// Stable operation code. pub(crate) operation: std::string::String, /// Profile used by the orchestration. pub(crate) profile_name: std::string::String, /// Classified cluster code. pub(crate) cluster: std::string::String, /// Genesis hash returned by the endpoint. pub(crate) genesis_hash: std::string::String, /// Source wallet public key. pub(crate) wallet_public_key: std::string::String, /// Exact wallet balance rendered without JSON integer precision loss. pub(crate) balance_lamports: std::string::String, /// Estimated fee rendered without JSON integer precision loss. pub(crate) fee_lamports: std::option::Option, /// Exact raw token amount. pub(crate) amount_raw: std::string::String, /// Expected mint decimals. pub(crate) decimals: u8, /// Aggregate stateful preflight status. pub(crate) readiness_status: std::string::String, /// Whether exact simulation succeeded. pub(crate) simulation_success: bool, /// Runtime simulation error when present. pub(crate) simulation_error: std::option::Option, /// Submitted transaction signature. pub(crate) transaction_signature: std::option::Option, /// Terminal confirmation status. pub(crate) confirmation_status: std::option::Option, /// Whether canonical hydration was validated. pub(crate) canonical_inserted: bool, /// Whether core extraction was validated. pub(crate) core_extracted: bool, /// Whether SPL Token decode replay was validated. pub(crate) decode_replayed: bool, /// Number of materialized rows for the submitted instruction. pub(crate) materialization_count: u32, /// Whether the second replay produced no failure or new output. pub(crate) idempotence_validated: bool, /// Pretty JSON representation of the exact execution plan. pub(crate) plan_json: std::string::String, /// Pretty JSON representation of stateful readiness. pub(crate) readiness_json: std::string::String, /// Pretty JSON representation of the exact simulation result. pub(crate) simulation_json: std::string::String, /// Pretty JSON representation of confirmation and replay diagnostics. pub(crate) diagnostics_json: std::string::String, } /// Bounded exact filters for the classic SPL Token materialized journal. #[derive(Clone, Debug, serde::Deserialize, TS)] #[serde(rename_all = "camelCase")] #[ts( export, export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_token/DemoSplTokenJournalRequest.ts" )] pub(crate) struct DemoSplTokenJournalRequest { /// Devnet profile whose PostgreSQL store is queried. pub(crate) profile_name: std::string::String, /// Optional partial signature handled by the bounded store query. pub(crate) signature_contains: std::option::Option, /// Optional exact mint account. pub(crate) mint: std::option::Option, /// Optional exact account occurring in the ordered account list. pub(crate) account: std::option::Option, /// Optional exact materialized family. pub(crate) family: std::option::Option, /// Optional exact operation code without the `spl_token.` prefix. pub(crate) operation: std::option::Option, /// Maximum number of rows returned after typed filtering. pub(crate) limit: u32, } /// UI-safe materialized classic SPL Token journal row. #[derive(Clone, Debug, serde::Serialize, TS)] #[serde(rename_all = "camelCase")] #[ts( export, export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_token/DemoSplTokenJournalRow.ts" )] pub(crate) struct DemoSplTokenJournalRow { /// Materializer processor name. pub(crate) processor_name: std::string::String, /// Materializer processor version. pub(crate) processor_version: std::string::String, /// Stable processor-owned output key. pub(crate) output_key: std::string::String, /// Transaction signature. pub(crate) signature: std::string::String, /// Decimal slot rendered as text to preserve JSON precision. pub(crate) slot: std::string::String, /// Materialized family code. pub(crate) family: std::string::String, /// Exact operation when present. pub(crate) operation: std::option::Option, /// Exact mint account when explicitly available. pub(crate) mint: std::option::Option, /// Exact raw amount when carried by the materialized fact. pub(crate) amount_raw: std::option::Option, /// Stable outer or inner instruction path when present. pub(crate) instruction_path: std::option::Option, /// Ordered account keys preserved by the decoder. pub(crate) account_keys: std::vec::Vec, /// Complete bounded typed payload rendered as JSON text. pub(crate) payload_json: std::string::String, /// Database creation timestamp. pub(crate) created_at: std::string::String, /// Database replacement timestamp. pub(crate) updated_at: std::string::String, } pub(crate) fn demo_spl_token_summary_payload( summary: kb_pipeline::DevnetSplTokenExecutionSummary, amount_raw: std::string::String, decimals: u8, ) -> crate::DemoExecutionSplTokenSummaryPayload { let plan_json = crate::pretty_json(&summary.plan); let readiness_json = crate::pretty_json(&summary.stateful_readiness); let simulation_json = crate::pretty_json(&summary.simulation); let transaction_signature = summary.send_result.as_ref().map(|value| return value.signature.0.clone()); let confirmation_status = summary.confirmation.as_ref().map(|value| { return crate::confirmation_status_code(value.status); }); let post_execution = summary.post_execution.as_ref(); let canonical_inserted = post_execution.is_some_and(|value| return value.canonical_inserted); let core_extracted = post_execution.is_some_and(|value| return value.core_extracted); let decode_replayed = post_execution.is_some_and(|value| return value.decode_replayed); let idempotence_validated = summary.idempotence_replay.as_ref().is_some_and(|replay| { return replay.failed_inputs == 0 && !replay.cancelled && replay.processors.iter().all(|processor| { return processor.failed == 0 && processor.materialized_outputs == 0 && processor.materialization_refused == 0; }); }); let materialization_count = match u32::try_from(summary.materializations.len()) { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => u32::MAX, }; let diagnostics = serde_json::json!({ "confirmation": summary.confirmation, "postExecution": summary.post_execution, "backfill": summary.backfill.as_ref().map(|value| { return serde_json::json!({ "canonicalInserted": value.canonical_inserted, "canonicalSkipped": value.canonical_skipped, "existingSkipped": value.existing_skipped, "missing": value.missing, "failed": value.failed }); }), "coreExtraction": summary.core_extraction.as_ref().map(|value| { return serde_json::json!({ "selected": value.selected, "extracted": value.extracted, "skipped": value.skipped, "failed": value.failed, "cancelled": value.cancelled }); }), "decodeReplay": summary.decode_replay.as_ref().map(|value| { return replay_diagnostics(value); }), "idempotenceReplay": summary.idempotence_replay.as_ref().map(|value| { return replay_diagnostics(value); }), "materializations": summary.materializations }); return crate::DemoExecutionSplTokenSummaryPayload { operation: summary.stateful_readiness.operation_code, profile_name: summary.profile_name, cluster: crate::execution_cluster_code(summary.cluster), genesis_hash: summary.genesis_hash, wallet_public_key: summary.wallet.public_key, balance_lamports: summary.balance_lamports.to_string(), fee_lamports: summary.fee.fee_lamports.map(|value| return value.to_string()), amount_raw, decimals, readiness_status: match summary.stateful_readiness.status { kb_pipeline::SplTokenStatefulReadinessStatus::Ready => "ready".to_string(), kb_pipeline::SplTokenStatefulReadinessStatus::Blocked => "blocked".to_string(), }, simulation_success: summary.simulation.success, simulation_error: summary.simulation.error, transaction_signature, confirmation_status, canonical_inserted, core_extracted, decode_replayed, materialization_count, idempotence_validated, plan_json, readiness_json, simulation_json, diagnostics_json: crate::pretty_json(&diagnostics), }; } fn replay_diagnostics(summary: &kb_pipeline::DecodeReplaySummary) -> serde_json::Value { return serde_json::json!({ "campaignId": summary.campaign_id, "selected": summary.selected, "completed": summary.completed, "failedInputs": summary.failed_inputs, "cancelled": summary.cancelled, "processors": summary.processors.iter().map(|processor| { return serde_json::json!({ "name": processor.processor_name, "version": processor.processor_version, "decoded": processor.decoded, "skipped": processor.skipped, "failed": processor.failed, "materializedOutputs": processor.materialized_outputs, "materializationRefused": processor.materialization_refused }); }).collect::>() }); } pub(crate) fn validate_journal_request( request: crate::DemoSplTokenJournalRequest, ) -> std::result::Result { if request.limit == 0 || request.limit > kb_store_core::MAX_MATERIALIZED_EVENT_QUERY_ROWS { return std::result::Result::Err(format!( "journal limit must be between 1 and {}", kb_store_core::MAX_MATERIALIZED_EVENT_QUERY_ROWS )); } if request.profile_name.trim().is_empty() { return std::result::Result::Err("journal profile name must not be empty".to_string()); } let signature_contains = match bounded_optional(request.signature_contains, "signature filter", 128) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let mint = match bounded_optional(request.mint, "mint filter", 64) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let account = match bounded_optional(request.account, "account filter", 64) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let operation = match bounded_optional(request.operation, "operation filter", 64) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let family = match bounded_optional(request.family, "family filter", 32) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if family.as_ref().is_some_and(|value| { return !matches!(value.as_str(), "token_account" | "admin" | "risk"); }) { return std::result::Result::Err( "journal family must be token_account, admin or risk".to_string(), ); } return std::result::Result::Ok(crate::DemoSplTokenJournalRequest { profile_name: request.profile_name.trim().to_string(), signature_contains, mint, account, family, operation, limit: request.limit, }); } fn bounded_optional( value: std::option::Option, label: &str, maximum_length: usize, ) -> std::result::Result, std::string::String> { let trimmed = value.map(|text| return text.trim().to_string()); let trimmed = trimmed.filter(|text| return !text.is_empty()); if trimmed.as_ref().is_some_and(|text| return text.len() > maximum_length) { return std::result::Result::Err(format!("{label} must not exceed {maximum_length} bytes")); } return std::result::Result::Ok(trimmed); } pub(crate) fn payload_matches( payload: &serde_json::Value, request: &crate::DemoSplTokenJournalRequest, ) -> bool { if request.operation.as_ref().is_some_and(|expected| { return payload.get("operation").and_then(serde_json::Value::as_str) != std::option::Option::Some(expected.as_str()); }) { return false; } if request.mint.as_ref().is_some_and(|expected| { return payload_mint(payload).as_deref() != std::option::Option::Some(expected.as_str()); }) { return false; } if request.account.as_ref().is_some_and(|expected| { return !payload_account_keys(payload).iter().any(|value| return value == expected); }) { return false; } return true; } pub(crate) fn demo_spl_token_journal_row( row: kb_store_core::MaterializedEventQueryRow, ) -> DemoSplTokenJournalRow { let operation = row .payload_json .get("operation") .and_then(serde_json::Value::as_str) .map(std::string::ToString::to_string); let amount_raw = row .payload_json .get("amountRaw") .or_else(|| { return row .payload_json .get("parameters") .and_then(|value| return value.get("amountRaw")); }) .and_then(serde_json::Value::as_str) .map(std::string::ToString::to_string); let instruction_path = row .payload_json .get("instructionPath") .and_then(serde_json::Value::as_str) .map(std::string::ToString::to_string); let payload_json = crate::pretty_json(&row.payload_json); return DemoSplTokenJournalRow { processor_name: row.processor_name, processor_version: row.processor_version, output_key: row.output_key, signature: row.signature, slot: row.slot.to_string(), family: row.materialized_family, operation, mint: payload_mint(&row.payload_json), amount_raw, instruction_path, account_keys: payload_account_keys(&row.payload_json), payload_json, created_at: row.created_at, updated_at: row.updated_at, }; } fn payload_mint(payload: &serde_json::Value) -> std::option::Option { let direct = payload.get("mint").and_then(serde_json::Value::as_str); if let std::option::Option::Some(value) = direct { return std::option::Option::Some(value.to_string()); } return payload.get("accounts").and_then(serde_json::Value::as_array).and_then(|rows| { return rows.iter().find_map(|row| { if row.get("role").and_then(serde_json::Value::as_str) != std::option::Option::Some("mint") { return std::option::Option::None; } return row .get("accountKey") .and_then(serde_json::Value::as_str) .map(std::string::ToString::to_string); }); }); } fn payload_account_keys(payload: &serde_json::Value) -> std::vec::Vec { let values = payload.get("accounts").and_then(serde_json::Value::as_array).map(|rows| { return rows .iter() .filter_map(|row| { return row .get("accountKey") .and_then(serde_json::Value::as_str) .map(std::string::ToString::to_string); }) .collect(); }); return match values { std::option::Option::Some(values) => values, std::option::Option::None => std::vec::Vec::new(), }; } #[cfg(test)] mod tests { #[test] fn journal_filters_are_exact_bounded_and_preserve_raw_amounts() { let request = crate::DemoSplTokenJournalRequest { profile_name: "local_devnet".to_string(), signature_contains: std::option::Option::None, mint: std::option::Option::Some("mint111".to_string()), account: std::option::Option::Some("source111".to_string()), family: std::option::Option::Some("token_account".to_string()), operation: std::option::Option::Some("transfer_checked".to_string()), limit: 25, }; let payload = serde_json::json!({ "operation": "transfer_checked", "mint": "mint111", "amountRaw": "18446744073709551615", "accounts": [ {"role": "source", "accountKey": "source111"}, {"role": "mint", "accountKey": "mint111"} ] }); assert!(crate::payload_matches(&payload, &request)); assert_eq!( payload.get("amountRaw").and_then(serde_json::Value::as_str), std::option::Option::Some("18446744073709551615") ); let row = crate::demo_spl_token_journal_row(kb_store_core::MaterializedEventQueryRow { processor_name: "spl_token_accounts".to_string(), processor_version: "0.4.4".to_string(), input_key: "input".to_string(), output_key: "output".to_string(), source_event_key: "event".to_string(), source_decoder_name: "spl_token".to_string(), source_decoder_version: "0.4.4".to_string(), signature: "signature".to_string(), slot: u64::MAX, materialized_family: "token_account".to_string(), payload_json: payload, created_at: "created".to_string(), updated_at: "updated".to_string(), }); assert_eq!(row.slot, u64::MAX.to_string()); assert_eq!(row.amount_raw, std::option::Option::Some(u64::MAX.to_string())); } #[test] fn journal_rejects_unbounded_or_unknown_family_requests() { let request = crate::DemoSplTokenJournalRequest { profile_name: "local_devnet".to_string(), signature_contains: std::option::Option::None, mint: std::option::Option::None, account: std::option::Option::None, family: std::option::Option::Some("fee".to_string()), operation: std::option::Option::None, limit: 501, }; assert!(crate::validate_journal_request(request).is_err()); } }