// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #include "validate_address_dialog.h" #include "../../app.h" #include "../../rpc/rpc_client.h" #include "../../rpc/rpc_worker.h" #include "../schema/ui_schema.h" #include "../material/draw_helpers.h" #include "../theme.h" #include "imgui.h" namespace dragonx { namespace ui { // Static member initialization bool ValidateAddressDialog::s_open = false; bool ValidateAddressDialog::s_validated = false; bool ValidateAddressDialog::s_validating = false; char ValidateAddressDialog::s_address_input[512] = ""; bool ValidateAddressDialog::s_is_valid = false; bool ValidateAddressDialog::s_is_mine = false; std::string ValidateAddressDialog::s_address_type; std::string ValidateAddressDialog::s_error_message; void ValidateAddressDialog::show() { s_open = true; s_validated = false; s_validating = false; s_address_input[0] = '\0'; s_is_valid = false; s_is_mine = false; s_address_type.clear(); s_error_message.clear(); } bool ValidateAddressDialog::isOpen() { return s_open; } void ValidateAddressDialog::render(App* app) { if (!s_open) return; auto& S = schema::UI(); auto win = S.window("dialogs.validate-address"); auto valBtn = S.button("dialogs.validate-address", "validate-button"); auto pasteBtn = S.button("dialogs.validate-address", "paste-button"); auto lbl = S.label("dialogs.validate-address", "label"); auto closeBtn = S.button("dialogs.validate-address", "close-button"); if (material::BeginOverlayDialog("Validate Address", &s_open, win.width, 0.94f)) { ImGui::TextWrapped("Enter a DragonX address to check if it's valid and whether it belongs to this wallet."); ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); // Address input ImGui::Text("Address:"); ImGui::SetNextItemWidth(-1); bool enter_pressed = ImGui::InputText("##ValidateAddr", s_address_input, sizeof(s_address_input), ImGuiInputTextFlags_EnterReturnsTrue); ImGui::Spacing(); // Validate button bool can_validate = strlen(s_address_input) > 0 && !s_validating && app->isConnected(); if (!can_validate) { ImGui::BeginDisabled(); } if (material::StyledButton("Validate", ImVec2(valBtn.width, 0), S.resolveFont(valBtn.font)) || (enter_pressed && can_validate)) { s_validating = true; s_validated = false; s_error_message.clear(); std::string address(s_address_input); // Determine if z-address or t-address bool is_zaddr = !address.empty() && address[0] == 'z'; if (is_zaddr) { if (app->worker()) { app->worker()->post([rpc = app->rpc(), address]() -> rpc::RPCWorker::MainCb { bool valid = false, mine = false; std::string error; try { auto result = rpc->call("validateaddress", {address}); valid = result.value("isvalid", false); mine = result.value("ismine", false); } catch (const std::exception& e) { error = e.what(); } return [valid, mine, error]() { if (error.empty()) { s_is_valid = valid; s_is_mine = mine; s_address_type = "Shielded (z-address)"; } else { s_error_message = error; s_is_valid = false; } s_validated = true; s_validating = false; }; }); } } else { if (app->worker()) { app->worker()->post([rpc = app->rpc(), address]() -> rpc::RPCWorker::MainCb { bool valid = false, mine = false; std::string error; try { auto result = rpc->call("validateaddress", {address}); valid = result.value("isvalid", false); mine = result.value("ismine", false); } catch (const std::exception& e) { error = e.what(); } return [valid, mine, error]() { if (error.empty()) { s_is_valid = valid; s_is_mine = mine; s_address_type = "Transparent (t-address)"; } else { s_error_message = error; s_is_valid = false; } s_validated = true; s_validating = false; }; }); } } } if (!can_validate) { ImGui::EndDisabled(); } ImGui::SameLine(); if (material::StyledButton("Paste", ImVec2(pasteBtn.width, 0), S.resolveFont(pasteBtn.font))) { const char* clipboard = ImGui::GetClipboardText(); if (clipboard) { strncpy(s_address_input, clipboard, sizeof(s_address_input) - 1); s_address_input[sizeof(s_address_input) - 1] = '\0'; s_validated = false; } } if (s_validating) { ImGui::SameLine(); ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Validating..."); } ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); // Results if (s_validated) { ImGui::Text("Results:"); ImGui::Spacing(); if (!s_error_message.empty()) { ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Error: %s", s_error_message.c_str()); } else { // Valid/Invalid indicator ImGui::Text("Status:"); ImGui::SameLine(lbl.position); if (s_is_valid) { ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), "VALID"); } else { ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "INVALID"); } if (s_is_valid) { // Address type ImGui::Text("Type:"); ImGui::SameLine(lbl.position); ImGui::Text("%s", s_address_type.c_str()); // Is mine? ImGui::Text("Ownership:"); ImGui::SameLine(lbl.position); if (s_is_mine) { ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), "This wallet owns this address"); } else { ImGui::TextDisabled("Not owned by this wallet"); } } } } else if (!app->isConnected()) { ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), "Not connected to daemon"); } ImGui::Spacing(); // Close button at bottom float button_width = closeBtn.width; ImGui::SetCursorPosX((ImGui::GetWindowWidth() - button_width) / 2.0f); if (material::StyledButton("Close", ImVec2(button_width, 0), S.resolveFont(closeBtn.font))) { s_open = false; } material::EndOverlayDialog(); } } } // namespace ui } // namespace dragonx