ObsidianDragon - DragonX ImGui Wallet
Full-node GUI wallet for DragonX cryptocurrency. Built with Dear ImGui, SDL3, and OpenGL3/DX11. Features: - Send/receive shielded and transparent transactions - Autoshield with merged transaction display - Built-in CPU mining (xmrig) - Peer management and network monitoring - Wallet encryption with PIN lock - QR code generation for receive addresses - Transaction history with pagination - Console for direct RPC commands - Cross-platform (Linux, Windows)
This commit is contained in:
526
src/app.h
Normal file
526
src/app.h
Normal file
@@ -0,0 +1,526 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include "data/wallet_state.h"
|
||||
#include "ui/sidebar.h"
|
||||
#include "ui/windows/console_tab.h"
|
||||
#include "imgui.h"
|
||||
|
||||
// Forward declarations
|
||||
namespace dragonx {
|
||||
namespace rpc {
|
||||
class RPCClient;
|
||||
class RPCWorker;
|
||||
struct ConnectionConfig;
|
||||
}
|
||||
namespace config { class Settings; }
|
||||
namespace daemon { class EmbeddedDaemon; class XmrigManager; }
|
||||
namespace util { class Bootstrap; class SecureVault; }
|
||||
}
|
||||
|
||||
namespace dragonx {
|
||||
|
||||
/**
|
||||
* @brief First-run wizard states
|
||||
*/
|
||||
enum class WizardPhase {
|
||||
None, // No wizard — normal operation
|
||||
BootstrapOffer, // Step 1: offer bootstrap download
|
||||
BootstrapInProgress,// downloading / extracting
|
||||
BootstrapFailed, // error with retry/skip option
|
||||
Appearance, // Step 2: visual effects / performance options
|
||||
EncryptOffer, // Step 3: offer wallet encryption
|
||||
EncryptInProgress, // encrypting + daemon restarting
|
||||
PinSetup, // Step 4: optional PIN setup (after encryption)
|
||||
Done // wizard complete, launch normally
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Encrypt-wallet dialog phases (settings page flow)
|
||||
*/
|
||||
enum class EncryptDialogPhase {
|
||||
PassphraseEntry, // Enter & confirm passphrase
|
||||
Encrypting, // In-progress animation
|
||||
PinSetup, // Offer PIN after successful encryption
|
||||
Done // Finished — close dialog
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Main application class
|
||||
*
|
||||
* Manages application state, RPC connection, and coordinates UI rendering.
|
||||
*/
|
||||
class App {
|
||||
public:
|
||||
App();
|
||||
~App();
|
||||
|
||||
// Non-copyable
|
||||
App(const App&) = delete;
|
||||
App& operator=(const App&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Initialize the application
|
||||
* @return true if initialization succeeded
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief Update application state (called every frame)
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* @brief Render the application UI (called every frame)
|
||||
*/
|
||||
void render();
|
||||
|
||||
/**
|
||||
* @brief Clean shutdown
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief Check if app should exit (render loop can stop)
|
||||
* Enforces minimum 1-second display of shutdown screen so user sees feedback.
|
||||
*/
|
||||
bool shouldQuit() const {
|
||||
if (!quit_requested_ || !shutdown_complete_) return false;
|
||||
auto elapsed = std::chrono::steady_clock::now() - shutdown_start_time_;
|
||||
return elapsed >= std::chrono::seconds(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request application exit — begins async shutdown
|
||||
*/
|
||||
void requestQuit();
|
||||
|
||||
/**
|
||||
* @brief Begin graceful shutdown (called on window close)
|
||||
* Starts daemon stop on a background thread while UI keeps rendering.
|
||||
*/
|
||||
void beginShutdown();
|
||||
|
||||
/**
|
||||
* @brief Whether we are in the shutdown phase
|
||||
*/
|
||||
bool isShuttingDown() const { return shutting_down_; }
|
||||
|
||||
/**
|
||||
* @brief Render the shutdown overlay (called instead of normal UI during shutdown)
|
||||
*/
|
||||
void renderShutdownScreen();
|
||||
|
||||
/**
|
||||
* @brief Render loading overlay in content area while daemon is starting/syncing
|
||||
* @param contentH Height of the content area child window
|
||||
*/
|
||||
void renderLoadingOverlay(float contentH);
|
||||
|
||||
// Accessors for subsystems
|
||||
rpc::RPCClient* rpc() { return rpc_.get(); }
|
||||
rpc::RPCWorker* worker() { return worker_.get(); }
|
||||
config::Settings* settings() { return settings_.get(); }
|
||||
WalletState& state() { return state_; }
|
||||
const WalletState& state() const { return state_; }
|
||||
const WalletState& getWalletState() const { return state_; }
|
||||
|
||||
// Connection state (convenience wrappers)
|
||||
bool isConnected() const { return state_.connected; }
|
||||
int getBlockHeight() const { return state_.sync.blocks; }
|
||||
const std::string& getConnectionStatus() const { return connection_status_; }
|
||||
const std::string& getDaemonStatus() const { return daemon_status_; }
|
||||
|
||||
// Balance info (convenience wrappers)
|
||||
double getShieldedBalance() const { return state_.shielded_balance; }
|
||||
double getTransparentBalance() const { return state_.transparent_balance; }
|
||||
double getTotalBalance() const { return state_.total_balance; }
|
||||
double getUnconfirmedBalance() const { return state_.unconfirmed_balance; }
|
||||
|
||||
// Addresses
|
||||
const std::vector<AddressInfo>& getZAddresses() const { return state_.z_addresses; }
|
||||
const std::vector<AddressInfo>& getTAddresses() const { return state_.t_addresses; }
|
||||
|
||||
// Transactions
|
||||
const std::vector<TransactionInfo>& getTransactions() const { return state_.transactions; }
|
||||
|
||||
// Mining
|
||||
const MiningInfo& getMiningInfo() const { return state_.mining; }
|
||||
void startMining(int threads);
|
||||
void stopMining();
|
||||
bool isMiningToggleInProgress() const { return mining_toggle_in_progress_.load(std::memory_order_relaxed); }
|
||||
|
||||
// Pool mining (xmrig)
|
||||
void startPoolMining(int threads);
|
||||
void stopPoolMining();
|
||||
|
||||
// Peers
|
||||
const std::vector<PeerInfo>& getPeers() const { return state_.peers; }
|
||||
const std::vector<BannedPeer>& getBannedPeers() const { return state_.bannedPeers; }
|
||||
void banPeer(const std::string& ip, int duration_seconds = 86400);
|
||||
void unbanPeer(const std::string& ip);
|
||||
void clearBans();
|
||||
|
||||
// Address operations
|
||||
void createNewZAddress(std::function<void(const std::string&)> callback = nullptr);
|
||||
void createNewTAddress(std::function<void(const std::string&)> callback = nullptr);
|
||||
|
||||
// Hide/unhide addresses from the address list (persisted in settings)
|
||||
void hideAddress(const std::string& addr);
|
||||
void unhideAddress(const std::string& addr);
|
||||
bool isAddressHidden(const std::string& addr) const;
|
||||
int getHiddenAddressCount() const;
|
||||
|
||||
void favoriteAddress(const std::string& addr);
|
||||
void unfavoriteAddress(const std::string& addr);
|
||||
bool isAddressFavorite(const std::string& addr) const;
|
||||
|
||||
// Key export/import
|
||||
void exportPrivateKey(const std::string& address, std::function<void(const std::string&)> callback);
|
||||
void exportAllKeys(std::function<void(const std::string&)> callback);
|
||||
void importPrivateKey(const std::string& key, std::function<void(bool, const std::string&)> callback);
|
||||
|
||||
// Wallet backup
|
||||
void backupWallet(const std::string& destination, std::function<void(bool, const std::string&)> callback);
|
||||
|
||||
// Transaction operations
|
||||
void sendTransaction(const std::string& from, const std::string& to,
|
||||
double amount, double fee, const std::string& memo,
|
||||
std::function<void(bool success, const std::string& result)> callback);
|
||||
|
||||
// Force refresh
|
||||
void refreshNow();
|
||||
void refreshMiningInfo();
|
||||
void refreshPeerInfo();
|
||||
void refreshMarketData();
|
||||
|
||||
// UI navigation
|
||||
void setCurrentPage(ui::NavPage page) { current_page_ = page; }
|
||||
ui::NavPage getCurrentPage() const { return current_page_; }
|
||||
|
||||
// Dialog triggers (used by settings page to open modal dialogs)
|
||||
void showImportKeyDialog() { show_import_key_ = true; }
|
||||
void showExportKeyDialog() { show_export_key_ = true; }
|
||||
void showBackupDialog() { show_backup_ = true; }
|
||||
void showAboutDialog() { show_about_ = true; }
|
||||
|
||||
// Legacy tab compat — maps int to NavPage
|
||||
void setCurrentTab(int tab);
|
||||
int getCurrentTab() const { return static_cast<int>(current_page_); }
|
||||
|
||||
// Payment URI handling
|
||||
void handlePaymentURI(const std::string& uri);
|
||||
bool hasPendingPayment() const { return pending_payment_valid_; }
|
||||
void clearPendingPayment() { pending_payment_valid_ = false; }
|
||||
std::string getPendingToAddress() const { return pending_to_address_; }
|
||||
double getPendingAmount() const { return pending_amount_; }
|
||||
std::string getPendingMemo() const { return pending_memo_; }
|
||||
std::string getPendingLabel() const { return pending_label_; }
|
||||
|
||||
// Embedded daemon control
|
||||
bool startEmbeddedDaemon();
|
||||
void stopEmbeddedDaemon();
|
||||
bool isEmbeddedDaemonRunning() const;
|
||||
bool isUsingEmbeddedDaemon() const { return use_embedded_daemon_; }
|
||||
void setUseEmbeddedDaemon(bool use) { use_embedded_daemon_ = use; }
|
||||
|
||||
// Get daemon memory usage in MB (uses embedded daemon handle if available,
|
||||
// falls back to platform-level process scan for external daemons)
|
||||
double getDaemonMemoryUsageMB() const;
|
||||
|
||||
// Diagnostic string describing daemon memory detection path (for debugging)
|
||||
const std::string& getDaemonMemDiag() const { return daemon_mem_diag_; }
|
||||
|
||||
// Background gradient overlay texture
|
||||
void setGradientTexture(ImTextureID tex) { gradient_tex_ = tex; }
|
||||
ImTextureID getGradientTexture() const { return gradient_tex_; }
|
||||
|
||||
// Logo texture accessor (wallet branding icon)
|
||||
ImTextureID getLogoTexture() const { return logo_tex_; }
|
||||
|
||||
// Coin logo texture accessor (DragonX currency icon for balance tab)
|
||||
ImTextureID getCoinLogoTexture() const { return coin_logo_tex_; }
|
||||
|
||||
/**
|
||||
* @brief Reload theme images (background gradient + logo) from new paths
|
||||
* @param bgPath Path to background image override (empty = use default)
|
||||
* @param logoPath Path to logo image override (empty = use default)
|
||||
*/
|
||||
void reloadThemeImages(const std::string& bgPath, const std::string& logoPath);
|
||||
|
||||
// Wizard / first-run
|
||||
WizardPhase getWizardPhase() const { return wizard_phase_; }
|
||||
bool isFirstRun() const;
|
||||
|
||||
/**
|
||||
* @brief Stop daemon and re-run the setup wizard
|
||||
* Called from Settings. Daemon restarts automatically when wizard completes.
|
||||
*/
|
||||
void restartWizard();
|
||||
|
||||
/**
|
||||
* @brief Restart the embedded daemon (e.g. after changing debug categories)
|
||||
* Shows "Restarting daemon..." in the loading overlay while the daemon cycles.
|
||||
*/
|
||||
void restartDaemon();
|
||||
|
||||
// Wallet encryption helpers
|
||||
void encryptWalletWithPassphrase(const std::string& passphrase);
|
||||
void unlockWallet(const std::string& passphrase, int timeout);
|
||||
void lockWallet();
|
||||
void changePassphrase(const std::string& oldPass, const std::string& newPass);
|
||||
|
||||
// Dialog triggers for encryption (from settings page)
|
||||
void showEncryptDialog() {
|
||||
show_encrypt_dialog_ = true;
|
||||
encrypt_dialog_phase_ = EncryptDialogPhase::PassphraseEntry;
|
||||
encrypt_status_.clear();
|
||||
memset(encrypt_pass_buf_, 0, sizeof(encrypt_pass_buf_));
|
||||
memset(encrypt_confirm_buf_, 0, sizeof(encrypt_confirm_buf_));
|
||||
memset(enc_dlg_pin_buf_, 0, sizeof(enc_dlg_pin_buf_));
|
||||
memset(enc_dlg_pin_confirm_buf_, 0, sizeof(enc_dlg_pin_confirm_buf_));
|
||||
enc_dlg_saved_passphrase_.clear();
|
||||
enc_dlg_pin_status_.clear();
|
||||
}
|
||||
void showChangePassphraseDialog() { show_change_passphrase_ = true; }
|
||||
void showDecryptDialog() {
|
||||
show_decrypt_dialog_ = true;
|
||||
decrypt_phase_ = 0; // passphrase entry
|
||||
decrypt_status_.clear();
|
||||
decrypt_in_progress_ = false;
|
||||
memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_));
|
||||
}
|
||||
|
||||
// Dialog triggers for PIN (from settings page)
|
||||
void showPinSetupDialog() { show_pin_setup_ = true; pin_status_.clear(); }
|
||||
void showPinChangeDialog() { show_pin_change_ = true; pin_status_.clear(); }
|
||||
void showPinRemoveDialog() { show_pin_remove_ = true; pin_status_.clear(); }
|
||||
bool hasPinVault() const;
|
||||
|
||||
/// @brief Check if RPC worker has queued results waiting to be processed
|
||||
bool hasPendingRPCResults() const;
|
||||
|
||||
private:
|
||||
// Subsystems
|
||||
std::unique_ptr<rpc::RPCClient> rpc_;
|
||||
std::unique_ptr<rpc::RPCWorker> worker_;
|
||||
std::unique_ptr<config::Settings> settings_;
|
||||
std::unique_ptr<daemon::EmbeddedDaemon> embedded_daemon_;
|
||||
std::unique_ptr<daemon::XmrigManager> xmrig_manager_;
|
||||
bool pending_antivirus_dialog_ = false; // Show Windows Defender help dialog
|
||||
|
||||
// Wallet state
|
||||
WalletState state_;
|
||||
|
||||
// Shutdown state
|
||||
std::atomic<bool> shutting_down_{false};
|
||||
std::atomic<bool> shutdown_complete_{false};
|
||||
std::atomic<bool> refresh_in_progress_{false};
|
||||
bool address_list_dirty_ = false; // P8: dedup rebuildAddressList
|
||||
std::string shutdown_status_;
|
||||
std::thread shutdown_thread_;
|
||||
float shutdown_timer_ = 0.0f;
|
||||
std::chrono::steady_clock::time_point shutdown_start_time_;
|
||||
|
||||
// Daemon restart (e.g. after changing debug log categories)
|
||||
std::atomic<bool> daemon_restarting_{false};
|
||||
std::thread daemon_restart_thread_;
|
||||
|
||||
// UI State
|
||||
bool quit_requested_ = false;
|
||||
bool show_demo_window_ = false;
|
||||
bool show_settings_ = false;
|
||||
bool show_about_ = false;
|
||||
bool show_import_key_ = false;
|
||||
bool show_export_key_ = false;
|
||||
bool show_backup_ = false;
|
||||
bool show_address_book_ = false;
|
||||
|
||||
// Embedded daemon state
|
||||
bool use_embedded_daemon_ = true;
|
||||
std::string daemon_status_;
|
||||
mutable std::string daemon_mem_diag_; // diagnostic info for daemon memory detection
|
||||
|
||||
// Export/Import state
|
||||
std::string export_result_;
|
||||
char import_key_input_[512] = {0};
|
||||
std::string export_address_;
|
||||
std::string import_status_;
|
||||
bool import_success_ = false;
|
||||
std::string backup_status_;
|
||||
bool backup_success_ = false;
|
||||
|
||||
// Connection
|
||||
std::string connection_status_ = "Disconnected";
|
||||
bool connection_in_progress_ = false;
|
||||
float loading_timer_ = 0.0f; // spinner animation for loading overlay
|
||||
|
||||
// Current page (sidebar navigation)
|
||||
ui::NavPage current_page_ = ui::NavPage::Overview;
|
||||
ui::NavPage prev_page_ = ui::NavPage::Overview;
|
||||
float page_alpha_ = 1.0f; // 0→1 fade on page switch
|
||||
bool sidebar_collapsed_ = false; // true = icon-only mode
|
||||
bool sidebar_user_toggled_ = false; // user manually toggled — suppress auto-collapse
|
||||
float sidebar_width_anim_ = 0.0f; // animated width (0 = uninitialized)
|
||||
float prev_dpi_scale_ = 0.0f; // detect DPI changes to snap sidebar width
|
||||
|
||||
// Background gradient overlay
|
||||
ImTextureID gradient_tex_ = 0;
|
||||
|
||||
// Logo texture (reload on skin change / dark↔light switch)
|
||||
ImTextureID logo_tex_ = 0;
|
||||
int logo_w_ = 0;
|
||||
int logo_h_ = 0;
|
||||
bool logo_loaded_ = false;
|
||||
bool logo_is_dark_variant_ = true; // tracks which variant is currently loaded
|
||||
|
||||
// Coin logo texture (DragonX currency icon, separate from wallet branding)
|
||||
ImTextureID coin_logo_tex_ = 0;
|
||||
int coin_logo_w_ = 0;
|
||||
int coin_logo_h_ = 0;
|
||||
bool coin_logo_loaded_ = false;
|
||||
|
||||
// Console tab
|
||||
ui::ConsoleTab console_tab_;
|
||||
|
||||
// Pending payment from URI
|
||||
bool pending_payment_valid_ = false;
|
||||
std::string pending_to_address_;
|
||||
double pending_amount_ = 0.0;
|
||||
std::string pending_memo_;
|
||||
std::string pending_label_;
|
||||
|
||||
// Timers (in seconds since last update)
|
||||
float refresh_timer_ = 0.0f;
|
||||
float price_timer_ = 0.0f;
|
||||
float fast_refresh_timer_ = 0.0f; // For mining stats
|
||||
|
||||
// Refresh intervals (seconds)
|
||||
static constexpr float REFRESH_INTERVAL = 5.0f;
|
||||
static constexpr float PRICE_INTERVAL = 60.0f;
|
||||
static constexpr float FAST_REFRESH_INTERVAL = 1.0f;
|
||||
|
||||
// Mining refresh guard (prevents worker queue pileup)
|
||||
std::atomic<bool> mining_refresh_in_progress_{false};
|
||||
int mining_slow_counter_ = 0; // counts fast ticks; fires slow refresh every N
|
||||
|
||||
// Mining toggle guard (prevents concurrent setgenerate calls)
|
||||
std::atomic<bool> mining_toggle_in_progress_{false};
|
||||
|
||||
// Auto-shield guard (prevents concurrent auto-shield operations)
|
||||
std::atomic<bool> auto_shield_pending_{false};
|
||||
|
||||
// P4: Incremental transaction cache
|
||||
int last_tx_block_height_ = -1; // block height at last full tx fetch
|
||||
|
||||
// First-run wizard state
|
||||
WizardPhase wizard_phase_ = WizardPhase::None;
|
||||
std::unique_ptr<util::Bootstrap> bootstrap_;
|
||||
std::string wizard_pending_passphrase_; // held until daemon connects
|
||||
std::string wizard_saved_passphrase_; // held until PinSetup completes/skipped
|
||||
|
||||
// Deferred encryption (wizard background task)
|
||||
std::string deferred_encrypt_passphrase_;
|
||||
std::string deferred_encrypt_pin_;
|
||||
bool deferred_encrypt_pending_ = false;
|
||||
|
||||
// Wizard: stopping an external daemon before bootstrap
|
||||
bool wizard_stopping_external_ = false;
|
||||
std::string wizard_stop_status_;
|
||||
std::thread wizard_stop_thread_;
|
||||
|
||||
// PIN vault
|
||||
std::unique_ptr<util::SecureVault> vault_;
|
||||
|
||||
// Lock screen state
|
||||
bool lock_screen_was_visible_ = false; // tracks lock→unlock transitions for auto-focus
|
||||
bool lock_use_pin_ = true; // true = PIN input, false = passphrase input
|
||||
char lock_pin_buf_[16] = {};
|
||||
char lock_passphrase_buf_[256] = {};
|
||||
std::string lock_error_msg_;
|
||||
float lock_error_timer_ = 0.0f;
|
||||
int lock_attempts_ = 0;
|
||||
float lock_lockout_timer_ = 0.0f;
|
||||
bool lock_unlock_in_progress_ = false;
|
||||
|
||||
// Encrypt wallet dialog state
|
||||
bool show_encrypt_dialog_ = false;
|
||||
bool show_change_passphrase_ = false;
|
||||
EncryptDialogPhase encrypt_dialog_phase_ = EncryptDialogPhase::PassphraseEntry;
|
||||
char encrypt_pass_buf_[256] = {};
|
||||
char encrypt_confirm_buf_[256] = {};
|
||||
char change_old_pass_buf_[256] = {};
|
||||
char change_new_pass_buf_[256] = {};
|
||||
char change_confirm_buf_[256] = {};
|
||||
std::string encrypt_status_;
|
||||
bool encrypt_in_progress_ = false;
|
||||
std::string enc_dlg_saved_passphrase_; // held for PIN setup after encrypt
|
||||
char enc_dlg_pin_buf_[16] = {};
|
||||
char enc_dlg_pin_confirm_buf_[16] = {};
|
||||
std::string enc_dlg_pin_status_;
|
||||
|
||||
// PIN setup dialog state (settings page)
|
||||
bool show_pin_setup_ = false;
|
||||
bool show_pin_change_ = false;
|
||||
bool show_pin_remove_ = false;
|
||||
char pin_buf_[16] = {};
|
||||
char pin_confirm_buf_[16] = {};
|
||||
char pin_old_buf_[16] = {};
|
||||
char pin_passphrase_buf_[256] = {};
|
||||
std::string pin_status_;
|
||||
bool pin_in_progress_ = false;
|
||||
|
||||
// Decrypt wallet dialog state
|
||||
bool show_decrypt_dialog_ = false;
|
||||
int decrypt_phase_ = 0; // 0=passphrase, 1=working, 2=done, 3=error
|
||||
char decrypt_pass_buf_[256] = {};
|
||||
std::string decrypt_status_;
|
||||
bool decrypt_in_progress_ = false;
|
||||
|
||||
// Wizard PIN setup state
|
||||
char wizard_pin_buf_[16] = {};
|
||||
char wizard_pin_confirm_buf_[16] = {};
|
||||
std::string wizard_pin_status_;
|
||||
|
||||
// Auto-lock on idle
|
||||
std::chrono::steady_clock::time_point last_interaction_ = std::chrono::steady_clock::now();
|
||||
|
||||
// Private methods - rendering
|
||||
void renderStatusBar();
|
||||
void renderAboutDialog();
|
||||
void renderImportKeyDialog();
|
||||
void renderExportKeyDialog();
|
||||
void renderBackupDialog();
|
||||
void renderFirstRunWizard();
|
||||
void renderLockScreen();
|
||||
void renderEncryptWalletDialog();
|
||||
void renderDecryptWalletDialog();
|
||||
void renderPinDialogs();
|
||||
void renderAntivirusHelpDialog();
|
||||
void processDeferredEncryption();
|
||||
|
||||
// Private methods - connection
|
||||
void tryConnect();
|
||||
void onConnected();
|
||||
void onDisconnected(const std::string& reason);
|
||||
|
||||
// Private methods - data refresh
|
||||
void refreshData();
|
||||
void refreshBalance();
|
||||
void refreshAddresses();
|
||||
void refreshTransactions();
|
||||
void refreshPrice();
|
||||
void refreshWalletEncryptionState();
|
||||
void checkAutoLock();
|
||||
};
|
||||
|
||||
} // namespace dragonx
|
||||
Reference in New Issue
Block a user