alina schanz

codepythonmit

stakeyield

what staked ether earns, read from the contracts: lido's daily rebases from its own events, ten liquid staking tokens' rates now and a week ago, the withdrawal queue, the deposit contract.

head 50136bc2026-09-1523 files451 lines of python

contents
  1. record
  2. readme
  3. files
  4. history

record

commits
4 on main, 2026-09-15 to 2026-09-15
signed
4 good signature from 17BA…F3AB
tests
11 passed 11 skipped
python 3.11.9 on my machine, 2026-09-23
ci
ci.yml lists python 3.10, 3.12, 3.13, 3.14.
history
every commit up to 50136bc, each with its signed record. the history itself stays off this site, so from here you cannot tie a file to a commit.
source
stakeyield.tar.gz, 16.9 KB · sha256 877ce5a02ddd…: the files at 50136bc, without README.md, which is shown below.
license
mit (LICENSE)

readme

rendered from README.md at 50136bc. i left out the badges, which load from github and shields.io, and one link, which shows as plain text. the install lines point at github, which answers 404 for this account at the moment.

stakeyield

what staked ether earns, read from the contracts and nowhere else: lido's daily rebases from its own events, and for ten liquid staking tokens the ether one of them is worth now and a week ago, annualised. the lido withdrawal queue and the deposit contract's count next to it. public rpcs, no keys, no dashboard in between.

$ stakeyield
what staked ether earns, read from the contracts. block 25,982,706 (2026-09-15 11:56 utc), 7.0 days back to block 25,932,306

token    issuer             eth per token   days ago     apr  eth behind
stETH    lido                1 (rebasing)              2.26%       9.69m  the last 7 rebases, 2.22% to 2.33%
wstETH   lido, wrapped           1.244021   1.243482   2.25%       4.61m
rETH     rocket pool             1.171783   1.171297   2.15%     371.06k
cbETH    coinbase                1.139469   1.138950   2.37%     448.67k
sfrxETH  frax                    1.169382   1.168786   2.65%      43.22k
mETH     mantle                  1.098835   1.098381   2.15%     243.36k
osETH    stakewise               1.079046   1.078620   2.06%     160.27k
ETHx     stader                  1.097244   1.096761   2.29%      84.89k
LsETH    liquid collective       1.117306   1.116811   2.30%     320.38k
swETH    swell                   1.130828   1.130377   2.07%      12.42k
ankrETH  ankr                    1.239249   1.239249   0.00%       8.49k  the rate did not move: nobody updated it, which is not a yield of zero

lido: 9.69m eth pooled; the newest rebase at 2026-09-14 12:00 utc paid 2.22% annualised over 24 hours; the protocol and the operators took 65 to 68 eth a day in fees
lido withdrawal queue: 14,592 steth in 147 requests waiting; request 135,705 is the newest, 135,558 the last one paid out
deposit contract: 2,555,318 deposits since it opened in 2020

apr: the change of ether per token between the two blocks, annualised simply, the way lido states its own;
every token quotes its rate in its own function (see the readme). eth behind: total supply at the rate.
numbers, not calls: none of this is a promise of the next week, and none of it says where ether should go.

