v0.5.2-pre.006

This commit is contained in:
2026-08-10 23:14:17 +02:00
parent e3acb8ceb1
commit 066d969f5a
51 changed files with 1937 additions and 88 deletions

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_config.rs
// version: 5
// version: 6
//! Configuration demo payloads with explicit public and bounded diagnostic projections.
@@ -203,6 +203,8 @@ pub(crate) struct DemoConfigWalletDiagnosticPayload {
pub(crate) wallet_directory_state: std::string::String,
/// Wallet cluster.
pub(crate) cluster: std::string::String,
/// Persistent native wallet alias selected by configuration, when present.
pub(crate) wallet_alias: std::option::Option<std::string::String>,
/// Whether the managed temporary wallet is enabled.
pub(crate) temporary_wallet_enabled: bool,
/// Temporary wallet alias, which is internal but not secret.
@@ -387,6 +389,7 @@ fn diagnostic_config_payload(state: &crate::AppState) -> DemoConfigDiagnosticPay
wallet: DemoConfigWalletDiagnosticPayload {
wallet_directory_state: configured_state(profile.wallet.wallet_dir.as_str()),
cluster: profile.wallet.cluster.clone(),
wallet_alias: profile.wallet.wallet_alias.clone(),
temporary_wallet_enabled: profile.wallet.temporary_wallet_enabled,
temporary_wallet_alias: profile.wallet.temporary_wallet_alias.clone(),
temporary_wallet_persist: profile.wallet.temporary_wallet_persist,
@@ -547,6 +550,7 @@ mod tests {
wallet: super::DemoConfigWalletDiagnosticPayload {
wallet_directory_state: super::configured_state(profile.wallet.wallet_dir.as_str()),
cluster: profile.wallet.cluster.clone(),
wallet_alias: profile.wallet.wallet_alias.clone(),
temporary_wallet_enabled: profile.wallet.temporary_wallet_enabled,
temporary_wallet_alias: profile.wallet.temporary_wallet_alias.clone(),
temporary_wallet_persist: profile.wallet.temporary_wallet_persist,

View File

@@ -0,0 +1,559 @@
// file: kb-app-demo-desktop/src/demo_wallet.rs
// version: 4
//! UI-safe wallet management and public on-chain exploration.
use ts_rs::TS; // rust-rules: derive-import
const DEMO_WALLET_PASSWORD_ENV: &str = "KB_SECRET_DEMO_WALLET_PASSWORD";
const LAMPORTS_PER_SOL: u64 = 1_000_000_000;
/// UI-safe inventory of native wallets visible to the active profile.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_wallet/DemoWalletInventoryPayload.ts"
)]
pub(crate) struct DemoWalletInventoryPayload {
/// Active resolved application profile.
pub(crate) profile: std::string::String,
/// Active wallet cluster.
pub(crate) cluster: std::string::String,
/// Profiles available for explicit read-only on-chain exploration.
pub(crate) onchain_profiles: std::vec::Vec<DemoWalletOnchainProfilePayload>,
/// Canonical classic SPL Token program identifier used by the explorer.
pub(crate) token_program_id: std::string::String,
/// Canonical Token-2022 program identifier used by the explorer.
pub(crate) token_2022_program_id: std::string::String,
/// Whether the backend-only demo password environment variable is present.
pub(crate) create_password_configured: bool,
/// Persistent alias selected by configuration, when present.
pub(crate) selected_alias: std::option::Option<std::string::String>,
/// Structurally valid native wallet identities discovered in the configured directory.
pub(crate) wallets: std::vec::Vec<DemoWalletIdentityPayload>,
}
/// One UI-safe native wallet identity.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_wallet/DemoWalletIdentityPayload.ts"
)]
pub(crate) struct DemoWalletIdentityPayload {
/// Non-sensitive wallet alias.
pub(crate) alias: std::string::String,
/// Public key declared by the native wallet header.
pub(crate) public_key: std::string::String,
/// Native wallet format version.
pub(crate) format_version: u16,
/// Whether this alias is selected by the active profile.
pub(crate) selected: bool,
}
/// Request to create one native wallet in the active profile store.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_wallet/DemoWalletCreateRequest.ts"
)]
pub(crate) struct DemoWalletCreateRequest {
/// Alias of the new persistent wallet.
pub(crate) alias: std::string::String,
}
/// Exact SOL balance returned for one public wallet address.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_wallet/DemoWalletBalancePayload.ts"
)]
pub(crate) struct DemoWalletBalancePayload {
/// Explicitly selected resolved application profile.
pub(crate) profile: std::string::String,
/// Cluster selected by that profile.
pub(crate) cluster: std::string::String,
/// Public address queried on-chain.
pub(crate) address: std::string::String,
/// Exact lamport balance represented as a decimal string.
pub(crate) lamports: std::string::String,
/// Exact SOL balance represented without floating-point conversion.
pub(crate) sol: std::string::String,
/// Endpoint selected for the balance query.
pub(crate) endpoint_name: std::string::String,
/// Provider selected for the balance query.
pub(crate) provider: std::string::String,
}
/// One selectable profile for read-only wallet on-chain exploration.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_wallet/DemoWalletOnchainProfilePayload.ts"
)]
pub(crate) struct DemoWalletOnchainProfilePayload {
/// Resolved profile name.
pub(crate) name: std::string::String,
/// Cluster selected by that profile.
pub(crate) cluster: std::string::String,
/// Whether this is the application-wide active profile.
pub(crate) active: bool,
}
/// Request for one wallet-scoped public JSON-RPC read on an explicitly selected profile.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_wallet/DemoWalletRpcRequest.ts"
)]
pub(crate) struct DemoWalletRpcRequest {
/// Resolved profile name used for transport selection.
pub(crate) profile: std::string::String,
/// Allowed read-only Solana JSON-RPC method.
pub(crate) method: std::string::String,
/// Complete positional JSON-RPC parameters encoded as one JSON array.
pub(crate) params_json: std::string::String,
}
/// Result of one wallet-scoped public JSON-RPC read.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_wallet/DemoWalletRpcExecutionPayload.ts"
)]
pub(crate) struct DemoWalletRpcExecutionPayload {
/// Resolved profile name used by the request.
pub(crate) profile: std::string::String,
/// Cluster selected by that profile.
pub(crate) cluster: std::string::String,
/// Selected endpoint name.
pub(crate) endpoint_name: std::string::String,
/// Selected provider code.
pub(crate) provider: std::string::String,
/// JSON-RPC method name.
pub(crate) method: std::string::String,
/// Pretty JSON response text.
pub(crate) response_json: std::string::String,
}
/// Returns the native wallet inventory visible to the active profile without secret material.
pub(crate) async fn demo_wallet_inventory(
state: &crate::AppState,
) -> std::result::Result<DemoWalletInventoryPayload, std::string::String> {
let profile = state.active_profile();
let manager = match wallet_manager_for_profile(profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let handles = match manager.scan().await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let selected_alias = profile.wallet.wallet_alias.clone();
let mut wallets = std::vec::Vec::with_capacity(handles.len());
for handle in handles {
wallets.push(wallet_identity_payload(&handle, selected_alias.as_deref()));
}
return std::result::Result::Ok(DemoWalletInventoryPayload {
profile: profile.name.clone(),
cluster: profile.wallet.cluster.clone(),
onchain_profiles: onchain_profile_payloads(state),
token_program_id: ks_program_ids::SPL_TOKEN_PROGRAM_ID.to_string(),
token_2022_program_id: ks_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string(),
create_password_configured: std::env::var_os(DEMO_WALLET_PASSWORD_ENV).is_some(),
selected_alias,
wallets,
});
}
/// Creates one password-protected native wallet without sending the password through Tauri IPC.
pub(crate) async fn demo_wallet_create_native(
state: &crate::AppState,
request: crate::DemoWalletCreateRequest,
) -> std::result::Result<DemoWalletIdentityPayload, std::string::String> {
let alias = match ks_wallet::WalletAlias::parse(request.alias.trim().to_string()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let password_text = match std::env::var(DEMO_WALLET_PASSWORD_ENV) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return std::result::Result::Err(format!(
"backend secret {DEMO_WALLET_PASSWORD_ENV} is not configured as UTF-8"
));
},
};
let password = match ks_wallet::WalletPassword::new(password_text) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let profile = state.active_profile();
let manager = match wallet_manager_for_profile(profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let wallet = match manager.create(alias, password).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let identity = wallet.identity();
let payload = DemoWalletIdentityPayload {
selected: profile.wallet.wallet_alias.as_deref()
== std::option::Option::Some(identity.alias.as_str()),
alias: identity.alias.as_str().to_string(),
public_key: identity.public_key,
format_version: 1,
};
wallet.lock();
return std::result::Result::Ok(payload);
}
/// Inspects one explicitly selected native wallet file and returns only its safe identity.
pub(crate) async fn demo_wallet_inspect_file(
state: &crate::AppState,
path: std::string::String,
) -> std::result::Result<DemoWalletIdentityPayload, std::string::String> {
let profile = state.active_profile();
let manager = match wallet_manager_for_profile(profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let handle = match manager.inspect_file(std::path::PathBuf::from(path)).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(wallet_identity_payload(
&handle,
profile.wallet.wallet_alias.as_deref(),
));
}
/// Loads the exact SOL balance of one public address through an explicitly selected profile.
pub(crate) async fn demo_wallet_balance(
state: &crate::AppState,
profile: std::string::String,
address: std::string::String,
) -> std::result::Result<crate::DemoWalletBalancePayload, std::string::String> {
let selected_profile = match onchain_profile(state, &profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let pool = match ks_onchain_transport::HttpEndpointPool::from_profile(selected_profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let address = address.trim().to_string();
if address.is_empty() {
return std::result::Result::Err("wallet address must not be empty".to_string());
}
let role = match best_http_role_for_method(&pool, "getBalance") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let client = match pool.select_client_for_role_and_method(&role, "getBalance") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let result = match client
.get_balance(
&ks_lib::MdPubkey(address.clone()),
&ks_onchain_transport::GetBalanceConfig::confirmed(),
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(crate::DemoWalletBalancePayload {
profile: selected_profile.name.clone(),
cluster: selected_profile.wallet.cluster.clone(),
address,
lamports: result.lamports.to_string(),
sol: format_lamports_as_sol(result.lamports),
endpoint_name: client.endpoint_name().to_string(),
provider: client.provider().to_string(),
});
}
/// Executes one bounded public wallet explorer RPC through an explicitly selected profile.
pub(crate) async fn demo_wallet_rpc_execute(
state: &crate::AppState,
request: crate::DemoWalletRpcRequest,
) -> std::result::Result<crate::DemoWalletRpcExecutionPayload, std::string::String> {
let selected_profile = match onchain_profile(state, &request.profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let method = request.method.trim().to_string();
if !wallet_rpc_method_allowed(&method) {
return std::result::Result::Err(format!(
"wallet explorer RPC method '{method}' is not allowed"
));
}
let params = match serde_json::from_str::<std::vec::Vec<serde_json::Value>>(
request.params_json.as_str(),
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!(
"wallet explorer params must be a JSON array: {error}"
));
},
};
let pool = match ks_onchain_transport::HttpEndpointPool::from_profile(selected_profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let role = match best_http_role_for_method(&pool, &method) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let client = match pool.select_client_for_role_and_method(&role, &method) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let response = match client.execute_json_rpc_result_raw(method.clone(), params).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let response_json = match serde_json::to_string_pretty(&response) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(crate::DemoWalletRpcExecutionPayload {
profile: selected_profile.name.clone(),
cluster: selected_profile.wallet.cluster.clone(),
endpoint_name: client.endpoint_name().to_string(),
provider: client.provider().to_string(),
method,
response_json,
});
}
fn onchain_profile<'a>(
state: &'a crate::AppState,
profile_name: &str,
) -> std::result::Result<&'a ks_config::ProfileConfig, std::string::String> {
let profile_name = profile_name.trim();
if profile_name.is_empty() {
return std::result::Result::Err("wallet on-chain profile must not be empty".to_string());
}
for profile in &state.app_config().profiles {
if profile.name == profile_name {
return std::result::Result::Ok(profile);
}
}
return std::result::Result::Err(format!(
"wallet on-chain profile '{profile_name}' is not configured"
));
}
fn onchain_profile_payloads(
state: &crate::AppState,
) -> std::vec::Vec<crate::DemoWalletOnchainProfilePayload> {
let mut profiles = std::vec::Vec::new();
for profile in &state.app_config().profiles {
let has_enabled_http =
profile.solana.http_endpoints.iter().any(|endpoint| return endpoint.enabled);
if !has_enabled_http {
continue;
}
profiles.push(crate::DemoWalletOnchainProfilePayload {
name: profile.name.clone(),
cluster: profile.wallet.cluster.clone(),
active: profile.name == state.active_profile().name,
});
}
return profiles;
}
fn wallet_rpc_method_allowed(method: &str) -> bool {
return method == "getTokenAccountsByOwner"
|| method == "getSignaturesForAddress"
|| method == "getTransaction";
}
fn wallet_manager_for_profile(
profile: &ks_config::ProfileConfig,
) -> std::result::Result<ks_wallet::WalletManager, 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)
};
return match ks_wallet::WalletManager::new(directory) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
};
}
fn wallet_identity_payload(
handle: &ks_wallet::WalletFileHandle,
selected_alias: std::option::Option<&str>,
) -> crate::DemoWalletIdentityPayload {
return crate::DemoWalletIdentityPayload {
selected: selected_alias == std::option::Option::Some(handle.alias().as_str()),
alias: handle.alias().as_str().to_string(),
public_key: handle.public_key().to_string(),
format_version: handle.format_version(),
};
}
fn best_http_role_for_method(
pool: &ks_onchain_transport::HttpEndpointPool,
method: &str,
) -> std::result::Result<std::string::String, std::string::String> {
let request_kind = ks_onchain_transport::request_kind_from_method(method);
let mut selected = std::option::Option::<(u32, std::string::String)>::None;
for endpoint in pool.snapshot() {
for role in endpoint.roles {
if !role.enabled {
continue;
}
let supports_kind = role
.request_kinds
.iter()
.any(|kind| return kind == &request_kind || kind == "*");
if !supports_kind {
continue;
}
let replace = match &selected {
std::option::Option::Some((priority, selected_role)) => {
role.priority < *priority
|| (role.priority == *priority && role.role < *selected_role)
},
std::option::Option::None => true,
};
if replace {
selected = std::option::Option::Some((role.priority, role.role));
}
}
}
return match selected {
std::option::Option::Some((_, role)) => std::result::Result::Ok(role),
std::option::Option::None => std::result::Result::Err(format!(
"active HTTP profile has no role for request kind '{request_kind}'"
)),
};
}
fn format_lamports_as_sol(lamports: u64) -> std::string::String {
let whole = lamports / LAMPORTS_PER_SOL;
let fraction = lamports % LAMPORTS_PER_SOL;
if fraction == 0 {
return whole.to_string();
}
let fraction_text = format!("{fraction:09}");
let trimmed = fraction_text.trim_end_matches('0');
return format!("{whole}.{trimmed}");
}
#[cfg(test)]
mod tests {
#[test]
fn wallet_frontend_is_wired_to_management_and_onchain_read_commands() {
let html = include_str!("../frontend/demo_wallet.html");
let script = include_str!("../frontend/ts/demo_wallet.ts");
let main_html = include_str!("../frontend/main.html");
let main_script = include_str!("../frontend/ts/main.ts");
let tauri_runtime = include_str!("tauri.rs");
let capability = include_str!("../capabilities/default.json");
let vite_config = include_str!("../vite.config.ts");
for required in [
"createWalletAliasInput",
"createWalletButton",
"refreshWalletInventoryButton",
"walletInventoryTableBody",
"walletOnchainProfileSelect",
"walletAddressInput",
"loadWalletOnchainButton",
"walletTokensTableBody",
"walletTransactionsTableBody",
"walletTransactionJson",
"walletExternalPathInput",
"inspectWalletFileButton",
"walletInspectionJson",
] {
assert!(html.contains(required));
}
assert!(script.contains("demo_wallet_create_native"));
assert!(script.contains("demo_wallet_balance"));
assert!(script.contains("demo_wallet_inventory"));
assert!(script.contains("demo_wallet_rpc_execute"));
assert!(script.contains("demo_wallet_inspect_file"));
assert!(!script.contains("demo_http_execute_request"));
assert!(script.contains("getTokenAccountsByOwner"));
assert!(script.contains("getSignaturesForAddress"));
assert!(script.contains("getTransaction"));
assert!(!html.to_ascii_lowercase().contains("type=\"password\""));
assert!(main_html.contains("openDemoWalletLink"));
assert!(main_script.contains("open_demo_wallet_window"));
assert!(tauri_runtime.contains("open_demo_wallet_window"));
assert!(tauri_runtime.contains("demo_wallet_create_native"));
assert!(tauri_runtime.contains("demo_wallet_balance"));
assert!(tauri_runtime.contains("demo_wallet_rpc_execute"));
assert!(tauri_runtime.contains("demo_wallet.html"));
assert!(capability.contains("\"demo_wallet\""));
assert!(vite_config.contains("frontend/demo_wallet.html"));
}
#[test]
fn wallet_inventory_payload_contains_only_explicit_safe_fields() {
let payload = super::DemoWalletInventoryPayload {
profile: "mainnet_research".to_string(),
cluster: "mainnet-beta".to_string(),
onchain_profiles: std::vec![super::DemoWalletOnchainProfilePayload {
name: "local_devnet".to_string(),
cluster: "devnet".to_string(),
active: false,
}],
token_program_id: ks_program_ids::SPL_TOKEN_PROGRAM_ID.to_string(),
token_2022_program_id: ks_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string(),
create_password_configured: true,
selected_alias: std::option::Option::Some("selected-wallet".to_string()),
wallets: std::vec![super::DemoWalletIdentityPayload {
alias: "selected-wallet".to_string(),
public_key: "11111111111111111111111111111111".to_string(),
format_version: 1,
selected: true,
}],
};
let serialized = match serde_json::to_string(&payload) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("wallet inventory must serialize: {error}"),
};
for forbidden in [
"canary-wallet-password",
"ciphertext",
"nonce",
"salt",
"wallet_path",
"storage_path",
".kswallet",
"http://",
"https://",
] {
assert!(!serialized.contains(forbidden));
}
}
#[test]
fn wallet_rpc_surface_is_read_only_and_bounded() {
for allowed in ["getTokenAccountsByOwner", "getSignaturesForAddress", "getTransaction"] {
assert!(super::wallet_rpc_method_allowed(allowed));
}
for forbidden in
["requestAirdrop", "sendTransaction", "simulateTransaction", "getProgramAccounts"]
{
assert!(!super::wallet_rpc_method_allowed(forbidden));
}
}
#[test]
fn lamport_formatting_is_exact_without_float_conversion() {
assert_eq!(super::format_lamports_as_sol(0), "0");
assert_eq!(super::format_lamports_as_sol(1), "0.000000001");
assert_eq!(super::format_lamports_as_sol(1_500_000_000), "1.5");
assert_eq!(super::format_lamports_as_sol(u64::MAX), "18446744073.709551615");
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/lib.rs
// version: 37
// version: 40
//! Tauri desktop demo application for `khadhroony-bot3`.
@@ -30,6 +30,7 @@ mod demo_sql_pg_core;
mod demo_sql_pg_raw;
mod demo_sql_replay_candidates;
mod demo_transport;
mod demo_wallet;
mod demo_ws;
mod desktop_config;
mod frontend_log;
@@ -362,6 +363,30 @@ pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_programs;
pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_transactions;
/// UI-safe endpoint role shared by HTTP and WebSocket diagnostics.
pub(crate) use self::demo_transport::DemoEndpointRolePayload;
/// Exact public SOL balance projected by the wallet demo.
pub(crate) use self::demo_wallet::DemoWalletBalancePayload;
/// Request to create one native wallet from the backend-only demo secret.
pub(crate) use self::demo_wallet::DemoWalletCreateRequest;
/// UI-safe native wallet identity exposed by the desktop adapter.
pub(crate) use self::demo_wallet::DemoWalletIdentityPayload;
/// UI-safe native wallet inventory exposed by the desktop adapter.
pub(crate) use self::demo_wallet::DemoWalletInventoryPayload;
/// One selectable profile for read-only wallet on-chain exploration.
pub(crate) use self::demo_wallet::DemoWalletOnchainProfilePayload;
/// Result of one wallet-scoped public JSON-RPC read.
pub(crate) use self::demo_wallet::DemoWalletRpcExecutionPayload;
/// Request for one wallet-scoped public JSON-RPC read.
pub(crate) use self::demo_wallet::DemoWalletRpcRequest;
/// Loads the exact SOL balance of one public wallet address.
pub(crate) use self::demo_wallet::demo_wallet_balance;
/// Creates one password-protected native wallet without exposing the password to Tauri IPC.
pub(crate) use self::demo_wallet::demo_wallet_create_native;
/// Inspects an explicitly selected native wallet file without exposing its path in the result.
pub(crate) use self::demo_wallet::demo_wallet_inspect_file;
/// Returns native wallet identities without paths, passwords or protected payloads.
pub(crate) use self::demo_wallet::demo_wallet_inventory;
/// Executes one bounded read-only wallet explorer RPC through an explicitly selected profile.
pub(crate) use self::demo_wallet::demo_wallet_rpc_execute;
/// UI-safe WebSocket endpoint snapshot.
pub(crate) use self::demo_ws::DemoWsEndpointPayload;
/// WebSocket execution response payload.

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/tauri.rs
// version: 38
// version: 42
//! Tauri runtime assembly and private command wrappers.
@@ -36,6 +36,12 @@ pub fn run() -> ks_core::Result<()> {
load_project_readme,
open_demo_config_window,
load_demo_config,
open_demo_wallet_window,
demo_wallet_inventory,
demo_wallet_create_native,
demo_wallet_balance,
demo_wallet_inspect_file,
demo_wallet_rpc_execute,
open_demo_http_window,
demo_http_list_pool_clients,
demo_http_options,
@@ -235,6 +241,53 @@ fn load_demo_config(state: tauri::State<'_, crate::AppState>) -> crate::DemoConf
return crate::demo_config_payload(state.inner());
}
#[tauri::command]
fn open_demo_wallet_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
return open_or_focus_demo_window(app_handle, "demo_wallet", "demo_wallet.html", "Wallets");
}
#[tauri::command]
async fn demo_wallet_inventory(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoWalletInventoryPayload, std::string::String> {
return crate::demo_wallet_inventory(state.inner()).await;
}
#[tauri::command]
async fn demo_wallet_create_native(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoWalletCreateRequest,
) -> std::result::Result<crate::DemoWalletIdentityPayload, std::string::String> {
return crate::demo_wallet_create_native(state.inner(), request).await;
}
#[tauri::command]
async fn demo_wallet_balance(
state: tauri::State<'_, crate::AppState>,
profile: std::string::String,
address: std::string::String,
) -> std::result::Result<crate::DemoWalletBalancePayload, std::string::String> {
return crate::demo_wallet_balance(state.inner(), profile, address).await;
}
#[tauri::command]
async fn demo_wallet_inspect_file(
state: tauri::State<'_, crate::AppState>,
path: std::string::String,
) -> std::result::Result<crate::DemoWalletIdentityPayload, std::string::String> {
return crate::demo_wallet_inspect_file(state.inner(), path).await;
}
#[tauri::command]
async fn demo_wallet_rpc_execute(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoWalletRpcRequest,
) -> std::result::Result<crate::DemoWalletRpcExecutionPayload, std::string::String> {
return crate::demo_wallet_rpc_execute(state.inner(), request).await;
}
#[tauri::command]
fn open_demo_backfill_window(
app_handle: tauri::AppHandle,