mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-12-26 18:16:45 +00:00
add support for multiple payments in a tx
This commit is contained in:
parent
661e88447f
commit
8b07336d71
4 changed files with 92 additions and 32 deletions
|
@ -120,8 +120,8 @@ public class PSBT {
|
|||
for(TransactionOutput txOutput : transaction.getOutputs()) {
|
||||
try {
|
||||
Address address = txOutput.getScript().getToAddresses()[0];
|
||||
if(address.equals(walletTransaction.getRecipientAddress())) {
|
||||
outputNodes.add(wallet.getWalletAddresses().getOrDefault(walletTransaction.getRecipientAddress(), null));
|
||||
if(walletTransaction.getPayments().stream().anyMatch(payment -> payment.getAddress().equals(address))) {
|
||||
outputNodes.add(wallet.getWalletAddresses().getOrDefault(address, null));
|
||||
} else if(address.equals(wallet.getAddress(walletTransaction.getChangeNode()))) {
|
||||
outputNodes.add(walletTransaction.getChangeNode());
|
||||
}
|
||||
|
|
49
src/main/java/com/sparrowwallet/drongo/wallet/Payment.java
Normal file
49
src/main/java/com/sparrowwallet/drongo/wallet/Payment.java
Normal file
|
@ -0,0 +1,49 @@
|
|||
package com.sparrowwallet.drongo.wallet;
|
||||
|
||||
import com.sparrowwallet.drongo.address.Address;
|
||||
|
||||
public class Payment {
|
||||
private Address address;
|
||||
private String label;
|
||||
private long amount;
|
||||
private boolean sendMax;
|
||||
|
||||
public Payment(Address address, String label, long amount, boolean sendMax) {
|
||||
this.address = address;
|
||||
this.label = label;
|
||||
this.amount = amount;
|
||||
this.sendMax = sendMax;
|
||||
}
|
||||
|
||||
public Address getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(Address address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public long getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(long amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public boolean isSendMax() {
|
||||
return sendMax;
|
||||
}
|
||||
|
||||
public void setSendMax(boolean sendMax) {
|
||||
this.sendMax = sendMax;
|
||||
}
|
||||
}
|
|
@ -434,8 +434,9 @@ public class Wallet {
|
|||
return getFee(changeOutput, feeRate, longTermFeeRate);
|
||||
}
|
||||
|
||||
public WalletTransaction createWalletTransaction(List<UtxoSelector> utxoSelectors, List<UtxoFilter> utxoFilters, Address recipientAddress, long recipientAmount, double feeRate, double longTermFeeRate, Long fee, Integer currentBlockHeight, boolean sendAll, boolean groupByAddress, boolean includeMempoolChange) throws InsufficientFundsException {
|
||||
long valueRequiredAmt = recipientAmount;
|
||||
public WalletTransaction createWalletTransaction(List<UtxoSelector> utxoSelectors, List<UtxoFilter> utxoFilters, List<Payment> payments, double feeRate, double longTermFeeRate, Long fee, Integer currentBlockHeight, boolean groupByAddress, boolean includeMempoolChange) throws InsufficientFundsException {
|
||||
long totalPaymentAmount = payments.stream().map(Payment::getAmount).mapToLong(v -> v).sum();
|
||||
long valueRequiredAmt = totalPaymentAmount;
|
||||
|
||||
while(true) {
|
||||
Map<BlockTransactionHashIndex, WalletNode> selectedUtxos = selectInputs(utxoSelectors, utxoFilters, valueRequiredAmt, feeRate, longTermFeeRate, groupByAddress, includeMempoolChange);
|
||||
|
@ -457,8 +458,11 @@ public class Wallet {
|
|||
txInput.setSequenceNumber(TransactionInput.SEQUENCE_RBF_ENABLED);
|
||||
}
|
||||
|
||||
//Add recipient output
|
||||
transaction.addOutput(recipientAmount, recipientAddress);
|
||||
//Add recipient outputs
|
||||
for(Payment payment : payments) {
|
||||
transaction.addOutput(payment.getAmount(), payment.getAddress());
|
||||
}
|
||||
|
||||
int noChangeVSize = transaction.getVirtualSize();
|
||||
long noChangeFeeRequiredAmt = (fee == null ? (long)(feeRate * noChangeVSize) : fee);
|
||||
|
||||
|
@ -467,13 +471,19 @@ public class Wallet {
|
|||
|
||||
//If sending all selected utxos, set the recipient amount to equal to total of those utxos less the no change fee
|
||||
long maxSendAmt = totalSelectedAmt - noChangeFeeRequiredAmt;
|
||||
if(sendAll && recipientAmount != maxSendAmt) {
|
||||
recipientAmount = maxSendAmt;
|
||||
continue;
|
||||
Optional<Payment> optMaxPayment = payments.stream().filter(payment -> payment.isSendMax()).findFirst();
|
||||
if(optMaxPayment.isPresent()) {
|
||||
Payment maxPayment = optMaxPayment.get();
|
||||
maxSendAmt = maxSendAmt - payments.stream().filter(payment -> !maxPayment.equals(payment)).map(Payment::getAmount).mapToLong(v -> v).sum();
|
||||
if(maxPayment.getAmount() != maxSendAmt) {
|
||||
maxPayment.setAmount(maxSendAmt);
|
||||
totalPaymentAmount = payments.stream().map(Payment::getAmount).mapToLong(v -> v).sum();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//Calculate what is left over from selected utxos after paying recipient
|
||||
long differenceAmt = totalSelectedAmt - recipientAmount;
|
||||
long differenceAmt = totalSelectedAmt - totalPaymentAmount;
|
||||
|
||||
//If insufficient fee, increase value required from inputs to include the fee and try again
|
||||
if(differenceAmt < noChangeFeeRequiredAmt) {
|
||||
|
@ -503,10 +513,10 @@ public class Wallet {
|
|||
//Add change output
|
||||
transaction.addOutput(changeAmt, getOutputScript(changeNode));
|
||||
|
||||
return new WalletTransaction(this, transaction, utxoSelectors, selectedUtxos, recipientAddress, recipientAmount, changeNode, changeAmt, changeFeeRequiredAmt);
|
||||
return new WalletTransaction(this, transaction, utxoSelectors, selectedUtxos, payments, changeNode, changeAmt, changeFeeRequiredAmt);
|
||||
}
|
||||
|
||||
return new WalletTransaction(this, transaction, utxoSelectors, selectedUtxos, recipientAddress, recipientAmount, differenceAmt);
|
||||
return new WalletTransaction(this, transaction, utxoSelectors, selectedUtxos, payments, differenceAmt);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.sparrowwallet.drongo.address.Address;
|
|||
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -16,23 +17,21 @@ public class WalletTransaction {
|
|||
private final Transaction transaction;
|
||||
private final List<UtxoSelector> utxoSelectors;
|
||||
private final Map<BlockTransactionHashIndex, WalletNode> selectedUtxos;
|
||||
private final Address recipientAddress;
|
||||
private final long recipientAmount;
|
||||
private final List<Payment> payments;
|
||||
private final WalletNode changeNode;
|
||||
private final long changeAmount;
|
||||
private final long fee;
|
||||
|
||||
public WalletTransaction(Wallet wallet, Transaction transaction, List<UtxoSelector> utxoSelectors, Map<BlockTransactionHashIndex, WalletNode> selectedUtxos, Address recipientAddress, long recipientAmount, long fee) {
|
||||
this(wallet, transaction, utxoSelectors, selectedUtxos, recipientAddress, recipientAmount, null, 0L, fee);
|
||||
public WalletTransaction(Wallet wallet, Transaction transaction, List<UtxoSelector> utxoSelectors, Map<BlockTransactionHashIndex, WalletNode> selectedUtxos, List<Payment> payments, long fee) {
|
||||
this(wallet, transaction, utxoSelectors, selectedUtxos, payments, null, 0L, fee);
|
||||
}
|
||||
|
||||
public WalletTransaction(Wallet wallet, Transaction transaction, List<UtxoSelector> utxoSelectors, Map<BlockTransactionHashIndex, WalletNode> selectedUtxos, Address recipientAddress, long recipientAmount, WalletNode changeNode, long changeAmount, long fee) {
|
||||
public WalletTransaction(Wallet wallet, Transaction transaction, List<UtxoSelector> utxoSelectors, Map<BlockTransactionHashIndex, WalletNode> selectedUtxos, List<Payment> payments, WalletNode changeNode, long changeAmount, long fee) {
|
||||
this.wallet = wallet;
|
||||
this.transaction = transaction;
|
||||
this.utxoSelectors = utxoSelectors;
|
||||
this.selectedUtxos = selectedUtxos;
|
||||
this.recipientAddress = recipientAddress;
|
||||
this.recipientAmount = recipientAmount;
|
||||
this.payments = payments;
|
||||
this.changeNode = changeNode;
|
||||
this.changeAmount = changeAmount;
|
||||
this.fee = fee;
|
||||
|
@ -58,12 +57,8 @@ public class WalletTransaction {
|
|||
return selectedUtxos;
|
||||
}
|
||||
|
||||
public Address getRecipientAddress() {
|
||||
return recipientAddress;
|
||||
}
|
||||
|
||||
public long getRecipientAmount() {
|
||||
return recipientAmount;
|
||||
public List<Payment> getPayments() {
|
||||
return payments;
|
||||
}
|
||||
|
||||
public WalletNode getChangeNode() {
|
||||
|
@ -102,19 +97,25 @@ public class WalletTransaction {
|
|||
return !utxoSelectors.isEmpty() && utxoSelectors.get(0) instanceof PresetUtxoSelector;
|
||||
}
|
||||
|
||||
public boolean isConsolidationSend() {
|
||||
if(getRecipientAddress() != null && getWallet() != null) {
|
||||
return getWallet().isWalletOutputScript(getRecipientAddress().getOutputScript());
|
||||
public boolean isConsolidationSend(Payment payment) {
|
||||
if(payment.getAddress() != null && getWallet() != null) {
|
||||
return getWallet().isWalletOutputScript(payment.getAddress().getOutputScript());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public WalletNode getConsolidationSendNode() {
|
||||
if(getRecipientAddress() != null && getWallet() != null) {
|
||||
return getWallet().getWalletOutputScripts().get(getRecipientAddress().getOutputScript());
|
||||
public List<WalletNode> getConsolidationSendNodes() {
|
||||
List<WalletNode> walletNodes = new ArrayList<>();
|
||||
for(Payment payment : payments) {
|
||||
if(payment.getAddress() != null && getWallet() != null) {
|
||||
WalletNode walletNode = getWallet().getWalletOutputScripts().get(payment.getAddress().getOutputScript());
|
||||
if(walletNode != null) {
|
||||
walletNodes.add(walletNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return walletNodes;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue