deposit view shows base address and externally used addresses

update deposit view on new block
avoid extra request to tx pool on deposit view init
This commit is contained in:
woodser 2023-06-03 12:02:53 -04:00
parent c64131ced2
commit 9fffd74ddb
5 changed files with 111 additions and 57 deletions

View file

@ -43,7 +43,8 @@ public final class XmrAddressEntry implements PersistablePayload {
OFFER_FUNDING, OFFER_FUNDING,
RESERVED_FOR_TRADE, RESERVED_FOR_TRADE,
MULTI_SIG, MULTI_SIG,
TRADE_PAYOUT TRADE_PAYOUT,
BASE_ADDRESS
} }
// keyPair can be null in case the object is created from deserialization as it is transient. // keyPair can be null in case the object is created from deserialization as it is transient.

View file

@ -39,6 +39,7 @@ import monero.wallet.MoneroWallet;
import monero.wallet.MoneroWalletRpc; import monero.wallet.MoneroWalletRpc;
import monero.wallet.model.MoneroCheckTx; import monero.wallet.model.MoneroCheckTx;
import monero.wallet.model.MoneroDestination; import monero.wallet.model.MoneroDestination;
import monero.wallet.model.MoneroIncomingTransfer;
import monero.wallet.model.MoneroOutputQuery; import monero.wallet.model.MoneroOutputQuery;
import monero.wallet.model.MoneroOutputWallet; import monero.wallet.model.MoneroOutputWallet;
import monero.wallet.model.MoneroSubaddress; import monero.wallet.model.MoneroSubaddress;
@ -488,7 +489,7 @@ public class XmrWalletService {
daemon.flushTxPool(txHash); // flush tx from pool daemon.flushTxPool(txHash); // flush tx from pool
} catch (MoneroRpcError err) { } catch (MoneroRpcError err) {
System.out.println(daemon.getRpcConnection()); System.out.println(daemon.getRpcConnection());
throw err.getCode() == -32601 ? new RuntimeException("Failed to flush tx from pool. Arbitrator must use trusted, unrestricted daemon") : err; throw err.getCode().equals(-32601) ? new RuntimeException("Failed to flush tx from pool. Arbitrator must use trusted, unrestricted daemon") : err;
} }
} }
return new Tuple2<>(tx, actualSecurityDeposit); return new Tuple2<>(tx, actualSecurityDeposit);
@ -838,24 +839,29 @@ public class XmrWalletService {
// ----------------------------- LEGACY APP ------------------------------- // ----------------------------- LEGACY APP -------------------------------
public synchronized XmrAddressEntry getNewAddressEntry() { public synchronized XmrAddressEntry getNewAddressEntry() {
return getNewAddressEntry(XmrAddressEntry.Context.AVAILABLE); return getNewAddressEntryAux(null, XmrAddressEntry.Context.AVAILABLE);
} }
public synchronized XmrAddressEntry getNewAddressEntry(String offerId, XmrAddressEntry.Context context) { public synchronized XmrAddressEntry getNewAddressEntry(String offerId, XmrAddressEntry.Context context) {
// try to use available and not yet used entries // try to use available and not yet used entries
try { try {
List<MoneroTxWallet> incomingTxs = getIncomingTxs(); // prefetch all incoming txs to avoid query per subaddress List<MoneroTxWallet> incomingTxs = getTxsWithIncomingOutputs(); // prefetch all incoming txs to avoid query per subaddress
Optional<XmrAddressEntry> emptyAvailableAddressEntry = getAddressEntryListAsImmutableList().stream().filter(e -> XmrAddressEntry.Context.AVAILABLE == e.getContext()).filter(e -> isSubaddressUnused(e.getSubaddressIndex(), incomingTxs)).findAny(); Optional<XmrAddressEntry> emptyAvailableAddressEntry = getAddressEntryListAsImmutableList().stream().filter(e -> XmrAddressEntry.Context.AVAILABLE == e.getContext()).filter(e -> isSubaddressUnused(e.getSubaddressIndex(), incomingTxs)).findAny();
if (emptyAvailableAddressEntry.isPresent()) return xmrAddressEntryList.swapAvailableToAddressEntryWithOfferId(emptyAvailableAddressEntry.get(), context, offerId); if (emptyAvailableAddressEntry.isPresent()) return xmrAddressEntryList.swapAvailableToAddressEntryWithOfferId(emptyAvailableAddressEntry.get(), context, offerId);
} catch (Exception e) { } catch (Exception e) {
log.warn("Error getting new address entriy based on incoming transactions"); log.warn("Error getting new address entry based on incoming transactions");
e.printStackTrace(); e.printStackTrace();
} }
// create new subaddress and entry // create new entry
return getNewAddressEntryAux(offerId, context);
}
private XmrAddressEntry getNewAddressEntryAux(String offerId, XmrAddressEntry.Context context) {
MoneroSubaddress subaddress = wallet.createSubaddress(0); MoneroSubaddress subaddress = wallet.createSubaddress(0);
XmrAddressEntry entry = new XmrAddressEntry(subaddress.getIndex(), subaddress.getAddress(), context, offerId, null); XmrAddressEntry entry = new XmrAddressEntry(subaddress.getIndex(), subaddress.getAddress(), context, offerId, null);
log.info("Add new XmrAddressEntry {}", entry);
xmrAddressEntryList.addAddressEntry(entry); xmrAddressEntryList.addAddressEntry(entry);
return entry; return entry;
} }
@ -866,6 +872,12 @@ public class XmrWalletService {
else return unusedAddressEntries.get(0); else return unusedAddressEntries.get(0);
} }
public synchronized XmrAddressEntry getFreshAddressEntry(List<MoneroTxWallet> cachedTxs) {
List<XmrAddressEntry> unusedAddressEntries = getUnusedAddressEntries(cachedTxs);
if (unusedAddressEntries.isEmpty()) return getNewAddressEntry();
else return unusedAddressEntries.get(0);
}
public synchronized XmrAddressEntry recoverAddressEntry(String offerId, String address, XmrAddressEntry.Context context) { public synchronized XmrAddressEntry recoverAddressEntry(String offerId, String address, XmrAddressEntry.Context context) {
var available = findAddressEntry(address, XmrAddressEntry.Context.AVAILABLE); var available = findAddressEntry(address, XmrAddressEntry.Context.AVAILABLE);
if (!available.isPresent()) return null; if (!available.isPresent()) return null;
@ -883,7 +895,7 @@ public class XmrWalletService {
Optional<XmrAddressEntry> addressEntry = getAddressEntryListAsImmutableList().stream() Optional<XmrAddressEntry> addressEntry = getAddressEntryListAsImmutableList().stream()
.filter(e -> context == e.getContext()) .filter(e -> context == e.getContext())
.findAny(); .findAny();
return addressEntry.isPresent() ? addressEntry.get() : getNewAddressEntry(context); return addressEntry.isPresent() ? addressEntry.get() : getNewAddressEntryAux(null, context);
} }
public synchronized Optional<XmrAddressEntry> getAddressEntry(String offerId, XmrAddressEntry.Context context) { public synchronized Optional<XmrAddressEntry> getAddressEntry(String offerId, XmrAddressEntry.Context context) {
@ -923,14 +935,6 @@ public class XmrWalletService {
swapTradeEntryToAvailableEntry(offerId, XmrAddressEntry.Context.TRADE_PAYOUT); swapTradeEntryToAvailableEntry(offerId, XmrAddressEntry.Context.TRADE_PAYOUT);
} }
private XmrAddressEntry getNewAddressEntry(XmrAddressEntry.Context context) {
MoneroSubaddress subaddress = wallet.createSubaddress(0);
XmrAddressEntry entry = new XmrAddressEntry(subaddress.getIndex(), subaddress.getAddress(), context, null, null);
log.info("getOrCreateAddressEntry: add new XmrAddressEntry {}", entry);
xmrAddressEntryList.addAddressEntry(entry);
return entry;
}
private Optional<XmrAddressEntry> findAddressEntry(String address, XmrAddressEntry.Context context) { private Optional<XmrAddressEntry> findAddressEntry(String address, XmrAddressEntry.Context context) {
return getAddressEntryListAsImmutableList().stream().filter(e -> address.equals(e.getAddressString())).filter(e -> context == e.getContext()).findAny(); return getAddressEntryListAsImmutableList().stream().filter(e -> address.equals(e.getAddressString())).filter(e -> context == e.getContext()).findAny();
} }
@ -961,13 +965,25 @@ public class XmrWalletService {
} }
public List<XmrAddressEntry> getAddressEntryListAsImmutableList() { public List<XmrAddressEntry> getAddressEntryListAsImmutableList() {
List<MoneroSubaddress> subaddresses = wallet.getSubaddresses(0);
for (MoneroSubaddress subaddress : subaddresses) {
boolean exists = xmrAddressEntryList.getAddressEntriesAsListImmutable().stream().filter(addressEntry -> addressEntry.getAddressString().equals(subaddress.getAddress())).findAny().isPresent();
if (!exists) {
XmrAddressEntry entry = new XmrAddressEntry(subaddress.getIndex(), subaddress.getAddress(), subaddress.getIndex() == 0 ? XmrAddressEntry.Context.BASE_ADDRESS : XmrAddressEntry.Context.AVAILABLE, null, null);
log.info("Add XmrAddressEntry from existing subaddress: {}", entry);
xmrAddressEntryList.addAddressEntry(entry);
}
}
return xmrAddressEntryList.getAddressEntriesAsListImmutable(); return xmrAddressEntryList.getAddressEntriesAsListImmutable();
} }
public List<XmrAddressEntry> getUnusedAddressEntries() { public List<XmrAddressEntry> getUnusedAddressEntries() {
List<MoneroTxWallet> incomingTxs = getIncomingTxs(); // prefetch all incoming txs to avoid query per subaddress return getUnusedAddressEntries(getTxsWithIncomingOutputs());
}
public List<XmrAddressEntry> getUnusedAddressEntries(List<MoneroTxWallet> cachedTxs) {
return getAvailableAddressEntries().stream() return getAvailableAddressEntries().stream()
.filter(e -> isSubaddressUnused(e.getSubaddressIndex(), incomingTxs)) .filter(e -> isSubaddressUnused(e.getSubaddressIndex(), cachedTxs))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -976,34 +992,55 @@ public class XmrWalletService {
} }
private boolean isSubaddressUnused(int subaddressIndex, List<MoneroTxWallet> incomingTxs) { private boolean isSubaddressUnused(int subaddressIndex, List<MoneroTxWallet> incomingTxs) {
return getNumTxOutputsForSubaddress(subaddressIndex, incomingTxs) == 0; return getNumOutputsForSubaddress(subaddressIndex, incomingTxs) == 0;
} }
public int getNumTxOutputsForSubaddress(int subaddressIndex) { public int getNumOutputsForSubaddress(int subaddressIndex) {
return getNumTxOutputsForSubaddress(subaddressIndex, null); return getNumOutputsForSubaddress(subaddressIndex, null);
} }
public int getNumTxOutputsForSubaddress(int subaddressIndex, List<MoneroTxWallet> incomingTxs) { public int getNumOutputsForSubaddress(int subaddressIndex, List<MoneroTxWallet> incomingTxs) {
if (incomingTxs == null) incomingTxs = getIncomingTxs(subaddressIndex); if (incomingTxs == null) incomingTxs = getTxsWithIncomingOutputs(subaddressIndex);
int numUnspentOutputs = 0; int numUnspentOutputs = 0;
for (MoneroTxWallet tx : incomingTxs) { for (MoneroTxWallet tx : incomingTxs) {
if (tx.getTransfers(new MoneroTransferQuery().setSubaddressIndex(subaddressIndex)).isEmpty()) continue; if (tx.getTransfers(new MoneroTransferQuery().setSubaddressIndex(subaddressIndex)).isEmpty()) continue;
numUnspentOutputs += tx.isConfirmed() ? tx.getOutputsWallet(new MoneroOutputQuery().setSubaddressIndex(subaddressIndex)).size() : 1; // TODO: monero-project does not provide outputs for unconfirmed txs numUnspentOutputs += tx.isConfirmed() ? tx.getOutputsWallet(new MoneroOutputQuery().setAccountIndex(0).setSubaddressIndex(subaddressIndex)).size() : 1; // TODO: monero-project does not provide outputs for unconfirmed txs
} }
return numUnspentOutputs; return numUnspentOutputs;
} }
public List<MoneroTxWallet> getIncomingTxs() { public List<MoneroTxWallet> getTxsWithIncomingOutputs() {
return getIncomingTxs(null); return getTxsWithIncomingOutputs(null);
} }
public List<MoneroTxWallet> getIncomingTxs(Integer subaddressIndex) { public List<MoneroTxWallet> getTxsWithIncomingOutputs(Integer subaddressIndex) {
return wallet.getTxs(new MoneroTxQuery() List<MoneroTxWallet> txs = wallet.getTxs(new MoneroTxQuery().setIncludeOutputs(true));
.setTransferQuery((new MoneroTransferQuery() return getTxsWithIncomingOutputs(txs, subaddressIndex);
.setAccountIndex(0) }
.setSubaddressIndex(subaddressIndex)
.setIsIncoming(true))) public static List<MoneroTxWallet> getTxsWithIncomingOutputs(List<MoneroTxWallet> txs, Integer subaddressIndex) {
.setIncludeOutputs(true)); List<MoneroTxWallet> incomingTxs = new ArrayList<>();
for (MoneroTxWallet tx : txs) {
boolean isIncoming = false;
if (tx.getIncomingTransfers() != null) {
for (MoneroIncomingTransfer transfer : tx.getIncomingTransfers()) {
if (transfer.getAccountIndex().equals(0) && (subaddressIndex == null || transfer.getSubaddressIndex().equals(subaddressIndex))) {
isIncoming = true;
break;
}
}
}
if (tx.getOutputs() != null && !isIncoming) {
for (MoneroOutputWallet output : tx.getOutputsWallet()) {
if (output.getAccountIndex().equals(0) && (subaddressIndex == null || output.getSubaddressIndex().equals(subaddressIndex))) {
isIncoming = true;
break;
}
}
}
if (isIncoming) incomingTxs.add(tx);
}
return incomingTxs;
} }
public BigInteger getBalanceForAddress(String address) { public BigInteger getBalanceForAddress(String address) {

View file

@ -20,7 +20,6 @@ package haveno.desktop.main.funds.deposit;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import common.types.Filter;
import haveno.core.locale.Res; import haveno.core.locale.Res;
import haveno.core.trade.HavenoUtils; import haveno.core.trade.HavenoUtils;
import haveno.core.util.coin.CoinFormatter; import haveno.core.util.coin.CoinFormatter;
@ -34,8 +33,6 @@ import javafx.beans.property.StringProperty;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import monero.daemon.model.MoneroTx; import monero.daemon.model.MoneroTx;
import monero.wallet.model.MoneroTransferQuery;
import monero.wallet.model.MoneroTxQuery;
import monero.wallet.model.MoneroTxWallet; import monero.wallet.model.MoneroTxWallet;
import java.math.BigInteger; import java.math.BigInteger;
@ -49,7 +46,7 @@ class DepositListItem {
private BigInteger balanceAsBI; private BigInteger balanceAsBI;
private String usage = "-"; private String usage = "-";
private XmrBalanceListener balanceListener; private XmrBalanceListener balanceListener;
private int numTxOutputs = 0; private int numTxsWithOutputs = 0;
private final Supplier<LazyFields> lazyFieldsSupplier; private final Supplier<LazyFields> lazyFieldsSupplier;
private static class LazyFields { private static class LazyFields {
@ -98,8 +95,8 @@ class DepositListItem {
} }
private void updateUsage(int subaddressIndex, List<MoneroTxWallet> cachedTxs) { private void updateUsage(int subaddressIndex, List<MoneroTxWallet> cachedTxs) {
numTxOutputs = xmrWalletService.getNumTxOutputsForSubaddress(addressEntry.getSubaddressIndex(), cachedTxs); numTxsWithOutputs = XmrWalletService.getTxsWithIncomingOutputs(cachedTxs, addressEntry.getSubaddressIndex()).size();
usage = numTxOutputs == 0 ? Res.get("funds.deposit.unused") : Res.get("funds.deposit.usedInTx", numTxOutputs); usage = subaddressIndex == 0 ? "Base address" : numTxsWithOutputs == 0 ? Res.get("funds.deposit.unused") : Res.get("funds.deposit.usedInTx", numTxsWithOutputs);
} }
public void cleanup() { public void cleanup() {
@ -114,6 +111,10 @@ class DepositListItem {
return addressEntry.getAddressString(); return addressEntry.getAddressString();
} }
public int getSubaddressIndex() {
return addressEntry.getSubaddressIndex();
}
public String getUsage() { public String getUsage() {
return usage; return usage;
} }
@ -130,8 +131,8 @@ class DepositListItem {
return balanceAsBI; return balanceAsBI;
} }
public int getNumTxOutputs() { public int getNumTxsWithOutputs() {
return numTxOutputs; return numTxsWithOutputs;
} }
public long getNumConfirmationsSinceFirstUsed(List<MoneroTxWallet> incomingTxs) { public long getNumConfirmationsSinceFirstUsed(List<MoneroTxWallet> incomingTxs) {
@ -139,14 +140,10 @@ class DepositListItem {
return tx == null ? 0 : tx.getNumConfirmations(); return tx == null ? 0 : tx.getNumConfirmations();
} }
private MoneroTxWallet getTxWithFewestConfirmations(List<MoneroTxWallet> incomingTxs) { private MoneroTxWallet getTxWithFewestConfirmations(List<MoneroTxWallet> allIncomingTxs) {
// get txs with incoming transfers to subaddress // get txs with incoming outputs to subaddress index
MoneroTxQuery query = new MoneroTxQuery() List<MoneroTxWallet> txs = XmrWalletService.getTxsWithIncomingOutputs(allIncomingTxs, addressEntry.getSubaddressIndex());
.setTransferQuery(new MoneroTransferQuery()
.setIsIncoming(true)
.setSubaddressIndex(addressEntry.getSubaddressIndex()));
List<MoneroTxWallet> txs = incomingTxs == null ? xmrWalletService.getWallet().getTxs(query) : Filter.apply(query, incomingTxs);
// get tx with fewest confirmations // get tx with fewest confirmations
MoneroTxWallet highestTx = null; MoneroTxWallet highestTx = null;

View file

@ -62,6 +62,7 @@ import javafx.scene.layout.VBox;
import javafx.util.Callback; import javafx.util.Callback;
import monero.wallet.model.MoneroTxConfig; import monero.wallet.model.MoneroTxConfig;
import monero.wallet.model.MoneroTxWallet; import monero.wallet.model.MoneroTxWallet;
import monero.wallet.model.MoneroWalletListener;
import net.glxn.qrgen.QRCode; import net.glxn.qrgen.QRCode;
import net.glxn.qrgen.image.ImageType; import net.glxn.qrgen.image.ImageType;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
@ -104,9 +105,11 @@ public class DepositView extends ActivatableView<VBox, Void> {
private final ObservableList<DepositListItem> observableList = FXCollections.observableArrayList(); private final ObservableList<DepositListItem> observableList = FXCollections.observableArrayList();
private final SortedList<DepositListItem> sortedList = new SortedList<>(observableList); private final SortedList<DepositListItem> sortedList = new SortedList<>(observableList);
private XmrBalanceListener balanceListener; private XmrBalanceListener balanceListener;
private MoneroWalletListener walletListener;
private Subscription amountTextFieldSubscription; private Subscription amountTextFieldSubscription;
private ChangeListener<DepositListItem> tableViewSelectionListener; private ChangeListener<DepositListItem> tableViewSelectionListener;
private int gridRow = 0; private int gridRow = 0;
List<MoneroTxWallet> txsWithIncomingOutputs;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle // Constructor, lifecycle
@ -130,8 +133,11 @@ public class DepositView extends ActivatableView<VBox, Void> {
confirmationsColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.confirmations"))); confirmationsColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.confirmations")));
usageColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.usage"))); usageColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.usage")));
// trigger creation of at least 1 savings address // prefetch all incoming txs to avoid query per subaddress
xmrWalletService.getFreshAddressEntry(); txsWithIncomingOutputs = xmrWalletService.getTxsWithIncomingOutputs();
// trigger creation of at least 1 address
xmrWalletService.getFreshAddressEntry(txsWithIncomingOutputs);
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.deposit.noAddresses"))); tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.deposit.noAddresses")));
@ -147,13 +153,10 @@ public class DepositView extends ActivatableView<VBox, Void> {
setUsageColumnCellFactory(); setUsageColumnCellFactory();
setConfidenceColumnCellFactory(); setConfidenceColumnCellFactory();
// prefetch all incoming txs to avoid query per subaddress
List<MoneroTxWallet> incomingTxs = xmrWalletService.getIncomingTxs();
addressColumn.setComparator(Comparator.comparing(DepositListItem::getAddressString)); addressColumn.setComparator(Comparator.comparing(DepositListItem::getAddressString));
balanceColumn.setComparator(Comparator.comparing(DepositListItem::getBalanceAsBI)); balanceColumn.setComparator(Comparator.comparing(DepositListItem::getBalanceAsBI));
confirmationsColumn.setComparator(Comparator.comparingLong(o -> o.getNumConfirmationsSinceFirstUsed(incomingTxs))); confirmationsColumn.setComparator(Comparator.comparingLong(o -> o.getNumConfirmationsSinceFirstUsed(txsWithIncomingOutputs)));
usageColumn.setComparator(Comparator.comparingInt(DepositListItem::getNumTxOutputs)); usageColumn.setComparator(Comparator.comparingInt(DepositListItem::getNumTxsWithOutputs));
tableView.getSortOrder().add(usageColumn); tableView.getSortOrder().add(usageColumn);
tableView.setItems(sortedList); tableView.setItems(sortedList);
@ -199,7 +202,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
generateNewAddressButton = buttonCheckBoxHBox.first; generateNewAddressButton = buttonCheckBoxHBox.first;
generateNewAddressButton.setOnAction(event -> { generateNewAddressButton.setOnAction(event -> {
boolean hasUnUsedAddress = observableList.stream().anyMatch(e -> e.getNumTxOutputs() == 0); boolean hasUnUsedAddress = observableList.stream().anyMatch(e -> e.getSubaddressIndex() != 0 && xmrWalletService.getTxsWithIncomingOutputs(e.getSubaddressIndex()).isEmpty());
if (hasUnUsedAddress) { if (hasUnUsedAddress) {
new Popup().warning(Res.get("funds.deposit.selectUnused")).show(); new Popup().warning(Res.get("funds.deposit.selectUnused")).show();
} else { } else {
@ -219,6 +222,13 @@ public class DepositView extends ActivatableView<VBox, Void> {
} }
}; };
walletListener = new MoneroWalletListener() {
@Override
public void onNewBlock(long height) {
updateList();
}
};
GUIUtil.focusWhenAddedToScene(amountTextField); GUIUtil.focusWhenAddedToScene(amountTextField);
} }
@ -230,6 +240,8 @@ public class DepositView extends ActivatableView<VBox, Void> {
updateList(); updateList();
xmrWalletService.addBalanceListener(balanceListener); xmrWalletService.addBalanceListener(balanceListener);
xmrWalletService.addWalletListener(walletListener);
amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> { amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> {
addressTextField.setAmount(HavenoUtils.parseXmr(t)); addressTextField.setAmount(HavenoUtils.parseXmr(t));
updateQRCode(); updateQRCode();
@ -245,6 +257,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
sortedList.comparatorProperty().unbind(); sortedList.comparatorProperty().unbind();
observableList.forEach(DepositListItem::cleanup); observableList.forEach(DepositListItem::cleanup);
xmrWalletService.removeBalanceListener(balanceListener); xmrWalletService.removeBalanceListener(balanceListener);
xmrWalletService.removeWalletListener(walletListener);
amountTextFieldSubscription.unsubscribe(); amountTextFieldSubscription.unsubscribe();
} }
@ -295,9 +308,14 @@ public class DepositView extends ActivatableView<VBox, Void> {
observableList.forEach(DepositListItem::cleanup); observableList.forEach(DepositListItem::cleanup);
observableList.clear(); observableList.clear();
List<MoneroTxWallet> incomingTxs = xmrWalletService.getIncomingTxs(); // cache incoming txs for performance // cache incoming txs
txsWithIncomingOutputs = xmrWalletService.getTxsWithIncomingOutputs();
// add available address entries and base address
xmrWalletService.getAvailableAddressEntries() xmrWalletService.getAvailableAddressEntries()
.forEach(e -> observableList.add(new DepositListItem(e, xmrWalletService, formatter, incomingTxs))); .forEach(e -> observableList.add(new DepositListItem(e, xmrWalletService, formatter, txsWithIncomingOutputs)));
xmrWalletService.getAddressEntries(XmrAddressEntry.Context.BASE_ADDRESS)
.forEach(e -> observableList.add(new DepositListItem(e, xmrWalletService, formatter, txsWithIncomingOutputs)));
} }
private Coin getAmount() { private Coin getAmount() {

View file

@ -1308,6 +1308,7 @@ message XmrAddressEntry {
RESERVED_FOR_TRADE = 4; RESERVED_FOR_TRADE = 4;
MULTI_SIG = 5; MULTI_SIG = 5;
TRADE_PAYOUT = 6; TRADE_PAYOUT = 6;
BASE_ADDRESS = 7;
} }
int32 subaddress_index = 7; int32 subaddress_index = 7;