nine of the ten tokens paid between 2.06% and 2.65% over that week, a spread of half a point that comes from fees (lido keeps 10%, rocket pool's operators take a commission), from how much of the ether sits idle waiting for a validator, and from mev luck. the tenth did not move at all: ankr's ratio is written by an off-chain updater, and for that week nobody wrote it. a rate that does not move is not a yield of zero, it is a number nobody updated, and the table says so.

install

pipx install git+https://github.com/alinaschanz/stakeyield

or clone it and run python -m stakeyield from the folder. python 3.10 or newer, no dependencies.

use

stakeyield                          # every token, seven days, lido's rebases and queue
stakeyield --days 30                # a month back instead (1 to 30)
stakeyield --tokens rETH,cbETH      # just these
stakeyield --no-lido                # the token table only
stakeyield --json
stakeyield --summary-append data/daily.csv --quiet   # one row per token, the dataset
stakeyield --rpc https://your.archive.node           # the older block needs state from a week ago

how it works

  • lido. every day the accounting oracle reports and steth rebases; the token emits TokenRebased(reportTimestamp, timeElapsed, preTotalShares, preTotalEther, postTotalShares, postTotalEther, sharesMintedAsFees). the apr of one report is lido's own definition, the change of ether per share annualised: (post / pre - 1) * 365 days / timeElapsed. the last seven are read with eth_getLogs in pages of a thousand blocks; the fee line is sharesMintedAsFees at the post-report share price. getTotalPooledEther() is the ether behind steth. the queue is the withdrawal queue's unfinalizedStETH(), unfinalizedRequestNumber(), getLastRequestId(), getLastFinalizedRequestId().
  • the other tokens do not rebase; the token gets worth more ether over time, and each says so in its own function. the table in stakeyield/tokens.py keeps the call next to the token: wsteth stEthPerToken(), reth getExchangeRate(), cbeth exchangeRate(), sfrxeth pricePerShare(), meth mETHToETH(1e18) on mantle's staking contract, oseth convertToAssets(1e18) on stakewise's controller, ethx getExchangeRate() on stader's manager, lseth underlyingBalanceFromShares(1e18), sweth swETHToETHRate(), ankreth ratio() which is the other way round (tokens per ether) and is inverted. every rate is read twice, at the newest block and at the block seven days earlier (7,200 blocks a day), and annualised the same way as lido's: (now / then - 1) * 365 days / the seconds between the two blocks.
  • eth behind is the token's totalSupply() times its rate: the ether those tokens are a claim on. for cbeth that is the wrapped part only; coinbase stakes more than it wraps.
  • the deposit contract's get_deposit_count() is the number of 32-ether deposits ever made, a little-endian counter in a bytes answer.
  • the older rate is an eth_call at a block a week old, which needs an endpoint that keeps state that far back: tenderly, drpc and mevblocker do, publicnode does not, and the list is tried in that order (or --rpc yours). a token whose contract does not answer is left out with a note.

reading the table

  • an apr over seven days is what happened, not what is promised: consensus rewards move with the size of the validator set, execution rewards with the blocks' fees and mev, and one lucky block moves a small token's week.
  • a rate is a contract's word about itself. lido's rebase comes from its oracle committee, rocket pool's from its oracle dao, coinbase's from coinbase; the table reads them, it cannot audit them.
  • "eth behind" says how much ether stands behind the token, not how easily it comes back out: that is a queue (lido's is on the last line), a redemption window, or a market price on a dex.
  • numbers, not calls: none of this says which token to hold. depegs, smart contract risk, slashing and who controls the keys are outside these columns.

the dataset

data/daily.csv gets one row per token every night at 00:47 utc: the rate now and seven days earlier, the apr, the ether behind the token, and for steth the mean of the last seven rebases and the withdrawal queue. columns are in data/README.md; a rerun for the same day replaces the day.

exit codes and scripting

0 after a table, 2 when a token name is wrong, --days is out of range or no rpc answered. --json carries every rebase with its numbers, so stakeyield --json | jq '.lido.rebases[] | {report_time_utc, apr}' prints the week day by day.

see also

verify a release

every release carries the sdist and the wheel, a SHA256SUMS file, an opentimestamps proof of that file, and a build provenance attestation made in github's own signing flow. with the files downloaded into one folder:

sha256sum -c SHA256SUMS
gh attestation verify ./*.whl --owner alinaschanz
ots verify SHA256SUMS.ots

the commit itself is signed.

license

mit. numbers, not calls. not financial advice.

files

history

  1. 50136bcmegood signature from 17BA…F3AB

  2. 1232305megood signature from 17BA…F3AB

  3. 895ed4dmegood signature from 17BA…F3AB

  4. bcbc121megood signature from 17BA…F3AB

the latest 4 commits. all 4, with the signature of each: the commit list.