feat(utxo): report per-transaction amounts in BTC/LTC history

Electrum's get_history is txid, height and fee, and we passed exactly that
through. The wallet read total_received and total_sent off those rows, got
undefined for both, defaulted them to zero, and rendered every Bitcoin and
Litecoin row as a confident "sent 0". Silent, so it read as an empty wallet
rather than as a bug.

The amounts come from the verbose transaction, and the exactness helpers
this needed already existed on VerboseTransaction and simply had no caller:
total_received_at is unambiguous from the outputs, and total_sent_from
answers only when the server resolved EVERY input's prevout, so a server
that resolves none leaves the sent side unknown rather than reporting a
partial figure that looks exact.

Best effort throughout. A transaction that cannot be fetched keeps null, not
zero, because the client distinguishes "not determined" from "nothing moved"
and one slow upstream must not fail the whole history request. Resolution is
bounded to the newest rows: each one costs a transaction fetch, so an
unbounded history would turn a single request into hundreds. Rows past the
bound arrive without amounts and say so.

The multi-address path resolves each distinct txid once and computes against
the whole address set, so a transfer between two of the user's own addresses
nets out instead of reading as a payment to a stranger.

openapi.json is regenerated, not edited.
This commit is contained in:
2026-09-20 11:33:56 -04:00
parent c653dec341
commit e24dd44c69
2 changed files with 165 additions and 15 deletions
+36
View File
@@ -3781,6 +3781,24 @@
"format": "int64",
"description": "Block height (`0`/negative for unconfirmed)."
},
"total_received": {
"type": [
"integer",
"null"
],
"format": "int64",
"description": "Satoshis this transaction paid to the queried address, when known.\n\nElectrum's `get_history` carries no amounts at all, so these are filled\nin from the verbose transaction. Absent means \"not determined\", never\nzero: a client that defaults a missing amount to 0 renders every row as\na confident \"sent 0\", which is what this wallet used to do.",
"minimum": 0
},
"total_sent": {
"type": [
"integer",
"null"
],
"format": "int64",
"description": "Satoshis this transaction spent FROM the queried address, when the\nserver resolved every input's prevout. Absent on a server that resolves\nnone (public ElectrumX), because a partial answer would look exact.",
"minimum": 0
},
"txid": {
"type": "string"
}
@@ -5112,6 +5130,24 @@
"format": "int64",
"description": "Block height (`0`/negative for unconfirmed)."
},
"total_received": {
"type": [
"integer",
"null"
],
"format": "int64",
"description": "See [`HistoryEntry::total_received`].",
"minimum": 0
},
"total_sent": {
"type": [
"integer",
"null"
],
"format": "int64",
"description": "See [`HistoryEntry::total_sent`].",
"minimum": 0
},
"txid": {
"type": "string"
}