v0.1.0-pre.053

This commit is contained in:
2026-07-25 23:57:48 +02:00
parent 930056d422
commit 4e76263f88
23 changed files with 4575 additions and 82 deletions

View File

@@ -0,0 +1,438 @@
// file: kb-app-demo-desktop/src/demo_spl_ata.rs
// version: 4
//! Thin Tauri adapter for ATA execution, derivation and lifecycle journal reads.
use ts_rs::TS; // rust-rules: derive-import
/// Request for one representative ATA creation 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_ata/DemoExecutionSplAtaRequest.ts"
)]
pub(crate) struct DemoExecutionSplAtaRequest {
/// Selected Devnet profile.
pub(crate) profile_name: std::string::String,
/// Wallet owner used by canonical derivation.
pub(crate) wallet_owner: std::string::String,
/// Mint account.
pub(crate) mint: std::string::String,
/// `classic` or `token2022`.
pub(crate) token_program: std::string::String,
/// `create` or `create_idempotent`.
pub(crate) mode: std::string::String,
/// Whether to sign and submit after simulation.
pub(crate) submit: bool,
/// Explicit operator confirmation.
pub(crate) operator_confirmed: bool,
/// Whether post-execution outputs should be force-replayed.
pub(crate) force_post_validation_replay: bool,
}
/// Request used to derive the representative wallet ATA before execution.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_spl_ata/DemoSplAtaDerivationRequest.ts"
)]
pub(crate) struct DemoSplAtaDerivationRequest {
/// Selected Devnet profile.
pub(crate) profile_name: std::string::String,
/// Mint account.
pub(crate) mint: std::string::String,
/// `classic` or `token2022`.
pub(crate) token_program: std::string::String,
}
/// Readonly payer, wallet, Token Program and derived ATA values.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_spl_ata/DemoSplAtaDerivationPayload.ts"
)]
pub(crate) struct DemoSplAtaDerivationPayload {
/// Profile wallet used as payer.
pub(crate) payer: std::string::String,
/// Wallet owner selected by the representative panel.
pub(crate) wallet_owner: std::string::String,
/// Exact Token Program ID.
pub(crate) token_program_id: std::string::String,
/// Canonically derived ATA.
pub(crate) associated_token_account: std::string::String,
}
/// UI-safe ATA execution result.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_spl_ata/DemoExecutionSplAtaSummaryPayload.ts"
)]
pub(crate) struct DemoExecutionSplAtaSummaryPayload {
/// Stable operation code.
pub(crate) operation: std::string::String,
/// Profile name.
pub(crate) profile_name: std::string::String,
/// Profile wallet public key.
pub(crate) wallet_public_key: std::string::String,
/// Target Token Program ID.
pub(crate) token_program_id: std::string::String,
/// Derived ATA from the exact plan.
pub(crate) associated_token_account: std::string::String,
/// Wallet balance as lossless JSON text.
pub(crate) balance_lamports: std::string::String,
/// Fee estimate as lossless JSON text.
pub(crate) fee_lamports: std::option::Option<std::string::String>,
/// Aggregate stateful readiness.
pub(crate) readiness_status: std::string::String,
/// Exact simulation outcome.
pub(crate) simulation_success: bool,
/// Runtime simulation error.
pub(crate) simulation_error: std::option::Option<std::string::String>,
/// Submitted signature.
pub(crate) transaction_signature: std::option::Option<std::string::String>,
/// Terminal confirmation status.
pub(crate) confirmation_status: std::option::Option<std::string::String>,
/// Number of ATA-owned materialized facts.
pub(crate) materialization_count: u32,
/// Whether the second replay created no output.
pub(crate) idempotence_validated: bool,
/// Exact plan JSON.
pub(crate) plan_json: std::string::String,
/// Stateful readiness JSON.
pub(crate) readiness_json: std::string::String,
/// Simulation JSON.
pub(crate) simulation_json: std::string::String,
/// Confirmation and replay diagnostics JSON.
pub(crate) diagnostics_json: std::string::String,
}
/// Bounded ATA lifecycle journal request.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_spl_ata/DemoSplAtaJournalRequest.ts"
)]
pub(crate) struct DemoSplAtaJournalRequest {
/// Selected Devnet profile.
pub(crate) profile_name: std::string::String,
/// Optional partial signature.
pub(crate) signature_contains: std::option::Option<std::string::String>,
/// Optional exact mint.
pub(crate) mint: std::option::Option<std::string::String>,
/// Optional exact ATA.
pub(crate) associated_token_account: std::option::Option<std::string::String>,
/// Optional exact materialized operation.
pub(crate) operation: std::option::Option<std::string::String>,
/// Maximum returned rows.
pub(crate) limit: u32,
}
/// One UI-safe ATA lifecycle 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_ata/DemoSplAtaJournalRow.ts"
)]
pub(crate) struct DemoSplAtaJournalRow {
/// Transaction signature.
pub(crate) signature: std::string::String,
/// Slot as lossless JSON text.
pub(crate) slot: std::string::String,
/// Materialized family.
pub(crate) family: std::string::String,
/// Lifecycle operation.
pub(crate) operation: std::option::Option<std::string::String>,
/// Exact mint when present.
pub(crate) mint: std::option::Option<std::string::String>,
/// Exact ATA when present.
pub(crate) associated_token_account: std::option::Option<std::string::String>,
/// Full bounded payload JSON.
pub(crate) payload_json: std::string::String,
}
pub(crate) async fn load_profile_wallet(
profile: &kb_config::ProfileConfig,
) -> std::result::Result<kb_wallet::TemporaryWallet, std::string::String> {
let configured = std::path::PathBuf::from(profile.wallet.wallet_dir.as_str());
let directory = if configured.is_absolute() {
configured
} else {
crate::workspace_root_dir().join(configured)
};
let store = match kb_wallet::TemporaryWalletStore::new(directory) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let alias = match kb_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.to_string()),
};
return match store.load_or_create(alias).await {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
};
}
pub(crate) fn parse_token_program(
value: &str,
) -> std::result::Result<kb_lib::ExSplAssociatedTokenProgram, std::string::String> {
return match value.trim() {
"classic" => std::result::Result::Ok(kb_lib::ExSplAssociatedTokenProgram::Classic),
"token2022" => std::result::Result::Ok(kb_lib::ExSplAssociatedTokenProgram::Token2022),
_ => std::result::Result::Err("Token Program must be classic or token2022".to_string()),
};
}
pub(crate) fn derive_ata(
wallet: &str,
mint: &str,
token_program: &str,
) -> std::result::Result<std::string::String, std::string::String> {
let wallet: solana_pubkey::Pubkey = match wallet.parse() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!("invalid wallet: {error}"));
},
};
let mint: solana_pubkey::Pubkey = match mint.parse() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!("invalid mint: {error}"));
},
};
let token_program: solana_pubkey::Pubkey = match token_program.parse() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!("invalid Token Program: {error}"));
},
};
return std::result::Result::Ok(
spl_associated_token_account_interface::address::get_associated_token_address_with_program_id(
&wallet,
&mint,
&token_program,
)
.to_string(),
);
}
pub(crate) fn demo_spl_ata_summary_payload(
summary: kb_pipeline_demo_scenarios::DevnetSplAssociatedTokenAccountExecutionSummary,
) -> crate::DemoExecutionSplAtaSummaryPayload {
let (associated_token_account, token_program_id) = match summary.plan.instructions.first() {
std::option::Option::Some(instruction) => {
let ata = match instruction.accounts.get(1) {
std::option::Option::Some(account) => account.pubkey.0.clone(),
std::option::Option::None => std::string::String::new(),
};
let token_program = match instruction.accounts.last() {
std::option::Option::Some(account) => account.pubkey.0.clone(),
std::option::Option::None => std::string::String::new(),
};
(ata, token_program)
},
std::option::Option::None => (std::string::String::new(), std::string::String::new()),
};
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 idempotence_validated = summary.idempotence_replay.as_ref().is_some_and(|replay| {
return replay.failed_inputs == 0
&& replay.processors.iter().all(|processor| {
return processor.failed == 0
&& processor.materialized_outputs == 0
&& processor.materialization_refused == 0;
});
});
let materialization_count = match u32::try_from(summary.materializations.len()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => u32::MAX,
};
let diagnostics = serde_json::json!({
"confirmation": summary.confirmation.as_ref(),
"postStateValidation": summary.post_state_validation.as_ref(),
"postExecution": summary.post_execution.as_ref(),
"backfill": summary.backfill.as_ref().map(|value| {
return serde_json::json!({
"candidatesCompleted": value.candidates_completed,
"candidatesCancelled": value.candidates_cancelled,
"candidatesNotStarted": value.candidates_not_started,
"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 serde_json::json!({
"campaignId": value.campaign_id,
"selected": value.selected,
"completed": value.completed,
"unmatched": value.unmatched,
"failedInputs": value.failed_inputs,
"cancelled": value.cancelled
});
}),
"idempotenceReplay": summary.idempotence_replay.as_ref().map(|value| {
return serde_json::json!({
"campaignId": value.campaign_id,
"selected": value.selected,
"completed": value.completed,
"unmatched": value.unmatched,
"failedInputs": value.failed_inputs,
"cancelled": value.cancelled,
"materializedOutputs": value.processors.iter().map(|processor| {
return processor.materialized_outputs;
}).sum::<u64>(),
"materializationRefused": value.processors.iter().map(|processor| {
return processor.materialization_refused;
}).sum::<u64>()
});
}),
"materializations": summary.materializations.iter().map(|value| {
return serde_json::json!({
"processorName": value.processor_name,
"outputKey": value.output_key,
"family": value.materialized_family,
"signature": value.signature,
"slot": value.slot.to_string(),
"payload": value.payload_json
});
}).collect::<std::vec::Vec<_>>()
});
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 diagnostics_json = crate::pretty_json(&diagnostics);
return crate::DemoExecutionSplAtaSummaryPayload {
operation: summary.stateful_readiness.operation_code,
profile_name: summary.profile_name,
wallet_public_key: summary.wallet.public_key,
token_program_id,
associated_token_account,
balance_lamports: summary.balance_lamports.to_string(),
fee_lamports: summary.fee.fee_lamports.map(|value| return value.to_string()),
readiness_status: match summary.stateful_readiness.status {
kb_pipeline::SplAssociatedTokenAccountStatefulReadinessStatus::Ready => {
"ready".to_string()
},
kb_pipeline::SplAssociatedTokenAccountStatefulReadinessStatus::Blocked => {
"blocked".to_string()
},
},
simulation_success: summary.simulation.success,
simulation_error: summary.simulation.error,
transaction_signature,
confirmation_status,
materialization_count,
idempotence_validated,
plan_json,
readiness_json,
simulation_json,
diagnostics_json,
};
}
pub(crate) fn journal_matches(
payload: &serde_json::Value,
request: &crate::DemoSplAtaJournalRequest,
) -> bool {
for (expected, key) in [
(&request.operation, "operation"),
(&request.mint, "mint"),
(&request.associated_token_account, "associatedTokenAccount"),
] {
if expected.as_ref().is_some_and(|value| {
return payload.get(key).and_then(serde_json::Value::as_str)
!= std::option::Option::Some(value.as_str());
}) {
return false;
}
}
return true;
}
pub(crate) fn journal_row(row: kb_store::MaterializedEventQueryRow) -> crate::DemoSplAtaJournalRow {
let text = |key: &str| {
return row
.payload_json
.get(key)
.and_then(serde_json::Value::as_str)
.map(std::string::ToString::to_string);
};
let operation = text("operation");
let mint = text("mint");
let associated_token_account = text("associatedTokenAccount");
let payload_json = crate::pretty_json(&row.payload_json);
return crate::DemoSplAtaJournalRow {
signature: row.signature,
slot: row.slot.to_string(),
family: row.materialized_family,
operation,
mint,
associated_token_account,
payload_json,
};
}
#[cfg(test)]
mod tests {
#[test]
fn classic_and_token2022_derivations_are_distinct_and_canonical() {
let wallet = kb_program_ids::SYSTEM_PROGRAM_ID;
let mint = "So11111111111111111111111111111111111111112";
let classic = crate::derive_ata(wallet, mint, kb_program_ids::SPL_TOKEN_PROGRAM_ID)
.unwrap_or_else(|error| panic!("classic derivation failed: {error}"));
let token2022 = crate::derive_ata(wallet, mint, kb_program_ids::SPL_TOKEN2022_PROGRAM_ID)
.unwrap_or_else(|error| panic!("Token-2022 derivation failed: {error}"));
assert_eq!(classic, "aqxoAhCwpy3oB1BpNw9hL1HdLYLgPpbPjzxDrrQj3Fs");
assert_eq!(token2022, "2sZUUBGq1i6aE47ZoxCaCW89jmYm2EXLPPmNMgMDXHMS");
}
#[test]
fn lifecycle_journal_filters_are_exact() {
let request = crate::DemoSplAtaJournalRequest {
profile_name: "local_devnet".to_string(),
signature_contains: std::option::Option::None,
mint: std::option::Option::Some("mint111".to_string()),
associated_token_account: std::option::Option::Some("ata111".to_string()),
operation: std::option::Option::Some("create_idempotent".to_string()),
limit: 100,
};
assert!(crate::journal_matches(
&serde_json::json!({
"operation": "create_idempotent",
"mint": "mint111",
"associatedTokenAccount": "ata111"
}),
&request,
));
assert!(!crate::journal_matches(
&serde_json::json!({
"operation": "create",
"mint": "mint111",
"associatedTokenAccount": "ata111"
}),
&request,
));
}
}