v0.5.2-pre.006-fix-004

This commit is contained in:
2026-08-11 00:30:03 +02:00
parent 066d969f5a
commit 1ee1d0297d
17 changed files with 698 additions and 41 deletions

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_wallet.rs
// version: 4
// version: 5
//! UI-safe wallet management and public on-chain exploration.
@@ -21,6 +21,8 @@ pub(crate) struct DemoWalletInventoryPayload {
pub(crate) cluster: std::string::String,
/// Profiles available for explicit read-only on-chain exploration.
pub(crate) onchain_profiles: std::vec::Vec<DemoWalletOnchainProfilePayload>,
/// Secret transfer formats supported by the reusable wallet boundary.
pub(crate) transfer_formats: std::vec::Vec<DemoWalletTransferFormatPayload>,
/// 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.
@@ -61,6 +63,77 @@ pub(crate) struct DemoWalletCreateRequest {
pub(crate) alias: std::string::String,
}
/// One transfer format selectable by the wallet demo.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_wallet/DemoWalletTransferFormatPayload.ts"
)]
pub(crate) struct DemoWalletTransferFormatPayload {
/// Stable machine-readable transfer format code.
pub(crate) code: std::string::String,
/// Human-readable transfer format label.
pub(crate) label: std::string::String,
/// Conventional extension suggested for explicit export paths.
pub(crate) default_extension: std::string::String,
}
/// Request to inspect one external keypair transfer file without importing it.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_wallet/DemoWalletTransferInspectionRequest.ts"
)]
pub(crate) struct DemoWalletTransferInspectionRequest {
/// Explicit path of the external transfer file.
pub(crate) source_path: std::string::String,
/// Stable transfer format code selected by the operator.
pub(crate) format: std::string::String,
}
/// Safe public identity extracted from one external keypair transfer file.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_wallet/DemoWalletTransferInspectionPayload.ts"
)]
pub(crate) struct DemoWalletTransferInspectionPayload {
/// Public key derived from the validated secret keypair.
pub(crate) public_key: std::string::String,
/// Stable transfer format code used for validation.
pub(crate) format: std::string::String,
}
/// Request to import one external keypair into the active native wallet store.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_wallet/DemoWalletImportRequest.ts"
)]
pub(crate) struct DemoWalletImportRequest {
/// Alias assigned to the newly created native wallet.
pub(crate) alias: std::string::String,
/// Explicit path of the external source file.
pub(crate) source_path: std::string::String,
/// Stable transfer format code selected by the operator.
pub(crate) format: std::string::String,
}
/// Request to export one native wallet to an explicit external file.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_wallet/DemoWalletExportRequest.ts"
)]
pub(crate) struct DemoWalletExportRequest {
/// Alias of the native wallet to export.
pub(crate) alias: std::string::String,
/// Explicit destination path created without overwrite.
pub(crate) destination_path: std::string::String,
/// Stable transfer format code selected by the operator.
pub(crate) format: std::string::String,
}
/// Exact SOL balance returned for one public wallet address.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
@@ -157,6 +230,7 @@ pub(crate) async fn demo_wallet_inventory(
profile: profile.name.clone(),
cluster: profile.wallet.cluster.clone(),
onchain_profiles: onchain_profile_payloads(state),
transfer_formats: wallet_transfer_format_payloads(),
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(),
@@ -174,17 +248,9 @@ pub(crate) async fn demo_wallet_create_native(
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) {
let password = match demo_wallet_password() {
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()),
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let profile = state.active_profile();
let manager = match wallet_manager_for_profile(profile) {
@@ -207,6 +273,100 @@ pub(crate) async fn demo_wallet_create_native(
return std::result::Result::Ok(payload);
}
/// Inspects one external transfer file and returns only the derived public identity.
pub(crate) async fn demo_wallet_inspect_transfer_file(
request: crate::DemoWalletTransferInspectionRequest,
) -> std::result::Result<DemoWalletTransferInspectionPayload, std::string::String> {
let format = match wallet_transfer_format_from_code(request.format.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let inspection = match ks_wallet::inspect_transfer_file(
std::path::PathBuf::from(request.source_path),
format,
)
.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(DemoWalletTransferInspectionPayload {
public_key: inspection.public_key().to_string(),
format: inspection.format().code().to_string(),
});
}
/// Imports one external keypair into a new password-protected native wallet.
pub(crate) async fn demo_wallet_import_file(
state: &crate::AppState,
request: crate::DemoWalletImportRequest,
) -> 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 format = match wallet_transfer_format_from_code(request.format.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let password = match demo_wallet_password() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
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
.import_file(alias, password, std::path::PathBuf::from(request.source_path), format)
.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);
}
/// Exports one native wallet after backend-only password authentication.
pub(crate) async fn demo_wallet_export_file(
state: &crate::AppState,
request: crate::DemoWalletExportRequest,
) -> std::result::Result<(), 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 format = match wallet_transfer_format_from_code(request.format.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let password = match demo_wallet_password() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let manager = match wallet_manager_for_profile(state.active_profile()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return match manager
.export_file(&alias, password, std::path::PathBuf::from(request.destination_path), format)
.await
{
std::result::Result::Ok(()) => std::result::Result::Ok(()),
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
};
}
/// Inspects one explicitly selected native wallet file and returns only its safe identity.
pub(crate) async fn demo_wallet_inspect_file(
state: &crate::AppState,
@@ -329,6 +489,45 @@ pub(crate) async fn demo_wallet_rpc_execute(
});
}
fn demo_wallet_password() -> std::result::Result<ks_wallet::WalletPassword, std::string::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"
));
},
};
return match ks_wallet::WalletPassword::new(password_text) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
};
}
fn wallet_transfer_format_payloads() -> std::vec::Vec<crate::DemoWalletTransferFormatPayload> {
let mut formats = std::vec::Vec::new();
for format in ks_wallet::WalletTransferFormat::supported() {
formats.push(crate::DemoWalletTransferFormatPayload {
code: format.code().to_string(),
label: format.label().to_string(),
default_extension: format.default_extension().to_string(),
});
}
return formats;
}
fn wallet_transfer_format_from_code(
code: &str,
) -> std::result::Result<ks_wallet::WalletTransferFormat, std::string::String> {
let code = code.trim();
for format in ks_wallet::WalletTransferFormat::supported() {
if format.code() == code {
return std::result::Result::Ok(format);
}
}
return std::result::Result::Err(format!("wallet transfer format '{code}' is not supported"));
}
fn onchain_profile<'a>(
state: &'a crate::AppState,
profile_name: &str,
@@ -470,6 +669,15 @@ mod tests {
"walletTokensTableBody",
"walletTransactionsTableBody",
"walletTransactionJson",
"walletImportSourcePathInput",
"walletImportFormatSelect",
"walletImportAliasInput",
"inspectWalletTransferButton",
"importWalletTransferButton",
"walletExportAliasSelect",
"walletExportFormatSelect",
"walletExportDestinationPathInput",
"exportWalletTransferButton",
"walletExternalPathInput",
"inspectWalletFileButton",
"walletInspectionJson",
@@ -477,6 +685,9 @@ mod tests {
assert!(html.contains(required));
}
assert!(script.contains("demo_wallet_create_native"));
assert!(script.contains("demo_wallet_inspect_transfer_file"));
assert!(script.contains("demo_wallet_import_file"));
assert!(script.contains("demo_wallet_export_file"));
assert!(script.contains("demo_wallet_balance"));
assert!(script.contains("demo_wallet_inventory"));
assert!(script.contains("demo_wallet_rpc_execute"));
@@ -490,6 +701,9 @@ mod tests {
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_inspect_transfer_file"));
assert!(tauri_runtime.contains("demo_wallet_import_file"));
assert!(tauri_runtime.contains("demo_wallet_export_file"));
assert!(tauri_runtime.contains("demo_wallet_balance"));
assert!(tauri_runtime.contains("demo_wallet_rpc_execute"));
assert!(tauri_runtime.contains("demo_wallet.html"));
@@ -507,6 +721,7 @@ mod tests {
cluster: "devnet".to_string(),
active: false,
}],
transfer_formats: super::wallet_transfer_format_payloads(),
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,
@@ -549,6 +764,17 @@ mod tests {
}
}
#[test]
fn wallet_transfer_format_inventory_matches_reusable_wallet_boundary() {
let formats = super::wallet_transfer_format_payloads();
assert_eq!(formats.len(), 2);
assert_eq!(formats[0].code, "solana_cli_json");
assert_eq!(formats[1].code, "solana_private_key_base58");
assert!(super::wallet_transfer_format_from_code("solana_cli_json").is_ok());
assert!(super::wallet_transfer_format_from_code("solana_private_key_base58").is_ok());
assert!(super::wallet_transfer_format_from_code("unknown").is_err());
}
#[test]
fn lamport_formatting_is_exact_without_float_conversion() {
assert_eq!(super::format_lamports_as_sol(0), "0");

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/lib.rs
// version: 40
// version: 41
//! Tauri desktop demo application for `khadhroony-bot3`.
@@ -367,8 +367,12 @@ pub(crate) use self::demo_transport::DemoEndpointRolePayload;
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;
/// Request to export one native wallet secret through the backend-only demo secret.
pub(crate) use self::demo_wallet::DemoWalletExportRequest;
/// UI-safe native wallet identity exposed by the desktop adapter.
pub(crate) use self::demo_wallet::DemoWalletIdentityPayload;
/// Request to import one external keypair into the native wallet store.
pub(crate) use self::demo_wallet::DemoWalletImportRequest;
/// 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.
@@ -377,12 +381,24 @@ pub(crate) use self::demo_wallet::DemoWalletOnchainProfilePayload;
pub(crate) use self::demo_wallet::DemoWalletRpcExecutionPayload;
/// Request for one wallet-scoped public JSON-RPC read.
pub(crate) use self::demo_wallet::DemoWalletRpcRequest;
/// One secret transfer format selectable by the wallet demo.
pub(crate) use self::demo_wallet::DemoWalletTransferFormatPayload;
/// Safe public identity extracted from one external keypair transfer file.
pub(crate) use self::demo_wallet::DemoWalletTransferInspectionPayload;
/// Request to inspect one external keypair transfer file without importing it.
pub(crate) use self::demo_wallet::DemoWalletTransferInspectionRequest;
/// 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;
/// Exports one native wallet through an explicitly selected transfer format.
pub(crate) use self::demo_wallet::demo_wallet_export_file;
/// Imports one external keypair into a new native wallet.
pub(crate) use self::demo_wallet::demo_wallet_import_file;
/// Inspects an explicitly selected native wallet file without exposing its path in the result.
pub(crate) use self::demo_wallet::demo_wallet_inspect_file;
/// Inspects one external transfer file and returns only its derived public identity.
pub(crate) use self::demo_wallet::demo_wallet_inspect_transfer_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.

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/tauri.rs
// version: 42
// version: 43
//! Tauri runtime assembly and private command wrappers.
@@ -39,6 +39,9 @@ pub fn run() -> ks_core::Result<()> {
open_demo_wallet_window,
demo_wallet_inventory,
demo_wallet_create_native,
demo_wallet_inspect_transfer_file,
demo_wallet_import_file,
demo_wallet_export_file,
demo_wallet_balance,
demo_wallet_inspect_file,
demo_wallet_rpc_execute,
@@ -263,6 +266,29 @@ async fn demo_wallet_create_native(
return crate::demo_wallet_create_native(state.inner(), request).await;
}
#[tauri::command]
async fn demo_wallet_inspect_transfer_file(
request: crate::DemoWalletTransferInspectionRequest,
) -> std::result::Result<crate::DemoWalletTransferInspectionPayload, std::string::String> {
return crate::demo_wallet_inspect_transfer_file(request).await;
}
#[tauri::command]
async fn demo_wallet_import_file(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoWalletImportRequest,
) -> std::result::Result<crate::DemoWalletIdentityPayload, std::string::String> {
return crate::demo_wallet_import_file(state.inner(), request).await;
}
#[tauri::command]
async fn demo_wallet_export_file(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoWalletExportRequest,
) -> std::result::Result<(), std::string::String> {
return crate::demo_wallet_export_file(state.inner(), request).await;
}
#[tauri::command]
async fn demo_wallet_balance(
state: tauri::State<'_, crate::AppState>,