Skip to content

Commit

Permalink
fix scheduling offers by computing spendable amount from txs
Browse files Browse the repository at this point in the history
  • Loading branch information
woodser committed Dec 17, 2024
1 parent 4e1132b commit 3bc42f1
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ public Offer createAndGetOffer(String offerId,
isPrivateOffer,
buyerAsTakerWithoutDeposit);


// verify buyer as taker security deposit
boolean isBuyerMaker = offerUtil.isBuyOffer(direction);
if (!isBuyerMaker && !isPrivateOffer && buyerAsTakerWithoutDeposit) {
Expand Down
66 changes: 38 additions & 28 deletions core/src/main/java/haveno/core/offer/OpenOfferManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ private void doProcessPendingOffer(List<OpenOffer> openOffers, OpenOffer openOff
if (openOffer.getScheduledTxHashes() != null) {
boolean scheduledTxsAvailable = true;
for (MoneroTxWallet tx : xmrWalletService.getTxs(openOffer.getScheduledTxHashes())) {
if (!tx.isLocked() && !isOutputsAvailable(tx)) {
if (!tx.isLocked() && !hasSpendableFunds(tx)) {
scheduledTxsAvailable = false;
break;
}
Expand Down Expand Up @@ -1165,31 +1165,21 @@ private void scheduleWithEarliestTxs(List<OpenOffer> openOffers, OpenOffer openO
throw new RuntimeException("Not enough money in Haveno wallet");
}

// get earliest available or pending txs with sufficient incoming amount
// get earliest available or pending txs with sufficient spendable amount
BigInteger scheduledAmount = BigInteger.ZERO;
Set<MoneroTxWallet> scheduledTxs = new HashSet<MoneroTxWallet>();
for (MoneroTxWallet tx : xmrWalletService.getTxs()) {

// skip if no funds available
BigInteger sentToSelfAmount = xmrWalletService.getAmountSentToSelf(tx); // amount sent to self always shows 0, so compute from destinations manually
if (sentToSelfAmount.equals(BigInteger.ZERO) && (tx.getIncomingTransfers() == null || tx.getIncomingTransfers().isEmpty())) continue;
if (!isOutputsAvailable(tx)) continue;
// get spendable amount
BigInteger spendableAmount = getSpendableAmount(tx);

// skip if no spendable amount or already scheduled
if (spendableAmount.equals(BigInteger.ZERO)) continue;
if (isTxScheduledByOtherOffer(openOffers, openOffer, tx.getHash())) continue;

// schedule transaction if funds sent to self, because they are not included in incoming transfers // TODO: fix in libraries?
if (sentToSelfAmount.compareTo(BigInteger.ZERO) > 0) {
scheduledAmount = scheduledAmount.add(sentToSelfAmount);
scheduledTxs.add(tx);
} else if (tx.getIncomingTransfers() != null) {

// schedule transaction if incoming tranfers to account 0
for (MoneroIncomingTransfer transfer : tx.getIncomingTransfers()) {
if (transfer.getAccountIndex() == 0) {
scheduledAmount = scheduledAmount.add(transfer.getAmount());
scheduledTxs.add(tx);
}
}
}
// schedule tx
scheduledAmount = scheduledAmount.add(spendableAmount);
scheduledTxs.add(tx);

// break if sufficient funds
if (scheduledAmount.compareTo(offerReserveAmount) >= 0) break;
Expand All @@ -1202,6 +1192,34 @@ private void scheduleWithEarliestTxs(List<OpenOffer> openOffers, OpenOffer openO
openOffer.setState(OpenOffer.State.PENDING);
}

private BigInteger getSpendableAmount(MoneroTxWallet tx) {

// compute spendable amount from outputs if confirmed
if (tx.isConfirmed()) {
BigInteger spendableAmount = BigInteger.ZERO;
if (tx.getOutputsWallet() != null) {
for (MoneroOutputWallet output : tx.getOutputsWallet()) {
if (!output.isSpent() && !output.isFrozen() && output.getAccountIndex() == 0) {
spendableAmount = spendableAmount.add(output.getAmount());
}
}
}
return spendableAmount;
}

// funds sent to self always show 0 incoming amount, so compute from destinations manually
// TODO: this excludes change output, so change is missing from spendable amount until confirmed
BigInteger sentToSelfAmount = xmrWalletService.getAmountSentToSelf(tx);
if (sentToSelfAmount.compareTo(BigInteger.ZERO) > 0) return sentToSelfAmount;

// if not confirmed and not sent to self, return incoming amount
return tx.getIncomingAmount() == null ? BigInteger.ZERO : tx.getIncomingAmount();
}

private boolean hasSpendableFunds(MoneroTxWallet tx) {
return getSpendableAmount(tx).compareTo(BigInteger.ZERO) > 0;
}

private BigInteger getScheduledAmount(List<OpenOffer> openOffers) {
BigInteger scheduledAmount = BigInteger.ZERO;
for (OpenOffer openOffer : openOffers) {
Expand Down Expand Up @@ -1233,14 +1251,6 @@ private boolean isTxScheduledByOtherOffer(List<OpenOffer> openOffers, OpenOffer
return false;
}

private boolean isOutputsAvailable(MoneroTxWallet tx) {
if (tx.getOutputsWallet() == null) return false;
for (MoneroOutputWallet output : tx.getOutputsWallet()) {
if (output.isSpent() || output.isFrozen()) return false;
}
return true;
}

private void signAndPostOffer(OpenOffer openOffer,
boolean useSavingsWallet, // TODO: remove this?
TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
Expand Down

0 comments on commit 3bc42f1

Please sign in to comment.