The owner picked it on 2026-10-12, from post-release-roadmap.md’s Kept in mind (“Small ones: … rewards history (chunk 13)”): “Rewards history on the Staking and governance page. Mainnet needs a new data-layer route; preprod has Koios’s account_rewards.”
Status: ✅ built 2026-10-12 on web-wallet/rewards-history, from main after #301. One PR: the plan, the build and the docs together, so this file went straight to the archive. What still holds is in architecture.md (Staking: the request), flows.md (Staking and voting: the page), privacy.md (What Koios learns) and seedelf-data’s README (the route). The privacy policy is unchanged: its staking row already says “your rewards” go to the Cardano server as the stake address, and nothing is kept.
account_rewards is deprecated, and preprod’s refuses an order (column record.earned_epoch does not exist: its rows are nested, one per stake key). account_reward_history replaces it, on both networks: flat rows, earned_epoch, spendable_epoch, amount (text), type, pool_id_bech32 (null for a reward no pool paid).reward (member, leader, refund) and reward_rest (reserves, treasury, proposal_refund). The next epoch’s are listed before they’re paid: db-sync inserts them during the epoch before (preprod at epoch 318 listed epoch 319’s), and its docs say some may be missing until then. What account_info counts as paid is spendable_epoch at or before the current epoch. (1.5.0’s release review, C05: they’re paid only to a key still registered at that epoch’s start. A stopped key still has one listed in each of up to three epochs after it stops; at each epoch’s start that one goes to the treasury, and db-sync deletes its row.)member, from two pools (LOGIC and GRADA), epochs 223 to 319 (tests/fixtures/rewards-preprod.json, recorded 2026-10-12 by record-rewards.mjs).One each time the page opens, 1,000 rows a request (Koios’s most), newest first (oldest first since 1.5.0’s release review: below):
POST account_reward_history?select=earned_epoch,spendable_epoch,amount,type,pool_id_bech32
&order=spendable_epoch.desc,earned_epoch.desc,type.asc,pool_id_bech32.asc,amount.asc&offset=0&limit=1000
{"_stake_addresses":["stake1…"]}
data-layer.ts).spendable_epoch.asc,earned_epoch.asc,type.asc,pool_id_bech32.asc,amount.asc, oldest first, where those changes land on the last page, and sorts the rows newest first itself. It drops none as a repeat: rows alike in every column, such as two governance deposits back in one epoch, were each paid.)A 22nd public route, POST /api/v1/account_reward_history (api/src/public/account.rs): Koios’s path and JSON, taking exactly the request above (one stake key, that select and order, limit=1000, an offset that’s a multiple of 1,000, up to 100,000). About one user, so never kept. (Since 1.5.0’s release review it takes the oldest-first order alone, and refuses the newest-first one, which only a build from before that review asked: C24.)
grest.account_reward_history (CC-BY-4.0): reward joined to pool_hash, whose view is the bech32 Koios makes, union all reward_rest with no pool, both by addr_id (db-sync’s idx_reward_addr_id, home’s idx_reward_rest_addr_id), sorted in SQL as the order says (ascending puts a null pool last, as PostgREST does).rewardHistory, and DataLayerKoios’s override, as every public read.rewardHistory (the reward-history request): the active account’s stake key; each row checked (whole epochs and an amount in lovelace, or the row is left out, since the page adds them up; a pool ID that isn’t pool1… is dropped, and the row reads Other) and its kind read from type (other for one the wallet doesn’t know); each pool named from the device alone (poolsKnown: this session’s pool details, then the pool list), never asked for.StakeInfo.rewarded: rewards available plus withdrawals over 0, from the reading’s own account_info), so stopping doesn’t hide them. (1.5.0’s release review, C26: under Not staking only when the key isn’t registered; a registered key with no pool had the button twice.)Rewards.tsx): what’s been paid in all; Next epoch, the rows later than now (epochAt), and since 1.5.0’s release review only while the key is registered, since a stopped key’s are never paid (C05); then each year, newest first, on this device’s calendar, its total on its heading; 50 rows at first, Show more for 50 more, asking no one. A row: the day its epoch started, then “Epoch 319 · LOGIC”, the pool by its ticker or its ID cut short (whole on hover), “LOGIC, as its operator”, “LOGIC’s deposit back”, “From the reserves”, “From the treasury”, “A governance action’s deposit back”, or “Other”. The title’s ⓘ says a pool pays at an epoch’s start, for the epoch two before. A note says it’s read from the server, which already knows the account, each time the page opens.public_grammar.rs takes the one request and refuses every other (another page size, an offset off the thousands or past 100,000, two keys, _epoch_no, another select or order); live_public.rs reads the four keys above whole and checks the pages make one list in the order, Koios’s five columns, a pool exactly on a pool’s rows, and what’s been paid against account_info.staking.test.ts: the exact request, 87 rows, tickers from the session and the pool list with no pool_info, a second page past 1,000, the rows left out, the active account’s key, and rewarded; data-layer.test.ts: every page on the data layer, or all again on Koios when one fails, and its weight; rewards-screen.test.ts: the totals, the years, next epoch’s apart, the first 50, who paid each, hidden amounts, nothing yet, and where Staking offers it; e2e/extension.spec.ts: opening Staking asks nothing about rewards, the history asks once, Show more asks no one, and it’s read again each time it opens.staking.test.ts reads the history while rows are added between pages, and while a stopped key’s are deleted, skipping and repeating none, and keeps rows alike in every column (C24); the fakes serve the route in the order asked. rewards-screen.test.ts leaves out a stopped key’s next epoch (C05), and Staking offers the page once to a registered key with no pool (C26). The data layer’s grammar test refuses the old order (C24).