fix(settings): scope portfolio groups by wallet identity
Market-tab portfolio groups were tied to a wallet by a hash of its address set. That hash changed whenever an address was added, so a group built for a wallet could silently drop out of view once the wallet gained a new address. - Groups are now scoped by the wallet's stable id, which is persisted and never derived from the address set, so adding an address can no longer orphan a group. - An older build's address-hash scope fails open (the group stays visible) and is migrated once per wallet per session to the stable id. A legacy group is claimed when its old hash matches, when one of its own addresses belongs to this wallet, or when only one wallet is known; anything ambiguous is left legacy and stays visible. - Scope filtering waits until the wallet's addresses are loaded, so a group never flashes an empty 0.0000 balance and Add stays disabled until the wallet is resolved. This works on Lite, which never stamps an address-update time. Behaviour: an older build opened after this change hides groups tagged with a stable "w:" scope (they are not deleted and return on the next upgrade) - worth a release note. Tests: group visibility across global, stable and legacy scopes with the wallet resolved and not; the migration rules (hash match, address overlap, single-wallet claim, ambiguous left alone, idempotent); and a save/load round trip where an address-set change cannot orphan the scope. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1520,6 +1520,27 @@ std::string App::activeWalletScopeId()
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string App::portfolioScopeId()
|
||||
{
|
||||
if (capture_mode_) return "w:sweep-demo"; // in-memory only: Settings/WalletIndex saves are latched in a sweep
|
||||
if (state_.z_addresses.empty() && state_.t_addresses.empty()) return std::string(); // not loaded yet
|
||||
return activeWalletScopeId();
|
||||
}
|
||||
|
||||
void App::reattachLegacyPortfolioScopes()
|
||||
{
|
||||
if (!settings_ || capture_mode_ || !state_.connected) return;
|
||||
const std::string scopeId = portfolioScopeId();
|
||||
if (scopeId.empty() || scopeId == portfolio_scope_migrated_for_) return;
|
||||
portfolio_scope_migrated_for_ = scopeId; // once per wallet per session (re-runs after a wallet switch)
|
||||
std::set<std::string> mine;
|
||||
for (const auto& a : state_.z_addresses) if (!a.address.empty()) mine.insert(a.address);
|
||||
for (const auto& a : state_.t_addresses) if (!a.address.empty()) mine.insert(a.address);
|
||||
const int moved = settings_->reattachLegacyPortfolioScopes(
|
||||
scopeId, activeWalletIdentityHash(), mine, wallet_index_.entries().size() <= 1);
|
||||
if (moved > 0) settings_->save(); // UI thread, once
|
||||
}
|
||||
|
||||
void App::updateWalletIndexForActiveWallet(bool markOpened)
|
||||
{
|
||||
if (!settings_) return;
|
||||
|
||||
Reference in New Issue
Block a user