history export: make fees bitcoin, add hook, rm local tx
What changed, and why it matters
This commit changes how Electrum exports a wallet's transaction history to a CSV or JSON file. It switches the fee column from satoshis (tiny Bitcoin units) to whole bitcoin units so it matches other amount columns, lets plugins take over history export entirely, and removes unconfirmed/local transactions from the export because their order isn't stable and they aren't useful for accounting. There is no direct security fix here; it is a usability/consistency improvement for exported reports.
No immediate security action required. Treat as a normal feature/usability update. If reviewing for security, verify that the new plugin hook is documented and that plugins replacing export_history_to_file handle file_path safely, since the hook short-circuits the built-in exporter.
Security signals we found
No memory-safety, cryptographic, or authorization changes
No input validation or parsing changes
Plugin hook added: run_hook('export_history_to_file', ...) allows third-party plugins to replace the export behavior
Export data semantics changed: fee values converted from satoshis to bitcoin units; unconfirmed/local transactions excluded
Evidence from the diff
The patch modifies Abstract_Wallet.export_history_to_file() in electrum/wallet.py and its call site in electrum/gui/qt/history_list.py. It makes the method’s parameters keyword-only, adds a plugin hook (run_hook(‘export_history_to_file’, …)) that can short-circuit the built-in exporter, filters out transactions whose timestamp is None or 0 (unconfirmed/local), and changes the CSV/JSON fee field from an integer satoshi value to a bitcoin-formatted string via util.format_satoshis(fees_sat). The column header is renamed from network_fee_satoshi to network_fee_bc. The diff does not alter transaction handling, cryptography, networking, or privilege boundaries.
Changed components
electrum/wallet.pyelectrum/gui/qt/history_list.pyInspect captured patch +14 / −8
diff --git a/electrum/gui/qt/history_list.py b/electrum/gui/qt/history_list.py
index 366a27c..b3493e9 100644
--- a/electrum/gui/qt/history_list.py
+++ b/electrum/gui/qt/history_list.py
@@ -845,9 +845,9 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
return
try:
self.wallet.export_history_to_file(
- self.main_window.fx if self.hm.should_show_fiat() else None,
- filename,
- csv_button.isChecked(),
+ fx=self.main_window.fx if self.hm.should_show_fiat() else None,
+ file_path=filename,
+ is_csv=csv_button.isChecked(),
)
except (IOError, os.error) as reason:
export_error_label = _("Electrum was unable to produce a transaction export.")
diff --git a/electrum/wallet.py b/electrum/wallet.py
index aea8a5c..d7ae300 100644
--- a/electrum/wallet.py
+++ b/electrum/wallet.py
@@ -3614,9 +3614,14 @@ class Abstract_Wallet(ABC, Logger, EventListener):
util.trigger_callback('wallet_updated', self)
self.adb.set_future_tx(tx.txid(), wanted_height=wanted_height)
- def export_history_to_file(self, fx: Optional['FxThread'], file_path: str, is_csv: bool):
+ def export_history_to_file(self, *, fx: Optional['FxThread'], file_path: str, is_csv: bool):
+ """Create a file containing the wallet history in either json or csv format, e.g. for bookkeeping."""
+ if run_hook('export_history_to_file', self, fx, file_path, is_csv):
+ return # allow for plugins to create history fancy export
txns = self.get_full_history(fx=fx)
- lines = []
+ # remove unconfirmed/local tx as their ordering is not deterministic, and they don't seem
+ # useful for a wallet export (can't do accounting on a tx that hasn't happened yet)
+ txns = {k: v for k, v in txns.items() if v['timestamp'] not in (None, 0)}
def get_all_fees_paid_by_item(h_item: dict) -> Tuple[int, Optional[Fiat]]:
# gets all fees paid in an item (or group), as the outer group doesn't contain the
@@ -3642,9 +3647,10 @@ class Abstract_Wallet(ABC, Logger, EventListener):
return fees_sat, fees_fiat
+ lines = []
if is_csv:
# sort by timestamp so the generated csv is more understandable on first sight
- txns = dict(sorted(txns.items(), key=lambda h_item: h_item[1]['timestamp'] or 0))
+ txns = dict(sorted(txns.items(), key=lambda h_item: h_item[1]['timestamp']))
for item in txns.values():
# tx groups will are shown as single element
fees_sat, fees_fiat = get_all_fees_paid_by_item(item)
@@ -3659,7 +3665,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
item['bc_value'],
item['ln_value'],
item.get('fiat_value', ''),
- fees_sat,
+ util.format_satoshis(fees_sat),
str(fees_fiat or ''),
item['date']
]
@@ -3676,7 +3682,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
"amount_chain_bc",
"amount_lightning_bc",
"fiat_value",
- "network_fee_satoshi",
+ "network_fee_bc",
"fiat_fee",
"timestamp"])
for line in lines:
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.