v0.5.2-pre.006-fix-004
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>,
|
||||
|
||||
Reference in New Issue
Block a user