diff --git a/src/app.cpp b/src/app.cpp index 59037f1..f4f704a 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -5081,6 +5081,22 @@ void App::renderSeedMigrationDialog() { const bool canSweep = !state_.isLocked() && seed_migration_balance_ > 0.0; if (!canSweep) seed_migration_confirm_sweep_ = false; // never keep a stale armed state + // The sweep (z_shieldcoinbase / z_mergetoaddress) needs an unlocked wallet, so an encrypted + // wallet silently disabled the button. Offer an inline unlock instead of a dead end. + if (state_.isLocked()) { + static char s_mig_unlock_pass[128] = {0}; + ImGui::TextWrapped("%s", TR("mig_unlock_prompt")); + ImGui::SetNextItemWidth(220 * dp); + const bool submit = ImGui::InputText("##mig_unlock", s_mig_unlock_pass, sizeof(s_mig_unlock_pass), + ImGuiInputTextFlags_Password | ImGuiInputTextFlags_EnterReturnsTrue); + ImGui::SameLine(); + if ((ui::material::TactileButton(TR("mig_unlock_btn"), ImVec2(120 * dp, 0)) || submit) + && s_mig_unlock_pass[0]) { + unlockWallet(s_mig_unlock_pass, 600); + sodium_memzero(s_mig_unlock_pass, sizeof(s_mig_unlock_pass)); // don't retain the secret + } + ImGui::Spacing(); + } if (!seed_migration_confirm_sweep_) { if (!canSweep) ImGui::BeginDisabled(); if (ui::material::TactileButton(TR("mig_sweep_all"), ImVec2(170 * dp, 0))) diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index 1f4ceab..bf8d9a6 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -512,6 +512,8 @@ void I18n::loadBuiltinEnglish() strings_["mig_to"] = "To: "; strings_["mig_unlock_first"] = "Unlock your wallet first to spend from it."; strings_["mig_sweep_all"] = "Sweep all funds"; + strings_["mig_unlock_prompt"] = "Your wallet is locked. Enter your passphrase to unlock it, then sweep."; + strings_["mig_unlock_btn"] = "Unlock"; strings_["mig_later"] = "Later"; strings_["mig_sweeping"] = "Sweeping your funds into the new wallet…"; strings_["mig_confirming"] = "Waiting for the sweep to confirm on-chain before switching wallets — this guarantees your funds have actually moved.";