mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-23 20:36:44 +00:00
send all utxos support
This commit is contained in:
parent
3305d0630a
commit
c8e38de5aa
8 changed files with 170 additions and 22 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
|||
Subproject commit 2ff9c94c62d1f20e408f89d7a41e2e66426b8634
|
||||
Subproject commit 5d14be5c9c8cfaaa0fbf9d362b6609873dce38e0
|
|
@ -4,9 +4,7 @@ import com.sparrowwallet.drongo.Utils;
|
|||
import com.sparrowwallet.drongo.address.Address;
|
||||
import com.sparrowwallet.drongo.wallet.BlockTransaction;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.ReceiveActionEvent;
|
||||
import com.sparrowwallet.sparrow.event.ReceiveToEvent;
|
||||
import com.sparrowwallet.sparrow.event.ViewTransactionEvent;
|
||||
import com.sparrowwallet.sparrow.event.*;
|
||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||
import com.sparrowwallet.sparrow.wallet.*;
|
||||
import javafx.application.Platform;
|
||||
|
@ -14,11 +12,14 @@ import javafx.geometry.Pos;
|
|||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
import javafx.scene.layout.HBox;
|
||||
import org.controlsfx.glyphfont.FontAwesome;
|
||||
import org.controlsfx.glyphfont.Glyph;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class EntryCell extends TreeTableCell<Entry, Entry> {
|
||||
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
|
@ -84,6 +85,7 @@ class EntryCell extends TreeTableCell<Entry, Entry> {
|
|||
tooltip.setText(hashIndexEntry.getHashIndex().toString());
|
||||
setTooltip(tooltip);
|
||||
|
||||
HBox actionBox = new HBox();
|
||||
Button viewTransactionButton = new Button("");
|
||||
Glyph searchGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.SEARCH);
|
||||
searchGlyph.setFontSize(12);
|
||||
|
@ -91,7 +93,33 @@ class EntryCell extends TreeTableCell<Entry, Entry> {
|
|||
viewTransactionButton.setOnAction(event -> {
|
||||
EventManager.get().post(new ViewTransactionEvent(hashIndexEntry.getBlockTransaction(), hashIndexEntry));
|
||||
});
|
||||
setGraphic(viewTransactionButton);
|
||||
actionBox.getChildren().add(viewTransactionButton);
|
||||
|
||||
if(hashIndexEntry.getType().equals(HashIndexEntry.Type.OUTPUT) && !hashIndexEntry.isSpent()) {
|
||||
Button spendUtxoButton = new Button("");
|
||||
Glyph sendGlyph = new Glyph("FontAwesome", FontAwesome.Glyph.SEND);
|
||||
sendGlyph.setFontSize(12);
|
||||
spendUtxoButton.setGraphic(sendGlyph);
|
||||
spendUtxoButton.setOnAction(event -> {
|
||||
List<HashIndexEntry> utxoEntries = getTreeTableView().getSelectionModel().getSelectedCells().stream()
|
||||
.map(tp -> tp.getTreeItem().getValue())
|
||||
.filter(e -> e instanceof HashIndexEntry)
|
||||
.map(e -> (HashIndexEntry)e)
|
||||
.filter(e -> e.getType().equals(HashIndexEntry.Type.OUTPUT) && !hashIndexEntry.isSpent())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if(!utxoEntries.contains(hashIndexEntry)) {
|
||||
utxoEntries = List.of(hashIndexEntry);
|
||||
}
|
||||
|
||||
final List<HashIndexEntry> spendingUtxoEntries = utxoEntries;
|
||||
EventManager.get().post(new SendActionEvent(utxoEntries));
|
||||
Platform.runLater(() -> EventManager.get().post(new SpendUtxoEvent(spendingUtxoEntries)));
|
||||
});
|
||||
actionBox.getChildren().add(spendUtxoButton);
|
||||
}
|
||||
|
||||
setGraphic(actionBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package com.sparrowwallet.sparrow.event;
|
||||
|
||||
import com.sparrowwallet.sparrow.wallet.HashIndexEntry;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SendActionEvent {
|
||||
private final List<HashIndexEntry> utxoEntries;
|
||||
|
||||
public SendActionEvent(List<HashIndexEntry> utxoEntries) {
|
||||
this.utxoEntries = utxoEntries;
|
||||
}
|
||||
|
||||
public List<HashIndexEntry> getUtxoEntries() {
|
||||
return utxoEntries;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.sparrowwallet.sparrow.event;
|
||||
|
||||
import com.sparrowwallet.sparrow.wallet.HashIndexEntry;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SpendUtxoEvent {
|
||||
private final List<HashIndexEntry> utxoEntries;
|
||||
|
||||
public SpendUtxoEvent(List<HashIndexEntry> utxoEntries) {
|
||||
this.utxoEntries = utxoEntries;
|
||||
}
|
||||
|
||||
public List<HashIndexEntry> getUtxoEntries() {
|
||||
return utxoEntries;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.sparrowwallet.sparrow.wallet;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
||||
import com.sparrowwallet.drongo.wallet.UtxoSelector;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
public class MaxUtxoSelector implements UtxoSelector {
|
||||
@Override
|
||||
public Collection<BlockTransactionHashIndex> select(long targetValue, Collection<BlockTransactionHashIndex> candidates) {
|
||||
return Collections.unmodifiableCollection(candidates);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import com.sparrowwallet.sparrow.BitcoinUnit;
|
|||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.control.*;
|
||||
import com.sparrowwallet.sparrow.event.FeeRatesUpdatedEvent;
|
||||
import com.sparrowwallet.sparrow.event.SpendUtxoEvent;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
|
@ -50,6 +51,9 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
@FXML
|
||||
private ComboBox<BitcoinUnit> amountUnit;
|
||||
|
||||
@FXML
|
||||
private Button maxButton;
|
||||
|
||||
@FXML
|
||||
private Slider targetBlocks;
|
||||
|
||||
|
@ -69,10 +73,10 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
private TransactionDiagram transactionDiagram;
|
||||
|
||||
@FXML
|
||||
private Button clear;
|
||||
private Button clearButton;
|
||||
|
||||
@FXML
|
||||
private Button create;
|
||||
private Button createButton;
|
||||
|
||||
private final BooleanProperty userFeeSet = new SimpleBooleanProperty(false);
|
||||
|
||||
|
@ -129,6 +133,7 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
addValidation();
|
||||
|
||||
address.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
maxButton.setDisable(!isValidRecipientAddress());
|
||||
updateTransaction();
|
||||
});
|
||||
|
||||
|
@ -145,6 +150,8 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
}
|
||||
});
|
||||
|
||||
maxButton.setDisable(!isValidRecipientAddress());
|
||||
|
||||
insufficientInputsProperty.addListener((observable, oldValue, newValue) -> {
|
||||
amount.textProperty().removeListener(amountListener);
|
||||
String amt = amount.getText();
|
||||
|
@ -193,7 +200,7 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
feeAmountUnit.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||
Long value = getFeeValueSats(oldValue);
|
||||
if(value != null) {
|
||||
setFee(value);
|
||||
setFeeValueSats(value);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -210,20 +217,35 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
}
|
||||
});
|
||||
|
||||
utxoSelectorProperty.addListener((observable, oldValue, utxoSelector) -> {
|
||||
if(utxoSelector instanceof PresetUtxoSelector) {
|
||||
PresetUtxoSelector presetUtxoSelector = (PresetUtxoSelector)utxoSelector;
|
||||
int num = presetUtxoSelector.getPresetUtxos().size();
|
||||
String selection = " (" + num + " UTXO" + (num > 1 ? "s" : "") + " selected)";
|
||||
maxButton.setText("Max" + selection);
|
||||
clearButton.setText("Clear" + selection);
|
||||
} else {
|
||||
maxButton.setText("Max");
|
||||
clearButton.setText("Clear");
|
||||
}
|
||||
});
|
||||
|
||||
walletTransactionProperty.addListener((observable, oldValue, walletTransaction) -> {
|
||||
if(walletTransaction != null) {
|
||||
setRecipientValueSats(walletTransaction.getRecipientAmount());
|
||||
|
||||
double feeRate = (double)walletTransaction.getFee() / walletTransaction.getTransaction().getVirtualSize();
|
||||
if(userFeeSet.get()) {
|
||||
setTargetBlocks(getTargetBlocks(feeRate));
|
||||
} else {
|
||||
setFee(walletTransaction.getFee());
|
||||
setFeeValueSats(walletTransaction.getFee());
|
||||
}
|
||||
|
||||
setFeeRate(feeRate);
|
||||
}
|
||||
|
||||
transactionDiagram.update(walletTransaction);
|
||||
create.setDisable(walletTransaction == null);
|
||||
createButton.setDisable(walletTransaction == null);
|
||||
});
|
||||
|
||||
address.setText("32YSPMaUePf511u5adEckiNq8QLec9ksXX");
|
||||
|
@ -247,12 +269,17 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
}
|
||||
|
||||
private void updateTransaction() {
|
||||
updateTransaction(false);
|
||||
}
|
||||
|
||||
private void updateTransaction(boolean sendAll) {
|
||||
try {
|
||||
Address recipientAddress = getRecipientAddress();
|
||||
Long recipientAmount = getRecipientValueSats();
|
||||
Long recipientAmount = sendAll ? Long.valueOf(1L) : getRecipientValueSats();
|
||||
if(recipientAmount != null && recipientAmount != 0 && (!userFeeSet.get() || (getFeeValueSats() != null && getFeeValueSats() > 0))) {
|
||||
Wallet wallet = getWalletForm().getWallet();
|
||||
WalletTransaction walletTransaction = wallet.createWalletTransaction(getUtxoSelectors(), recipientAddress, recipientAmount, getFeeRate(), userFeeSet.get() ? getFeeValueSats() : null);
|
||||
Long userFee = userFeeSet.get() ? getFeeValueSats() : null;
|
||||
WalletTransaction walletTransaction = wallet.createWalletTransaction(getUtxoSelectors(), recipientAddress, recipientAmount, getFeeRate(), userFee, sendAll);
|
||||
walletTransactionProperty.setValue(walletTransaction);
|
||||
insufficientInputsProperty.set(false);
|
||||
|
||||
|
@ -268,6 +295,10 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
}
|
||||
|
||||
private List<UtxoSelector> getUtxoSelectors() {
|
||||
if(utxoSelectorProperty.get() != null) {
|
||||
return List.of(utxoSelectorProperty.get());
|
||||
}
|
||||
|
||||
UtxoSelector priorityUtxoSelector = new PriorityUtxoSelector(AppController.getCurrentBlockHeight());
|
||||
return List.of(priorityUtxoSelector);
|
||||
}
|
||||
|
@ -299,6 +330,14 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
return null;
|
||||
}
|
||||
|
||||
private void setRecipientValueSats(long recipientValue) {
|
||||
amount.textProperty().removeListener(amountListener);
|
||||
DecimalFormat df = new DecimalFormat("#.#");
|
||||
df.setMaximumFractionDigits(8);
|
||||
amount.setText(df.format(amountUnit.getValue().getValue(recipientValue)));
|
||||
amount.textProperty().addListener(amountListener);
|
||||
}
|
||||
|
||||
private Long getFeeValueSats() {
|
||||
return getFeeValueSats(feeAmountUnit.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
@ -312,6 +351,14 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
return null;
|
||||
}
|
||||
|
||||
private void setFeeValueSats(long feeValue) {
|
||||
fee.textProperty().removeListener(feeListener);
|
||||
DecimalFormat df = new DecimalFormat("#.#");
|
||||
df.setMaximumFractionDigits(8);
|
||||
fee.setText(df.format(feeAmountUnit.getValue().getValue(feeValue)));
|
||||
fee.textProperty().addListener(feeListener);
|
||||
}
|
||||
|
||||
private Integer getTargetBlocks() {
|
||||
int index = (int)targetBlocks.getValue();
|
||||
return TARGET_BLOCKS_RANGE.get(index);
|
||||
|
@ -356,14 +403,6 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
feeRate.setText(String.format("%.2f", feeRateAmt) + " sats/vByte");
|
||||
}
|
||||
|
||||
private void setFee(long feeValue) {
|
||||
fee.textProperty().removeListener(feeListener);
|
||||
DecimalFormat df = new DecimalFormat("#.#");
|
||||
df.setMaximumFractionDigits(8);
|
||||
fee.setText(df.format(feeAmountUnit.getValue().getValue(feeValue)));
|
||||
fee.textProperty().addListener(feeListener);
|
||||
}
|
||||
|
||||
private Node getSliderThumb() {
|
||||
return targetBlocks.lookup(".thumb");
|
||||
}
|
||||
|
@ -373,14 +412,30 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
}
|
||||
|
||||
public void setMaxInput(ActionEvent event) {
|
||||
UtxoSelector utxoSelector = utxoSelectorProperty.get();
|
||||
if(utxoSelector == null) {
|
||||
MaxUtxoSelector maxUtxoSelector = new MaxUtxoSelector();
|
||||
utxoSelectorProperty.set(maxUtxoSelector);
|
||||
}
|
||||
|
||||
updateTransaction(true);
|
||||
}
|
||||
|
||||
public void clear(ActionEvent event) {
|
||||
address.setText("");
|
||||
label.setText("");
|
||||
|
||||
amount.textProperty().removeListener(amountListener);
|
||||
amount.setText("");
|
||||
amount.textProperty().addListener(amountListener);
|
||||
|
||||
fee.textProperty().removeListener(feeListener);
|
||||
fee.setText("");
|
||||
fee.textProperty().addListener(feeListener);
|
||||
|
||||
userFeeSet.set(false);
|
||||
targetBlocks.setValue(4);
|
||||
utxoSelectorProperty.setValue(null);
|
||||
walletTransactionProperty.setValue(null);
|
||||
}
|
||||
|
||||
|
@ -394,4 +449,13 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
feeRatesChart.select(getTargetBlocks());
|
||||
setFeeRate(event.getTargetBlockFeeRates().get(getTargetBlocks()));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void spendUtxos(SpendUtxoEvent event) {
|
||||
if(!event.getUtxoEntries().isEmpty() && event.getUtxoEntries().get(0).getWallet().equals(getWalletForm().getWallet())) {
|
||||
List<BlockTransactionHashIndex> utxos = event.getUtxoEntries().stream().map(HashIndexEntry::getHashIndex).collect(Collectors.toList());
|
||||
setUtxoSelector(new PresetUtxoSelector(utxos));
|
||||
updateTransaction(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.google.common.eventbus.Subscribe;
|
|||
import com.sparrowwallet.sparrow.AppController;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.ReceiveActionEvent;
|
||||
import com.sparrowwallet.sparrow.event.SendActionEvent;
|
||||
import com.sparrowwallet.sparrow.event.WalletSettingsChangedEvent;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
|
@ -113,4 +114,11 @@ public class WalletController extends WalletFormController implements Initializa
|
|||
selectFunction(Function.RECEIVE);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void sendAction(SendActionEvent event) {
|
||||
if(!event.getUtxoEntries().isEmpty() && event.getUtxoEntries().get(0).getWallet().equals(walletForm.getWallet())) {
|
||||
selectFunction(Function.SEND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,9 +99,9 @@
|
|||
<Insets left="25.0" right="25.0" bottom="25.0" />
|
||||
</padding>
|
||||
<HBox AnchorPane.rightAnchor="10">
|
||||
<Button fx:id="clear" text="Clear" cancelButton="true" onAction="#clear" />
|
||||
<Button fx:id="clearButton" text="Clear" cancelButton="true" onAction="#clear" />
|
||||
<Region HBox.hgrow="ALWAYS" style="-fx-min-width: 20px" />
|
||||
<Button fx:id="create" text="Create Transaction" defaultButton="true" disable="true" onAction="#createTransaction" />
|
||||
<Button fx:id="createButton" text="Create Transaction" defaultButton="true" disable="true" onAction="#createTransaction" />
|
||||
</HBox>
|
||||
</AnchorPane>
|
||||
</bottom>
|
||||
|
|
Loading…
Reference in a new issue