v0.2.12-pre.008-fix.002
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 296
|
# version: 297
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
|
members = ["crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.2.12-pre.8.fix.1"
|
version = "0.2.12-pre.8.fix.2"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: crates/ksp-app-wallet-desk/frontend/main.html -->
|
<!-- file: crates/ksp-app-wallet-desk/frontend/main.html -->
|
||||||
<!-- version: 6 -->
|
<!-- version: 7 -->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
<h1 id="viewTitle" class="h3 mb-1">Dashboard</h1>
|
<h1 id="viewTitle" class="h3 mb-1">Dashboard</h1>
|
||||||
<p class="text-body-secondary mb-0">Identité autorisée et balance SOL via le Transport HTTP composite sont actives.</p>
|
<p class="text-body-secondary mb-0">Identité autorisée et balance SOL via le Transport HTTP composite sont actives.</p>
|
||||||
</div>
|
</div>
|
||||||
<span class="badge text-bg-secondary">0.2.6</span>
|
<span id="appVersionBadge" class="badge text-bg-secondary">—</span>
|
||||||
</div>
|
</div>
|
||||||
<section data-view-panel="dashboard">
|
<section data-view-panel="dashboard">
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
@@ -98,6 +98,7 @@
|
|||||||
<dt class="col-5">Notes</dt><dd id="currentWalletNotes" class="col-7 text-break">—</dd>
|
<dt class="col-5">Notes</dt><dd id="currentWalletNotes" class="col-7 text-break">—</dd>
|
||||||
<dt class="col-5">Balance SOL</dt><dd id="currentWalletBalanceSol" class="col-7">—</dd>
|
<dt class="col-5">Balance SOL</dt><dd id="currentWalletBalanceSol" class="col-7">—</dd>
|
||||||
<dt class="col-5">SOL/USD moyen</dt><dd id="currentWalletSolUsdAverage" class="col-7">N.A.</dd>
|
<dt class="col-5">SOL/USD moyen</dt><dd id="currentWalletSolUsdAverage" class="col-7">N.A.</dd>
|
||||||
|
<dt class="col-5">Équivalent USD</dt><dd id="currentWalletSolUsdEquivalent" class="col-7">N.A.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -257,6 +258,8 @@
|
|||||||
<dt class="col-md-4">Notes</dt><dd id="detailsWalletNotes" class="col-md-8 text-break">—</dd>
|
<dt class="col-md-4">Notes</dt><dd id="detailsWalletNotes" class="col-md-8 text-break">—</dd>
|
||||||
<dt class="col-md-4">Lamports</dt><dd id="detailsBalanceLamports" class="col-md-8">—</dd>
|
<dt class="col-md-4">Lamports</dt><dd id="detailsBalanceLamports" class="col-md-8">—</dd>
|
||||||
<dt class="col-md-4">SOL</dt><dd id="detailsBalanceSol" class="col-md-8">—</dd>
|
<dt class="col-md-4">SOL</dt><dd id="detailsBalanceSol" class="col-md-8">—</dd>
|
||||||
|
<dt class="col-md-4">SOL/USD moyen</dt><dd id="detailsSolUsdAverage" class="col-md-8">N.A.</dd>
|
||||||
|
<dt class="col-md-4">Équivalent USD</dt><dd id="detailsSolUsdEquivalent" class="col-md-8">N.A.</dd>
|
||||||
<dt class="col-md-4">RPC slot</dt><dd id="detailsBalanceSlot" class="col-md-8">—</dd>
|
<dt class="col-md-4">RPC slot</dt><dd id="detailsBalanceSlot" class="col-md-8">—</dd>
|
||||||
<dt class="col-md-4">RPC api_version</dt><dd id="detailsBalanceApiVersion" class="col-md-8">—</dd>
|
<dt class="col-md-4">RPC api_version</dt><dd id="detailsBalanceApiVersion" class="col-md-8">—</dd>
|
||||||
<dt class="col-md-4">Transport</dt><dd id="detailsBalanceTransport" class="col-md-8">—</dd>
|
<dt class="col-md-4">Transport</dt><dd id="detailsBalanceTransport" class="col-md-8">—</dd>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-wallet-desk/frontend/ts/main.ts
|
// file: crates/ksp-app-wallet-desk/frontend/ts/main.ts
|
||||||
// version: 17
|
// version: 18
|
||||||
|
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
import DataTable from "datatables.net-bs5";
|
import DataTable from "datatables.net-bs5";
|
||||||
@@ -379,8 +379,11 @@ function clearStrongViewSensitiveInputs(): void {
|
|||||||
function clearBalanceProjection(): void {
|
function clearBalanceProjection(): void {
|
||||||
setText("#currentWalletBalanceSol", "—");
|
setText("#currentWalletBalanceSol", "—");
|
||||||
setText("#currentWalletSolUsdAverage", "N.A.");
|
setText("#currentWalletSolUsdAverage", "N.A.");
|
||||||
|
setText("#currentWalletSolUsdEquivalent", "N.A.");
|
||||||
setText("#detailsBalanceLamports", "—");
|
setText("#detailsBalanceLamports", "—");
|
||||||
setText("#detailsBalanceSol", "—");
|
setText("#detailsBalanceSol", "—");
|
||||||
|
setText("#detailsSolUsdAverage", "N.A.");
|
||||||
|
setText("#detailsSolUsdEquivalent", "N.A.");
|
||||||
setText("#detailsBalanceSlot", "—");
|
setText("#detailsBalanceSlot", "—");
|
||||||
setText("#detailsBalanceApiVersion", "—");
|
setText("#detailsBalanceApiVersion", "—");
|
||||||
setText("#detailsBalanceTransport", "—");
|
setText("#detailsBalanceTransport", "—");
|
||||||
@@ -1380,14 +1383,17 @@ function renderWalletBalance(balance: WalletBalanceDto): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setText("#currentWalletBalanceSol", balance.sol);
|
setText("#currentWalletBalanceSol", balance.sol);
|
||||||
setText("#currentWalletSolUsdAverage", balance.solUsdAverage ? `${balance.solUsdAverage} USD` : "N.A.");
|
setText("#currentWalletSolUsdAverage", balance.solUsdAverage ? `${balance.solUsdAverage} USD/SOL` : "N.A.");
|
||||||
|
setText("#currentWalletSolUsdEquivalent", balance.solUsdEquivalent ? `${balance.solUsdEquivalent} USD` : "N.A.");
|
||||||
setText("#detailsBalanceLamports", balance.lamports);
|
setText("#detailsBalanceLamports", balance.lamports);
|
||||||
setText("#detailsBalanceSol", balance.sol);
|
setText("#detailsBalanceSol", balance.sol);
|
||||||
|
setText("#detailsSolUsdAverage", balance.solUsdAverage ? `${balance.solUsdAverage} USD/SOL` : "N.A.");
|
||||||
|
setText("#detailsSolUsdEquivalent", balance.solUsdEquivalent ? `${balance.solUsdEquivalent} USD` : "N.A.");
|
||||||
setText("#detailsBalanceSlot", balance.slot.toString());
|
setText("#detailsBalanceSlot", balance.slot.toString());
|
||||||
setText("#detailsBalanceApiVersion", balance.apiVersion ?? "—");
|
setText("#detailsBalanceApiVersion", balance.apiVersion ?? "—");
|
||||||
setText("#detailsBalanceTransport", `${balance.transportProfile} / ${balance.transportRole}`);
|
setText("#detailsBalanceTransport", `${balance.transportProfile} / ${balance.transportRole}`);
|
||||||
setText("#balanceStatus", "Balance réseau et prix moyen SOL/USD actualisés.");
|
setText("#balanceStatus", "Balance réseau, prix moyen SOL/USD et équivalent USD actualisés.");
|
||||||
frontendInfo("main", "Wallet balance rendered", { averageAvailable: balance.solUsdAverage !== null, lamports: balance.lamports, slot: balance.slot, walletId: balance.walletId });
|
frontendInfo("main", "Wallet balance rendered", { averageAvailable: balance.solUsdAverage !== null, equivalentAvailable: balance.solUsdEquivalent !== null, lamports: balance.lamports, slot: balance.slot, walletId: balance.walletId });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshWalletBalance(): Promise<void> {
|
async function refreshWalletBalance(): Promise<void> {
|
||||||
@@ -1417,6 +1423,7 @@ function bindBalanceActions(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderRuntimeStatus(status: RuntimeStatusDto): void {
|
function renderRuntimeStatus(status: RuntimeStatusDto): void {
|
||||||
|
setText("#appVersionBadge", status.applicationVersion);
|
||||||
setText("#runtimeVersion", status.applicationVersion);
|
setText("#runtimeVersion", status.applicationVersion);
|
||||||
setText("#runtimeCompositeProfile", status.activeCompositeProfile);
|
setText("#runtimeCompositeProfile", status.activeCompositeProfile);
|
||||||
setText("#runtimeLoggingProfile", status.activeLoggingProfile ?? "fallback transitoire");
|
setText("#runtimeLoggingProfile", status.activeLoggingProfile ?? "fallback transitoire");
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ksp-app-wallet-desk",
|
"name": "ksp-app-wallet-desk",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.2.12-pre.8.fix.1",
|
"version": "0.2.12-pre.8.fix.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-wallet-desk/src/app_state.rs
|
// file: crates/ksp-app-wallet-desk/src/app_state.rs
|
||||||
// version: 20
|
// version: 21
|
||||||
|
|
||||||
//! Shared backend state owned by the Wallet Desk Tauri application.
|
//! Shared backend state owned by the Wallet Desk Tauri application.
|
||||||
|
|
||||||
@@ -1160,17 +1160,19 @@ impl AppState {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
let lamports = result.value();
|
let lamports = result.value();
|
||||||
|
let sol_usd_equivalent = crate::sol_usd_equivalent(lamports, sol_usd_average.as_deref());
|
||||||
let dto = crate::WalletBalanceDto {
|
let dto = crate::WalletBalanceDto {
|
||||||
api_version: result.context().api_version().map(str::to_owned),
|
api_version: result.context().api_version().map(str::to_owned),
|
||||||
lamports: lamports.to_string(),
|
lamports: lamports.to_string(),
|
||||||
slot: result.context().slot(),
|
slot: result.context().slot(),
|
||||||
sol: crate::format_lamports_as_sol(lamports),
|
sol: crate::format_lamports_as_sol(lamports),
|
||||||
sol_usd_average,
|
sol_usd_average,
|
||||||
|
sol_usd_equivalent,
|
||||||
transport_profile: self.transport_runtime.profile_id().to_owned(),
|
transport_profile: self.transport_runtime.profile_id().to_owned(),
|
||||||
transport_role: self.transport_runtime.role().as_str().to_owned(),
|
transport_role: self.transport_runtime.role().as_str().to_owned(),
|
||||||
wallet_id: context.wallet_id.clone(),
|
wallet_id: context.wallet_id.clone(),
|
||||||
};
|
};
|
||||||
ksp_logging_lib::info!(target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_TRANSPORT, wallet_id = context.wallet_id.as_str(), capability = context.capability, lamports, slot = dto.slot, sol_usd_average_available = dto.sol_usd_average.is_some(), "Wallet balance refresh succeeded");
|
ksp_logging_lib::info!(target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_TRANSPORT, wallet_id = context.wallet_id.as_str(), capability = context.capability, lamports, slot = dto.slot, sol_usd_average_available = dto.sol_usd_average.is_some(), sol_usd_equivalent_available = dto.sol_usd_equivalent.is_some(), "Wallet balance refresh succeeded");
|
||||||
return std::result::Result::Ok(dto);
|
return std::result::Result::Ok(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-wallet-desk/src/lib.rs
|
// file: crates/ksp-app-wallet-desk/src/lib.rs
|
||||||
// version: 14
|
// version: 15
|
||||||
|
|
||||||
//! Tauri desktop application shell for KSP Wallet management and inspection.
|
//! Tauri desktop application shell for KSP Wallet management and inspection.
|
||||||
|
|
||||||
@@ -168,6 +168,8 @@ pub(crate) use self::wallet_balance::WalletBalanceDto;
|
|||||||
pub(crate) use self::wallet_balance::average_sol_usd_prices;
|
pub(crate) use self::wallet_balance::average_sol_usd_prices;
|
||||||
/// Formats exact lamports as a decimal SOL string without floating point.
|
/// Formats exact lamports as a decimal SOL string without floating point.
|
||||||
pub(crate) use self::wallet_balance::format_lamports_as_sol;
|
pub(crate) use self::wallet_balance::format_lamports_as_sol;
|
||||||
|
/// Computes the exact USD equivalent of a lamport balance from the auxiliary SOL/USD average.
|
||||||
|
pub(crate) use self::wallet_balance::sol_usd_equivalent;
|
||||||
/// Resolved Wallet Config and directory preparation status captured during application bootstrap.
|
/// Resolved Wallet Config and directory preparation status captured during application bootstrap.
|
||||||
pub(crate) use self::wallet_config::WalletConfigStartup;
|
pub(crate) use self::wallet_config::WalletConfigStartup;
|
||||||
/// Resolves the composite-selected Wallet Config and prepares its application-owned directory tree.
|
/// Resolves the composite-selected Wallet Config and prepares its application-owned directory tree.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-wallet-desk/src/wallet_balance.rs
|
// file: crates/ksp-app-wallet-desk/src/wallet_balance.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! Safe Wallet balance projection produced by the typed HTTP Transport path.
|
//! Safe Wallet balance projection produced by the typed HTTP Transport path.
|
||||||
|
|
||||||
@@ -20,6 +20,8 @@ pub(crate) struct WalletBalanceDto {
|
|||||||
pub(crate) sol: String,
|
pub(crate) sol: String,
|
||||||
/// Arithmetic mean of successful SOL/USD observations produced by this refresh, or unavailable when no provider returned one.
|
/// Arithmetic mean of successful SOL/USD observations produced by this refresh, or unavailable when no provider returned one.
|
||||||
pub(crate) sol_usd_average: std::option::Option<String>,
|
pub(crate) sol_usd_average: std::option::Option<String>,
|
||||||
|
/// Exact USD equivalent of the current SOL balance using `sol_usd_average`, or unavailable when the auxiliary price is unavailable.
|
||||||
|
pub(crate) sol_usd_equivalent: std::option::Option<String>,
|
||||||
/// Composite-selected Transport profile used for the RPC.
|
/// Composite-selected Transport profile used for the RPC.
|
||||||
pub(crate) transport_profile: String,
|
pub(crate) transport_profile: String,
|
||||||
/// Logical Transport role used for the RPC.
|
/// Logical Transport role used for the RPC.
|
||||||
@@ -111,6 +113,58 @@ pub(crate) fn average_sol_usd_prices(prices: &[ksp_offchain_transport_lib::Marke
|
|||||||
return std::option::Option::Some(format_scaled_decimal(coefficient, target_scale));
|
return std::option::Option::Some(format_scaled_decimal(coefficient, target_scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes the exact USD equivalent of a lamport balance from the current SOL/USD average.
|
||||||
|
///
|
||||||
|
/// The output is rounded half-up to at most eight fractional digits. Missing or invalid auxiliary price data and arithmetic overflow degrade to `None`; the
|
||||||
|
/// authoritative SOL balance remains unaffected.
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn sol_usd_equivalent(lamports: u64, sol_usd_average: std::option::Option<&str>) -> std::option::Option<String> {
|
||||||
|
const LAMPORT_SCALE: u8 = 9;
|
||||||
|
const OUTPUT_SCALE: u8 = 8;
|
||||||
|
let source = match sol_usd_average {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return std::option::Option::None,
|
||||||
|
};
|
||||||
|
let average = match ksp_offchain_transport_lib::MarketPriceDecimal::parse(source) {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return std::option::Option::None,
|
||||||
|
};
|
||||||
|
let coefficient = match u128::from(lamports).checked_mul(average.coefficient()) {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return std::option::Option::None,
|
||||||
|
};
|
||||||
|
let source_scale = u16::from(LAMPORT_SCALE) + u16::from(average.scale());
|
||||||
|
let target_scale = u16::from(OUTPUT_SCALE);
|
||||||
|
let rounded_coefficient = if source_scale <= target_scale {
|
||||||
|
let exponent = u32::from(target_scale - source_scale);
|
||||||
|
let factor = match 10_u128.checked_pow(exponent) {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return std::option::Option::None,
|
||||||
|
};
|
||||||
|
match coefficient.checked_mul(factor) {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return std::option::Option::None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let exponent = u32::from(source_scale - target_scale);
|
||||||
|
let divisor = match 10_u128.checked_pow(exponent) {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return std::option::Option::None,
|
||||||
|
};
|
||||||
|
let quotient = coefficient / divisor;
|
||||||
|
let remainder = coefficient % divisor;
|
||||||
|
if remainder >= divisor.div_ceil(2) {
|
||||||
|
match quotient.checked_add(1) {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return std::option::Option::None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quotient
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return std::option::Option::Some(format_scaled_decimal(rounded_coefficient, OUTPUT_SCALE));
|
||||||
|
}
|
||||||
|
|
||||||
fn format_scaled_decimal(mut coefficient: u128, mut scale: u8) -> String {
|
fn format_scaled_decimal(mut coefficient: u128, mut scale: u8) -> String {
|
||||||
while scale > 0 && coefficient.is_multiple_of(10) {
|
while scale > 0 && coefficient.is_multiple_of(10) {
|
||||||
coefficient /= 10;
|
coefficient /= 10;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "KSP Wallet Desk",
|
"productName": "KSP Wallet Desk",
|
||||||
"version": "0.2.12-pre.8.fix.1",
|
"version": "0.2.12-pre.8.fix.2",
|
||||||
"identifier": "com.sasedev.ksp-app-wallet-desk",
|
"identifier": "com.sasedev.ksp-app-wallet-desk",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": {
|
"beforeDevCommand": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-wallet-desk/tests/desktop_contract.rs
|
// file: crates/ksp-app-wallet-desk/tests/desktop_contract.rs
|
||||||
// version: 26
|
// version: 27
|
||||||
|
|
||||||
//! Desktop build, shell and Config-status contract audits for Wallet Desk.
|
//! Desktop build, shell and Config-status contract audits for Wallet Desk.
|
||||||
|
|
||||||
@@ -465,10 +465,6 @@ fn pre_018_packaged_runtime_bundles_config_resources_and_keeps_wallet_desk_versi
|
|||||||
let package_version = package.pointer("/version").and_then(serde_json::Value::as_str);
|
let package_version = package.pointer("/version").and_then(serde_json::Value::as_str);
|
||||||
assert_packaged_version_floor(package_version, "package.json");
|
assert_packaged_version_floor(package_version, "package.json");
|
||||||
assert_eq!(tauri_version, package_version, "desktop package metadata versions must remain synchronized");
|
assert_eq!(tauri_version, package_version, "desktop package metadata versions must remain synchronized");
|
||||||
let packaged_version = match package_version {
|
|
||||||
std::option::Option::Some(value) => value,
|
|
||||||
std::option::Option::None => return,
|
|
||||||
};
|
|
||||||
let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object);
|
let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object);
|
||||||
assert!(resources.is_some(), "packaged Wallet Desk Config resources map must exist");
|
assert!(resources.is_some(), "packaged Wallet Desk Config resources map must exist");
|
||||||
if let std::option::Option::Some(resources) = resources {
|
if let std::option::Option::Some(resources) = resources {
|
||||||
@@ -495,7 +491,9 @@ fn pre_018_packaged_runtime_bundles_config_resources_and_keeps_wallet_desk_versi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
let main = read_text(root.join("frontend/main.html").as_path());
|
let main = read_text(root.join("frontend/main.html").as_path());
|
||||||
assert!(main.contains(packaged_version));
|
let main_ts = read_text(root.join("frontend/ts/main.ts").as_path());
|
||||||
|
assert!(main.contains(r#"id="appVersionBadge""#));
|
||||||
|
assert!(main_ts.contains(r#"setText("#appVersionBadge", status.applicationVersion);"#));
|
||||||
let tauri_source = read_text(root.join("src/tauri.rs").as_path());
|
let tauri_source = read_text(root.join("src/tauri.rs").as_path());
|
||||||
assert!(tauri_source.contains("ksp_config_lib::prepare_packaged_runtime"));
|
assert!(tauri_source.contains("ksp_config_lib::prepare_packaged_runtime"));
|
||||||
assert!(tauri_source.contains("tauri::utils::platform::resource_dir"));
|
assert!(tauri_source.contains("tauri::utils::platform::resource_dir"));
|
||||||
@@ -503,7 +501,7 @@ fn pre_018_packaged_runtime_bundles_config_resources_and_keeps_wallet_desk_versi
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn v0_2_12_pre_008_wallet_balance_refresh_adds_one_auxiliary_sol_usd_average_only() {
|
fn v0_2_12_pre_008_wallet_balance_refresh_adds_average_and_exact_usd_equivalent() {
|
||||||
let root = app_root();
|
let root = app_root();
|
||||||
let manifest = read_text(root.join("Cargo.toml").as_path());
|
let manifest = read_text(root.join("Cargo.toml").as_path());
|
||||||
let composite = read_text(root.join("../../config/composite.ksp-app-wallet-desk.json").as_path());
|
let composite = read_text(root.join("../../config/composite.ksp-app-wallet-desk.json").as_path());
|
||||||
@@ -519,10 +517,16 @@ fn v0_2_12_pre_008_wallet_balance_refresh_adds_one_auxiliary_sol_usd_average_onl
|
|||||||
assert!(state.contains("tokio::join!"));
|
assert!(state.contains("tokio::join!"));
|
||||||
assert!(state.contains("refresh_all().await"));
|
assert!(state.contains("refresh_all().await"));
|
||||||
assert!(balance.contains("sol_usd_average"));
|
assert!(balance.contains("sol_usd_average"));
|
||||||
|
assert!(balance.contains("sol_usd_equivalent"));
|
||||||
assert!(balance.contains("average_sol_usd_prices"));
|
assert!(balance.contains("average_sol_usd_prices"));
|
||||||
assert!(html.contains(r#"id="currentWalletSolUsdAverage""#));
|
assert!(html.contains(r#"id="currentWalletSolUsdAverage""#));
|
||||||
assert_eq!(html.matches("currentWalletSolUsdAverage").count(), 1);
|
assert!(html.contains(r#"id="currentWalletSolUsdEquivalent""#));
|
||||||
assert!(main.contains(r#"balance.solUsdAverage ? `${balance.solUsdAverage} USD` : "N.A.""#));
|
assert!(html.contains(r#"id="detailsSolUsdAverage""#));
|
||||||
|
assert!(html.contains(r#"id="detailsSolUsdEquivalent""#));
|
||||||
|
assert!(main.contains(r#"balance.solUsdAverage ? `${balance.solUsdAverage} USD/SOL` : "N.A.""#));
|
||||||
|
assert!(main.contains(r#"balance.solUsdEquivalent ? `${balance.solUsdEquivalent} USD` : "N.A.""#));
|
||||||
|
assert!(!main.contains("parseFloat("));
|
||||||
|
assert!(!main.contains("Number(balance.sol"));
|
||||||
assert!(!main.contains("refresh_market_price"));
|
assert!(!main.contains("refresh_market_price"));
|
||||||
assert!(!main.contains("refresh_all_market_prices"));
|
assert!(!main.contains("refresh_all_market_prices"));
|
||||||
assert!(!main.contains("fetch("));
|
assert!(!main.contains("fetch("));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-wallet-desk/unit_tests/wallet_balance.rs
|
// file: crates/ksp-app-wallet-desk/unit_tests/wallet_balance.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
//! Unit tests for exact Wallet balance presentation.
|
//! Unit tests for exact Wallet balance presentation.
|
||||||
|
|
||||||
@@ -31,3 +31,13 @@ fn sol_usd_average_returns_none_without_observation_and_handles_integer_fraction
|
|||||||
assert_eq!(crate::average_sol_usd_prices(&[one, two]), std::option::Option::Some("1.5".to_owned()));
|
assert_eq!(crate::average_sol_usd_prices(&[one, two]), std::option::Option::Some("1.5".to_owned()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sol_usd_equivalent_is_exact_rounded_and_unavailable_without_average() {
|
||||||
|
assert_eq!(crate::sol_usd_equivalent(0, std::option::Option::Some("104.01730745")), std::option::Option::Some("0".to_owned()));
|
||||||
|
assert_eq!(crate::sol_usd_equivalent(1_000_000_000, std::option::Option::Some("104.01730745")), std::option::Option::Some("104.01730745".to_owned()),);
|
||||||
|
assert_eq!(crate::sol_usd_equivalent(1_500_000_000, std::option::Option::Some("104.01730745")), std::option::Option::Some("156.02596118".to_owned()),);
|
||||||
|
assert_eq!(crate::sol_usd_equivalent(1, std::option::Option::Some("104.01730745")), std::option::Option::Some("0.0000001".to_owned()));
|
||||||
|
assert_eq!(crate::sol_usd_equivalent(1_000_000_000, std::option::Option::None), std::option::Option::None);
|
||||||
|
assert_eq!(crate::sol_usd_equivalent(1_000_000_000, std::option::Option::Some("invalid")), std::option::Option::None);
|
||||||
|
}
|
||||||
|
|||||||
78
deltas/0.2.12/pre.008-fix.002.md
Normal file
78
deltas/0.2.12/pre.008-fix.002.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<!-- file: deltas/0.2.12/pre.008-fix.002.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta `0.2.12-pre.008-fix.002` — équivalent USD Wallet et version desktop dynamique
|
||||||
|
|
||||||
|
## Base
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.2.12-pre.008-fix.001
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cause
|
||||||
|
|
||||||
|
Le gate opérateur de `pre.008-fix.001` confirme `cargo fmt`, les audits KSP, `cargo check --workspace`, Clippy et le démarrage Tauri. Le refresh Wallet exécute aussi correctement `getBalance` et la collecte SOL/USD : huit providers sont chargés, six observations sont produites et la moyenne est disponible.
|
||||||
|
|
||||||
|
Deux écarts restent néanmoins visibles :
|
||||||
|
|
||||||
|
- la vue Details n'affiche ni le prix moyen SOL/USD calculé ni l'équivalent USD de la balance demandé par l'opérateur ;
|
||||||
|
- le badge principal de Wallet Desk reste figé à `0.2.6`, et le canari historique de packaging recherche encore une version littérale dans `frontend/main.html`, ce qui fait échouer `cargo test -p ksp-app-wallet-desk` après le passage à `0.2.12-pre.8.fix.1`.
|
||||||
|
|
||||||
|
## Correctif
|
||||||
|
|
||||||
|
- version workspace : `0.2.12-pre.8.fix.2` ;
|
||||||
|
- versions npm/Tauri Wallet Desk synchronisées sur `0.2.12-pre.8.fix.2` ;
|
||||||
|
- `WalletBalanceDto` gagne `sol_usd_equivalent: Option<String>` ;
|
||||||
|
- l'équivalent USD est calculé côté Rust à partir des lamports et de `sol_usd_average`, sans `f64`/`f32`, avec arrondi half-up à huit décimales maximum et dégradation à `None` si le prix auxiliaire est absent ou si l'arithmétique ne peut pas être représentée ;
|
||||||
|
- Dashboard et Details affichent désormais le prix moyen en `USD/SOL` et l'équivalent de la balance en `USD`, ou `N.A.` lorsque la moyenne auxiliaire est indisponible ;
|
||||||
|
- le badge statique `0.2.6` est remplacé par un élément alimenté par `RuntimeStatusDto.application_version` ;
|
||||||
|
- le canari `pre_018_packaged_runtime_bundles_config_resources_and_keeps_wallet_desk_version_coherent` vérifie le binding dynamique de version au lieu de rechercher la version courante en littéral dans le HTML ;
|
||||||
|
- les canaris `pre.008` vérifient l'absence de calcul flottant frontend et la présence des projections moyenne/équivalent dans Dashboard et Details.
|
||||||
|
|
||||||
|
## Invariants préservés
|
||||||
|
|
||||||
|
```text
|
||||||
|
getBalance autoritatif inchangé
|
||||||
|
MarketPriceService::refresh_all inchangé
|
||||||
|
providers utilisés pour la moyenne succès du refresh courant uniquement
|
||||||
|
prix auxiliaire indisponible balance SOL valide, moyenne/équivalent N.A.
|
||||||
|
provider_id / URL / credential frontend absents
|
||||||
|
commande prix dédiée Wallet absente
|
||||||
|
fetch / calcul prix frontend absents
|
||||||
|
WalletSession VIEW/OWNER inchangée
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucune donnée provider détaillée n'est ajoutée à Wallet Desk. L'équivalent USD est une projection auxiliaire dérivée de la moyenne déjà calculée par `pre.008` ; SOL Prices Desk reste l'application dédiée aux observations et disponibilités provider par provider.
|
||||||
|
|
||||||
|
## Fichiers modifiés/ajoutés
|
||||||
|
|
||||||
|
```text
|
||||||
|
Cargo.toml
|
||||||
|
crates/ksp-app-wallet-desk/package.json
|
||||||
|
crates/ksp-app-wallet-desk/tauri.conf.json
|
||||||
|
crates/ksp-app-wallet-desk/frontend/main.html
|
||||||
|
crates/ksp-app-wallet-desk/frontend/ts/main.ts
|
||||||
|
crates/ksp-app-wallet-desk/src/app_state.rs
|
||||||
|
crates/ksp-app-wallet-desk/src/lib.rs
|
||||||
|
crates/ksp-app-wallet-desk/src/wallet_balance.rs
|
||||||
|
crates/ksp-app-wallet-desk/tests/desktop_contract.rs
|
||||||
|
crates/ksp-app-wallet-desk/unit_tests/wallet_balance.rs
|
||||||
|
docs/plans/019-V0_2_12_SOL_PRICES_DESK_PLAN.md
|
||||||
|
docs/validation/015-V0_2_12_SOL_PRICES_DESK.md
|
||||||
|
deltas/0.2.12/pre.008-fix.002.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Gate opérateur attendu
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fmt --all
|
||||||
|
python3 scripts/audit_rust_workspace_rules.py
|
||||||
|
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.12
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets
|
||||||
|
cargo test -p ksp-app-wallet-desk
|
||||||
|
cargo test --workspace
|
||||||
|
(cd crates/ksp-app-wallet-desk && cargo tauri dev)
|
||||||
|
```
|
||||||
|
|
||||||
|
Le contrôle manuel doit confirmer que Details affiche le prix moyen SOL/USD et l'équivalent USD après `Refresh balance`, que Dashboard reflète les mêmes valeurs, et que le badge d'application n'affiche plus une version statique obsolète.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/plans/019-V0_2_12_SOL_PRICES_DESK_PLAN.md -->
|
<!-- file: docs/plans/019-V0_2_12_SOL_PRICES_DESK_PLAN.md -->
|
||||||
<!-- version: 15 -->
|
<!-- version: 16 -->
|
||||||
|
|
||||||
# Plan `0.2.12` — SOL Prices Desk + intégration prix Wallet Desk
|
# Plan `0.2.12` — SOL Prices Desk + intégration prix Wallet Desk
|
||||||
|
|
||||||
@@ -555,15 +555,21 @@ Finalisation de la table responsive sans changer le contrat de dispatch : résum
|
|||||||
|
|
||||||
### `pre.008` — Intégration Wallet minimale + recalibrage de clôture
|
### `pre.008` — Intégration Wallet minimale + recalibrage de clôture
|
||||||
|
|
||||||
**Statut : implémenté ; corrigé par `pre.008-fix.001`, gate opérateur à confirmer.**
|
**Statut : réalisé ; corrigé par `pre.008-fix.001` puis `pre.008-fix.002`, gate opérateur final à confirmer.**
|
||||||
|
|
||||||
Absorption de l'ancien couloir Wallet `pre.009`–`pre.011` dans une seule tranche : ajout `offchain_transport/public_keyless` au composite Wallet, runtime Off-chain auxiliaire, `refresh_all` lors de `refresh_wallet_balance`, moyenne SOL/USD backend sans `f64`, `WalletBalanceDto.sol_usd_average` et une seule case UI `SOL/USD moyen`/`N.A.`. Aucun panneau provider, aucune command prix Wallet, aucun provider ID frontend et aucune valorisation USD du solde. La tranche réconcilie également le présent forecast avec cette réduction de scope.
|
Absorption de l'ancien couloir Wallet `pre.009`–`pre.011` dans une seule tranche : ajout `offchain_transport/public_keyless` au composite Wallet, runtime Off-chain auxiliaire, `refresh_all` lors de `refresh_wallet_balance`, moyenne SOL/USD backend sans `f64` et projection compacte dans Wallet Desk. La correction `fix.002` complète le besoin opérateur réellement attendu : `WalletBalanceDto` expose également un équivalent USD exact calculé côté Rust à partir des lamports et de la moyenne du refresh courant ; Dashboard et Details affichent le prix moyen et l'équivalent ou `N.A.`. Aucun panneau provider, aucune command prix Wallet et aucun provider ID frontend ne sont ajoutés. La tranche réconcilie également le forecast avec cette réduction de scope.
|
||||||
|
|
||||||
#### `pre.008-fix.001` — Correction du canari desktop et Clippy de la moyenne
|
#### `pre.008-fix.001` — Correction du canari desktop et Clippy de la moyenne
|
||||||
|
|
||||||
|
**Statut : réalisé ; gate opérateur technique partiellement PASS, interrompu par un canari historique de version.**
|
||||||
|
|
||||||
|
Le premier gate de `pre.008` confirme `cargo check --workspace` mais révèle deux défauts purement techniques : des littéraux Rust non échappés dans le nouveau canari `desktop_contract.rs`, qui empêchent `cargo fmt` et les targets de test de parser le fichier, et un warning Clippy `manual_is_multiple_of` dans le formateur décimal Wallet. Le fix remplace les littéraux fragiles par des raw strings Rust et applique `u128::is_multiple_of(10)`. Son gate confirme ensuite `fmt`, audits, `check` et Clippy, puis s'arrête sur le canari `pre_018_packaged_runtime_bundles_config_resources_and_keeps_wallet_desk_version_coherent`, encore couplé à une version littérale dans le HTML. Le runtime Tauri et le refresh balance/prix fonctionnent.
|
||||||
|
|
||||||
|
#### `pre.008-fix.002` — Équivalent USD, projection Details et version desktop dynamique
|
||||||
|
|
||||||
**Statut : implémenté ; gate opérateur à confirmer.**
|
**Statut : implémenté ; gate opérateur à confirmer.**
|
||||||
|
|
||||||
Le premier gate de `pre.008` confirme `cargo check --workspace` mais révèle deux défauts purement techniques : des littéraux Rust non échappés dans le nouveau canari `desktop_contract.rs`, qui empêchent `cargo fmt` et les targets de test de parser le fichier, et un warning Clippy `manual_is_multiple_of` dans le formateur décimal Wallet. Le fix remplace les littéraux fragiles par des raw strings Rust et applique `u128::is_multiple_of(10)`. Aucune logique de moyenne, aucun appel Off-chain, aucun DTO ni aucune UI n'est modifié.
|
Correction du dernier écart fonctionnel de la tranche : le refresh produisait bien une moyenne SOL/USD mais elle n'était visible que dans le résumé Dashboard, tandis que Details n'affichait ni cette moyenne ni la valeur USD équivalente de la balance. `WalletBalanceDto.sol_usd_equivalent` est désormais calculé côté Rust sans flottant, à partir des lamports et de `sol_usd_average`, avec arrondi half-up à huit décimales maximum ; l'UI rend moyenne et équivalent dans Dashboard et Details, ou `N.A.` si le prix auxiliaire est indisponible. Le badge statique `0.2.6` est remplacé par `RuntimeStatusDto.application_version`, et le canari packaging vérifie ce binding version-neutral au lieu d'exiger la version courante en littéral dans `main.html`.
|
||||||
|
|
||||||
### `pre.009` — Hardening transversal + gate technique/live final
|
### `pre.009` — Hardening transversal + gate technique/live final
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/validation/015-V0_2_12_SOL_PRICES_DESK.md -->
|
<!-- file: docs/validation/015-V0_2_12_SOL_PRICES_DESK.md -->
|
||||||
<!-- version: 14 -->
|
<!-- version: 15 -->
|
||||||
|
|
||||||
# Validation `0.2.12` — SOL Prices Desk + intégration Wallet Desk
|
# Validation `0.2.12` — SOL Prices Desk + intégration Wallet Desk
|
||||||
|
|
||||||
@@ -586,9 +586,10 @@ getBalance + refresh_all lancés dans l
|
|||||||
observations prises pour moyenne uniquement succès du refresh courant
|
observations prises pour moyenne uniquement succès du refresh courant
|
||||||
moyenne backend, aucune conversion f64, 8 décimales max
|
moyenne backend, aucune conversion f64, 8 décimales max
|
||||||
WalletBalanceDto.sol_usd_average Option<String>
|
WalletBalanceDto.sol_usd_average Option<String>
|
||||||
UI Wallet une case SOL/USD moyen / N.A.
|
WalletBalanceDto.sol_usd_equivalent Option<String>, calcul exact backend
|
||||||
|
UI Wallet moyenne + équivalent dans Dashboard/Details, sinon N.A.
|
||||||
provider_id / URL / credential / command prix dans frontend absent
|
provider_id / URL / credential / command prix dans frontend absent
|
||||||
valorisation balance SOL en USD absente
|
calcul de valorisation frontend / f64 absent
|
||||||
WalletSession / VIEW / OWNER / secrets inchangés
|
WalletSession / VIEW / OWNER / secrets inchangés
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -615,16 +616,60 @@ cargo check --workspace PASS
|
|||||||
cargo clippy --workspace --all-targets FAIL — desktop_contract.rs + manual_is_multiple_of
|
cargo clippy --workspace --all-targets FAIL — desktop_contract.rs + manual_is_multiple_of
|
||||||
```
|
```
|
||||||
|
|
||||||
Le correctif attendu est strictement mécanique :
|
`pre.008-fix.001` corrige ces deux défauts mécaniques. Son gate opérateur du `27 août 2026` donne ensuite :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
desktop_contract.rs raw strings Rust valides
|
cargo fmt --all PASS
|
||||||
wallet_balance.rs coefficient.is_multiple_of(10)
|
python3 scripts/audit_rust_workspace_rules.py PASS
|
||||||
version 0.2.12-pre.8.fix.1
|
python3 scripts/audit_markdown_tables.py README.md ... deltas PASS
|
||||||
logique moyenne / Off-chain / DTO / frontend inchangée
|
cargo check --workspace PASS
|
||||||
|
cargo clippy --workspace --all-targets PASS
|
||||||
|
cargo test -p ksp-app-wallet-desk FAIL — 1 canari desktop historique
|
||||||
|
cargo test --workspace NOT RUN après l'échec ciblé
|
||||||
|
cargo tauri dev Wallet Desk PASS
|
||||||
```
|
```
|
||||||
|
|
||||||
Gate opérateur à rejouer après application :
|
Le test ciblé passe `61` unit tests, `3` tests de composition et `18/19` tests `desktop_contract`. L'unique échec est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
pre_018_packaged_runtime_bundles_config_resources_and_keeps_wallet_desk_version_coherent
|
||||||
|
assertion failed: main.contains(packaged_version)
|
||||||
|
```
|
||||||
|
|
||||||
|
Le runtime manuel confirme en parallèle que le chemin métier est opérationnel :
|
||||||
|
|
||||||
|
```text
|
||||||
|
provider_count=8
|
||||||
|
observed_provider_count=6
|
||||||
|
average_available=true
|
||||||
|
sol_usd_average_available=true
|
||||||
|
getBalance succès
|
||||||
|
cargo tauri dev succès
|
||||||
|
```
|
||||||
|
|
||||||
|
Deux écarts restent donc à corriger dans `pre.008-fix.002` : le badge principal est encore figé à `0.2.6`, et la vue Details ne montre pas l'information prix auxiliaire. La demande opérateur précise en outre que l'équivalent USD de la balance doit être visible.
|
||||||
|
|
||||||
|
## 26. Gate `0.2.12-pre.008-fix.002` — équivalent USD et version dynamique
|
||||||
|
|
||||||
|
Le correctif attendu reste dans la responsabilité Wallet condensée de `pre.008` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
WalletBalanceDto.sol_usd_average conservé
|
||||||
|
WalletBalanceDto.sol_usd_equivalent ajouté, Option<String>
|
||||||
|
source de l'équivalent lamports + moyenne du refresh courant
|
||||||
|
calcul Rust/u128, aucun f64/f32/JS
|
||||||
|
arrondi équivalent half-up, 8 décimales max
|
||||||
|
prix auxiliaire indisponible moyenne N.A. + équivalent N.A.
|
||||||
|
balance SOL autoritative inchangée
|
||||||
|
Dashboard moyenne + équivalent
|
||||||
|
Details moyenne + équivalent
|
||||||
|
badge 0.2.6 statique supprimé
|
||||||
|
version affichée RuntimeStatusDto.application_version
|
||||||
|
canari packaging version-neutral, vérifie le binding dynamique
|
||||||
|
provider_id / URL / credential / command prix Wallet absent
|
||||||
|
```
|
||||||
|
|
||||||
|
Gate opérateur à rejouer :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo fmt --all
|
cargo fmt --all
|
||||||
@@ -636,4 +681,3 @@ cargo test -p ksp-app-wallet-desk
|
|||||||
cargo test --workspace
|
cargo test --workspace
|
||||||
(cd crates/ksp-app-wallet-desk && cargo tauri dev)
|
(cd crates/ksp-app-wallet-desk && cargo tauri dev)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user