// file: kb-app-demo-desktop/src/demo_spl_token.rs // version: 9 //! 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_desktop/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_desktop/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_desktop/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_desktop/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, } /// Executes one checked SPL Token simulation or explicitly authorized Devnet submission. pub(crate) async fn demo_execution_spl_token_execute( app_handle: tauri::AppHandle, state: tauri::State<'_, crate::AppState>, request: crate::DemoExecutionSplTokenRequest, ) -> std::result::Result { let acquire_result = state.demo_execution_solana_core_running().compare_exchange( false, true, std::sync::atomic::Ordering::AcqRel, std::sync::atomic::Ordering::Acquire, ); if acquire_result.is_err() { return std::result::Result::Err("a Devnet execution is already running".to_string()); } let _run_guard = crate::DemoExecutionRunGuard { running: state.demo_execution_solana_core_running(), }; state .demo_execution_solana_core_cancel_requested() .store(false, std::sync::atomic::Ordering::Release); let profile = match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let http_pool = match kb_onchain_transport::HttpEndpointPool::from_profile(&profile) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), }; let store = match crate::connect_postgres_store(&profile).await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), }; if let std::result::Result::Err(error) = store.initialize_store_schema().await { return std::result::Result::Err(error.to_string()); } let amount_raw = request.amount_raw.trim().to_string(); let operation = kb_lib::ExSplClassicTokenOperation::Instruction { value: kb_lib::ExSplClassicTokenSingleOperation::TransferChecked { source: kb_lib::MdPubkey(request.source.trim().to_string()), mint: kb_lib::MdPubkey(request.mint.trim().to_string()), destination: kb_lib::MdPubkey(request.destination.trim().to_string()), authority: kb_lib::ExSplClassicTokenAuthority { authority: kb_lib::MdPubkey(request.authority.trim().to_string()), multisig_signers: std::vec::Vec::new(), }, amount: kb_lib::ExSplClassicTokenAmount(amount_raw.clone()), decimals: request.decimals, }, }; let mut pipeline_request = kb_pipeline_demo_scenarios::DevnetSplTokenExecutionRequest::new( format!("demo-spl-token-execution-{}", chrono::Utc::now().timestamp_micros()), operation, ); pipeline_request.submit = request.submit; pipeline_request.operator_confirmed = request.operator_confirmed; pipeline_request.post_validation_max_retries = 20; pipeline_request.force_post_validation_replay = request.force_post_validation_replay; let decoders: std::vec::Vec> = std::vec![std::sync::Arc::new(kb_lib::DcSplTokenDecoder)]; let materializers: std::vec::Vec> = std::vec![ std::sync::Arc::new(kb_lib::MtTokenAccountsMaterializer), std::sync::Arc::new(kb_lib::MtAdminMaterializer), std::sync::Arc::new(kb_lib::MtRiskMaterializer), ]; let observer = crate::DemoExecutionSolanaCoreObserver { app_handle, cancel_requested: state.demo_execution_solana_core_cancel_requested(), }; let workspace_root = crate::workspace_root_dir(); let summary = match kb_pipeline_demo_scenarios::execute_devnet_spl_token( &http_pool, &store, &profile, workspace_root.as_path(), &pipeline_request, decoders.as_slice(), materializers.as_slice(), &observer, ) .await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), }; return std::result::Result::Ok(summary_payload(summary, amount_raw, request.decimals)); } /// Loads a bounded, typed journal of committed classic SPL Token projections. pub(crate) async fn demo_spl_token_journal( state: tauri::State<'_, crate::AppState>, request: crate::DemoSplTokenJournalRequest, ) -> std::result::Result, std::string::String> { let validated = match validate_journal_request(request) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let profile = match crate::select_devnet_profile(state.app_config(), validated.profile_name.as_str()) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let store = match crate::connect_postgres_store(&profile).await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), }; if let std::result::Result::Err(error) = store.initialize_store_schema().await { return std::result::Result::Err(error.to_string()); } let needs_payload_filter = validated.mint.is_some() || validated.account.is_some() || validated.operation.is_some(); let query_limit = if needs_payload_filter { kb_store::MAX_MATERIALIZED_EVENT_QUERY_ROWS } else { validated.limit }; let filter = match kb_store::MaterializedEventFilter::new( std::option::Option::None, validated.family.clone(), validated.signature_contains.clone(), query_limit, ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), }; let rows = match kb_store::DecodePipelineStore::list_materialized_events(&store, &filter).await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), }; let mut output = std::vec::Vec::new(); for row in rows { if row.source_decoder_name != "spl.token" || !payload_matches(&row.payload_json, &validated) { continue; } output.push(journal_row(row)); if output.len() >= validated.limit as usize { break; } } return std::result::Result::Ok(output); } fn summary_payload( summary: kb_pipeline_demo_scenarios::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.processing_error_inputs == 0 && !replay.cancelled && replay.processors.iter().all(|processor| { return processor.failed == 0 && processor.processing_errors == 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, "processingErrorInputs": summary.processing_error_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, "processingErrors": processor.processing_errors, "materializedOutputs": processor.materialized_outputs, "materializationRefused": processor.materialization_refused }); }).collect::>() }); } fn validate_journal_request( request: crate::DemoSplTokenJournalRequest, ) -> std::result::Result { if request.limit == 0 || request.limit > kb_store::MAX_MATERIALIZED_EVENT_QUERY_ROWS { return std::result::Result::Err(format!( "journal limit must be between 1 and {}", kb_store::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); } 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; } fn journal_row(row: kb_store::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!(super::payload_matches(&payload, &request)); assert_eq!( payload.get("amountRaw").and_then(serde_json::Value::as_str), std::option::Option::Some("18446744073709551615") ); let row = super::journal_row(kb_store::MaterializedEventQueryRow { processor_name: "materializer.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!(super::validate_journal_request(request).is_err()); } }