Problem
QuickStats and History.tsx's HistorySummary both compute "Average Fee" by averaging the fee field across all loaded transactions — sent and received alike — but a transaction's fee is only ever paid by its source account. For a direction: 'received' transaction, fee reflects what the counterparty paid, not the current wallet. Averaging it in as if it were the user's own cost skews the displayed figure.
// src/components/dashboard/QuickStats.tsx:23-29
const avgFee = transactions.length
? (
transactions.reduce((s, t) => s + parseFloat(t.fee || '0'), 0) /
transactions.length /
10_000_000
).toFixed(7)
: '0'
// src/pages/History.tsx:107
const totalFees = txs.reduce((s, t) => s + parseFloat(t.fee || '0') / 10_000_000, 0)
Neither filters transactions/txs to direction === 'sent' before summing/averaging fee. fetchTransactionsFromHorizon (src/lib/api.ts:410-430) maps fee: r.fee_charged straight from Horizon's per-transaction fee_charged — the fee actually charged to that transaction's source_account — for every record regardless of direction, and direction is computed independently (r.source_account === publicKey ? 'sent' : 'received'). So a received transaction's fee value in this app's data model is real and correctly sourced, but it represents money the sender paid, not the current wallet.
Why it matters
"Average Fee" is presented to the user as a statistic about their own spending (it sits alongside "Total Sent," "Total Received," and "Transactions" on both the Dashboard's QuickStats cards and History's summary cards, with no qualifier distinguishing it as anything but "your average fee"). Including received transactions' fees means:
- A wallet that mostly receives payments (e.g. a merchant or payment-request recipient — a first-class use case in this app, given the whole Payment Requests feature) has its "Average Fee" pulled toward whatever fees other people's wallets happened to pay when sending to it, which has no relationship to what this wallet has actually spent on fees.
- The number is silently wrong in a way that's invisible without cross-referencing individual transactions — there's no error state, no "N/A," it's just a plausible-looking, incorrect average.
This is a distinct, standalone correctness bug from the already-noted duplication of this aggregation code across three files (filed separately in this batch as an architectural issue) — it's the same underlying defect independently present in both QuickStats.tsx and History.tsx's HistorySummary, which is itself evidence of why the duplication matters: this specific mistake would need to be fixed twice (and Dashboard.tsx's ActivityChart doesn't compute fees at all, so it's unaffected, but the two places that do both have the same bug).
Reproduction
- Construct a
transactions fixture with one direction: 'sent' entry with fee: '100' and one direction: 'received' entry with fee: '10000000' (a much larger fee, as if the sender used a very different fee-market rate). Feed it through QuickStats's avgFee calculation — the received transaction's large fee pulls the average far above what the wallet itself has ever actually paid (which, from a single sent transaction with fee: '100', should be 100 / 10_000_000 stroops-to-XLM, not an average blended with the unrelated received fee).
Suggested fix
- Filter to
direction === 'sent' before summing/averaging fees in both QuickStats.tsx and History.tsx's HistorySummary — mirroring the pattern both files already use correctly for totalSent/totalReceived (which do filter by direction).
Additional Notes
src/components/dashboard/QuickStats.tsx:12-29 (sent/received are correctly filtered for the sent/received totals two lines above avgFee, which isn't), src/pages/History.tsx:105-107 (HistorySummary, same pattern).
- Related to, but distinct from, the "duplicated day-bucketing/aggregation logic across QuickStats/HistoryChart/ActivityChart" architectural issue filed separately in this batch — that issue is about the duplication making any fix (including this one) need to be applied multiple times; this issue is about the specific correctness defect itself, independent of where it happens to live.
- Testing strategy: a unit test (once the aggregation logic is extracted per the architecture issue, or in place per-file today) asserting a wallet's computed "Average Fee" is unaffected by the
fee value of any direction: 'received' transaction in the input set.
Problem
QuickStatsandHistory.tsx'sHistorySummaryboth compute "Average Fee" by averaging thefeefield across all loaded transactions — sent and received alike — but a transaction's fee is only ever paid by its source account. For adirection: 'received'transaction,feereflects what the counterparty paid, not the current wallet. Averaging it in as if it were the user's own cost skews the displayed figure.Neither filters
transactions/txstodirection === 'sent'before summing/averagingfee.fetchTransactionsFromHorizon(src/lib/api.ts:410-430) mapsfee: r.fee_chargedstraight from Horizon's per-transactionfee_charged— the fee actually charged to that transaction'ssource_account— for every record regardless of direction, anddirectionis computed independently (r.source_account === publicKey ? 'sent' : 'received'). So areceivedtransaction'sfeevalue in this app's data model is real and correctly sourced, but it represents money the sender paid, not the current wallet.Why it matters
"Average Fee" is presented to the user as a statistic about their own spending (it sits alongside "Total Sent," "Total Received," and "Transactions" on both the Dashboard's
QuickStatscards and History's summary cards, with no qualifier distinguishing it as anything but "your average fee"). Including received transactions' fees means:This is a distinct, standalone correctness bug from the already-noted duplication of this aggregation code across three files (filed separately in this batch as an architectural issue) — it's the same underlying defect independently present in both
QuickStats.tsxandHistory.tsx'sHistorySummary, which is itself evidence of why the duplication matters: this specific mistake would need to be fixed twice (andDashboard.tsx'sActivityChartdoesn't compute fees at all, so it's unaffected, but the two places that do both have the same bug).Reproduction
transactionsfixture with onedirection: 'sent'entry withfee: '100'and onedirection: 'received'entry withfee: '10000000'(a much larger fee, as if the sender used a very different fee-market rate). Feed it throughQuickStats'savgFeecalculation — the received transaction's large fee pulls the average far above what the wallet itself has ever actually paid (which, from a single sent transaction withfee: '100', should be100 / 10_000_000stroops-to-XLM, not an average blended with the unrelated received fee).Suggested fix
direction === 'sent'before summing/averaging fees in bothQuickStats.tsxandHistory.tsx'sHistorySummary— mirroring the pattern both files already use correctly fortotalSent/totalReceived(which do filter bydirection).Additional Notes
src/components/dashboard/QuickStats.tsx:12-29(sent/receivedare correctly filtered for the sent/received totals two lines aboveavgFee, which isn't),src/pages/History.tsx:105-107(HistorySummary, same pattern).feevalue of anydirection: 'received'transaction in the input set.