fix(market): populate combined address list so portfolio sees addresses
The Market-tab portfolio (editor checklist + SumPortfolioBalance) reads the combined WalletState::addresses view, but the full-node refresh path only ever updated the authoritative z_addresses/t_addresses lists — rebuildAddressList() was never called, so `addresses` stayed empty. Result: the manage-portfolio modal showed no addresses to pick, and addresses added via the overview right-click menu contributed no value to their entry (SumPortfolioBalance found nothing in the empty combined list). Rebuild the combined view in NetworkRefreshService::applyAddressRefreshResult (the sole bulk updater of the address lists), and push newly created addresses into the combined view immediately in the full-node create paths for zero-latency parity with the overview (matching the lite branch). This also repairs two other silently-broken full-node consumers of state.addresses: the auto-shield target-address finder and the pool-mining transparent fallback. Add a regression test (testWalletStateAddressListRebuild) covering the empty-before / union-after rebuild and pending-send delta reflection. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1980,6 +1980,7 @@ void App::createNewZAddress(std::function<void(const std::string&)> callback)
|
||||
info.type = "shielded";
|
||||
info.balance = 0.0;
|
||||
state_.z_addresses.push_back(info);
|
||||
state_.addresses.push_back(info); // keep combined view in sync immediately
|
||||
address_list_dirty_ = true;
|
||||
// Also trigger full refresh to get proper balances
|
||||
addresses_dirty_ = true;
|
||||
@@ -2027,6 +2028,7 @@ void App::createNewTAddress(std::function<void(const std::string&)> callback)
|
||||
info.type = "transparent";
|
||||
info.balance = 0.0;
|
||||
state_.t_addresses.push_back(info);
|
||||
state_.addresses.push_back(info); // keep combined view in sync immediately
|
||||
address_list_dirty_ = true;
|
||||
// Also trigger full refresh to get proper balances
|
||||
addresses_dirty_ = true;
|
||||
|
||||
@@ -1255,6 +1255,11 @@ void NetworkRefreshService::applyAddressRefreshResult(WalletState& state,
|
||||
{
|
||||
state.z_addresses = std::move(result.shieldedAddresses);
|
||||
state.t_addresses = std::move(result.transparentAddresses);
|
||||
// Keep the combined `addresses` view in sync with the authoritative z/t lists.
|
||||
// Consumers that read state.addresses (Market-tab portfolio, auto-shield target
|
||||
// selection, pool-mining transparent fallback) depend on this being rebuilt — the
|
||||
// full-node refresh path is the only place that bulk-updates the address lists.
|
||||
state.rebuildAddressList();
|
||||
}
|
||||
|
||||
void NetworkRefreshService::applyTransactionRefreshResult(WalletState& state,
|
||||
|
||||
@@ -2622,6 +2622,46 @@ void testPortfolioHelpers()
|
||||
EXPECT_EQ(group[0], std::string("R-t2"));
|
||||
}
|
||||
|
||||
// Regression: the Market-tab portfolio (and other consumers) read the combined
|
||||
// WalletState::addresses view, which the full-node refresh path builds from the
|
||||
// authoritative z/t lists via rebuildAddressList(). Before the fix that rebuild
|
||||
// was never called, so `addresses` stayed empty and every portfolio group summed
|
||||
// to 0 and showed no addresses to pick. Guard that the rebuilt view carries the
|
||||
// z/t union with balances, so SumPortfolioBalance resolves right-click-added
|
||||
// addresses.
|
||||
void testWalletStateAddressListRebuild()
|
||||
{
|
||||
using dragonx::AddressInfo;
|
||||
using dragonx::data::SumPortfolioBalance;
|
||||
|
||||
dragonx::WalletState state;
|
||||
auto mk = [](const char* addr, double bal, const char* type) {
|
||||
AddressInfo a; a.address = addr; a.balance = bal; a.type = type; return a;
|
||||
};
|
||||
state.z_addresses.push_back(mk("zs-s1", 10.0, "shielded"));
|
||||
state.z_addresses.push_back(mk("zs-s2", 0.25, "shielded"));
|
||||
state.t_addresses.push_back(mk("R-t1", 1.5, "transparent"));
|
||||
|
||||
// Before the rebuild the combined view is empty -> the reported bug: a portfolio
|
||||
// group of real addresses sums to 0 because none are found in state.addresses.
|
||||
EXPECT_EQ(state.addresses.size(), static_cast<size_t>(0));
|
||||
EXPECT_NEAR(SumPortfolioBalance({"zs-s1", "R-t1"}, state.addresses), 0.0, 1e-9);
|
||||
|
||||
// After the rebuild the combined view is the z/t union with balances.
|
||||
state.rebuildAddressList();
|
||||
EXPECT_EQ(state.addresses.size(), static_cast<size_t>(3));
|
||||
// z entries come first, then t entries (rebuild order).
|
||||
EXPECT_EQ(state.addresses.front().address, std::string("zs-s1"));
|
||||
EXPECT_EQ(state.addresses.back().address, std::string("R-t1"));
|
||||
// A group spanning both pools now resolves its balances.
|
||||
EXPECT_NEAR(SumPortfolioBalance({"zs-s1", "R-t1"}, state.addresses), 11.5, 1e-9);
|
||||
|
||||
// In-place z/t balance mutation (pending-send delta) is reflected on the next rebuild.
|
||||
state.z_addresses[0].balance = 4.0;
|
||||
state.rebuildAddressList();
|
||||
EXPECT_NEAR(SumPortfolioBalance({"zs-s1", "R-t1"}, state.addresses), 5.5, 1e-9);
|
||||
}
|
||||
|
||||
// ComputeConsoleFoldSpans — brace/bracket matching over pretty-printed JSON lines.
|
||||
void testConsoleFoldSpans()
|
||||
{
|
||||
@@ -5353,6 +5393,7 @@ int main()
|
||||
testConsoleTextLayout();
|
||||
testConsoleModel();
|
||||
testPortfolioHelpers();
|
||||
testWalletStateAddressListRebuild();
|
||||
testConsoleFoldSpans();
|
||||
testConsoleScrollController();
|
||||
testConsoleSelectionController();
|
||||
|
||||
Reference in New Issue
Block a user