mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2025-11-05 11:56:37 +00:00
Support for fee rate below 1sat/vb
This commit is contained in:
parent
2253a1bb97
commit
2058dbf084
4 changed files with 46 additions and 25 deletions
|
|
@ -87,8 +87,8 @@ public class AppServices {
|
||||||
private static final String TOR_DEFAULT_PROXY_CIRCUIT_ID = "default";
|
private static final String TOR_DEFAULT_PROXY_CIRCUIT_ID = "default";
|
||||||
|
|
||||||
public static final List<Integer> TARGET_BLOCKS_RANGE = List.of(1, 2, 3, 4, 5, 10, 25, 50);
|
public static final List<Integer> TARGET_BLOCKS_RANGE = List.of(1, 2, 3, 4, 5, 10, 25, 50);
|
||||||
public static final List<Long> LONG_FEE_RATES_RANGE = List.of(1L, 2L, 4L, 8L, 16L, 32L, 64L, 128L, 256L, 512L, 1024L, 2048L, 4096L, 8192L);
|
public static final List<Double> DOUBLE_FEE_RATES_RANGE = List.of(0.01D, 0.02D, 0.04D, 0.08D, 0.1D, 0.2D, 0.4D, 0.8D, 1D, 2D, 4D, 8D, 16D, 32D, 64D, 128D, 256D, 512D, 1024D, 2048D, 4096D, 8192D);
|
||||||
public static final List<Long> FEE_RATES_RANGE = LONG_FEE_RATES_RANGE.subList(0, LONG_FEE_RATES_RANGE.size() - 3);
|
public static final List<Double> FEE_RATES_RANGE = DOUBLE_FEE_RATES_RANGE.subList(0, DOUBLE_FEE_RATES_RANGE.size() - 9);
|
||||||
public static final double FALLBACK_FEE_RATE = 20000d / 1000;
|
public static final double FALLBACK_FEE_RATE = 20000d / 1000;
|
||||||
public static final double TESTNET_FALLBACK_FEE_RATE = 1000d / 1000;
|
public static final double TESTNET_FALLBACK_FEE_RATE = 1000d / 1000;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,25 @@
|
||||||
package com.sparrowwallet.sparrow.control;
|
package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
|
import static com.sparrowwallet.sparrow.AppServices.DOUBLE_FEE_RATES_RANGE;
|
||||||
|
import static com.sparrowwallet.sparrow.AppServices.FEE_RATES_RANGE;
|
||||||
|
import static com.sparrowwallet.sparrow.AppServices.TARGET_BLOCKS_RANGE;
|
||||||
|
import static com.sparrowwallet.sparrow.AppServices.getFallbackFeeRate;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.sparrowwallet.sparrow.AppServices;
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
import com.sparrowwallet.sparrow.net.FeeRatesSource;
|
import com.sparrowwallet.sparrow.net.FeeRatesSource;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Slider;
|
import javafx.scene.control.Slider;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static com.sparrowwallet.sparrow.AppServices.*;
|
|
||||||
|
|
||||||
public class FeeRangeSlider extends Slider {
|
public class FeeRangeSlider extends Slider {
|
||||||
private static final double FEE_RATE_SCROLL_INCREMENT = 0.01;
|
private static final double FEE_RATE_SCROLL_INCREMENT = 0.01;
|
||||||
|
|
||||||
|
|
@ -27,11 +35,11 @@ public class FeeRangeSlider extends Slider {
|
||||||
setLabelFormatter(new StringConverter<>() {
|
setLabelFormatter(new StringConverter<>() {
|
||||||
@Override
|
@Override
|
||||||
public String toString(Double object) {
|
public String toString(Double object) {
|
||||||
Long feeRate = LONG_FEE_RATES_RANGE.get(object.intValue());
|
Double feeRate = DOUBLE_FEE_RATES_RANGE.get(object.intValue());
|
||||||
if(isLongFeeRange() && feeRate >= 1000) {
|
if(isDoubleFeeRange() && feeRate >= 1000) {
|
||||||
return feeRate / 1000 + "k";
|
return feeRate / 1000 + "k";
|
||||||
}
|
}
|
||||||
return Long.toString(feeRate);
|
return feeRate < 1 ? Double.toString(feeRate) : String.format("%.0f", feeRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -51,10 +59,10 @@ public class FeeRangeSlider extends Slider {
|
||||||
setOnScroll(event -> {
|
setOnScroll(event -> {
|
||||||
if(event.getDeltaY() != 0) {
|
if(event.getDeltaY() != 0) {
|
||||||
double newFeeRate = getFeeRate() + (event.getDeltaY() > 0 ? FEE_RATE_SCROLL_INCREMENT : -FEE_RATE_SCROLL_INCREMENT);
|
double newFeeRate = getFeeRate() + (event.getDeltaY() > 0 ? FEE_RATE_SCROLL_INCREMENT : -FEE_RATE_SCROLL_INCREMENT);
|
||||||
if(newFeeRate < LONG_FEE_RATES_RANGE.get(0)) {
|
if(newFeeRate < DOUBLE_FEE_RATES_RANGE.get(0)) {
|
||||||
newFeeRate = LONG_FEE_RATES_RANGE.get(0);
|
newFeeRate = DOUBLE_FEE_RATES_RANGE.get(0);
|
||||||
} else if(newFeeRate > LONG_FEE_RATES_RANGE.get(LONG_FEE_RATES_RANGE.size() - 1)) {
|
} else if(newFeeRate > DOUBLE_FEE_RATES_RANGE.get(DOUBLE_FEE_RATES_RANGE.size() - 1)) {
|
||||||
newFeeRate = LONG_FEE_RATES_RANGE.get(LONG_FEE_RATES_RANGE.size() - 1);
|
newFeeRate = DOUBLE_FEE_RATES_RANGE.get(DOUBLE_FEE_RATES_RANGE.size() - 1);
|
||||||
}
|
}
|
||||||
setFeeRate(newFeeRate);
|
setFeeRate(newFeeRate);
|
||||||
}
|
}
|
||||||
|
|
@ -62,7 +70,17 @@ public class FeeRangeSlider extends Slider {
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getFeeRate() {
|
public double getFeeRate() {
|
||||||
return Math.pow(2.0, getValue());
|
double value = getValue();
|
||||||
|
// First range: 0.01, 0.02, 0.04, 0.08 and smooth values in between
|
||||||
|
if(value < 3) return 0.01 * Math.pow(2, value);
|
||||||
|
// Transition from 0.08 to 0.1 (smoothly, using factor 1.25)
|
||||||
|
if(value < 4) return 0.08 * Math.pow(1.25, value - 3);
|
||||||
|
// Second binary range: 0.1, 0.2, 0.4, 0.8 and smooth values in between
|
||||||
|
if(value < 7) return 0.1 * Math.pow(2, value - 4);
|
||||||
|
// Transition from 0.8 to 1.0 (smoothly, using factor 1.25)
|
||||||
|
if(value < 8) return 0.8 * Math.pow(1.25, value - 7);
|
||||||
|
// Third binary range: 1, 2, 4, 8, ... and smooth values in between
|
||||||
|
return Math.pow(2, value - 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFeeRate(double feeRate) {
|
public void setFeeRate(double feeRate) {
|
||||||
|
|
@ -72,16 +90,18 @@ public class FeeRangeSlider extends Slider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMaxFeeRange(double value) {
|
private void updateMaxFeeRange(double value) {
|
||||||
if(value >= getMax() && !isLongFeeRange()) {
|
if(value >= getMax() && !isDoubleFeeRange()) {
|
||||||
setMax(LONG_FEE_RATES_RANGE.size() - 1);
|
setMin(FEE_RATES_RANGE.size() - 2);
|
||||||
|
setMax(DOUBLE_FEE_RATES_RANGE.size() - 1);
|
||||||
updateTrackHighlight();
|
updateTrackHighlight();
|
||||||
} else if(value == getMin() && isLongFeeRange()) {
|
} else if(value == getMin() && isDoubleFeeRange()) {
|
||||||
|
setMin(0);
|
||||||
setMax(FEE_RATES_RANGE.size() - 1);
|
setMax(FEE_RATES_RANGE.size() - 1);
|
||||||
updateTrackHighlight();
|
updateTrackHighlight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isLongFeeRange() {
|
private boolean isDoubleFeeRange() {
|
||||||
return getMax() > FEE_RATES_RANGE.size() - 1;
|
return getMax() > FEE_RATES_RANGE.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,8 +158,8 @@ public class FeeRangeSlider extends Slider {
|
||||||
|
|
||||||
private int getPercentageOfFeeRange(Double feeRate) {
|
private int getPercentageOfFeeRange(Double feeRate) {
|
||||||
double index = Math.log(feeRate) / Math.log(2);
|
double index = Math.log(feeRate) / Math.log(2);
|
||||||
if(isLongFeeRange()) {
|
if(isDoubleFeeRange()) {
|
||||||
index *= ((double)FEE_RATES_RANGE.size() / (LONG_FEE_RATES_RANGE.size())) * 0.99;
|
index *= ((double)FEE_RATES_RANGE.size() / (DOUBLE_FEE_RATES_RANGE.size())) * 0.99;
|
||||||
}
|
}
|
||||||
return (int)Math.round(index * 10.0);
|
return (int)Math.round(index * 10.0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1139,7 +1139,7 @@ public class HeadersController extends TransactionFormController implements Init
|
||||||
|
|
||||||
if(fee.getValue() > 0) {
|
if(fee.getValue() > 0) {
|
||||||
double feeRateAmt = fee.getValue() / headersForm.getTransaction().getVirtualSize();
|
double feeRateAmt = fee.getValue() / headersForm.getTransaction().getVirtualSize();
|
||||||
if(feeRateAmt > AppServices.LONG_FEE_RATES_RANGE.get(AppServices.LONG_FEE_RATES_RANGE.size() - 1)) {
|
if(feeRateAmt > AppServices.DOUBLE_FEE_RATES_RANGE.get(AppServices.DOUBLE_FEE_RATES_RANGE.size() - 1)) {
|
||||||
Optional<ButtonType> optType = AppServices.showWarningDialog("Very high fee rate!",
|
Optional<ButtonType> optType = AppServices.showWarningDialog("Very high fee rate!",
|
||||||
"This transaction pays a very high fee rate of " + String.format("%.0f", feeRateAmt) + " sats/vB.\n\nBroadcast this transaction?", ButtonType.YES, ButtonType.NO);
|
"This transaction pays a very high fee rate of " + String.format("%.0f", feeRateAmt) + " sats/vB.\n\nBroadcast this transaction?", ButtonType.YES, ButtonType.NO);
|
||||||
if(optType.isPresent() && optType.get() == ButtonType.NO) {
|
if(optType.isPresent() && optType.get() == ButtonType.NO) {
|
||||||
|
|
|
||||||
|
|
@ -900,13 +900,13 @@ public class SendController extends WalletFormController implements Initializabl
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setFeeRatePriority(Double feeRateAmt) {
|
private void setFeeRatePriority(Double feeRateAmt) {
|
||||||
|
feeRateAmt = Math.round(feeRateAmt * 100.0) / 100.0; // Round to 2 decimal places
|
||||||
Map<Integer, Double> targetBlocksFeeRates = getTargetBlocksFeeRates();
|
Map<Integer, Double> targetBlocksFeeRates = getTargetBlocksFeeRates();
|
||||||
Integer targetBlocks = getTargetBlocks(feeRateAmt);
|
|
||||||
if(targetBlocksFeeRates.get(Integer.MAX_VALUE) != null) {
|
if(targetBlocksFeeRates.get(Integer.MAX_VALUE) != null) {
|
||||||
Double minFeeRate = targetBlocksFeeRates.get(Integer.MAX_VALUE);
|
Double minFeeRate = targetBlocksFeeRates.get(Integer.MAX_VALUE);
|
||||||
if(minFeeRate > 1.0 && feeRateAmt < minFeeRate) {
|
if(feeRateAmt > 0.01 && feeRateAmt < minFeeRate) {
|
||||||
feeRatePriority.setText("Below Minimum");
|
feeRatePriority.setText("Below Minimum");
|
||||||
feeRatePriority.setTooltip(new Tooltip("Transactions at this fee rate are currently being purged from the default sized mempool"));
|
feeRatePriority.setTooltip(new Tooltip("Transactions at this fee rate can be purged from the default sized mempool"));
|
||||||
feeRatePriorityGlyph.setStyle("-fx-text-fill: #a0a1a7cc");
|
feeRatePriorityGlyph.setStyle("-fx-text-fill: #a0a1a7cc");
|
||||||
feeRatePriorityGlyph.setIcon(FontAwesome5.Glyph.EXCLAMATION_CIRCLE);
|
feeRatePriorityGlyph.setIcon(FontAwesome5.Glyph.EXCLAMATION_CIRCLE);
|
||||||
return;
|
return;
|
||||||
|
|
@ -922,6 +922,7 @@ public class SendController extends WalletFormController implements Initializabl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Integer targetBlocks = getTargetBlocks(feeRateAmt);
|
||||||
if(targetBlocks != null) {
|
if(targetBlocks != null) {
|
||||||
if(targetBlocks < FeeRatesSource.BLOCKS_IN_HALF_HOUR) {
|
if(targetBlocks < FeeRatesSource.BLOCKS_IN_HALF_HOUR) {
|
||||||
Double maxFeeRate = FEE_RATES_RANGE.get(FEE_RATES_RANGE.size() - 1).doubleValue();
|
Double maxFeeRate = FEE_RATES_RANGE.get(FEE_RATES_RANGE.size() - 1).doubleValue();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue