qml: avoid hue wrap-around for fees < 1sat/vbyte, limit precision.
What changed, and why it matters
This commit fixes a visual bug in Electrum's mobile-style QML interface where very low Bitcoin transaction fees (below 1 satoshi per virtual byte) could cause the fee histogram bar colors to loop around the color wheel and display misleading colors. It also rounds fee values to two decimal places for cleaner display. There is no indication this affects funds, transactions, or wallet security.
No security action required. Treat as a normal UI/visual bug fix. Users do not need to upgrade urgently for security reasons.
Security signals we found
UI-only presentation fix
No input validation, parsing, or cryptographic code changed
No memory safety, privilege, or network behavior changes
No vendor security disclosure or CVE references present
Evidence from the diff
The patch addresses a UI color-mapping issue in NetworkOverview.qml. The hue for fee histogram bars was computed as 2/3 - 2/3 * log(min(600, fee))/log(600). For fees below 1 sat/vB, log(fee) is negative, pushing the hue above 2/3 and causing hue wrap-around in Qt’s HSV color model. The fix clamps the fee value to a minimum of 1 before taking the logarithm. In fee_policy.py, the fallback histogram data and precision are also adjusted: the default data spacing is cleaned up, and fee rates are rounded to two decimals (int(item[0] * 100) / 100) before being clamped to the minimum relay fee. This is a presentation-layer hardening change, not a cryptographic or consensus fix.
Changed components
electrum/fee_policy.py FeeHistogram.get_capped_data()electrum/gui/qml/components/NetworkOverview.qml fee histogram color mappingInspect captured patch +6 / −4
diff --git a/electrum/fee_policy.py b/electrum/fee_policy.py
index 9e8e660..2d76902 100644
--- a/electrum/fee_policy.py
+++ b/electrum/fee_policy.py
@@ -352,7 +352,7 @@ class FeeHistogram:
def get_capped_data(self):
""" used by QML """
- data = self._data or [[FEERATE_DEFAULT_RELAY/1000,1]]
+ data = self._data or [[FEERATE_DEFAULT_RELAY/1000, 1]]
# cap the histogram to a limited number of megabytes
bytes_limit = 10*1000*1000
bytes_current = 0
@@ -360,10 +360,12 @@ class FeeHistogram:
for item in sorted(data, key=lambda x: x[0], reverse=True):
if bytes_current >= bytes_limit:
break
- slot = min(item[1], bytes_limit-bytes_current)
+ slot = min(item[1], bytes_limit - bytes_current)
bytes_current += slot
+ # round & limit precision
+ value = int(item[0] * 100) / 100
capped_histogram.append([
- max(FEERATE_MIN_RELAY/1000, item[0]), # clamped to [FEERATE_MIN_RELAY/1000,inf[
+ max(FEERATE_MIN_RELAY/1000, value), # clamped to [FEERATE_MIN_RELAY/1000,inf]
slot, # width of bucket
bytes_current, # cumulative depth at far end of bucket
])
diff --git a/electrum/gui/qml/components/NetworkOverview.qml b/electrum/gui/qml/components/NetworkOverview.qml
index ecba3e6..7e9b21f 100644
--- a/electrum/gui/qml/components/NetworkOverview.qml
+++ b/electrum/gui/qml/components/NetworkOverview.qml
@@ -118,7 +118,7 @@ Pane {
Layout.preferredWidth: 300 * (modelData[1] / Network.feeHistogram.total)
Layout.fillWidth: true
height: parent.height
- color: Qt.hsva(2/3-(2/3*(Math.log(Math.min(600, modelData[0]))/Math.log(600))), 0.8, 1, 1)
+ color: Qt.hsva(2/3-(2/3*(Math.log(Math.min(600, Math.max(modelData[0], 1)))/Math.log(600))), 0.8, 1, 1)
ToolTip.text: (qsTr("%1 around depth %2")
.arg(modelData[0] + " " + UI_UNIT_NAME.FEERATE_SAT_PER_VB)
.arg((modelData[2]/1000000).toFixed(2) + " " + UI_UNIT_NAME.MEMPOOL_MB)
Why this scored 18/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.