Watch your rETH climb.
A read-only dashboard for Rocket Pool stakers. Paste an Ethereum address or ENS name and see what your rETH is worth in ETH, what it has really earned, and how fast it is growing.
rETH doesn't rebase: your token balance stays fixed while each token becomes worth more ETH. That makes the question stakers actually care about — how much ETH is my rETH worth, and how much has it earned? — surprisingly hard to answer. Portfolio trackers show rETH as one line in a long list, and the Rocket Pool site leaves the math to you.
RocketYield answers that question on one calm screen, designed to stay open on a second monitor.
- Live position in ETH. The current ETH value of your rETH, ticking up between oracle updates, with fiat value alongside.
- Real earnings. Today, 7d, 30d, 90d, and lifetime, calculated from your actual balance history. Buying more rETH doesn't inflate past earnings, and selling doesn't erase them.
- Yield. Trailing 7d and 30d APR / APY.
- Projections. Expected earnings per day, month, and year at the current 7-day rate, compared with your realized 30-day pace.
- Charts and history. rETH/ETH rate over time, daily earnings, and a daily earnings table with CSV and JSON export.
- Market vs. redemption rate. Live DEX premium or discount from the Curve rETH/WETH pool.
- Eight fiat currencies. USD, EUR, GBP, AUD, CAD, CNY, JPY, KRW.
- Shareable. The address lives in the URL (
?address=vitalik.eth), so any view can be bookmarked or shared.
- No wallet connection, no signatures. RocketYield only reads public chain data.
- No accounts, no cookies. Settings like the fiat currency are kept in your browser's
localStorage; chain data is cached in IndexedDB. - Cookieless, first-party visitor counts. No third-party analytics. Each page load pings
/api/visitors, which counts one visitor per browser per day using a salted hash that is deleted daily; only daily totals are stored (Cloudflare D1). Wallet addresses, ENS names, balances, and earnings are never recorded. The totals are public at/stats.
Earnings come from the change in the rETH/ETH exchange rate, weighted by how much rETH the address held at each point in time:
earnings = Σ (rETH balance held during period × rate change during that period)
Any window (7d, 30d, lifetime) is a slice of that timeline. The full write-up, including contracts, sampling, and limitations, is on the methodology page.
| Data | Source |
|---|---|
| Current balance and redemption rate | balanceOf and getExchangeRate on the mainnet rETH contract |
| Balance history | alchemy_getAssetTransfers on Alchemy; filtered rETH Transfer logs on other providers |
| Rate history | Shared timeline from GET /api/rates (Cloudflare KV), with historical getExchangeRate calls at the wallet's transfer blocks. Falls back to sampling entirely in the browser if the API is unavailable. |
| ETH fiat prices | Coinbase (live); /api/historical-prices → DefiLlama (daily history for the earnings table) |
| Market rate | GeckoTerminal (Curve rETH/WETH) |
Price providers are optional: if they fail, the on-chain ETH figures still load.
The ticking balance is an estimate. Rocket Pool's rate changes in discrete oracle updates, roughly every 24 hours. RocketYield smooths the recent realized rate between updates and snaps to the real on-chain value at each update.
React 19 + TypeScript + Vite · viem for chain reads · Lightweight Charts · Cloudflare Pages + Pages Functions + KV · Vitest and Playwright for tests.
- Node.js 20.19+ or 22.12+
- An Ethereum mainnet RPC endpoint with archive reads and broad
eth_getLogssupport. A free Alchemy or Infura key works. Generic free public endpoints usually reject the log ranges this app needs.
git clone https://github.com/novaheic/RocketYield.git
cd RocketYield
npm install
cp .env.example .env.local # Windows: copy .env.example .env.localSet VITE_ETHEREUM_RPC_URL in .env.local, then:
npm run devThis runs the full dashboard. Without Cloudflare Functions, /api/rates is absent and the browser samples rate history itself (slower on first load), and /stats has no data.
Note:
VITE_variables are bundled into the client and visible to anyone. Treat the browser RPC URL as public and use your provider's domain/origin restrictions.
To test the shared rate cache (/api/rates) and the stats endpoint locally:
cp .dev.vars.example .dev.vars # then fill in ETHEREUM_RPC_URL
npm run cf:devOpen http://localhost:8788. This needs a configured wrangler.jsonc — see Deploy your own.
| Variable | Where | Purpose |
|---|---|---|
VITE_ETHEREUM_RPC_URL |
.env.local / build env |
Archive RPC used by the browser. Public. |
ETHEREUM_RPC_URL |
.dev.vars / Pages secret |
Server-only archive RPC used to refresh the shared rate cache. |
RATE_HISTORY |
wrangler.jsonc (KV binding) |
KV namespace for the shared rate timeline. |
STATS_DB |
wrangler.jsonc (D1 binding) |
D1 database for daily visitor counts. Tables are created on first use. |
| Command | Description |
|---|---|
npm run dev |
Start the Vite dev server |
npm run build |
Type-check and build to dist/ |
npm run preview |
Preview the production build |
npm run cf:dev |
Build and run with Cloudflare Pages Functions locally |
npm run lint |
Run ESLint |
npm test |
Run unit tests (Vitest) |
npm run test:e2e |
Run responsive end-to-end tests (Playwright) |
npm run typecheck:functions |
Type-check the Pages Functions |
Any static host: run npm run build with VITE_ETHEREUM_RPC_URL set and publish dist/. Everything except the shared rate cache and /stats works this way.
Cloudflare Pages (full setup, free tier): the repo ships a GitHub Actions workflow (deploy-pages.yml) that builds and deploys on every push to main. Use it instead of Cloudflare's "Connect to Git" builds.
-
Create the Pages project (without connecting Git):
npx wrangler login npx wrangler pages project create rocketyield
-
Create the KV namespace for rate history and the D1 database for visitor counts:
npx wrangler kv namespace create RATE_HISTORY npx wrangler kv namespace create RATE_HISTORY --preview npx wrangler d1 create rocketyield-stats
-
Edit
wrangler.jsonc. It contains the IDs for the official rocketyield.net deployment. Replace the KVid/preview_idand the D1database_idwith yours. -
Add GitHub Actions secrets under Settings → Secrets and variables → Actions:
Secret Value CLOUDFLARE_API_TOKENAPI token with Account → Cloudflare Pages → Edit CLOUDFLARE_ACCOUNT_IDYour Cloudflare account ID VITE_ETHEREUM_RPC_URLArchive RPC URL baked into the browser build -
Add the Function secret:
npx wrangler pages secret put ETHEREUM_RPC_URL --project-name rocketyield
-
Push to
main(or run the Deploy Cloudflare Pages workflow manually). The deploy URL appears in the Actions log.
The shared rate cache refreshes lazily: when it's empty or older than about six hours, the next /api/rates request rebuilds it from the archive RPC. The first cold request after deploy can take a moment.
functions/ Cloudflare Pages Functions
api/rates.ts Shared rETH/ETH rate timeline (KV-backed)
api/visitors.ts Cookieless visitor counter and /stats data (D1-backed)
src/
components/ Dashboard UI (balance hero, metrics, charts, tables)
hooks/ useRocketYield: data loading and live ticking
lib/chain/ viem client, contracts, transfer history, IndexedDB cache
lib/analytics/ Earnings timeline, daily earnings, units
lib/ Formatting, fiat prices, market rate, CSV export
pages/ Methodology, stats, privacy, and legal pages
tests/ Playwright end-to-end tests
- The first load for an old, active wallet must fetch that address's full transfer history, which can take a while. Later visits only request newer blocks.
- RPC providers may rate-limit or reject archive requests. The UI reports these failures instead of substituting sample values.
- Fiat values and the market rate depend on third-party APIs and may be briefly unavailable.
Issues and pull requests are welcome. For larger changes, please open an issue first to discuss the idea.
Before submitting a PR, make sure these pass:
npm run lint
npm test
npm run buildRocketYield is an unofficial community tool and is not affiliated with Rocket Pool. Figures are informational estimates, not financial advice — verify anything important on-chain.