// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #pragma once #include #include #include #include #include #include namespace dragonx { namespace config { /** * @brief Application settings manager * * Handles loading and saving of user preferences. */ class Settings { public: Settings(); ~Settings(); /** * @brief Load settings from default location * @return true if loaded successfully */ bool load(); /** * @brief Load settings from specific path * @param path Path to settings file * @return true if loaded successfully */ bool load(const std::string& path); /** * @brief Save settings to default location * @return true if saved successfully */ bool save(); /** * @brief Save settings to specific path * @param path Path to settings file * @return true if saved successfully */ bool save(const std::string& path); /** * @brief Get default settings file path */ static std::string getDefaultPath(); enum class LiteServerSelectionPreferenceMode { Sticky, Random }; // Pool selection mode for the mining tab: Manual (user picks the pool) or // AutoBalance (the wallet spreads miners across the official pools by hashrate). enum class PoolSelectMode { Manual, AutoBalance }; struct LiteServerPreference { std::string url; std::string label; bool enabled = true; }; // A user-defined portfolio entry: a custom label tied to a group of wallet addresses. // The Market tab's portfolio card sums these addresses' balances under the label. struct PortfolioEntry { std::string label; std::vector addresses; std::string icon; // project_icons wallet-icon name; empty = no icon unsigned int color = 0; // packed IM_COL32 accent; 0 = theme default int outlineOpacity = 25; // accent-outline opacity, percent (0-100) // Per-group price data. priceBasis: 0 = live market (USD), 1 = live market (BTC), // 2 = DRGX only (no fiat value), 3 = manual price. Defaults preserve prior behavior // (show DRGX + USD value). int priceBasis = 0; double manualPrice = 0.0; // price per DRGX for the Manual basis std::string manualCurrency = "USD"; bool showDrgx = true; // show the DRGX amount on the card bool showValue = true; // show the converted/fiat value on the card bool show24h = false; // show the 24h % change (live-market bases only) bool showSparkline = false; // show a price-trend sparkline (live-market bases only) int sparklineInterval = 0; // 0=minute 1=hour 2=day 3=week 4=month (resample of price history) // Dashboard grid placement on the Market portfolio. col/row in fine ~32px square cells // (-1 = auto-place), w/h in cells (span). Set when the user drags/resizes a card. int gridCol = -1; int gridRow = -1; int gridW = 8; // default group width (cells); minimum is 8 int gridH = 3; // default group height (cells); minimum is 2, card design adapts }; // Theme std::string getTheme() const { return theme_; } void setTheme(const std::string& theme) { theme_ = theme; } // Unified skin std::string getSkinId() const { return skin_id_; } void setSkinId(const std::string& id) { skin_id_ = id; } // Stable z-address chosen for HushChat: the reply-to address in outgoing headers, and (for // legacy/non-mnemonic wallets) the source of the seed-derived chat identity. Persisted so the // identity + reply address don't shift when new addresses are generated. std::string getChatReplyZaddr() const { return chat_reply_zaddr_; } void setChatReplyZaddr(const std::string& z) { chat_reply_zaddr_ = z; } // Privacy bool getSaveZtxs() const { return save_ztxs_; } void setSaveZtxs(bool save) { save_ztxs_ = save; } bool getAutoShield() const { return auto_shield_; } void setAutoShield(bool shield) { auto_shield_ = shield; } bool getUseTor() const { return use_tor_; } void setUseTor(bool tor) { use_tor_ = tor; } // Fees bool getAllowCustomFees() const { return allow_custom_fees_; } void setAllowCustomFees(bool allow) { allow_custom_fees_ = allow; } double getDefaultFee() const { return default_fee_; } void setDefaultFee(double fee) { default_fee_ = fee; } // Price bool getFetchPrices() const { return fetch_prices_; } void setFetchPrices(bool fetch) { fetch_prices_ = fetch; } // Explorer URLs std::string getTxExplorerUrl() const { return tx_explorer_url_; } void setTxExplorerUrl(const std::string& url) { tx_explorer_url_ = url; } std::string getAddressExplorerUrl() const { return address_explorer_url_; } void setAddressExplorerUrl(const std::string& url) { address_explorer_url_ = url; } // Language std::string getLanguage() const { return language_; } void setLanguage(const std::string& lang) { language_ = lang; } // Visual effects bool getAcrylicEnabled() const { return acrylic_enabled_; } void setAcrylicEnabled(bool v) { acrylic_enabled_ = v; } int getAcrylicQuality() const { return acrylic_quality_; } void setAcrylicQuality(int v) { acrylic_quality_ = v; } float getBlurMultiplier() const { return blur_multiplier_; } void setBlurMultiplier(float v) { blur_multiplier_ = v; } bool getReducedTransparency() const { return ui_opacity_ >= 0.99f; } void setReducedTransparency(bool v) { if (v) ui_opacity_ = 1.0f; } float getUIOpacity() const { return ui_opacity_; } void setUIOpacity(float v) { ui_opacity_ = std::max(0.3f, std::min(1.0f, v)); } float getWindowOpacity() const { return window_opacity_; } void setWindowOpacity(float v) { window_opacity_ = std::max(0.3f, std::min(1.0f, v)); } float getNoiseOpacity() const { return noise_opacity_; } void setNoiseOpacity(float v) { noise_opacity_ = v; } // Gradient background mode (use gradient variant of theme background) bool getGradientBackground() const { return gradient_background_; } void setGradientBackground(bool v) { gradient_background_ = v; } // Balance layout (string ID, e.g. "classic", "donut") std::string getBalanceLayout() const { return balance_layout_; } void setBalanceLayout(const std::string& v) { balance_layout_ = v; } // Console scanline effect bool getScanlineEnabled() const { return scanline_enabled_; } void setScanlineEnabled(bool v) { scanline_enabled_ = v; } // Hidden addresses (addresses hidden from the UI by the user) const std::set& getHiddenAddresses() const { return hidden_addresses_; } bool isAddressHidden(const std::string& addr) const { return hidden_addresses_.count(addr) > 0; } void hideAddress(const std::string& addr) { hidden_addresses_.insert(addr); } void unhideAddress(const std::string& addr) { hidden_addresses_.erase(addr); } int getHiddenAddressCount() const { return (int)hidden_addresses_.size(); } // Favorite addresses (pinned to top of address list) const std::set& getFavoriteAddresses() const { return favorite_addresses_; } bool isAddressFavorite(const std::string& addr) const { return favorite_addresses_.count(addr) > 0; } void favoriteAddress(const std::string& addr) { favorite_addresses_.insert(addr); } void unfavoriteAddress(const std::string& addr) { favorite_addresses_.erase(addr); } int getFavoriteAddressCount() const { return (int)favorite_addresses_.size(); } // Address metadata (labels, icons, custom ordering) struct AddressMeta { std::string label; std::string icon; // material icon name, e.g. "savings" int sortOrder = -1; // -1 = auto (use default sort) bool mining = false; }; const AddressMeta& getAddressMeta(const std::string& addr) const { static const AddressMeta empty{}; auto it = address_meta_.find(addr); return it != address_meta_.end() ? it->second : empty; } void setAddressLabel(const std::string& addr, const std::string& label) { address_meta_[addr].label = label; } void setAddressIcon(const std::string& addr, const std::string& icon) { address_meta_[addr].icon = icon; } void setAddressSortOrder(const std::string& addr, int order) { address_meta_[addr].sortOrder = order; } bool isMiningAddress(const std::string& addr) const { auto it = address_meta_.find(addr); return it != address_meta_.end() && it->second.mining; } void setMiningAddress(const std::string& addr, bool mining) { address_meta_[addr].mining = mining; } std::set getMiningAddresses() const { std::set addresses; for (const auto& [addr, meta] : address_meta_) { if (meta.mining) addresses.insert(addr); } return addresses; } int getNextSortOrder() const { int mx = -1; for (const auto& [k, v] : address_meta_) if (v.sortOrder > mx) mx = v.sortOrder; return mx + 1; } void swapAddressOrder(const std::string& a, const std::string& b) { int oa = address_meta_[a].sortOrder; int ob = address_meta_[b].sortOrder; address_meta_[a].sortOrder = ob; address_meta_[b].sortOrder = oa; } // First-run wizard bool getWizardCompleted() const { return wizard_completed_; } void setWizardCompleted(bool v) { wizard_completed_ = v; } // Whether the one-time "back up your seed phrase" reminder has already been shown. bool getSeedBackupReminded() const { return seed_backup_reminded_; } void setSeedBackupReminded(bool v) { seed_backup_reminded_ = v; } // Pending "migrate to a seed wallet" state (Phase 1 created the wallet; a later sweep/adopt // step consumes it). dest = the new wallet's sweep-target z-address; tempDir = its datadir. bool getSeedMigrationPending() const { return seed_migration_pending_; } void setSeedMigrationPending(bool v) { seed_migration_pending_ = v; } std::string getSeedMigrationDest() const { return seed_migration_dest_; } void setSeedMigrationDest(const std::string& v) { seed_migration_dest_ = v; } std::string getSeedMigrationTempDir() const { return seed_migration_temp_dir_; } void setSeedMigrationTempDir(const std::string& v) { seed_migration_temp_dir_ = v; } // The sweep transaction id, persisted once the sweep is submitted — non-empty means the // migration is past the sweep, so a resume goes to the confirm/adopt stage (not sweep again). std::string getSeedMigrationSweepTxid() const { return seed_migration_sweep_txid_; } void setSeedMigrationSweepTxid(const std::string& v) { seed_migration_sweep_txid_ = v; } // Security — auto-lock timeout (seconds; 0 = disabled) int getAutoLockTimeout() const { return auto_lock_timeout_; } void setAutoLockTimeout(int seconds) { auto_lock_timeout_ = seconds; } // Security — unlock duration (seconds) for walletpassphrase int getUnlockDuration() const { return unlock_duration_; } void setUnlockDuration(int seconds) { unlock_duration_ = seconds; } // Security — PIN unlock enabled bool getPinEnabled() const { return pin_enabled_; } void setPinEnabled(bool v) { pin_enabled_ = v; } // Daemon — keep running in background when closing the app bool getKeepDaemonRunning() const { return keep_daemon_running_; } void setKeepDaemonRunning(bool v) { keep_daemon_running_ = v; } // Daemon — stop externally-started daemons on exit (default: false) bool getStopExternalDaemon() const { return stop_external_daemon_; } void setStopExternalDaemon(bool v) { stop_external_daemon_ = v; } // Daemon — maximum peer connections (0 = daemon default) int getMaxConnections() const { return max_connections_; } void setMaxConnections(int v) { max_connections_ = std::max(0, v); } // Lite wallet server selection LiteServerSelectionPreferenceMode getLiteServerSelectionMode() const { return lite_server_selection_mode_; } void setLiteServerSelectionMode(LiteServerSelectionPreferenceMode mode) { lite_server_selection_mode_ = mode; } std::string getLiteStickyServerUrl() const { return lite_sticky_server_url_; } void setLiteStickyServerUrl(const std::string& url) { lite_sticky_server_url_ = url; } std::string getLiteChainName() const { return lite_chain_name_; } void setLiteChainName(const std::string& chainName) { lite_chain_name_ = chainName; } std::size_t getLiteRandomSelectionSeed() const { return lite_random_selection_seed_; } void setLiteRandomSelectionSeed(std::size_t seed) { lite_random_selection_seed_ = seed; } bool getLitePersistSelectedServer() const { return lite_persist_selected_server_; } void setLitePersistSelectedServer(bool persist) { lite_persist_selected_server_ = persist; } const std::vector& getLiteServers() const { return lite_servers_; } void setLiteServers(const std::vector& servers) { lite_servers_ = servers; } // User-defined portfolio entries (Market tab). "All funds" is implicit, not stored here. const std::vector& getPortfolioEntries() const { return portfolio_entries_; } void setPortfolioEntries(const std::vector& entries) { portfolio_entries_ = entries; } // Lite servers the user has hidden from the Network tab (kept by URL, shown via a toggle). const std::set& getLiteHiddenServers() const { return lite_hidden_servers_; } bool isLiteServerHidden(const std::string& url) const { return lite_hidden_servers_.count(url) > 0; } void hideLiteServer(const std::string& url) { lite_hidden_servers_.insert(url); } void unhideLiteServer(const std::string& url) { lite_hidden_servers_.erase(url); } // Lite wallet rollout / kill-switch (see wallet/lite_rollout_policy.h). // Override: "auto" (honor rollout manifest), "force_on", or "force_off". std::string getLiteRolloutOverride() const { return lite_rollout_override_; } void setLiteRolloutOverride(const std::string& v) { lite_rollout_override_ = v; } // Stable, locally-generated install id used only to derive the staged-rollout bucket. // Never transmitted; carries no PII. Generated on first use if empty. std::string getLiteInstallId() const { return lite_install_id_; } void setLiteInstallId(const std::string& v) { lite_install_id_ = v; } // Verbose diagnostic logging (connection attempts, daemon state, port owner, etc.) bool getVerboseLogging() const { return verbose_logging_; } void setVerboseLogging(bool v) { verbose_logging_ = v; } // Daemon — debug logging categories const std::set& getDebugCategories() const { return debug_categories_; } void setDebugCategories(const std::set& cats) { debug_categories_ = cats; } bool hasDebugCategory(const std::string& cat) const { return debug_categories_.count(cat) > 0; } void toggleDebugCategory(const std::string& cat) { if (debug_categories_.count(cat)) debug_categories_.erase(cat); else debug_categories_.insert(cat); } // Visual effects — animated theme effects (hue cycling, shimmer, glow, etc.) bool getThemeEffectsEnabled() const { return theme_effects_enabled_; } void setThemeEffectsEnabled(bool v) { theme_effects_enabled_ = v; } // Low-spec mode — disables heavy visual effects for better performance bool getLowSpecMode() const { return low_spec_mode_; } void setLowSpecMode(bool v) { low_spec_mode_ = v; } // Reduce motion — disables animated transitions for accessibility bool getReduceMotion() const { return reduce_motion_; } void setReduceMotion(bool v) { reduce_motion_ = v; } // Market — last selected exchange + pair std::string getSelectedExchange() const { return selected_exchange_; } void setSelectedExchange(const std::string& v) { selected_exchange_ = v; } std::string getSelectedPair() const { return selected_pair_; } void setSelectedPair(const std::string& v) { selected_pair_ = v; } // Pool mining std::string getPoolUrl() const { return pool_url_; } void setPoolUrl(const std::string& v) { pool_url_ = v; } std::string getPoolAlgo() const { return pool_algo_; } void setPoolAlgo(const std::string& v) { pool_algo_ = v; } std::string getPoolWorker() const { return pool_worker_; } void setPoolWorker(const std::string& v) { pool_worker_ = v; } int getPoolThreads() const { return pool_threads_; } void setPoolThreads(int v) { pool_threads_ = v; } bool getPoolTls() const { return pool_tls_; } void setPoolTls(bool v) { pool_tls_ = v; } bool getPoolHugepages() const { return pool_hugepages_; } void setPoolHugepages(bool v) { pool_hugepages_ = v; } bool getPoolMode() const { return pool_mode_; } void setPoolMode(bool v) { pool_mode_ = v; } PoolSelectMode getPoolSelectMode() const { return pool_select_mode_; } void setPoolSelectMode(PoolSelectMode v) { pool_select_mode_ = v; } // Installed DRG-XMRig release tag (for in-app miner update detection); empty if unknown/bundled. std::string getXmrigVersion() const { return xmrig_version_; } void setXmrigVersion(const std::string& v) { xmrig_version_ = v; } // Mine when idle (auto-start mining when system is idle) bool getMineWhenIdle() const { return mine_when_idle_; } void setMineWhenIdle(bool v) { mine_when_idle_ = v; } int getMineIdleDelay() const { return mine_idle_delay_; } void setMineIdleDelay(int seconds) { mine_idle_delay_ = std::max(30, seconds); } // Idle thread scaling — scale thread count instead of start/stop bool getIdleThreadScaling() const { return idle_thread_scaling_; } void setIdleThreadScaling(bool v) { idle_thread_scaling_ = v; } int getIdleThreadsActive() const { return idle_threads_active_; } void setIdleThreadsActive(int v) { idle_threads_active_ = std::max(0, v); } int getIdleThreadsIdle() const { return idle_threads_idle_; } void setIdleThreadsIdle(int v) { idle_threads_idle_ = std::max(0, v); } bool getIdleGpuAware() const { return idle_gpu_aware_; } void setIdleGpuAware(bool v) { idle_gpu_aware_ = v; } // Saved pool URLs (user-managed favorites dropdown) const std::vector& getSavedPoolUrls() const { return saved_pool_urls_; } void addSavedPoolUrl(const std::string& url) { // Don't add duplicates for (const auto& u : saved_pool_urls_) if (u == url) return; saved_pool_urls_.push_back(url); } void removeSavedPoolUrl(const std::string& url) { saved_pool_urls_.erase( std::remove(saved_pool_urls_.begin(), saved_pool_urls_.end(), url), saved_pool_urls_.end()); } // Saved pool worker addresses (user-managed favorites dropdown) const std::vector& getSavedPoolWorkers() const { return saved_pool_workers_; } void addSavedPoolWorker(const std::string& addr) { for (const auto& a : saved_pool_workers_) if (a == addr) return; saved_pool_workers_.push_back(addr); } void removeSavedPoolWorker(const std::string& addr) { saved_pool_workers_.erase( std::remove(saved_pool_workers_.begin(), saved_pool_workers_.end(), addr), saved_pool_workers_.end()); } // Font scale (user accessibility setting, 1.0–1.5) float getFontScale() const { return font_scale_; } void setFontScale(float v) { font_scale_ = std::max(1.0f, std::min(1.5f, v)); } // Window size persistence (logical pixels at 1x scale) int getWindowWidth() const { return window_width_; } int getWindowHeight() const { return window_height_; } void setWindowSize(int w, int h) { window_width_ = w; window_height_ = h; } // Returns true once after an upgrade (version mismatch detected on load) bool needsUpgradeSave() const { return needs_upgrade_save_; } void clearUpgradeSave() { needs_upgrade_save_ = false; } private: std::string settings_path_; // Settings values std::string theme_ = "dragonx"; std::string skin_id_ = "dragonx"; std::string chat_reply_zaddr_; bool save_ztxs_ = true; bool auto_shield_ = true; bool use_tor_ = false; bool allow_custom_fees_ = false; double default_fee_ = 0.0001; bool fetch_prices_ = true; std::string tx_explorer_url_ = "https://explorer.dragonx.is/tx/"; std::string address_explorer_url_ = "https://explorer.dragonx.is/address/"; std::string language_ = "en"; bool acrylic_enabled_ = true; int acrylic_quality_ = 2; float blur_multiplier_ = 0.10f; float noise_opacity_ = 0.5f; bool gradient_background_ = false; #ifdef _WIN32 float ui_opacity_ = 0.50f; // Card/sidebar opacity (0.3–1.0, 1.0 = opaque) float window_opacity_ = 0.75f; // Background alpha (0.3–1.0, <1 = desktop visible) #else float ui_opacity_ = 1.0f; // Mac/Linux: default fully opaque float window_opacity_ = 1.0f; // Mac/Linux: default fully opaque #endif std::string balance_layout_ = "classic"; bool scanline_enabled_ = true; std::set hidden_addresses_; std::set favorite_addresses_; std::map address_meta_; bool wizard_completed_ = false; bool seed_backup_reminded_ = false; bool seed_migration_pending_ = false; std::string seed_migration_dest_; std::string seed_migration_temp_dir_; std::string seed_migration_sweep_txid_; int auto_lock_timeout_ = 900; // 15 minutes int unlock_duration_ = 600; // 10 minutes bool pin_enabled_ = false; bool keep_daemon_running_ = false; bool stop_external_daemon_ = false; int max_connections_ = 0; // 0 = daemon default // Lite wallet server preferences. These are user/server settings only; // wallet secrets, wallet files, and lifecycle state are never stored here. LiteServerSelectionPreferenceMode lite_server_selection_mode_ = LiteServerSelectionPreferenceMode::Sticky; std::string lite_sticky_server_url_ = "https://lite.dragonx.is"; std::string lite_chain_name_ = "main"; // SDXL backend chain id; must be main/test/regtest std::size_t lite_random_selection_seed_ = 0; bool lite_persist_selected_server_ = true; std::string lite_rollout_override_ = "auto"; // auto|force_on|force_off std::string lite_install_id_; // random local-only id; rollout-bucket source std::vector lite_servers_ = { {"https://lite.dragonx.is", "DragonX Lite", true}, {"https://lite1.dragonx.is", "DragonX Lite 1", true}, {"https://lite2.dragonx.is", "DragonX Lite 2", true}, {"https://lite3.dragonx.is", "DragonX Lite 3", true}, {"https://lite4.dragonx.is", "DragonX Lite 4", true}, {"https://lite5.dragonx.is", "DragonX Lite 5", true} }; std::set lite_hidden_servers_; // server URLs hidden from the Network tab std::vector portfolio_entries_; // Market tab custom portfolio groups bool verbose_logging_ = false; std::set debug_categories_; bool theme_effects_enabled_ = true; bool low_spec_mode_ = false; bool reduce_motion_ = false; std::string selected_exchange_ = "Nonkyc.io"; std::string selected_pair_ = "DRGX/USDT"; // Pool mining std::string pool_url_ = "pool.dragonx.is:3433"; std::string pool_algo_ = "rx/hush"; std::string pool_worker_ = ""; int pool_threads_ = 0; bool pool_tls_ = false; bool pool_hugepages_ = true; bool pool_mode_ = false; // false=solo, true=pool PoolSelectMode pool_select_mode_ = PoolSelectMode::Manual; // manual vs auto-balance pool choice std::string xmrig_version_; // installed DRG-XMRig release tag (update detection) bool mine_when_idle_ = false; // auto-start mining when system idle int mine_idle_delay_= 120; // seconds of idle before mining starts bool idle_thread_scaling_ = false; // scale threads instead of start/stop int idle_threads_active_ = 0; // threads when user active (0 = auto) int idle_threads_idle_ = 0; // threads when idle (0 = auto = all) bool idle_gpu_aware_ = true; // treat GPU activity as non-idle std::vector saved_pool_urls_; // user-saved pool URL favorites std::vector saved_pool_workers_; // user-saved worker address favorites // Font scale (user accessibility, 1.0–3.0; 1.0 = default) float font_scale_ = 1.0f; // Window size (logical pixels at 1x scale; 0 = use default 1200×775) int window_width_ = 0; int window_height_ = 0; // Wallet version that last wrote this settings file (for upgrade detection) std::string settings_version_; bool needs_upgrade_save_ = false; // true when version changed }; } // namespace config } // namespace dragonx