Appearance
Sample a long range
Read stETH's share rate once a day across two hundred thousand blocks, and watch it step up with every rebase.
cql
let stETH = ethereum:0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;
from stETH at 25_800_000..26_000_000 every 7200 as s
| project { block: $block, time: $timestamp, ethPerShare: format(s.getPooledEthByShares(1e18), 18) }29 rows; the first six:
| block | time | ethPerShare |
|---|---|---|
| 25804800 | 2026-08-21T16:46:59Z | 1.24219014278314575 |
| 25812000 | 2026-08-22T16:52:23Z | 1.242271090726486053 |
| 25819200 | 2026-08-23T16:57:47Z | 1.242346440645230173 |
| 25826400 | 2026-08-24T17:01:23Z | 1.242424181709375761 |
| 25833600 | 2026-08-25T17:07:35Z | 1.24250107943967682 |
| 25840800 | 2026-08-26T17:11:47Z | 1.242575976401495391 |
How it reads
at 25_800_000..26_000_000 every 7200 reads the contract at every block divisible by 7 200 inside the range — 28 of them — plus the range's end. On Ethereum 7 200 blocks is about a day. The grid is absolute, so the sampled blocks are 25_804_800, 25_812_000, … whatever the range's start; that is why the first row is not block 25 800 000.
ethPerShare rises a little from each row to the next because stETH rebases once a day, when Lido's oracle reports, and a sample a day catches each step once. Sample twice a day and every value appears twice; sample once a week and you skip six steps. $timestamp is the time of the block that answered, which drifts a few minutes later each day because 7 200 blocks is not quite 24 hours.
Leave every out and CQL picks a step itself to keep the source under 10 000 rows, and tells you which as CQL3900. Long ranges and limits has the rule.
Variations
- A summary instead of a list.
| summarize lo = min(ethPerShare), hi = max(ethPerShare)after theproject, or the first and last — Average daily change and annualised rate. - Ending at the head.
at latest-21600..latest every 7200is the last three days, and the rows move with the head each time you run it.
By time instead of block count
at time … every 1d samples at midnight UTC. $sampleAt holds the exact day, and $block the last block before it, except on the first row, which is where the range starts: the first block at or after its midnight.
cql
let stETH = ethereum:0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;
from stETH at time '2026-09-20'..'2026-09-23' every 1d as s
| project { day: $sampleAt, block: $block, ethPerShare: format(s.getPooledEthByShares(1e18), 18) }| day | block | ethPerShare |
|---|---|---|
| 2026-09-20T00:00:00Z | 26014998 | 1.244406447835658845 |
| 2026-09-21T00:00:00Z | 26022169 | 1.244482479264728163 |
| 2026-09-22T00:00:00Z | 26029315 | 1.24455806764908769 |
| 2026-09-23T00:00:00Z | 26036463 | 1.244633419602089156 |