// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #pragma once #include #include #include namespace dragonx { namespace wallet { struct LiteBridgeStringResult { bool ok = false; std::string value; std::string error; }; struct LiteClientBridgeApi { using WalletExistsFn = bool (*)(const char* chainName); using InitializeNewFn = char* (*)(bool dangerous, const char* server); using InitializeNewFromPhraseFn = char* (*)(bool dangerous, const char* server, const char* seed, unsigned long long birthday, unsigned long long account, bool overwrite); using InitializeExistingFn = char* (*)(bool dangerous, const char* server); using ExecuteFn = char* (*)(const char* command, const char* args); using FreeStringFn = void (*)(char* value); using CheckServerOnlineFn = bool (*)(const char* server); using ShutdownFn = void (*)(); WalletExistsFn walletExists = nullptr; InitializeNewFn initializeNew = nullptr; InitializeNewFromPhraseFn initializeNewFromPhrase = nullptr; InitializeExistingFn initializeExisting = nullptr; ExecuteFn execute = nullptr; FreeStringFn freeString = nullptr; CheckServerOnlineFn checkServerOnline = nullptr; ShutdownFn shutdown = nullptr; }; class LiteClientBridge { public: static LiteClientBridge unavailable(std::string reason); static LiteClientBridge fromApi(LiteClientBridgeApi api); static LiteClientBridge linkedSdxl(); LiteClientBridge(const LiteClientBridge&) = delete; LiteClientBridge& operator=(const LiteClientBridge&) = delete; LiteClientBridge(LiteClientBridge&& other) noexcept; LiteClientBridge& operator=(LiteClientBridge&& other) noexcept; ~LiteClientBridge(); bool available() const; const std::string& unavailableReason() const { return unavailableReason_; } bool walletExists(const std::string& chainName) const; bool checkServerOnline(const std::string& server) const; LiteBridgeStringResult initializeNew(bool dangerous, const std::string& server); LiteBridgeStringResult initializeNewFromPhrase(bool dangerous, const std::string& server, const std::string& seed, unsigned long long birthday, unsigned long long account, bool overwrite); LiteBridgeStringResult initializeExisting(bool dangerous, const std::string& server); LiteBridgeStringResult execute(const std::string& command, const std::string& args); void shutdown(); // Monotonic counter bumped AFTER every execute() of a command that could change wallet-visible // data (anything not on the read-only allow-list). The refresh worker samples this to detect // local mutations made on ANY thread without hooking each controller method. Compare-only (never // cleared): the worker stores a baseline and refreshes whenever it differs, so a mutation can // never be "consumed"/lost in a race. std::uint64_t mutationEpoch() const { return mutationEpoch_.load(std::memory_order_acquire); } private: LiteClientBridge(LiteClientBridgeApi api, std::string unavailableReason); LiteBridgeStringResult unavailableResult() const; LiteBridgeStringResult takeOwnedString(char* rawValue) const; static bool commandIsReadOnly(const std::string& command); LiteClientBridgeApi api_; std::string unavailableReason_; bool shutdownCalled_ = false; std::atomic mutationEpoch_{0}; }; } // namespace wallet } // namespace dragonx