Files
khadhroony-bot3/ks-pipeline-demo-scenarios/src/solana.rs
2026-08-10 01:36:42 +02:00

1434 lines
58 KiB
Rust

// file: ks-pipeline-demo-scenarios/src/solana.rs
// version: 14
//! Devnet Solana execution orchestration with canonical post-validation.
/// Progress severity for one Solana execution orchestration.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SolanaExecutionProgressLevel {
/// Diagnostic detail.
Debug,
/// Normal execution information.
Info,
/// Recoverable issue or non-terminal limitation.
Warning,
/// Terminal execution or validation failure.
Error,
}
impl crate::SolanaExecutionProgressLevel {
/// Returns the stable lowercase level code.
pub fn code(&self) -> &'static str {
return match self {
Self::Debug => "debug",
Self::Info => "info",
Self::Warning => "warn",
Self::Error => "error",
};
}
}
/// One operator-visible execution progress event.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SolanaExecutionProgressEvent {
/// UTC timestamp rendered in RFC 3339.
pub timestamp: std::string::String,
/// Severity.
pub level: crate::SolanaExecutionProgressLevel,
/// Stable execution stage code.
pub stage: std::string::String,
/// Human-readable message.
pub message: std::string::String,
/// Transaction signature when available.
pub signature: std::option::Option<std::string::String>,
}
impl crate::SolanaExecutionProgressEvent {
fn new(
level: crate::SolanaExecutionProgressLevel,
stage: impl std::convert::Into<std::string::String>,
message: impl std::convert::Into<std::string::String>,
signature: std::option::Option<std::string::String>,
) -> Self {
return Self {
timestamp: chrono::Utc::now().to_rfc3339(),
level,
stage: stage.into(),
message: message.into(),
signature,
};
}
}
/// Composite observer used by execution, backfill, extraction and decode replay.
pub trait SolanaExecutionObserver:
ks_pipeline::BackfillObserver
+ ks_pipeline::CoreExtractionObserver
+ ks_pipeline::DecodeReplayObserver
+ Sync
{
/// Receives one execution-specific progress event.
fn on_execution_progress(&self, event: &crate::SolanaExecutionProgressEvent);
/// Returns true when execution orchestration should stop before the next stage.
fn is_execution_cancelled(&self) -> bool;
}
/// No-op observer suitable for CLI tools and opt-in integration tests.
#[derive(Clone, Copy, Debug, Default)]
pub struct NoopSolanaExecutionObserver;
impl ks_pipeline::BackfillObserver for crate::NoopSolanaExecutionObserver {
fn on_progress(&self, _event: &ks_pipeline::BackfillProgressEvent) {
return;
}
fn is_cancelled(&self) -> bool {
return false;
}
}
impl ks_pipeline::CoreExtractionObserver for crate::NoopSolanaExecutionObserver {
fn on_progress(&self, _event: &ks_pipeline::CoreExtractionProgressEvent) {
return;
}
fn is_cancelled(&self) -> bool {
return false;
}
}
impl ks_pipeline::DecodeReplayObserver for crate::NoopSolanaExecutionObserver {
fn on_progress(&self, _event: &ks_pipeline::DecodeReplayProgressEvent) {
return;
}
fn is_cancelled(&self) -> bool {
return false;
}
}
impl crate::SolanaExecutionObserver for crate::NoopSolanaExecutionObserver {
fn on_execution_progress(&self, _event: &crate::SolanaExecutionProgressEvent) {
return;
}
fn is_execution_cancelled(&self) -> bool {
return false;
}
}
/// Complete request for one bounded System Program transfer on Devnet.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DevnetSystemTransferRequest {
/// Stable caller-provided identifier used in tracing and plan correlation.
pub intent_id: std::string::String,
/// Endpoint role used for cluster, blockhash, fee, balance, airdrop and hydration calls.
pub query_role: std::string::String,
/// Endpoint role used for simulation, submission and signature status polling.
pub transaction_role: std::string::String,
/// Recipient public key.
pub recipient: ks_lib::MdPubkey,
/// Lamports transferred by the System instruction.
pub lamports: u64,
/// Maximum faucet airdrop requested only when the source balance is insufficient.
pub airdrop_lamports: u64,
/// Explicitly authorizes signing and submission instead of returning after simulation.
pub submit: bool,
/// Explicit operator confirmation required by the active profile when configured.
pub operator_confirmed: bool,
/// Number of `getTransaction` retries after the first post-confirmation attempt.
pub post_validation_max_retries: u32,
/// Replaces existing core/decode outputs for the exact submitted signature.
pub force_post_validation_replay: bool,
/// Runs compatible materializers after decode replay.
pub materialize_after_decode: bool,
}
impl crate::DevnetSystemTransferRequest {
/// Creates a conservative simulation-only request.
pub fn new(
intent_id: impl std::convert::Into<std::string::String>,
recipient: ks_lib::MdPubkey,
lamports: u64,
) -> Self {
return Self {
intent_id: intent_id.into(),
query_role: "http_queries".to_string(),
transaction_role: "http_transactions".to_string(),
recipient,
lamports,
airdrop_lamports: 0,
submit: false,
operator_confirmed: false,
post_validation_max_retries: 10,
force_post_validation_replay: false,
materialize_after_decode: false,
};
}
/// Validates request-local values independently from one profile.
pub fn validate(&self) -> ks_core::Result<()> {
if self.intent_id.trim().is_empty() {
return std::result::Result::Err(ks_core::Error::config(
"devnet 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(ks_core::Error::config(
"devnet execution endpoint roles must not be empty",
));
}
let recipient_result = ks_onchain_transport::validate_solana_pubkey_text(
self.recipient.0.as_str(),
"devnet System transfer recipient",
);
if let std::result::Result::Err(error) = recipient_result {
return std::result::Result::Err(error);
}
if self.lamports == 0 {
return std::result::Result::Err(ks_core::Error::config(
"devnet System transfer lamports must be greater than zero",
));
}
if self.post_validation_max_retries > 20 {
return std::result::Result::Err(ks_core::Error::config(
"post-execution getTransaction retries must not exceed 20",
));
}
return std::result::Result::Ok(());
}
}
/// Complete result of one Devnet transfer orchestration.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DevnetSystemTransferSummary {
/// Profile used by the orchestration.
pub profile_name: std::string::String,
/// Classified cluster.
pub cluster: ks_lib::ExApiExecutionCluster,
/// Genesis hash returned by the selected endpoint.
pub genesis_hash: std::string::String,
/// Non-secret persistent wallet description.
pub wallet: ks_wallet::WalletSummary,
/// Transfer recipient.
pub recipient: ks_lib::MdPubkey,
/// Whether the recipient account existed before execution.
pub recipient_existed_before: bool,
/// Recipient balance before execution when the account existed.
pub recipient_balance_before_lamports: std::option::Option<u64>,
/// Minimum lamports required when creating a zero-data recipient account.
pub recipient_minimum_balance_lamports: u64,
/// Source wallet balance before an optional airdrop.
pub balance_before_lamports: u64,
/// Source wallet balance after optional funding.
pub balance_after_funding_lamports: u64,
/// Faucet signature when an airdrop was required.
pub airdrop_signature: std::option::Option<ks_lib::MdSignature>,
/// Airdrop confirmation when an airdrop was required.
pub airdrop_confirmation: std::option::Option<ks_lib::ExApiExecutionConfirmationResult>,
/// Exact prepared plan.
pub plan: ks_lib::ExApiPreparedExecutionPlan,
/// Recent blockhash and expiration height used by the transaction.
pub latest_blockhash: ks_onchain_transport::LatestBlockhashResult,
/// Fee estimate for the exact compiled message.
pub fee: ks_onchain_transport::FeeForMessageResult,
/// Exact simulation result bound to the compiled message.
pub simulation: ks_lib::ExApiExecutionSimulationResult,
/// Submission result when `submit` was enabled.
pub send_result: std::option::Option<ks_lib::ExApiExecutionSendResult>,
/// Confirmation result when the transaction was submitted.
pub confirmation: std::option::Option<ks_lib::ExApiExecutionConfirmationResult>,
/// Canonical hydration result after an observed transaction reached a terminal status.
pub backfill: std::option::Option<ks_pipeline::BackfillSummary>,
/// Core extraction result for the exact signature.
pub core_extraction: std::option::Option<ks_pipeline::CoreExtractionSummary>,
/// Contextual decode result for the exact signature.
pub decode_replay: std::option::Option<ks_pipeline::DecodeReplaySummary>,
/// Aggregated post-execution validation diagnostic.
pub post_execution: std::option::Option<ks_lib::ExApiPostExecutionDiagnostic>,
}
/// Executes a bounded Devnet System transfer and its canonical replay validation.
#[allow(clippy::too_many_arguments)]
pub async fn execute_devnet_system_transfer<S, O>(
http_pool: &ks_onchain_transport::HttpEndpointPool,
store: &S,
profile: &ks_config::ProfileConfig,
workspace_root: &std::path::Path,
request: &crate::DevnetSystemTransferRequest,
decoders: &[std::sync::Arc<dyn ks_lib::DcApiInstructionDecoder>],
materializers: &[std::sync::Arc<dyn ks_lib::MtApiEventMaterializer>],
observer: &O,
) -> ks_core::Result<crate::DevnetSystemTransferSummary>
where
S: ks_store::RawTransactionStore
+ ks_store::CoreExtractionStore
+ ks_store::DecodePipelineStore
+ Sync,
O: crate::SolanaExecutionObserver,
{
let request_result = request.validate();
if let std::result::Result::Err(error) = request_result {
return std::result::Result::Err(error);
}
let profile_result = validate_devnet_profile(profile, request);
if let std::result::Result::Err(error) = profile_result {
return std::result::Result::Err(error);
}
let cancellation_result = ensure_not_cancelled(observer, "validate");
if let std::result::Result::Err(error) = cancellation_result {
return std::result::Result::Err(error);
}
emit(
observer,
crate::SolanaExecutionProgressLevel::Info,
"cluster_check",
format!("checking Devnet cluster for profile {}", profile.name),
std::option::Option::None,
);
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(ks_lib::ExApiExecutionCluster::Devnet)
{
return std::result::Result::Err(ks_core::Error::new(
"execution_cluster_mismatch",
format!(
"expected Devnet genesis hash but endpoint returned {} classified as {:?}",
genesis.genesis_hash, genesis.classified_cluster
),
));
}
let cancellation_result = ensure_not_cancelled(observer, "wallet");
if let std::result::Result::Err(error) = cancellation_result {
return std::result::Result::Err(error);
}
let wallet = match load_profile_wallet(profile, workspace_root).await {
std::result::Result::Ok(wallet) => wallet,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let wallet_summary = wallet.summary();
let source_pubkey = ks_lib::MdPubkey(wallet_summary.public_key.clone());
if source_pubkey == request.recipient {
return std::result::Result::Err(ks_core::Error::config(
"devnet System transfer recipient must differ from the source wallet",
));
}
let account_info_config = ks_onchain_transport::GetAccountInfoConfig::confirmed();
let recipient_account = match http_pool
.get_account_info_for_role(
request.query_role.as_str(),
&request.recipient,
&account_info_config,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let recipient_existed_before = recipient_account.account.is_some();
let recipient_balance_before_lamports =
recipient_account.account.as_ref().map(|account| return account.lamports);
let recipient_minimum_balance_lamports = if recipient_existed_before {
0
} else {
let rent_config =
ks_onchain_transport::GetMinimumBalanceForRentExemptionConfig::confirmed();
let rent = match http_pool
.get_minimum_balance_for_rent_exemption_for_role(
request.query_role.as_str(),
0,
&rent_config,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
rent.minimum_balance_lamports
};
let recipient_validation = validate_recipient_transfer_amount(
recipient_existed_before,
request.lamports,
recipient_minimum_balance_lamports,
);
if let std::result::Result::Err(error) = recipient_validation {
return std::result::Result::Err(error);
}
let balance_config = ks_onchain_transport::GetBalanceConfig::confirmed();
let balance_before = match http_pool
.get_balance_for_role(request.query_role.as_str(), &source_pubkey, &balance_config)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let required_balance = match request.lamports.checked_add(profile.execution.max_fee_lamports) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(ks_core::Error::config(
"devnet transfer amount plus fee ceiling overflows u64",
));
},
};
let funding = match ensure_devnet_funding(
http_pool,
profile,
request,
observer,
&source_pubkey,
balance_before.lamports,
required_balance,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let cancellation_result = ensure_not_cancelled(observer, "plan");
if let std::result::Result::Err(error) = cancellation_result {
return std::result::Result::Err(error);
}
let plan = match build_transfer_plan(profile, request, source_pubkey.clone()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let plan_evaluation = match ks_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 == ks_lib::ExSafetyDecision::Deny {
return std::result::Result::Err(ks_core::Error::new(
"execution_plan_denied",
violation_message(plan_evaluation.violations.as_slice()),
));
}
let latest_config = ks_onchain_transport::GetLatestBlockhashConfig::confirmed();
let latest_blockhash = match http_pool
.get_latest_blockhash_for_role(request.query_role.as_str(), &latest_config)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let unsigned = match ks_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_config = ks_onchain_transport::GetFeeForMessageConfig::new(
ks_onchain_transport::RpcCommitmentLevel::Confirmed,
std::option::Option::Some(latest_blockhash.context.slot),
);
let message_base64 = unsigned.message_base64();
let fee = match http_pool
.get_fee_for_message_for_role(
request.query_role.as_str(),
message_base64.as_str(),
&fee_config,
)
.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(ks_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 ks_onchain_transport::SimulateTransactionConfig::new(
ks_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),
};
emit(
observer,
crate::SolanaExecutionProgressLevel::Info,
"simulation",
format!("simulating exact 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 blockhash_age_slots =
simulation_rpc.context.slot.saturating_sub(latest_blockhash.context.slot);
let simulation = simulation_rpc.to_execution_result(
ks_lib::ExApiExecutionCluster::Devnet,
ks_lib::ExApiExecutionBlockhashKind::Latest,
std::option::Option::Some(blockhash_age_slots),
std::option::Option::None,
std::option::Option::None,
std::option::Option::Some(&fee),
);
let evidence = unsigned.bind_simulation(simulation.clone());
let mut summary = crate::DevnetSystemTransferSummary {
profile_name: profile.name.clone(),
cluster: ks_lib::ExApiExecutionCluster::Devnet,
genesis_hash: genesis.genesis_hash,
wallet: wallet_summary,
recipient: request.recipient.clone(),
recipient_existed_before,
recipient_balance_before_lamports,
recipient_minimum_balance_lamports,
balance_before_lamports: balance_before.lamports,
balance_after_funding_lamports: funding.balance_after_lamports,
airdrop_signature: funding.airdrop_signature,
airdrop_confirmation: funding.airdrop_confirmation,
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,
post_execution: std::option::Option::None,
};
if !request.submit {
return std::result::Result::Ok(summary);
}
if !summary.simulation.success {
return std::result::Result::Err(ks_core::Error::new(
"execution_simulation_failed",
simulation_failure_message(&summary.simulation),
));
}
let cancellation_result = ensure_not_cancelled(observer, "sign");
if let std::result::Result::Err(error) = cancellation_result {
return std::result::Result::Err(error);
}
let signed = match evidence.message_hash() == unsigned.message_hash() {
true => match unsigned.sign_after_simulation(&evidence, &[wallet.as_signer()]) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
},
false => {
return std::result::Result::Err(ks_core::Error::new(
"execution_simulation_message_mismatch",
"simulation evidence does not match the unsigned transaction",
));
},
};
let verify_result = signed.verify_signatures();
if let std::result::Result::Err(error) = verify_result {
return std::result::Result::Err(error);
}
let signature = signed.primary_signature().clone();
let mut post_execution = ks_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 ks_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),
};
emit(
observer,
crate::SolanaExecutionProgressLevel::Info,
"send",
"submitting signed Devnet transaction",
std::option::Option::Some(signature.0.clone()),
);
let signed_transaction_base64 = signed.transaction_base64();
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) => {
post_execution
.diagnostics
.push(format!("sendTransaction failed for locally signed transaction: {error}"));
summary.post_execution = std::option::Option::Some(post_execution);
tracing::error!(
target: crate::TRACING_TARGET,
action = "execute_devnet_system_transfer",
stage = "send",
intent_id = %request.intent_id,
signature = %signature.0,
error_message = %error,
"Devnet System transfer submission failed"
);
return std::result::Result::Ok(summary);
},
};
summary.send_result =
std::option::Option::Some(sent.to_execution_result(ks_lib::ExApiExecutionCluster::Devnet));
let confirmation_config =
match ks_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) => {
post_execution.diagnostics.push(format!(
"confirmation policy could not be created after submission: {error}"
));
summary.post_execution = std::option::Option::Some(post_execution);
return std::result::Result::Ok(summary);
},
};
let confirmation = match http_pool
.confirm_transaction_for_roles(
request.transaction_role.as_str(),
request.query_role.as_str(),
ks_lib::ExApiExecutionCluster::Devnet,
&signature,
&confirmation_config,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
post_execution
.diagnostics
.push(format!("confirmation polling failed after transaction submission: {error}"));
summary.post_execution = std::option::Option::Some(post_execution);
tracing::error!(
target: crate::TRACING_TARGET,
action = "execute_devnet_system_transfer",
stage = "confirmation",
intent_id = %request.intent_id,
signature = %signature.0,
error_message = %error,
"Devnet System transfer confirmation failed"
);
return std::result::Result::Ok(summary);
},
};
let confirmation_status = confirmation.status;
summary.confirmation = std::option::Option::Some(confirmation);
if confirmation_status == ks_lib::ExApiExecutionConfirmationStatus::Expired
|| confirmation_status == ks_lib::ExApiExecutionConfirmationStatus::TimedOut
{
post_execution.diagnostics.push(format!(
"transaction was submitted but post-validation stopped at confirmation status {:?}",
confirmation_status
));
summary.post_execution = std::option::Option::Some(post_execution);
return std::result::Result::Ok(summary);
}
let cancellation_result = ensure_not_cancelled(observer, "canonical_insert");
if let std::result::Result::Err(error) = cancellation_result {
post_execution
.diagnostics
.push(format!("post-execution validation stopped after submission: {error}"));
summary.post_execution = std::option::Option::Some(post_execution);
return std::result::Result::Ok(summary);
}
let backfill = match execute_post_validation_backfill(
http_pool, store, profile, request, observer, &signature,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
post_execution
.diagnostics
.push(format!("canonical hydration failed after submission: {error}"));
summary.post_execution = std::option::Option::Some(post_execution);
return std::result::Result::Ok(summary);
},
};
let canonical_available = canonical_backfill_available(&backfill);
post_execution.canonical_inserted = canonical_available;
summary.backfill = std::option::Option::Some(backfill);
if !canonical_available {
post_execution.diagnostics.push(
"confirmed transaction was not available for canonical post-validation".to_string(),
);
summary.post_execution = std::option::Option::Some(post_execution);
return std::result::Result::Ok(summary);
}
let extraction_request = ks_pipeline::CoreExtractionRequest {
source: ks_pipeline::CoreExtractionSource::Signatures(std::vec![signature.0.clone()]),
limit: 1,
max_concurrent_extractions: 1,
force_replay: request.force_post_validation_replay,
};
let extraction =
match ks_pipeline::execute_core_extraction(store, &extraction_request, observer).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
post_execution
.diagnostics
.push(format!("core extraction failed after submission: {error}"));
summary.post_execution = std::option::Option::Some(post_execution);
return std::result::Result::Ok(summary);
},
};
let core_extracted = extraction.failed == 0
&& !extraction.cancelled
&& extraction.selected == 1
&& extraction.extracted.saturating_add(extraction.skipped) >= 1;
post_execution.core_extracted = core_extracted;
summary.core_extraction = std::option::Option::Some(extraction);
if !core_extracted {
post_execution
.diagnostics
.push("canonical transaction did not complete core extraction".to_string());
summary.post_execution = std::option::Option::Some(post_execution);
return std::result::Result::Ok(summary);
}
let program_ids = summary
.plan
.instructions
.iter()
.map(|instruction| return instruction.program_id.0.clone())
.collect::<std::vec::Vec<_>>();
let selection = match ks_store::DecodeSelectionFilter::new(
std::vec![signature.0.clone()],
std::vec![
ks_store::CoreInstructionProcessingState::Pending,
ks_store::CoreInstructionProcessingState::Failed,
ks_store::CoreInstructionProcessingState::ReplayRequested,
],
std::option::Option::None,
std::option::Option::None,
program_ids,
std::vec::Vec::new(),
false,
32,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
post_execution.diagnostics.push(format!(
"decode selection could not be constructed after submission: {error}"
));
summary.post_execution = std::option::Option::Some(post_execution);
return std::result::Result::Ok(summary);
},
};
let decode_request = ks_pipeline::DecodeReplayRequest {
campaign_id: ks_pipeline::new_decode_campaign_id(),
selection,
decoder_names: std::vec::Vec::new(),
dispatch_policy: ks_pipeline::DecodeDispatchPolicy::HighestPriority,
max_concurrent_inputs: 1,
force_replay: request.force_post_validation_replay,
force_replay_all_matching: false,
materialize_after_decode: request.materialize_after_decode,
};
let decode = match ks_pipeline::execute_decode_replay(
store,
&decode_request,
decoders,
materializers,
observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
post_execution
.diagnostics
.push(format!("decode replay failed after submission: {error}"));
summary.post_execution = std::option::Option::Some(post_execution);
return std::result::Result::Ok(summary);
},
};
let processors_clean = decode.processors.iter().all(|processor| {
return processor.failed == 0
&& processor.processing_errors == 0
&& processor.unsupported == 0;
});
let decoded_observations =
decode.processors.iter().map(|processor| return processor.decoded).sum::<u64>();
let decode_replayed = decode.failed_inputs == 0
&& decode.processing_error_inputs == 0
&& decode.unmatched == 0
&& !decode.cancelled
&& decode.completed >= 1
&& processors_clean
&& decoded_observations >= 1;
let materialized = request.materialize_after_decode
&& decode_replayed
&& decode
.processors
.iter()
.all(|processor| return processor.materialization_refused == 0);
post_execution.decode_replayed = decode_replayed;
post_execution.materialized = materialized;
summary.decode_replay = std::option::Option::Some(decode);
if confirmation_status == ks_lib::ExApiExecutionConfirmationStatus::Failed {
post_execution.diagnostics.push(
"transaction was committed with an on-chain error and replayed as a failed intent"
.to_string(),
);
} else if !decode_replayed {
post_execution.diagnostics.push(
"transaction reached core storage but decode replay did not complete cleanly"
.to_string(),
);
} else if request.materialize_after_decode && !materialized {
post_execution.diagnostics.push(
"decode replay completed but requested materialization was refused or failed"
.to_string(),
);
} else {
post_execution.diagnostics.push(
"transaction completed canonical insertion, core extraction and decode replay"
.to_string(),
);
}
let canonical_inserted = post_execution.canonical_inserted;
let core_extracted = post_execution.core_extracted;
let decode_replayed = post_execution.decode_replayed;
let materialized = post_execution.materialized;
summary.post_execution = std::option::Option::Some(post_execution);
tracing::info!(
target: crate::TRACING_TARGET,
action = "execute_devnet_system_transfer",
intent_id = %request.intent_id,
signature = %signature.0,
confirmation_status = ?confirmation_status,
canonical_inserted,
core_extracted,
decode_replayed,
materialized,
"Devnet System transfer orchestration completed"
);
return std::result::Result::Ok(summary);
}
async fn execute_post_validation_backfill<S, O>(
http_pool: &ks_onchain_transport::HttpEndpointPool,
store: &S,
profile: &ks_config::ProfileConfig,
request: &crate::DevnetSystemTransferRequest,
observer: &O,
signature: &ks_lib::MdSignature,
) -> ks_core::Result<ks_pipeline::BackfillSummary>
where
S: ks_store::RawTransactionStore + Sync,
O: crate::SolanaExecutionObserver,
{
let mut retry_index = 0_u32;
loop {
let backfill_request = ks_pipeline::BackfillRequest {
role: request.query_role.clone(),
commitment: "confirmed".to_string(),
source: ks_pipeline::BackfillSource::ExplicitSignatures(std::vec![signature.0.clone()]),
page_size: 1,
max_pages: 1,
max_concurrent_requests: 1,
max_retries: 0,
};
let backfill =
match ks_pipeline::execute_http_backfill(http_pool, store, &backfill_request, observer)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if canonical_backfill_available(&backfill)
|| retry_index >= request.post_validation_max_retries
|| observer.is_execution_cancelled()
{
return std::result::Result::Ok(backfill);
}
retry_index = retry_index.saturating_add(1);
let delay_ms = std::cmp::max(profile.execution.confirmation_poll_interval_ms, 500);
emit(
observer,
crate::SolanaExecutionProgressLevel::Warning,
"canonical_insert",
format!(
"getTransaction is not available yet for {}; retry {}/{} after {} ms",
signature.0, retry_index, request.post_validation_max_retries, delay_ms
),
std::option::Option::Some(signature.0.clone()),
);
tokio::time::sleep(std::time::Duration::from_millis(delay_ms)).await;
}
}
fn canonical_backfill_available(summary: &ks_pipeline::BackfillSummary) -> bool {
return summary.failed == 0
&& summary.missing == 0
&& summary.candidates_completed == 1
&& summary.candidates_cancelled == 0
&& summary.candidates_not_started == 0
&& summary
.canonical_inserted
.saturating_add(summary.canonical_skipped)
.saturating_add(summary.existing_skipped)
>= 1;
}
fn validate_recipient_transfer_amount(
recipient_exists: bool,
lamports: u64,
minimum_balance_lamports: u64,
) -> ks_core::Result<()> {
if !recipient_exists && lamports < minimum_balance_lamports {
return std::result::Result::Err(ks_core::Error::new(
"execution_recipient_rent_exemption_required",
format!(
"recipient account does not exist: transfer requires at least {minimum_balance_lamports} lamports for a zero-data rent-exempt account, requested {lamports}"
),
));
}
return std::result::Result::Ok(());
}
pub(crate) fn simulation_failure_message(
simulation: &ks_lib::ExApiExecutionSimulationResult,
) -> std::string::String {
let error = match simulation.error.as_deref() {
std::option::Option::Some(value) => value,
std::option::Option::None => "runtime returned no structured error",
};
let retained_logs = simulation.logs.iter().take(20).cloned().collect::<std::vec::Vec<_>>();
if retained_logs.is_empty() {
return format!("exact transaction simulation failed: error={error}; logs=[]");
}
return format!(
"exact transaction simulation failed: error={error}; logs=[{}]",
retained_logs.join(" | ")
);
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct FundingResult {
balance_after_lamports: u64,
airdrop_signature: std::option::Option<ks_lib::MdSignature>,
airdrop_confirmation: std::option::Option<ks_lib::ExApiExecutionConfirmationResult>,
}
fn validate_devnet_profile(
profile: &ks_config::ProfileConfig,
request: &crate::DevnetSystemTransferRequest,
) -> ks_core::Result<()> {
if profile.wallet.cluster != "devnet" {
return std::result::Result::Err(ks_core::Error::config(format!(
"profile {} wallet cluster must be devnet for this orchestration",
profile.name
)));
}
if !profile.wallet.temporary_wallet_enabled || !profile.wallet.temporary_wallet_persist {
return std::result::Result::Err(ks_core::Error::config(
"Devnet execution requires an enabled persistent temporary wallet",
));
}
if !profile.execution.require_simulation {
return std::result::Result::Err(ks_core::Error::config(
"Devnet execution requires simulation in the active profile",
));
}
if request.lamports > profile.execution.devnet_max_spend_lamports {
return std::result::Result::Err(ks_core::Error::config(format!(
"requested transfer {} exceeds Devnet spend ceiling {}",
request.lamports, profile.execution.devnet_max_spend_lamports
)));
}
if request.airdrop_lamports > profile.execution.devnet_airdrop_max_lamports {
return std::result::Result::Err(ks_core::Error::config(format!(
"requested airdrop {} exceeds Devnet airdrop ceiling {}",
request.airdrop_lamports, profile.execution.devnet_airdrop_max_lamports
)));
}
if request.submit && !profile.wallet.devnet_send_enabled {
return std::result::Result::Err(ks_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(ks_core::Error::config(
"Devnet submission requires explicit operator confirmation",
));
}
return std::result::Result::Ok(());
}
pub(crate) async fn load_profile_wallet(
profile: &ks_config::ProfileConfig,
workspace_root: &std::path::Path,
) -> ks_core::Result<ks_wallet::TemporaryWallet> {
let configured = std::path::PathBuf::from(profile.wallet.wallet_dir.as_str());
let directory = if configured.is_absolute() {
configured
} else {
workspace_root.join(configured)
};
let store = match ks_wallet::TemporaryWalletStore::new(directory) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let alias = match ks_wallet::WalletAlias::parse(profile.wallet.temporary_wallet_alias.clone()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return store.load_or_create(alias).await;
}
async fn ensure_devnet_funding<O>(
http_pool: &ks_onchain_transport::HttpEndpointPool,
profile: &ks_config::ProfileConfig,
request: &crate::DevnetSystemTransferRequest,
observer: &O,
source_pubkey: &ks_lib::MdPubkey,
balance_before_lamports: u64,
required_balance_lamports: u64,
) -> ks_core::Result<FundingResult>
where
O: crate::SolanaExecutionObserver,
{
if balance_before_lamports >= required_balance_lamports {
return std::result::Result::Ok(FundingResult {
balance_after_lamports: balance_before_lamports,
airdrop_signature: std::option::Option::None,
airdrop_confirmation: std::option::Option::None,
});
}
if !request.submit {
return std::result::Result::Err(ks_core::Error::new(
"execution_balance_insufficient",
format!(
"wallet balance {} is below required {} and simulation-only mode cannot request a funding transaction",
balance_before_lamports, required_balance_lamports
),
));
}
if request.airdrop_lamports == 0 {
return std::result::Result::Err(ks_core::Error::new(
"execution_airdrop_required",
format!(
"wallet balance {} is below required {}; configure a bounded Devnet airdrop",
balance_before_lamports, required_balance_lamports
),
));
}
let projected_balance = match balance_before_lamports.checked_add(request.airdrop_lamports) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(ks_core::Error::config(
"Devnet balance plus airdrop overflows u64",
));
},
};
if projected_balance < required_balance_lamports {
return std::result::Result::Err(ks_core::Error::config(format!(
"configured airdrop would raise balance only to {}, below required {}",
projected_balance, required_balance_lamports
)));
}
emit(
observer,
crate::SolanaExecutionProgressLevel::Info,
"airdrop",
format!("requesting {} Devnet lamports for temporary wallet", request.airdrop_lamports),
std::option::Option::None,
);
let airdrop = match http_pool
.request_airdrop_for_role(
request.query_role.as_str(),
source_pubkey,
request.airdrop_lamports,
&ks_onchain_transport::RequestAirdropConfig::confirmed(),
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let confirmation_config =
match ks_onchain_transport::ConfirmTransactionConfig::from_execution_config(
&profile.execution,
std::option::Option::None,
std::option::Option::None,
) {
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(),
ks_lib::ExApiExecutionCluster::Devnet,
&airdrop.signature,
&confirmation_config,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if confirmation.status != ks_lib::ExApiExecutionConfirmationStatus::Confirmed
&& confirmation.status != ks_lib::ExApiExecutionConfirmationStatus::Finalized
{
return std::result::Result::Err(ks_core::Error::new(
"execution_airdrop_not_confirmed",
format!("Devnet airdrop ended with confirmation status {:?}", confirmation.status),
));
}
let balance = match http_pool
.get_balance_for_role(
request.query_role.as_str(),
source_pubkey,
&ks_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 < required_balance_lamports {
return std::result::Result::Err(ks_core::Error::new(
"execution_balance_still_insufficient",
format!(
"confirmed airdrop left balance {} below required {}",
balance.lamports, required_balance_lamports
),
));
}
return std::result::Result::Ok(FundingResult {
balance_after_lamports: balance.lamports,
airdrop_signature: std::option::Option::Some(airdrop.signature),
airdrop_confirmation: std::option::Option::Some(confirmation),
});
}
fn build_transfer_plan(
profile: &ks_config::ProfileConfig,
request: &crate::DevnetSystemTransferRequest,
source_pubkey: ks_lib::MdPubkey,
) -> ks_core::Result<ks_lib::ExApiPreparedExecutionPlan> {
let policy = ks_lib::ExApiExecutionPolicy {
cluster: ks_lib::ExApiExecutionClusterPolicy {
expected_cluster: ks_lib::ExApiExecutionCluster::Devnet,
allow_mainnet: false,
mainnet_confirmation: false,
},
simulation: ks_lib::ExApiExecutionSimulationPolicy::Required,
blockhash: ks_lib::ExApiExecutionBlockhashPolicy {
kind: ks_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: ks_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: std::vec![source_pubkey.clone()],
dry_run: !request.submit,
post_execution_validation: ks_lib::ExApiPostExecutionValidationPolicy {
canonical_insert_required: request.submit,
core_extraction_required: request.submit,
decode_replay_required: request.submit,
materialization_required: request.submit && request.materialize_after_decode,
},
};
let intent = ks_lib::ExSolanaCoreExecutionIntent {
intent_id: request.intent_id.clone(),
fee_payer: source_pubkey.clone(),
policy,
operation: ks_lib::ExSolanaCoreOperation::SystemTransfer {
from: source_pubkey,
to: request.recipient.clone(),
lamports: request.lamports,
},
};
let executor = ks_lib::ExSolanaCoreExecutor;
return ks_lib::ExApiTypedInstructionExecutor::build_prepared_plan(&executor, &intent);
}
pub(crate) fn violation_message(violations: &[ks_lib::ExSafetyViolation]) -> std::string::String {
if violations.is_empty() {
return "execution policy denied the plan without a diagnostic".to_string();
}
return violations
.iter()
.map(|violation| return format!("{}: {}", violation.code, violation.message))
.collect::<std::vec::Vec<_>>()
.join("; ");
}
pub(crate) fn ensure_not_cancelled<O>(observer: &O, stage: &str) -> ks_core::Result<()>
where
O: crate::SolanaExecutionObserver,
{
if observer.is_execution_cancelled() {
return std::result::Result::Err(ks_core::Error::new(
"execution_cancelled",
format!("execution was cancelled before stage {stage}"),
));
}
return std::result::Result::Ok(());
}
pub(crate) fn emit<O>(
observer: &O,
level: crate::SolanaExecutionProgressLevel,
stage: impl std::convert::Into<std::string::String>,
message: impl std::convert::Into<std::string::String>,
signature: std::option::Option<std::string::String>,
) where
O: crate::SolanaExecutionObserver,
{
observer.on_execution_progress(&crate::SolanaExecutionProgressEvent::new(
level, stage, message, signature,
));
}
#[cfg(test)]
mod tests {
fn example_devnet_profile() -> ks_config::ProfileConfig {
let config =
match ks_config::parse_config_json(include_str!("../../config/app.config.json")) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("example config parse failed: {error}"),
};
return match crate::resolve_demo_devnet_profile(&config, std::option::Option::None) {
std::result::Result::Ok(profile) => profile,
std::result::Result::Err(error) => panic!("Devnet profile resolution failed: {error}"),
};
}
fn recipient() -> ks_lib::MdPubkey {
return ks_lib::MdPubkey("Vote111111111111111111111111111111111111111".to_string());
}
#[test]
fn request_and_profile_validation_are_conservative() {
let profile = example_devnet_profile();
let mut request = crate::DevnetSystemTransferRequest::new("intent-1", recipient(), 1_000);
assert!(request.validate().is_ok());
assert!(super::validate_devnet_profile(&profile, &request).is_ok());
request.submit = true;
assert!(super::validate_devnet_profile(&profile, &request).is_err());
request.operator_confirmed = true;
assert!(super::validate_devnet_profile(&profile, &request).is_ok());
request.airdrop_lamports = profile.execution.devnet_airdrop_max_lamports.saturating_add(1);
assert!(super::validate_devnet_profile(&profile, &request).is_err());
}
#[test]
fn resolved_devnet_profile_routes_complete_execution_flow() {
let profile = example_devnet_profile();
let pool = match ks_onchain_transport::HttpEndpointPool::from_profile(&profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("HTTP pool creation failed: {error}"),
};
for method in [
"getGenesisHash",
"getAccountInfo",
"getMinimumBalanceForRentExemption",
"getBalance",
"requestAirdrop",
"getLatestBlockhash",
"getFeeForMessage",
"getBlockHeight",
"getTransaction",
] {
let selected = pool.select_client_for_role_and_method("http_queries", method);
assert!(selected.is_ok(), "http_queries must route {method}");
}
for method in ["simulateTransaction", "sendTransaction", "getSignatureStatuses"] {
let selected = pool.select_client_for_role_and_method("http_transactions", method);
assert!(selected.is_ok(), "http_transactions must route {method}");
}
}
#[test]
fn transfer_plan_uses_exact_devnet_policy() {
let profile = example_devnet_profile();
let mut request = crate::DevnetSystemTransferRequest::new("intent-2", recipient(), 5_000);
request.submit = true;
request.operator_confirmed = true;
let source = ks_lib::MdPubkey("11111111111111111111111111111111".to_string());
let plan = match super::build_transfer_plan(&profile, &request, source.clone()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("plan build failed: {error}"),
};
assert_eq!(plan.fee_payer, source);
assert_eq!(plan.requested_spend_lamports, 5_000);
assert_eq!(plan.policy.cluster.expected_cluster, ks_lib::ExApiExecutionCluster::Devnet);
assert!(!plan.policy.dry_run);
assert!(plan.policy.post_execution_validation.canonical_insert_required);
assert_eq!(plan.instructions.len(), 1);
assert_eq!(plan.operation_code, ks_lib::EX_SOLANA_CORE_SYSTEM_TRANSFER_OPERATION);
}
#[test]
fn new_recipient_requires_zero_data_rent_exemption() {
assert!(super::validate_recipient_transfer_amount(false, 1_000, 890_880,).is_err());
assert!(super::validate_recipient_transfer_amount(false, 890_880, 890_880,).is_ok());
assert!(super::validate_recipient_transfer_amount(true, 1, 890_880,).is_ok());
}
#[test]
fn simulation_failure_message_preserves_runtime_error_and_logs() {
let simulation = ks_lib::ExApiExecutionSimulationResult {
simulated: true,
success: false,
cluster: ks_lib::ExApiExecutionCluster::Devnet,
blockhash_kind: ks_lib::ExApiExecutionBlockhashKind::Latest,
blockhash_age_slots: std::option::Option::Some(1),
replacement_blockhash: std::option::Option::None,
replacement_last_valid_block_height: std::option::Option::None,
nonce_account: std::option::Option::None,
nonce_authority: std::option::Option::None,
units_consumed: std::option::Option::Some(150),
estimated_fee_lamports: std::option::Option::Some(5_000),
logs: std::vec!["Program log: insufficient rent".to_string()],
return_data: std::option::Option::None,
error: std::option::Option::Some("InsufficientFundsForRent".to_string()),
};
let message = super::simulation_failure_message(&simulation);
assert!(message.contains("InsufficientFundsForRent"));
assert!(message.contains("insufficient rent"));
}
#[tokio::test]
async fn optional_devnet_system_transfer_from_env() {
if std::env::var("KS_DEVNET_EXECUTION_TEST").ok().as_deref()
!= std::option::Option::Some("1")
{
return;
}
let database_url = match std::env::var("KS_SECRET_POSTGRES_TEST_URL") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("KS_SECRET_POSTGRES_TEST_URL is required: {error}");
},
};
let mut profile = example_devnet_profile();
profile.database.backend = "postgres".to_string();
profile.database.postgres.url = database_url;
if let std::result::Result::Ok(directory) = std::env::var("KS_DEVNET_WALLET_DIR") {
profile.wallet.wallet_dir = directory;
}
let pool = match ks_onchain_transport::HttpEndpointPool::from_profile(&profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("HTTP pool creation failed: {error}"),
};
let store_options = match ks_store::PostgresStoreOptions::new(
profile.database.postgres.url.clone(),
profile.database.postgres.max_connections,
profile.database.postgres.connect_timeout_ms,
false,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("PostgreSQL options failed: {error}"),
};
let store = match ks_store::PostgresStore::connect(store_options).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("PostgreSQL connection failed: {error}"),
};
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
panic!("PostgreSQL schema initialization failed: {error}");
}
let recipient_alias = match ks_wallet::WalletAlias::parse("devnet-recipient") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("recipient alias failed: {error}"),
};
let recipient_wallet = ks_wallet::TemporaryWallet::generate(recipient_alias);
let transfer_lamports = std::env::var("KS_DEVNET_TRANSFER_LAMPORTS")
.ok()
.and_then(|value| return value.parse::<u64>().ok())
.unwrap_or(1_000_000);
let airdrop_lamports = std::env::var("KS_DEVNET_AIRDROP_LAMPORTS")
.ok()
.and_then(|value| return value.parse::<u64>().ok())
.unwrap_or(100_000_000);
let mut request = crate::DevnetSystemTransferRequest::new(
format!("devnet-test-{}", uuid::Uuid::new_v4()),
ks_lib::MdPubkey(recipient_wallet.public_key()),
transfer_lamports,
);
request.airdrop_lamports = airdrop_lamports;
request.post_validation_max_retries = 20;
request.submit = true;
request.operator_confirmed = true;
let decoders: std::vec::Vec<std::sync::Arc<dyn ks_lib::DcApiInstructionDecoder>> =
std::vec![std::sync::Arc::new(ks_lib::DcSolanaCoreDecoder,)];
let materializers: std::vec::Vec<std::sync::Arc<dyn ks_lib::MtApiEventMaterializer>> =
std::vec::Vec::new();
let workspace_root = match std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent() {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("workspace root cannot be resolved"),
};
let summary = match crate::execute_devnet_system_transfer(
&pool,
&store,
&profile,
workspace_root,
&request,
decoders.as_slice(),
materializers.as_slice(),
&crate::NoopSolanaExecutionObserver,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("Devnet execution failed: {error}"),
};
let confirmation = match summary.confirmation {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("transaction confirmation missing"),
};
assert!(
confirmation.status == ks_lib::ExApiExecutionConfirmationStatus::Confirmed
|| confirmation.status == ks_lib::ExApiExecutionConfirmationStatus::Finalized
);
let post_execution = match summary.post_execution {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("post-execution diagnostic missing"),
};
assert!(post_execution.canonical_inserted);
assert!(post_execution.core_extracted);
assert!(post_execution.decode_replayed);
}
}