v0.5.2-pre.006-fix-004
This commit is contained in:
@@ -1,8 +1,17 @@
|
||||
<!-- file: kb-app-demo-desktop/CHANGELOG.md -->
|
||||
<!-- version: 56 -->
|
||||
<!-- version: 57 -->
|
||||
|
||||
# CHANGELOG — kb-app-demo-desktop
|
||||
|
||||
## `0.5.2-pre.006-delta-fix-004`
|
||||
|
||||
- complète la fenêtre `Wallets` avec inspection publique, import et export des deux formats `ks-wallet` réellement supportés : Solana CLI JSON 64 octets et keypair Base58 64 octets ;
|
||||
- garde le password hors Tauri IPC : création, import et export utilisent uniquement `KB_SECRET_DEMO_WALLET_PASSWORD` côté backend ;
|
||||
- permet d'inspecter un ancien fichier keypair sans le convertir, d'en extraire uniquement la pubkey puis de l'envoyer directement vers l'explorateur Devnet/Mainnet ;
|
||||
- peuple les sélecteurs de formats depuis l'inventaire exposé par `ks-wallet` afin d'éviter une liste frontend indépendante du backend ;
|
||||
- aligne visuellement `Profil RPC`, `Adresse publique` et `Transactions`, puis affiche le profil/cluster sélectionné sous la ligne de contrôles ;
|
||||
- prépare la validation suivante où les démos d'exécution Devnet devront consommer une capacité `.kswallet` explicite au lieu de recréer directement leur wallet JSON temporaire.
|
||||
|
||||
## `0.5.2-pre.006-delta-fix-003`
|
||||
|
||||
- ajoute dans `Wallets` un sélecteur explicite de profil RPC pour l’explorateur on-chain ; le profil actif reste la valeur initiale mais Devnet/Mainnet peuvent être interrogés sans redémarrer l’application ni modifier `active_profile` ;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/README.md -->
|
||||
<!-- version: 19 -->
|
||||
<!-- version: 20 -->
|
||||
|
||||
# kb-app-demo-desktop
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
## Fonctionnalités
|
||||
|
||||
- configuration et diagnostics de logging ;
|
||||
- fenêtre Wallets dédiée : inventaire sûr des identités `.kswallet`, création d’un conteneur persistant via un secret backend-only, inspection explicite d’un fichier externe et exploration publique du solde SOL, des comptes SPL Token / Token-2022 et des transactions récentes sur un profil RPC explicitement sélectionnable ;
|
||||
- fenêtre Wallets dédiée : inventaire sûr des identités `.kswallet`, création d’un conteneur persistant via un secret backend-only, inspection/import/export des formats keypair supportés, extraction sûre de pubkey depuis un keypair legacy, inspection explicite d’un `.kswallet` externe et exploration publique du solde SOL, des comptes SPL Token / Token-2022 et des transactions récentes sur un profil RPC explicitement sélectionnable ;
|
||||
- transport HTTP JSON-RPC et WebSocket ;
|
||||
- diagnostics PostgreSQL ;
|
||||
- backfill, extraction Core et replay, avec enregistrement générique de Solana Program Metadata ;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<!-- file: kb-app-demo-desktop/TODO.md -->
|
||||
<!-- version: 16 -->
|
||||
<!-- version: 17 -->
|
||||
|
||||
# TODO — kb-app-demo-desktop
|
||||
|
||||
## Évolutions générales
|
||||
|
||||
- [ ] `0.5.2` — valider les démos Devnet Solana Core/SPL/Metadata avec un `.kswallet` sélectionné et déverrouillé côté backend, sans transmettre le password au frontend.
|
||||
- [ ] `0.5.4` — réduire les panneaux d'exécution à l'adaptation UI/Tauri en déplaçant toute orchestration de campagne encore réutilisable vers `ks-pipeline-demo-scenarios`.
|
||||
- [ ] UX — empêcher les doubles déclenchements pendant une préparation, simulation ou soumission active.
|
||||
- [ ] Observabilité — afficher explicitement les comptes relus et les projections matérialisées.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/USAGE.md -->
|
||||
<!-- version: 19 -->
|
||||
<!-- version: 20 -->
|
||||
|
||||
# Utilisation de kb-app-demo-desktop
|
||||
|
||||
@@ -11,10 +11,12 @@ L’application expose une interface opérateur pour diagnostiquer, simuler et v
|
||||
|
||||
Le backend desktop peut lister les `.kswallet` du répertoire configuré, créer un wallet natif protégé, inspecter un fichier sélectionné explicitement ailleurs et explorer l’état on-chain d’une adresse publique. Les DTO frontend ne contiennent jamais de matériau secret. Aucun password, chemin interne du store, sel, nonce, ciphertext ou byte privé n’est renvoyé par Tauri.
|
||||
|
||||
La fenêtre `Wallets`, accessible depuis le menu `Démos` de `main`, fournit quatre surfaces :
|
||||
La fenêtre `Wallets`, accessible depuis le menu `Démos` de `main`, fournit maintenant six surfaces :
|
||||
|
||||
- inventaire des wallets natifs du profil actif ;
|
||||
- création d’un nouveau `.kswallet` dans le store du profil actif ;
|
||||
- inspection et import d'un keypair externe dans un format supporté ;
|
||||
- export authentifié d'un `.kswallet` vers un format supporté ;
|
||||
- explorateur public on-chain ;
|
||||
- inspection structurale d’un `.kswallet` explicite situé hors du store.
|
||||
|
||||
@@ -31,6 +33,19 @@ Le secret peut aussi être placé dans le fichier `.env` local chargé par `ks-c
|
||||
|
||||
La création demande uniquement l’alias. Elle crée `<alias>.kswallet` dans le répertoire wallet résolu du profil actif et ne modifie pas automatiquement `wallet_alias` dans la configuration. Un changement de valeur de `KB_SECRET_DEMO_WALLET_PASSWORD` avant une autre session permet d’utiliser un autre mot de passe pour de nouveaux wallets ; ce secret de démonstration n’est pas un mot de passe global du format `.kswallet`.
|
||||
|
||||
### Inspecter, importer et exporter les formats keypair
|
||||
|
||||
Les sélecteurs de format sont alimentés par `ks-wallet` et exposent actuellement :
|
||||
|
||||
- `Solana CLI keypair JSON (64 octets)` ;
|
||||
- `Solana private key Base58 (64 octets)`.
|
||||
|
||||
L'action **Inspecter la keypair** ne crée aucun `.kswallet` : le backend valide le fichier avec le codec choisi et ne renvoie que la pubkey. Cette pubkey peut être envoyée directement à l'explorateur on-chain, ce qui permet par exemple de contrôler un ancien `wallets/temporary/local_devnet/*.json` avant de décider de le migrer.
|
||||
|
||||
L'action **Importer en .kswallet** demande en plus un alias destination et utilise `KB_SECRET_DEMO_WALLET_PASSWORD` uniquement côté backend. La source reste inchangée. L'export fonctionne dans le sens inverse : choix d'un alias natif, du format et d'un chemin destination explicite ; le backend authentifie le `.kswallet` avec le même secret de démonstration et refuse d'écraser un fichier existant.
|
||||
|
||||
Un keypair JSON de 64 octets ne contient pas son rôle métier. L'inspection peut dire « keypair Solana valide » et donner sa pubkey, mais elle ne peut pas décider localement si cette clé a servi de wallet opérateur, mint, authority ou autre fixture. Cette classification nécessite le contexte du scénario ou une lecture on-chain séparée.
|
||||
|
||||
### Explorer une adresse sur un profil RPC sélectionné
|
||||
|
||||
L’explorateur on-chain ne déverrouille aucun wallet : seule la pubkey publique est nécessaire. Le bouton `Explorer` d’un wallet inventorié remplit l’adresse automatiquement ; toute autre pubkey Solana peut également être saisie.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_wallet.html -->
|
||||
<!-- version: 3 -->
|
||||
<!-- version: 4 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -90,6 +90,62 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-xl-7">
|
||||
<div class="card shadow-sm border-0 h-100">
|
||||
<div class="card-body">
|
||||
<h2 class="h5 card-title mb-2">Importer ou inspecter une keypair externe</h2>
|
||||
<p class="text-body-secondary">Le fichier source reste inchangé. L'inspection valide la keypair et ne retourne que sa pubkey ; l'import crée ensuite un nouveau <code>.kswallet</code> dans le store du profil actif.</p>
|
||||
<label for="walletImportSourcePathInput" class="form-label">Chemin source</label>
|
||||
<input id="walletImportSourcePathInput" class="form-control font-monospace" type="text" autocomplete="off" placeholder="/chemin/vers/local-devnet-operator.json">
|
||||
<div class="row g-3 mt-1">
|
||||
<div class="col-12 col-md-6">
|
||||
<label for="walletImportFormatSelect" class="form-label">Format source</label>
|
||||
<select id="walletImportFormatSelect" class="form-select"></select>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label for="walletImportAliasInput" class="form-label">Alias destination</label>
|
||||
<input id="walletImportAliasInput" class="form-control font-monospace" type="text" autocomplete="off" maxlength="64" placeholder="local-devnet-operator">
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap gap-2 mt-3">
|
||||
<button id="inspectWalletTransferButton" class="btn btn-outline-primary" type="button">Inspecter la keypair</button>
|
||||
<button id="importWalletTransferButton" class="btn btn-primary" type="button">Importer en .kswallet</button>
|
||||
<button id="exploreWalletTransferButton" class="btn btn-outline-secondary" type="button" disabled>Explorer la pubkey</button>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<span class="fw-semibold">Pubkey détectée :</span>
|
||||
<code id="walletTransferPublicKey" class="ms-1 text-break">—</code>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<span class="fw-semibold">Statut :</span>
|
||||
<code id="walletTransferStatus" class="ms-1">En attente</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-xl-5">
|
||||
<div class="card shadow-sm border-0 h-100">
|
||||
<div class="card-body">
|
||||
<h2 class="h5 card-title mb-2">Exporter un wallet natif</h2>
|
||||
<p class="text-body-secondary">L'export exige le même secret backend que la création et refuse d'écraser un fichier existant.</p>
|
||||
<label for="walletExportAliasSelect" class="form-label">Wallet</label>
|
||||
<select id="walletExportAliasSelect" class="form-select"></select>
|
||||
<label for="walletExportFormatSelect" class="form-label mt-3">Format d'export</label>
|
||||
<select id="walletExportFormatSelect" class="form-select"></select>
|
||||
<label for="walletExportDestinationPathInput" class="form-label mt-3">Chemin destination</label>
|
||||
<input id="walletExportDestinationPathInput" class="form-control font-monospace" type="text" autocomplete="off" placeholder="/chemin/vers/export.json">
|
||||
<div class="d-flex flex-wrap gap-2 mt-3">
|
||||
<button id="exportWalletTransferButton" class="btn btn-primary" type="button">Exporter</button>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<span class="fw-semibold">Statut :</span>
|
||||
<code id="walletExportStatus" class="ms-1">En attente</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
@@ -104,7 +160,6 @@
|
||||
<div class="col-12 col-lg-3">
|
||||
<label for="walletOnchainProfileSelect" class="form-label">Profil RPC</label>
|
||||
<select id="walletOnchainProfileSelect" class="form-select"></select>
|
||||
<div class="small text-body-secondary mt-1">Cluster : <code id="walletOnchainCluster">—</code></div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-7">
|
||||
<label for="walletAddressInput" class="form-label">Adresse publique</label>
|
||||
@@ -115,6 +170,7 @@
|
||||
<input id="walletSignatureLimitInput" class="form-control" type="number" min="1" max="100" value="20">
|
||||
</div>
|
||||
</div>
|
||||
<div class="small text-body-secondary mt-2">Profil sélectionné : <code id="walletOnchainSelectedProfile">—</code> · Cluster : <code id="walletOnchainCluster">—</code></div>
|
||||
<div class="row g-3 mt-2">
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="border rounded p-3 h-100">
|
||||
@@ -242,4 +298,4 @@
|
||||
<script type="module" src="ts/demo_wallet.ts" defer></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_wallet.ts
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
import * as bootstrap from "bootstrap";
|
||||
import "simplebar";
|
||||
@@ -9,11 +9,16 @@ import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./fr
|
||||
import { renderJsonViewer } from "./json_viewer.ts";
|
||||
import type { DemoWalletBalancePayload } from "./bindings/kb_app_demo_desktop/demo_wallet/DemoWalletBalancePayload.ts";
|
||||
import type { DemoWalletCreateRequest } from "./bindings/kb_app_demo_desktop/demo_wallet/DemoWalletCreateRequest.ts";
|
||||
import type { DemoWalletExportRequest } from "./bindings/kb_app_demo_desktop/demo_wallet/DemoWalletExportRequest.ts";
|
||||
import type { DemoWalletIdentityPayload } from "./bindings/kb_app_demo_desktop/demo_wallet/DemoWalletIdentityPayload.ts";
|
||||
import type { DemoWalletImportRequest } from "./bindings/kb_app_demo_desktop/demo_wallet/DemoWalletImportRequest.ts";
|
||||
import type { DemoWalletInventoryPayload } from "./bindings/kb_app_demo_desktop/demo_wallet/DemoWalletInventoryPayload.ts";
|
||||
import type { DemoWalletOnchainProfilePayload } from "./bindings/kb_app_demo_desktop/demo_wallet/DemoWalletOnchainProfilePayload.ts";
|
||||
import type { DemoWalletRpcExecutionPayload } from "./bindings/kb_app_demo_desktop/demo_wallet/DemoWalletRpcExecutionPayload.ts";
|
||||
import type { DemoWalletRpcRequest } from "./bindings/kb_app_demo_desktop/demo_wallet/DemoWalletRpcRequest.ts";
|
||||
import type { DemoWalletTransferFormatPayload } from "./bindings/kb_app_demo_desktop/demo_wallet/DemoWalletTransferFormatPayload.ts";
|
||||
import type { DemoWalletTransferInspectionPayload } from "./bindings/kb_app_demo_desktop/demo_wallet/DemoWalletTransferInspectionPayload.ts";
|
||||
import type { DemoWalletTransferInspectionRequest } from "./bindings/kb_app_demo_desktop/demo_wallet/DemoWalletTransferInspectionRequest.ts";
|
||||
|
||||
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
||||
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
||||
@@ -37,6 +42,7 @@ interface ParsedSignatureInfo {
|
||||
}
|
||||
|
||||
let walletInventory: DemoWalletInventoryPayload | null = null;
|
||||
let inspectedTransferPublicKey = "";
|
||||
|
||||
function setText(selector: string, value: string): void {
|
||||
const element = document.querySelector<HTMLElement>(selector);
|
||||
@@ -63,6 +69,7 @@ function selectedOnchainProfilePayload(): DemoWalletOnchainProfilePayload | null
|
||||
|
||||
function updateOnchainClusterLabel(): void {
|
||||
const selected = selectedOnchainProfilePayload();
|
||||
setText("#walletOnchainSelectedProfile", selected?.name ?? "—");
|
||||
setText("#walletOnchainCluster", selected?.cluster ?? "—");
|
||||
}
|
||||
|
||||
@@ -102,6 +109,65 @@ function parseJsonResponse(response: DemoWalletRpcExecutionPayload): unknown {
|
||||
return JSON.parse(response.response_json) as unknown;
|
||||
}
|
||||
|
||||
function renderTransferFormats(formats: ReadonlyArray<DemoWalletTransferFormatPayload>): void {
|
||||
for (const selector of ["#walletImportFormatSelect", "#walletExportFormatSelect"]) {
|
||||
const select = document.querySelector<HTMLSelectElement>(selector);
|
||||
if (!select) {
|
||||
continue;
|
||||
}
|
||||
const previous = select.value;
|
||||
select.replaceChildren();
|
||||
for (const format of formats) {
|
||||
const option = document.createElement("option");
|
||||
option.value = format.code;
|
||||
option.textContent = `${format.label} (.${format.default_extension})`;
|
||||
option.dataset.defaultExtension = format.default_extension;
|
||||
select.appendChild(option);
|
||||
}
|
||||
if (formats.some(format => format.code === previous)) {
|
||||
select.value = previous;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderExportWalletAliases(wallets: ReadonlyArray<DemoWalletIdentityPayload>): void {
|
||||
const select = document.querySelector<HTMLSelectElement>("#walletExportAliasSelect");
|
||||
if (!select) {
|
||||
return;
|
||||
}
|
||||
const previous = select.value;
|
||||
select.replaceChildren();
|
||||
if (wallets.length === 0) {
|
||||
const option = document.createElement("option");
|
||||
option.value = "";
|
||||
option.textContent = "Aucun wallet natif";
|
||||
select.appendChild(option);
|
||||
return;
|
||||
}
|
||||
for (const wallet of wallets) {
|
||||
const option = document.createElement("option");
|
||||
option.value = wallet.alias;
|
||||
option.textContent = `${wallet.alias} — ${wallet.public_key}`;
|
||||
select.appendChild(option);
|
||||
}
|
||||
if (wallets.some(wallet => wallet.alias === previous)) {
|
||||
select.value = previous;
|
||||
}
|
||||
}
|
||||
|
||||
function selectedTransferFormat(selector: string): string {
|
||||
return document.querySelector<HTMLSelectElement>(selector)?.value.trim() ?? "";
|
||||
}
|
||||
|
||||
function setTransferPublicKey(publicKey: string): void {
|
||||
inspectedTransferPublicKey = publicKey;
|
||||
setText("#walletTransferPublicKey", publicKey.length > 0 ? publicKey : "—");
|
||||
const exploreButton = document.querySelector<HTMLButtonElement>("#exploreWalletTransferButton");
|
||||
if (exploreButton) {
|
||||
exploreButton.disabled = publicKey.length === 0;
|
||||
}
|
||||
}
|
||||
|
||||
function renderWalletRows(wallets: ReadonlyArray<DemoWalletIdentityPayload>): void {
|
||||
const body = document.querySelector<HTMLTableSectionElement>("#walletInventoryTableBody");
|
||||
if (!body) {
|
||||
@@ -158,6 +224,8 @@ async function refreshWalletInventory(): Promise<void> {
|
||||
setText("#walletProfile", payload.profile);
|
||||
setText("#walletCluster", payload.cluster);
|
||||
renderOnchainProfiles(payload.onchain_profiles, payload.profile);
|
||||
renderTransferFormats(payload.transfer_formats);
|
||||
renderExportWalletAliases(payload.wallets);
|
||||
setText("#walletCreateSecretStatus", payload.create_password_configured ? "configuré" : "absent");
|
||||
setText("#walletSelectedAlias", payload.selected_alias ?? "—");
|
||||
setText("#walletInventoryCount", String(payload.wallets.length));
|
||||
@@ -169,6 +237,7 @@ async function refreshWalletInventory(): Promise<void> {
|
||||
setText("#walletInventoryStatus", `Erreur : ${message}`);
|
||||
setText("#walletInventoryCount", "0");
|
||||
renderWalletRows([]);
|
||||
renderExportWalletAliases([]);
|
||||
frontendError("kb-app-demo-desktop.frontend.demo_wallet", `Wallet inventory loading failed: ${message}`);
|
||||
}
|
||||
}
|
||||
@@ -201,6 +270,89 @@ async function createWallet(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function inspectTransferFile(): Promise<void> {
|
||||
const sourcePath = inputValue("#walletImportSourcePathInput");
|
||||
const format = selectedTransferFormat("#walletImportFormatSelect");
|
||||
if (sourcePath.length === 0 || format.length === 0) {
|
||||
setText("#walletTransferStatus", "Chemin source et format requis");
|
||||
setTransferPublicKey("");
|
||||
return;
|
||||
}
|
||||
const request: DemoWalletTransferInspectionRequest = { source_path: sourcePath, format };
|
||||
setText("#walletTransferStatus", "Inspection...");
|
||||
setTransferPublicKey("");
|
||||
try {
|
||||
const payload = await invoke<DemoWalletTransferInspectionPayload>("demo_wallet_inspect_transfer_file", { request });
|
||||
setTransferPublicKey(payload.public_key);
|
||||
setText("#walletTransferStatus", `Keypair valide — ${payload.format}`);
|
||||
frontendDebug("kb-app-demo-desktop.frontend.demo_wallet", "external transfer keypair inspected");
|
||||
} catch (caughtError) {
|
||||
const message = caughtError instanceof Error ? caughtError.message : String(caughtError);
|
||||
setText("#walletTransferStatus", `Erreur : ${message}`);
|
||||
setTransferPublicKey("");
|
||||
frontendError("kb-app-demo-desktop.frontend.demo_wallet", `External transfer inspection failed: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function importTransferFile(): Promise<void> {
|
||||
const sourcePath = inputValue("#walletImportSourcePathInput");
|
||||
const format = selectedTransferFormat("#walletImportFormatSelect");
|
||||
const alias = inputValue("#walletImportAliasInput");
|
||||
if (sourcePath.length === 0 || format.length === 0 || alias.length === 0) {
|
||||
setText("#walletTransferStatus", "Chemin source, format et alias requis");
|
||||
return;
|
||||
}
|
||||
const request: DemoWalletImportRequest = { alias, source_path: sourcePath, format };
|
||||
setText("#walletTransferStatus", "Import...");
|
||||
try {
|
||||
const payload = await invoke<DemoWalletIdentityPayload>("demo_wallet_import_file", { request });
|
||||
setTransferPublicKey(payload.public_key);
|
||||
setText("#walletTransferStatus", `Importé : ${payload.alias}.kswallet`);
|
||||
const addressInput = document.querySelector<HTMLInputElement>("#walletAddressInput");
|
||||
if (addressInput) {
|
||||
addressInput.value = payload.public_key;
|
||||
}
|
||||
await refreshWalletInventory();
|
||||
frontendDebug("kb-app-demo-desktop.frontend.demo_wallet", `external keypair imported: ${payload.alias}`);
|
||||
} catch (caughtError) {
|
||||
const message = caughtError instanceof Error ? caughtError.message : String(caughtError);
|
||||
setText("#walletTransferStatus", `Erreur : ${message}`);
|
||||
frontendError("kb-app-demo-desktop.frontend.demo_wallet", `External keypair import failed: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function exportWallet(): Promise<void> {
|
||||
const alias = document.querySelector<HTMLSelectElement>("#walletExportAliasSelect")?.value.trim() ?? "";
|
||||
const format = selectedTransferFormat("#walletExportFormatSelect");
|
||||
const destinationPath = inputValue("#walletExportDestinationPathInput");
|
||||
if (alias.length === 0 || format.length === 0 || destinationPath.length === 0) {
|
||||
setText("#walletExportStatus", "Wallet, format et destination requis");
|
||||
return;
|
||||
}
|
||||
const request: DemoWalletExportRequest = { alias, destination_path: destinationPath, format };
|
||||
setText("#walletExportStatus", "Export...");
|
||||
try {
|
||||
await invoke<void>("demo_wallet_export_file", { request });
|
||||
setText("#walletExportStatus", `Exporté : ${alias}`);
|
||||
frontendDebug("kb-app-demo-desktop.frontend.demo_wallet", `native wallet exported: ${alias}`);
|
||||
} catch (caughtError) {
|
||||
const message = caughtError instanceof Error ? caughtError.message : String(caughtError);
|
||||
setText("#walletExportStatus", `Erreur : ${message}`);
|
||||
frontendError("kb-app-demo-desktop.frontend.demo_wallet", `Native wallet export failed: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function exploreInspectedTransfer(): void {
|
||||
if (inspectedTransferPublicKey.length === 0) {
|
||||
return;
|
||||
}
|
||||
const addressInput = document.querySelector<HTMLInputElement>("#walletAddressInput");
|
||||
if (addressInput) {
|
||||
addressInput.value = inspectedTransferPublicKey;
|
||||
}
|
||||
document.querySelector<HTMLElement>("#walletAddressInput")?.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
}
|
||||
|
||||
function parseTokenAccounts(value: unknown, program: string): ParsedTokenAccount[] {
|
||||
if (typeof value !== "object" || value === null || !("value" in value)) {
|
||||
return [];
|
||||
@@ -504,6 +656,26 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
void createWallet();
|
||||
}
|
||||
});
|
||||
document.querySelector<HTMLButtonElement>("#inspectWalletTransferButton")?.addEventListener("click", () => {
|
||||
void inspectTransferFile();
|
||||
});
|
||||
document.querySelector<HTMLButtonElement>("#importWalletTransferButton")?.addEventListener("click", () => {
|
||||
void importTransferFile();
|
||||
});
|
||||
document.querySelector<HTMLButtonElement>("#exploreWalletTransferButton")?.addEventListener("click", () => {
|
||||
exploreInspectedTransfer();
|
||||
});
|
||||
document.querySelector<HTMLButtonElement>("#exportWalletTransferButton")?.addEventListener("click", () => {
|
||||
void exportWallet();
|
||||
});
|
||||
document.querySelector<HTMLInputElement>("#walletImportSourcePathInput")?.addEventListener("input", () => {
|
||||
setTransferPublicKey("");
|
||||
setText("#walletTransferStatus", "En attente");
|
||||
});
|
||||
document.querySelector<HTMLSelectElement>("#walletImportFormatSelect")?.addEventListener("change", () => {
|
||||
setTransferPublicKey("");
|
||||
setText("#walletTransferStatus", "En attente");
|
||||
});
|
||||
document.querySelector<HTMLSelectElement>("#walletOnchainProfileSelect")?.addEventListener("change", () => {
|
||||
updateOnchainClusterLabel();
|
||||
});
|
||||
|
||||
@@ -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