// file: kb-pipeline-demo-scenarios/src/solana_token_2022_devnet_execution.rs // version: 5 //! Devnet Token-2022 execution with stateful and canonical post-validation. /// Complete request for one Devnet Token-2022 execution. #[derive(Clone, Debug, Eq, PartialEq)] pub struct DevnetSplToken2022ExecutionRequest { /// Stable caller-provided execution identifier. pub intent_id: std::string::String, /// Endpoint role used for state, balance, blockhash, fee and hydration calls. pub query_role: std::string::String, /// Endpoint role used for simulation, submission and confirmation polling. pub transaction_role: std::string::String, /// Exact typed Token-2022 operation. pub operation: kb_lib::ExSplToken2022Operation, /// Explicitly authorizes signing and submission after successful simulation. pub submit: bool, /// Explicit operator confirmation required by the active profile. pub operator_confirmed: bool, /// Number of `getTransaction` retries after the first hydration attempt. pub post_validation_max_retries: u32, /// Replaces existing core and decode outputs for the submitted signature. pub force_post_validation_replay: bool, } impl crate::DevnetSplToken2022ExecutionRequest { /// Creates a conservative simulation-only request. pub fn new( intent_id: impl std::convert::Into, operation: kb_lib::ExSplToken2022Operation, ) -> Self { return Self { intent_id: intent_id.into(), query_role: "http_queries".to_string(), transaction_role: "http_transactions".to_string(), operation, submit: false, operator_confirmed: false, post_validation_max_retries: 10, force_post_validation_replay: false, }; } /// Validates request-local bounds independently from one profile. pub fn validate(&self) -> kb_core::Result<()> { if self.intent_id.trim().is_empty() { return std::result::Result::Err(kb_core::Error::config( "Devnet Token-2022 execution intent id must not be empty", )); } if self.query_role.trim().is_empty() || self.transaction_role.trim().is_empty() { return std::result::Result::Err(kb_core::Error::config( "Devnet Token-2022 execution endpoint roles must not be empty", )); } if self.post_validation_max_retries > 20 { return std::result::Result::Err(kb_core::Error::config( "post-execution getTransaction retries must not exceed 20", )); } return std::result::Result::Ok(()); } } /// Complete result of one Devnet Token-2022 execution. #[derive(Clone, Debug, PartialEq)] pub struct DevnetSplToken2022ExecutionSummary { /// Profile used by the orchestration. pub profile_name: std::string::String, /// Exact classified cluster. pub cluster: kb_lib::ExApiExecutionCluster, /// Genesis hash returned by the selected endpoint. pub genesis_hash: std::string::String, /// Non-secret persistent wallet description. pub wallet: kb_wallet::WalletSummary, /// Wallet balance observed before planning. pub balance_lamports: u64, /// Stateful readiness report produced before plan simulation. pub stateful_preflight: kb_pipeline::Token2022PreflightReport, /// Exact prepared Token plan. pub plan: kb_lib::ExApiPreparedExecutionPlan, /// Recent blockhash used by the exact transaction. pub latest_blockhash: kb_onchain_transport::LatestBlockhashResult, /// Fee estimate for the exact compiled message. pub fee: kb_onchain_transport::FeeForMessageResult, /// Exact simulation result bound to the compiled message. pub simulation: kb_lib::ExApiExecutionSimulationResult, /// Submission result when explicitly authorized. pub send_result: std::option::Option, /// Confirmation result when submitted. pub confirmation: std::option::Option, /// Canonical hydration result for the exact signature. pub backfill: std::option::Option, /// Core extraction result for the exact signature. pub core_extraction: std::option::Option, /// First Token decode and materialization replay. pub decode_replay: std::option::Option, /// Second replay proving idempotence for the same decoder version and input. pub idempotence_replay: std::option::Option, /// Exact materialized rows produced for the submitted Token transaction. pub materializations: std::vec::Vec, /// Aggregated post-execution validation diagnostic. pub post_execution: std::option::Option, } struct PreparedToken2022Execution { wallet: kb_wallet::TemporaryWallet, unsigned: kb_lib::ExSolanaUnsignedTransaction, evidence: kb_lib::ExSolanaSimulationEvidence, summary: crate::DevnetSplToken2022ExecutionSummary, } /// Simulates one Devnet Token-2022 operation after stateful preflight. pub async fn simulate_devnet_spl_token_2022( http_pool: &kb_onchain_transport::HttpEndpointPool, profile: &kb_config::ProfileConfig, workspace_root: &std::path::Path, request: &crate::DevnetSplToken2022ExecutionRequest, observer: &O, ) -> kb_core::Result where O: crate::SolanaExecutionObserver, { if request.submit { return std::result::Result::Err(kb_core::Error::config( "simulate_devnet_spl_token_2022 requires submit=false", )); } let prepared = match prepare_execution(http_pool, profile, workspace_root, request, observer).await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; return std::result::Result::Ok(prepared.summary); } /// Executes one Devnet Token-2022 simulation or authorized submission. #[allow(clippy::too_many_arguments)] pub async fn execute_devnet_spl_token_2022( http_pool: &kb_onchain_transport::HttpEndpointPool, store: &S, profile: &kb_config::ProfileConfig, workspace_root: &std::path::Path, request: &crate::DevnetSplToken2022ExecutionRequest, decoders: &[std::sync::Arc], materializers: &[std::sync::Arc], observer: &O, ) -> kb_core::Result where S: kb_store::RawTransactionStore + kb_store::CoreExtractionStore + kb_store::DecodePipelineStore + Sync, O: crate::SolanaExecutionObserver, { let prepared = match prepare_execution(http_pool, profile, workspace_root, request, observer).await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if !request.submit { return std::result::Result::Ok(prepared.summary); } if !prepared.summary.simulation.success { return std::result::Result::Err(kb_core::Error::new( "execution_simulation_failed", crate::simulation_failure_message(&prepared.summary.simulation), )); } if let std::result::Result::Err(error) = validate_profile_wallet_signers( prepared.unsigned.required_signer_pubkeys(), prepared.summary.wallet.public_key.as_str(), ) { return std::result::Result::Err(error); } let send_evaluation = match kb_lib::ExSafetyChecker .evaluate_send(&prepared.summary.plan, &prepared.summary.simulation) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if send_evaluation.decision == kb_lib::ExSafetyDecision::Deny { return std::result::Result::Err(kb_core::Error::new( "execution_send_denied", crate::violation_message(send_evaluation.violations.as_slice()), )); } let signed = match prepared .unsigned .sign_after_simulation(&prepared.evidence, &[prepared.wallet.as_signer()]) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if let std::result::Result::Err(error) = signed.verify_signatures() { return std::result::Result::Err(error); } let signature = signed.primary_signature().clone(); let mut summary = prepared.summary; let mut diagnostic = kb_lib::ExApiPostExecutionDiagnostic { signature: signature.clone(), canonical_inserted: false, core_extracted: false, decode_replayed: false, materialized: false, diagnostics: std::vec::Vec::new(), }; let send_config = match kb_onchain_transport::SendTransactionConfig::from_execution_config( &profile.execution, std::option::Option::Some(summary.latest_blockhash.context.slot), ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let sent = match http_pool .send_transaction_for_role( request.transaction_role.as_str(), signed.transaction_base64().as_str(), &signature, &send_config, ) .await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { diagnostic.diagnostics.push(format!("Token-2022 submission failed: {error}")); summary.post_execution = std::option::Option::Some(diagnostic); return std::result::Result::Ok(summary); }, }; summary.send_result = std::option::Option::Some(sent.to_execution_result(kb_lib::ExApiExecutionCluster::Devnet)); let confirmation_config = match kb_onchain_transport::ConfirmTransactionConfig::from_execution_config( &profile.execution, std::option::Option::Some(summary.latest_blockhash.last_valid_block_height), std::option::Option::Some(summary.latest_blockhash.context.slot), ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let confirmation = match http_pool .confirm_transaction_for_roles( request.transaction_role.as_str(), request.query_role.as_str(), kb_lib::ExApiExecutionCluster::Devnet, &signature, &confirmation_config, ) .await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { diagnostic.diagnostics.push(format!("Token-2022 confirmation failed: {error}")); summary.post_execution = std::option::Option::Some(diagnostic); return std::result::Result::Ok(summary); }, }; let confirmation_status = confirmation.status; summary.confirmation = std::option::Option::Some(confirmation); if !matches!( confirmation_status, kb_lib::ExApiExecutionConfirmationStatus::Confirmed | kb_lib::ExApiExecutionConfirmationStatus::Finalized ) { diagnostic.diagnostics.push(format!( "Token-2022 post-validation stopped at confirmation status {confirmation_status:?}" )); summary.post_execution = std::option::Option::Some(diagnostic); return std::result::Result::Ok(summary); } let backfill = match crate::hydrate_signature( http_pool, store, profile, request.query_role.as_str(), request.post_validation_max_retries, observer, &signature, ) .await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { diagnostic.diagnostics.push(format!("Token-2022 hydration failed: {error}")); summary.post_execution = std::option::Option::Some(diagnostic); return std::result::Result::Ok(summary); }, }; diagnostic.canonical_inserted = crate::canonical_available(&backfill); summary.backfill = std::option::Option::Some(backfill); if !diagnostic.canonical_inserted { diagnostic.diagnostics.push( "confirmed Token-2022 transaction was unavailable for canonical hydration".to_string(), ); summary.post_execution = std::option::Option::Some(diagnostic); return std::result::Result::Ok(summary); } let extraction = match kb_pipeline::execute_core_extraction( store, &kb_pipeline::CoreExtractionRequest { source: kb_pipeline::CoreExtractionSource::Signatures(std::vec![signature.0.clone()]), limit: 1, max_concurrent_extractions: 1, force_replay: request.force_post_validation_replay, }, observer, ) .await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { diagnostic .diagnostics .push(format!("Token-2022 core extraction failed: {error}")); summary.post_execution = std::option::Option::Some(diagnostic); return std::result::Result::Ok(summary); }, }; diagnostic.core_extracted = extraction.failed == 0 && !extraction.cancelled && extraction.selected == 1 && extraction.extracted.saturating_add(extraction.skipped) >= 1; summary.core_extraction = std::option::Option::Some(extraction); if !diagnostic.core_extracted { diagnostic .diagnostics .push("Token-2022 core extraction did not complete".to_string()); summary.post_execution = std::option::Option::Some(diagnostic); return std::result::Result::Ok(summary); } let first_replay = match crate::replay_program( store, &signature, false, request.force_post_validation_replay, &[kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID], decoders, materializers, observer, ) .await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { diagnostic.diagnostics.push(format!("Token-2022 decode replay failed: {error}")); summary.post_execution = std::option::Option::Some(diagnostic); return std::result::Result::Ok(summary); }, }; diagnostic.decode_replayed = crate::decode_completed(&first_replay); summary.decode_replay = std::option::Option::Some(first_replay); let filter = match kb_store::MaterializedEventFilter::new( std::option::Option::None, std::option::Option::None, std::option::Option::Some(signature.0.clone()), 64, ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let rows = match kb_store::DecodePipelineStore::list_materialized_events(store, &filter).await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { diagnostic .diagnostics .push(format!("Token-2022 materialization query failed: {error}")); summary.post_execution = std::option::Option::Some(diagnostic); return std::result::Result::Ok(summary); }, }; summary.materializations = rows .into_iter() .filter(|row| return row.source_decoder_name == "spl.token_2022") .collect(); diagnostic.materialized = !summary.plan.policy.post_execution_validation.materialization_required || !summary.materializations.is_empty(); let second_replay = match crate::replay_program( store, &signature, true, request.force_post_validation_replay, &[kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID], decoders, materializers, observer, ) .await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { diagnostic .diagnostics .push(format!("Token-2022 idempotence replay failed: {error}")); summary.post_execution = std::option::Option::Some(diagnostic); return std::result::Result::Ok(summary); }, }; let idempotent = second_replay.failed_inputs == 0 && second_replay.processing_error_inputs == 0 && second_replay.processors.iter().all(|processor| { return processor.failed == 0 && processor.processing_errors == 0 && processor.materialized_outputs == 0 && processor.materialization_refused == 0; }); summary.idempotence_replay = std::option::Option::Some(second_replay); if !idempotent { diagnostic .diagnostics .push("second Token-2022 replay did not prove a clean idempotent skip".to_string()); } else if diagnostic.canonical_inserted && diagnostic.core_extracted && diagnostic.decode_replayed && diagnostic.materialized { diagnostic.diagnostics.push( "Token-2022 completed canonical hydration, core extraction, decode, materialization and idempotence validation" .to_string(), ); } summary.post_execution = std::option::Option::Some(diagnostic); return std::result::Result::Ok(summary); } async fn prepare_execution( http_pool: &kb_onchain_transport::HttpEndpointPool, profile: &kb_config::ProfileConfig, workspace_root: &std::path::Path, request: &crate::DevnetSplToken2022ExecutionRequest, observer: &O, ) -> kb_core::Result where O: crate::SolanaExecutionObserver, { if let std::result::Result::Err(error) = request.validate() { return std::result::Result::Err(error); } if let std::result::Result::Err(error) = validate_profile(profile, request) { return std::result::Result::Err(error); } if let std::result::Result::Err(error) = crate::ensure_not_cancelled(observer, "token_validate") { return std::result::Result::Err(error); } let genesis = match http_pool.get_genesis_hash_for_role(request.query_role.as_str()).await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if genesis.classified_cluster != std::option::Option::Some(kb_lib::ExApiExecutionCluster::Devnet) { return std::result::Result::Err(kb_core::Error::new( "execution_cluster_mismatch", format!( "expected Devnet genesis hash but endpoint returned {} classified as {:?}", genesis.genesis_hash, genesis.classified_cluster ), )); } let preflight_request = match preflight_request(request) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let readiness = match kb_pipeline::inspect_token_2022_preflight(http_pool, &preflight_request).await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let wallet = match crate::load_profile_wallet(profile, workspace_root).await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let wallet_summary = wallet.summary(); let fee_payer = kb_lib::MdPubkey(wallet_summary.public_key.clone()); let balance = match http_pool .get_balance_for_role( request.query_role.as_str(), &fee_payer, &kb_onchain_transport::GetBalanceConfig::confirmed(), ) .await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if balance.lamports < profile.execution.max_fee_lamports { return std::result::Result::Err(kb_core::Error::new( "execution_balance_insufficient", format!( "Devnet wallet balance {} is below the configured fee ceiling {}", balance.lamports, profile.execution.max_fee_lamports ), )); } let plan = match build_plan(profile, request, fee_payer.clone()) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let plan_evaluation = match kb_lib::ExSafetyChecker.evaluate_prepared_plan(&plan) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if plan_evaluation.decision == kb_lib::ExSafetyDecision::Deny { return std::result::Result::Err(kb_core::Error::new( "execution_plan_denied", crate::violation_message(plan_evaluation.violations.as_slice()), )); } let latest_blockhash = match http_pool .get_latest_blockhash_for_role( request.query_role.as_str(), &kb_onchain_transport::GetLatestBlockhashConfig::confirmed(), ) .await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let unsigned = match kb_lib::executor_solana_build_legacy_transaction( &plan, latest_blockhash.blockhash.as_str(), ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let fee = match http_pool .get_fee_for_message_for_role( request.query_role.as_str(), unsigned.message_base64().as_str(), &kb_onchain_transport::GetFeeForMessageConfig::new( kb_onchain_transport::RpcCommitmentLevel::Confirmed, std::option::Option::Some(latest_blockhash.context.slot), ), ) .await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if fee.fee_lamports.is_none() { return std::result::Result::Err(kb_core::Error::new( "execution_fee_unavailable", "getFeeForMessage returned null for the selected recent blockhash", )); } let unsigned_base64 = match unsigned.transaction_base64() { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let simulation_config = match kb_onchain_transport::SimulateTransactionConfig::new( kb_onchain_transport::RpcCommitmentLevel::Confirmed, false, false, std::option::Option::Some(latest_blockhash.context.slot), true, std::option::Option::None, ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; crate::emit( observer, crate::SolanaExecutionProgressLevel::Info, "spl_token_2022_simulation", format!("simulating exact Token-2022 message {}", unsigned.message_hash()), std::option::Option::None, ); let simulation_rpc = match http_pool .simulate_transaction_for_role( request.transaction_role.as_str(), unsigned_base64.as_str(), &simulation_config, ) .await { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let simulation = simulation_rpc.to_execution_result( kb_lib::ExApiExecutionCluster::Devnet, kb_lib::ExApiExecutionBlockhashKind::Latest, std::option::Option::Some( simulation_rpc.context.slot.saturating_sub(latest_blockhash.context.slot), ), std::option::Option::None, std::option::Option::None, std::option::Option::Some(&fee), ); let evidence = unsigned.bind_simulation(simulation.clone()); let summary = crate::DevnetSplToken2022ExecutionSummary { profile_name: profile.name.clone(), cluster: kb_lib::ExApiExecutionCluster::Devnet, genesis_hash: genesis.genesis_hash, wallet: wallet_summary, balance_lamports: balance.lamports, stateful_preflight: readiness, plan, latest_blockhash, fee, simulation, send_result: std::option::Option::None, confirmation: std::option::Option::None, backfill: std::option::Option::None, core_extraction: std::option::Option::None, decode_replay: std::option::Option::None, idempotence_replay: std::option::Option::None, materializations: std::vec::Vec::new(), post_execution: std::option::Option::None, }; return std::result::Result::Ok(PreparedToken2022Execution { wallet, unsigned, evidence, summary, }); } fn preflight_request( request: &crate::DevnetSplToken2022ExecutionRequest, ) -> kb_core::Result { let operation = match &request.operation { kb_lib::ExSplToken2022Operation::Instruction { value } => value.as_ref(), kb_lib::ExSplToken2022Operation::Batch { instructions: _ } => { return std::result::Result::Err(kb_core::Error::new( "execution_spl_token_2022_batch_not_supported", "Devnet Token-2022 validation scenarios require one non-batch operation", )); }, }; let mut requirements = std::vec::Vec::new(); match operation { kb_lib::ExSplTokenSingleOperation::MintToChecked { mint, destination, authority, amount: _, decimals, } => { requirements.push(mint_requirement("mint", mint, std::option::Option::Some(*decimals))); requirements.push(account_requirement( "destination", destination, mint, std::option::Option::None, )); if let std::result::Result::Err(error) = validate_single_authority(authority) { return std::result::Result::Err(error); } }, kb_lib::ExSplTokenSingleOperation::TransferChecked { source, mint, destination, authority, amount: _, decimals, } => { requirements.push(account_requirement( "source", source, mint, std::option::Option::Some(authority.authority.clone()), )); requirements.push(mint_requirement("mint", mint, std::option::Option::Some(*decimals))); requirements.push(account_requirement( "destination", destination, mint, std::option::Option::None, )); if let std::result::Result::Err(error) = validate_single_authority(authority) { return std::result::Result::Err(error); } }, kb_lib::ExSplTokenSingleOperation::ApproveChecked { source, mint, delegate: _, authority, amount: _, decimals, } => { requirements.push(account_requirement( "source", source, mint, std::option::Option::Some(authority.authority.clone()), )); requirements.push(mint_requirement("mint", mint, std::option::Option::Some(*decimals))); if let std::result::Result::Err(error) = validate_single_authority(authority) { return std::result::Result::Err(error); } }, kb_lib::ExSplTokenSingleOperation::Revoke { source, authority } => { requirements.push(account_requirement( "source", source, &kb_lib::MdPubkey(std::string::String::new()), std::option::Option::Some(authority.authority.clone()), )); requirements[0].expected_mint = std::option::Option::None; if let std::result::Result::Err(error) = validate_single_authority(authority) { return std::result::Result::Err(error); } }, kb_lib::ExSplTokenSingleOperation::BurnChecked { source, mint, authority, amount: _, decimals, } => { requirements.push(account_requirement( "source", source, mint, std::option::Option::Some(authority.authority.clone()), )); requirements.push(mint_requirement("mint", mint, std::option::Option::Some(*decimals))); if let std::result::Result::Err(error) = validate_single_authority(authority) { return std::result::Result::Err(error); } }, kb_lib::ExSplTokenSingleOperation::FreezeAccount { account, mint, authority } | kb_lib::ExSplTokenSingleOperation::ThawAccount { account, mint, authority } => { requirements.push(account_requirement( "account", account, mint, std::option::Option::None, )); requirements.push(mint_requirement("mint", mint, std::option::Option::None)); if let std::result::Result::Err(error) = validate_single_authority(authority) { return std::result::Result::Err(error); } }, kb_lib::ExSplTokenSingleOperation::CloseAccount { account, destination: _, authority } => { requirements.push(account_requirement( "account", account, &kb_lib::MdPubkey(std::string::String::new()), std::option::Option::Some(authority.authority.clone()), )); requirements[0].expected_mint = std::option::Option::None; if let std::result::Result::Err(error) = validate_single_authority(authority) { return std::result::Result::Err(error); } }, _ => { return std::result::Result::Err(kb_core::Error::new( "execution_spl_token_2022_devnet_operation_unsupported", format!( "Token-2022 Devnet validation does not expose {}", operation.operation_code() ), )); }, } return std::result::Result::Ok(kb_pipeline::Token2022PreflightRequest { query_role: request.query_role.clone(), min_context_slot: std::option::Option::None, max_accounts: kb_pipeline::MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS, max_total_data_bytes: kb_pipeline::MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES, requirements, elgamal_registry: std::option::Option::None, }); } fn validate_single_authority(authority: &kb_lib::ExSplTokenAuthority) -> kb_core::Result<()> { if !authority.multisig_signers.is_empty() { return std::result::Result::Err(kb_core::Error::new( "execution_spl_token_2022_devnet_multisig_not_supported", "the Devnet validation UI currently supports one profile-wallet authority", )); } return std::result::Result::Ok(()); } fn mint_requirement( role: &str, mint: &kb_lib::MdPubkey, decimals: std::option::Option, ) -> kb_pipeline::Token2022PreflightRequirement { return kb_pipeline::Token2022PreflightRequirement { role: role.to_string(), account: mint.clone(), kind: kb_lib::DcToken2022StateKind::Mint, max_data_bytes: 65_536, expected_mint: std::option::Option::None, expected_owner: std::option::Option::None, expected_decimals: decimals, required_extensions: std::vec::Vec::new(), context: kb_pipeline::Token2022StatefulContext::default(), }; } fn account_requirement( role: &str, account: &kb_lib::MdPubkey, mint: &kb_lib::MdPubkey, owner: std::option::Option, ) -> kb_pipeline::Token2022PreflightRequirement { return kb_pipeline::Token2022PreflightRequirement { role: role.to_string(), account: account.clone(), kind: kb_lib::DcToken2022StateKind::Account, max_data_bytes: 65_536, expected_mint: std::option::Option::Some(mint.clone()), expected_owner: owner, expected_decimals: std::option::Option::None, required_extensions: std::vec::Vec::new(), context: kb_pipeline::Token2022StatefulContext::default(), }; } fn validate_profile( profile: &kb_config::ProfileConfig, request: &crate::DevnetSplToken2022ExecutionRequest, ) -> kb_core::Result<()> { if profile.wallet.cluster != "devnet" { return std::result::Result::Err(kb_core::Error::config( "Token-2022 Devnet orchestration requires a Devnet wallet profile", )); } if !profile.wallet.temporary_wallet_enabled || !profile.wallet.temporary_wallet_persist { return std::result::Result::Err(kb_core::Error::config( "Token-2022 Devnet orchestration requires an enabled persistent temporary wallet", )); } if !profile.execution.require_simulation { return std::result::Result::Err(kb_core::Error::config( "Token-2022 Devnet orchestration requires simulation", )); } if request.submit && !profile.wallet.devnet_send_enabled { return std::result::Result::Err(kb_core::Error::config( "Devnet transaction submission is disabled by the wallet profile", )); } if request.submit && profile.execution.require_operator_confirmation && !request.operator_confirmed { return std::result::Result::Err(kb_core::Error::config( "Token-2022 Devnet submission requires explicit operator confirmation", )); } return std::result::Result::Ok(()); } fn build_plan( profile: &kb_config::ProfileConfig, request: &crate::DevnetSplToken2022ExecutionRequest, fee_payer: kb_lib::MdPubkey, ) -> kb_core::Result { let materialization_required = operation_requires_materialization(&request.operation); let authorized_signers = std::vec![fee_payer.clone()]; let intent = kb_lib::ExSplToken2022ExecutionIntent { intent_id: request.intent_id.clone(), fee_payer, policy: kb_lib::ExApiExecutionPolicy { cluster: kb_lib::ExApiExecutionClusterPolicy { expected_cluster: kb_lib::ExApiExecutionCluster::Devnet, allow_mainnet: false, mainnet_confirmation: false, }, simulation: kb_lib::ExApiExecutionSimulationPolicy::Required, blockhash: kb_lib::ExApiExecutionBlockhashPolicy { kind: kb_lib::ExApiExecutionBlockhashKind::Latest, max_age_slots: std::option::Option::Some( profile.execution.recent_blockhash_max_age_slots, ), nonce_account: std::option::Option::None, nonce_authority: std::option::Option::None, }, cost_limit: kb_lib::ExApiExecutionCostLimit { max_spend_lamports: std::option::Option::Some( profile.execution.devnet_max_spend_lamports, ), max_fee_lamports: std::option::Option::Some(profile.execution.max_fee_lamports), max_compute_unit_price_micro_lamports: std::option::Option::Some( profile.execution.max_compute_unit_price_micro_lamports, ), }, authorized_signers, dry_run: !request.submit, post_execution_validation: kb_lib::ExApiPostExecutionValidationPolicy { canonical_insert_required: true, core_extraction_required: true, decode_replay_required: true, materialization_required, }, }, operation: request.operation.clone(), }; return kb_lib::ExApiTypedInstructionExecutor::build_prepared_plan( &kb_lib::ExSplToken2022Executor, &intent, ); } fn operation_requires_materialization(_operation: &kb_lib::ExSplToken2022Operation) -> bool { return true; } fn validate_profile_wallet_signers( required_signers: &[std::string::String], wallet_pubkey: &str, ) -> kb_core::Result<()> { if required_signers.iter().any(|value| return value.as_str() != wallet_pubkey) { return std::result::Result::Err(kb_core::Error::new( "execution_spl_token_2022_external_signer_unavailable", format!( "the Devnet Token orchestrator can sign only with profile wallet {}; required signers are {}", wallet_pubkey, required_signers.join(",") ), )); } return std::result::Result::Ok(()); }