Skip to content

QuickStats/HistorySummary's 'Average Fee' averages fee across sent AND received transactions, but fees are only ever paid by the sender #58

Description

@abayomicornelius

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.

Metadata

Metadata

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26Third CampaignCampaign: Third CampaignbugSomething isn't workingvery hardVery difficult / senior-level bounty issue

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions