What changed, and why it matters
This commit adds a new optional Bitcoin transaction fee estimator source called block.xyz (using Block's 'Augur' fee estimation service) to the Sparrow Wallet desktop app. It lets users choose this service when estimating how much fee to pay for Bitcoin transactions. There is no obvious security bug in the code, but adding any new external data source creates a small expansion of the app's 'attack surface' and trust assumptions.
Treat as a routine feature addition. If reviewing for security, verify that HttpClientService enforces TLS certificate validation, has reasonable timeouts, and that the parsed fee values are bounded before being used in transaction construction. Consider whether the new endpoint should be documented in privacy/trust disclosures since it can learn user IP addresses when selected.
Security signals we found
New external HTTPS dependency introduced (pricing.bitcoin.block.xyz)
JSON deserialization into nested Java records without explicit schema/field validation in the visible diff
Fallback to DEFAULT_MIN_RELAY_FEE when data is missing, which could lead to underpaid transactions if the API is unreachable or returns malformed data
No visible change to TLS trust model, pinning, or request timeouts in the supplied diff
Feature is user-selectable, not enabled by default
Evidence from the diff
The patch introduces a new FeeRatesSource enum member BLOCK_XYZ that fetches fee estimates from https://pricing.bitcoin.block.xyz/fees, parses a JSON structure of block-target/probability/fee_rate mappings, and exposes four tiers (fastestFee, halfHourFee, hourFee, minimumFee). It also adds the source to the settings dropdown and includes a Block logo SVG. The implementation reuses existing HTTP/JSON infrastructure (HttpClientService.requestJson) and falls back to Transaction.DEFAULT_MIN_RELAY_FEE when expected fields are missing. No input validation beyond null checks is visible in the diff, and the diff does not show TLS/certificate pinning changes or timeout handling.
Changed components
Sparrow Wallet fee estimation settings UIFeeRatesSource enum / network fee fetching layerNew third-party data source: Block.xyz Augur fee estimatorInspect captured patch +71 / −0
diff --git a/src/main/java/com/sparrowwallet/sparrow/net/FeeRatesSource.java b/src/main/java/com/sparrowwallet/sparrow/net/FeeRatesSource.java
index 4eebee6..9a3b7ee 100644
--- a/src/main/java/com/sparrowwallet/sparrow/net/FeeRatesSource.java
+++ b/src/main/java/com/sparrowwallet/sparrow/net/FeeRatesSource.java
@@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.net;
import com.sparrowwallet.drongo.Network;
import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.drongo.protocol.Sha256Hash;
+import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.wallet.BlockTransaction;
import com.sparrowwallet.drongo.wallet.BlockTransactionHash;
import com.sparrowwallet.sparrow.AppServices;
@@ -71,6 +72,30 @@ public enum FeeRatesSource {
return network == Network.MAINNET || network == Network.TESTNET || network == Network.TESTNET4 || network == Network.SIGNET;
}
},
+ BLOCK_XYZ("block.xyz", true) {
+ /*
+ https://engineering.block.xyz/blog/augur-an-open-source-bitcoin-fee-estimation-library
+ */
+ @Override
+ public Map<Integer, Double> getBlockTargetFeeRates(Map<Integer, Double> defaultblockTargetFeeRates) {
+ String url = "https://pricing.bitcoin.block.xyz/fees";
+ return getThreeTierFeeRates(this, defaultblockTargetFeeRates, url);
+ }
+
+ @Override
+ public boolean supportsNetwork(Network network) {
+ return network == Network.MAINNET;
+ }
+
+ @Override
+ protected ThreeTierRates getThreeTierRates(String url, HttpClientService httpClientService) throws Exception {
+ BlockXyzRates rates = httpClientService.requestJson(url, BlockXyzRates.class, null);
+ if(rates.estimates == null) {
+ throw new Exception("Invalid response from " + url);
+ }
+ return rates.getThreeTierRates();
+ }
+ },
BITCOINFEES_EARN_COM("bitcoinfees.earn.com", true) {
@Override
public Map<Integer, Double> getBlockTargetFeeRates(Map<Integer, Double> defaultblockTargetFeeRates) {
@@ -343,6 +368,37 @@ public enum FeeRatesSource {
}
}
+ private record BlockXyzRates(Map<String, BlockXyzEstimate> estimates) {
+ public ThreeTierRates getThreeTierRates() {
+ // see https://engineering.block.xyz/blog/augur-an-open-source-bitcoin-fee-estimation-library
+ //
+ // fastestFee: 95% confidence at 3 blocks
+ // halfHourFee: 80% confidence at 3 blocks
+ // hourFee: 80% confidence at 6 blocks
+ // minimumFee: 80% confidence at 144 blocks
+ Double fastestFee = getFeeRate("3", "0.95");
+ Double halfHourFee = getFeeRate("3", "0.80");
+ Double hourFee = getFeeRate("6", "0.80");
+ Double minimumFee = getFeeRate("144", "0.80");
+ return new ThreeTierRates(fastestFee, halfHourFee, hourFee, minimumFee);
+ }
+
+ private Double getFeeRate(String blocks, String probability) {
+ BlockXyzEstimate estimate = estimates.get(blocks);
+ if(estimate != null && estimate.probabilities != null) {
+ BlockXyzFeeRate feeRate = estimate.probabilities.get(probability);
+ if(feeRate != null) {
+ return feeRate.fee_rate;
+ }
+ }
+ return Transaction.DEFAULT_MIN_RELAY_FEE;
+ }
+ }
+
+ private record BlockXyzEstimate(Map<String, BlockXyzFeeRate> probabilities) {}
+
+ private record BlockXyzFeeRate(Double fee_rate) {}
+
protected record MempoolBlock(Integer nTx, Double medianFee) {}
protected record MempoolBlockSummary(String id, Integer height, Long timestamp, Integer tx_count, Integer weight, MempoolBlockSummaryExtras extras) {
diff --git a/src/main/resources/com/sparrowwallet/sparrow/settings/general.fxml b/src/main/resources/com/sparrowwallet/sparrow/settings/general.fxml
index 69946fd..67e0b35 100644
--- a/src/main/resources/com/sparrowwallet/sparrow/settings/general.fxml
+++ b/src/main/resources/com/sparrowwallet/sparrow/settings/general.fxml
@@ -36,6 +36,7 @@
<FXCollections fx:factory="observableArrayList">
<FeeRatesSource fx:constant="ELECTRUM_SERVER" />
<FeeRatesSource fx:constant="MEMPOOL_SPACE" />
+ <FeeRatesSource fx:constant="BLOCK_XYZ" />
<FeeRatesSource fx:constant="MINIMUM" />
</FXCollections>
</items>
diff --git a/src/main/resources/image/feeratesource/block.xyz-icon.svg b/src/main/resources/image/feeratesource/block.xyz-icon.svg
new file mode 100644
index 0000000..aad459a
--- /dev/null
+++ b/src/main/resources/image/feeratesource/block.xyz-icon.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14px" height="14px" viewBox="0 0 14 14" version="1.1">
+<g id="surface1">
+<path style="" d="M 1.90625 1.398438 L 3.941406 1.398438 C 4.222656 1.398438 4.453125 1.625 4.453125 1.90625 L 4.453125 3.941406 C 4.453125 4.222656 4.222656 4.453125 3.941406 4.453125 L 1.90625 4.453125 C 1.625 4.453125 1.398438 4.222656 1.398438 3.941406 L 1.398438 1.90625 C 1.398438 1.625 1.625 1.398438 1.90625 1.398438 Z M 1.90625 1.398438 "/>
+<path style="" d="M 5.976562 1.398438 L 8.011719 1.398438 C 8.292969 1.398438 8.519531 1.625 8.519531 1.90625 L 8.519531 3.941406 C 8.519531 4.222656 8.292969 4.453125 8.011719 4.453125 L 5.976562 4.453125 C 5.695312 4.453125 5.46875 4.222656 5.46875 3.941406 L 5.46875 1.90625 C 5.46875 1.625 5.695312 1.398438 5.976562 1.398438 Z M 5.976562 1.398438 "/>
+<path style="" d="M 10.046875 1.398438 L 12.082031 1.398438 C 12.363281 1.398438 12.589844 1.625 12.589844 1.90625 L 12.589844 3.941406 C 12.589844 4.222656 12.363281 4.453125 12.082031 4.453125 L 10.046875 4.453125 C 9.765625 4.453125 9.539062 4.222656 9.539062 3.941406 L 9.539062 1.90625 C 9.539062 1.625 9.765625 1.398438 10.046875 1.398438 Z M 10.046875 1.398438 "/>
+<path style="" d="M 1.90625 5.46875 L 3.941406 5.46875 C 4.222656 5.46875 4.453125 5.695312 4.453125 5.976562 L 4.453125 8.011719 C 4.453125 8.292969 4.222656 8.519531 3.941406 8.519531 L 1.90625 8.519531 C 1.625 8.519531 1.398438 8.292969 1.398438 8.011719 L 1.398438 5.976562 C 1.398438 5.695312 1.625 5.46875 1.90625 5.46875 Z M 1.90625 5.46875 "/>
+<path style="" d="M 5.976562 5.46875 L 8.011719 5.46875 C 8.292969 5.46875 8.519531 5.695312 8.519531 5.976562 L 8.519531 8.011719 C 8.519531 8.292969 8.292969 8.519531 8.011719 8.519531 L 5.976562 8.519531 C 5.695312 8.519531 5.46875 8.292969 5.46875 8.011719 L 5.46875 5.976562 C 5.46875 5.695312 5.695312 5.46875 5.976562 5.46875 Z M 5.976562 5.46875 "/>
+<path style="" d="M 10.046875 5.46875 L 12.082031 5.46875 C 12.363281 5.46875 12.589844 5.695312 12.589844 5.976562 L 12.589844 8.011719 C 12.589844 8.292969 12.363281 8.519531 12.082031 8.519531 L 10.046875 8.519531 C 9.765625 8.519531 9.539062 8.292969 9.539062 8.011719 L 9.539062 5.976562 C 9.539062 5.695312 9.765625 5.46875 10.046875 5.46875 Z M 10.046875 5.46875 "/>
+<path style="" d="M 1.90625 9.539062 L 3.941406 9.539062 C 4.222656 9.539062 4.453125 9.765625 4.453125 10.046875 L 4.453125 12.082031 C 4.453125 12.363281 4.222656 12.589844 3.941406 12.589844 L 1.90625 12.589844 C 1.625 12.589844 1.398438 12.363281 1.398438 12.082031 L 1.398438 10.046875 C 1.398438 9.765625 1.625 9.539062 1.90625 9.539062 Z M 1.90625 9.539062 "/>
+<path style="" d="M 5.976562 9.539062 L 8.011719 9.539062 C 8.292969 9.539062 8.519531 9.765625 8.519531 10.046875 L 8.519531 12.082031 C 8.519531 12.363281 8.292969 12.589844 8.011719 12.589844 L 5.976562 12.589844 C 5.695312 12.589844 5.46875 12.363281 5.46875 12.082031 L 5.46875 10.046875 C 5.46875 9.765625 5.695312 9.539062 5.976562 9.539062 Z M 5.976562 9.539062 "/>
+<path style="" d="M 10.046875 9.539062 L 12.082031 9.539062 C 12.363281 9.539062 12.589844 9.765625 12.589844 10.046875 L 12.589844 12.082031 C 12.589844 12.363281 12.363281 12.589844 12.082031 12.589844 L 10.046875 12.589844 C 9.765625 12.589844 9.539062 12.363281 9.539062 12.082031 L 9.539062 10.046875 C 9.539062 9.765625 9.765625 9.539062 10.046875 9.539062 Z M 10.046875 9.539062 "/>
+</g>
+</svg>
\ No newline at end of file
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.