snbeat is a local, terminal based, Block explorer for the Starknet Blockchain. It supports multiple data sources, to give the best experience, while prioritizing privacy, recency and caching.
Block Detail![]() |
Transaction Detail![]() |
Address Info![]() |
Class Info![]() |
- Privacy first: snbeat works best with a local RPC node, and can connect directly to the Pathfinder DB. Fully local. External APIs (Dune/Voyager) can be used but are optional. It is open source. Hack any feature you need.
- Cache first: Every fetched data is cached. Any data visited is cached for subsequent queries. No unnecessary indexing of data that is never used.
- Recency first: See recent txs first, stream incoming txs, wait longer for full data fetch.
- Vim!
- Data backends - RPC, WS, Pathfinder query service, Dune and Voyager can be used simultaneously to fetch data
- ABI-aware decoding - decodes calldata, events, and multicalls using on-chain class ABIs
- Persistent local cache - SQLite; blocks, transactions, receipts, and ABIs are cached for instant fetch on subsequent visits
- Live feed - Stream new blocks, transactions, and events as they happen (requires WebSocket support from the RPC)
- Custom labels - tag addresses and transactions with human-readable names and searchable tags
- Fast search - prefix/substring search over labelled addresses; navigate by hash or block number
- Nonce-based and Block based navigation - jump to the next/previous transaction by account, by index, or jump to the next block
- Rust 1.85+ (edition 2024)
- A Starknet RPC endpoint
cargo install snbeatcargo install --git https://github.com/amanusk/snbeatThen place your config in ~/.config/snbeat/ (see Configuration below).
git clone https://github.com/amanusk/snbeat
cd snbeat
cargo build --releaseThe binary is at target/release/snbeat.
# From the repo (uses local .env)
cp .env.example .env
cargo run --release
# Or directly
./target/release/snbeatsnbeat reads environment variables directly, so you can also export them in your shell or pass them inline:
APP_RPC_URL=https://api.zan.top/public/starknet-mainnet/rpc/v0_10 snbeatsnbeat looks for configuration files in two locations, with local files taking priority:
- Current working directory - for development or per-project setups
~/.config/snbeat/- for system-wide installs (e.g.cargo install)
This applies to both .env and labels.toml. If a file exists in the current directory it is used; otherwise snbeat falls back to ~/.config/snbeat/.
mkdir -p ~/.config/snbeat
cp .env.example ~/.config/snbeat/.env
# Edit ~/.config/snbeat/.env with your settingsAll variables are optional except APP_RPC_URL.
| Variable | Default | Description |
|---|---|---|
APP_RPC_URL |
(required) | Starknet JSON-RPC endpoint |
APP_WS_URL |
- | WebSocket endpoint for new-block subscriptions |
APP_PATHFINDER_SERVICE_URL |
- | URL of a running pf-query instance |
VOYAGER_API_KEY |
- | Voyager API key for address metadata |
DUNE_API_KEY |
- | Dune Analytics API key |
APP_USER_LABELS |
labels.toml |
Path to your custom labels file |
APP_LOG_LEVEL |
info |
trace / debug / info / warn / error |
APP_LOG_DIR |
~/.config/snbeat/logs |
Log file directory |
Create a labels.toml file to tag addresses and transactions with names and searchable tags. Place it in the current directory or in ~/.config/snbeat/labels.toml for a global install.
[addresses]
# Simple name
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" = "ETH"
# Name + tags (tags are searchable in the / search bar)
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" = { name = "My Wallet", tags = ["defi", "nft"] }
[transactions]
# Optional transaction labels
"0xabc123..." = "Initial deploy"User labels take priority over the built-in known address registry (tokens, DEXes, bridges, etc.). The file is loaded at startup; a malformed file prints a warning but does not prevent startup.
snbeat fetches the class ABI for every contract it encounters and decodes:
- Calldata - function arguments with named parameters and typed values (u256, structs, enums, arrays, …)
- Multicalls - account transactions that bundle multiple inner calls are unpacked individually
- Events - keys and data fields are matched against the ABI and rendered with parameter names
Decoded ABIs are stored in the local SQLite cache (~/.config/snbeat/cache.db) and are never re-fetched.
Press d in a transaction detail view to toggle between raw and decoded calldata.
All fetched data is persisted to ~/.config/snbeat/cache.db (SQLite). The cache stores:
- Block headers
- Transactions and receipts
- Per-address event and transaction lists
- Parsed ABIs (keyed by class hash)
On subsequent visits to the same block or address, data is served from disk with no network round-trip.
| Key | Action |
|---|---|
j / ↓ |
Move down |
k / ↑ |
Move up |
l / → / Enter |
Drill in / navigate forward |
h / ← / Esc |
Go back |
Ctrl+O |
Jump back (vim-style) |
] |
Jump forward |
g |
Jump to top |
G |
Jump to bottom |
Ctrl+U / PgUp |
Next block or transaction |
Ctrl+D / PgDn |
Previous block or transaction |
n |
Next transaction by same sender |
N |
Previous transaction by same sender |
Tab |
Cycle tabs (Address Info view) |
q |
Jump to home / quit |
Ctrl+C |
Quit |
Press / to open the search bar. You can search by:
- Address, transaction hash, or class hash (0x…)
- Block number
- Label name or tag
Arrow keys to navigate suggestions; Enter confirms; Tab fills in the highlighted suggestion; Esc closes.
| Key | Action |
|---|---|
c |
Toggle raw calldata |
d |
Toggle ABI-decoded calldata |
v |
Enter visual mode (highlight addresses / block refs) |
r |
Refresh |
? |
Toggle help overlay |
Press v in a Transaction or Block detail view to enter visual mode. Use j/k to cycle through addresses and block references, then Enter to navigate to the highlighted item. Esc exits.
snbeat can draw data from multiple sources simultaneously. Configure only the ones you have access to.
Each cell shows the fetch priority when multiple sources provide the same data (1 = primary, 2 = fallback, etc.). A dash means the source cannot provide this data.
| Data point | RPC | WS | PF-query | Dune | Voyager |
|---|---|---|---|---|---|
| Blocks | |||||
| Latest block number | 1 (poll) | 1 (push) | - | - | - |
| Block header | 1 | - | - | - | - |
| Block transactions | 1 | - | 1 | - | - |
| Transactions | |||||
| Transaction by hash | 1 | - | - | - | - |
| Transaction receipt (fee, status) | 1 | - | - | - | - |
| Tx hash → block + index lookup | - | - | 1 | - | - |
| Calldata | 1 | - | - | - | - |
| Multicall decoding | 1 | - | - | - | - |
| Address - Account history | |||||
| Account tx history | 3 (events) | - | 1 | 2 | - |
| Nonce history (timeline) | - | - | 1 | - | - |
| Current nonce | 1 | - | - | - | - |
| Address - Contract history | |||||
| Contract call history | 2 (events) | - | - | 1 | - |
| Contract events | 1 | - | 1 | - | - |
| Activity range probe | - | - | - | 1 | - |
| Address - General | |||||
| Class hash at address | 1 | - | - | - | - |
| Token balances (ETH, STRK, …) | 1 (call) | - | - | - | - |
| Address label / metadata | - | - | - | - | 1 |
| Deploy tx detection | 1 | - | - | - | - |
| Classes & ABIs | |||||
| Class definition (Sierra ABI) | 1 | - | - | - | - |
| Selector → function/event name | 1 (via ABI) | - | - | - | - |
| Class declaration block | - | - | 1 | 2 | - |
| Declare tx info | 1 (block scan) | - | 1 (block lookup) | 2 | - |
| Contracts deployed with class | - | - | 1 | - | - |
| Class upgrade history | - | - | 1 | - | - |
| WebSocket subscriptions | |||||
| New block headers (live) | - | 1 | - | - | - |
| Live events for address | - | 1 | - | - | - |
| Live transactions from address | - | 1 | - | - | - |
| Search | |||||
| Block number lookup | 1 | - | - | - | - |
| Transaction hash search | 1 | - | - | - | - |
| Address resolution | 1 | - | - | - | - |
| Class hash search | 1 | - | - | - | - |
| Block hash search | 1 | - | - | - | - |
| Label / tag search | - | - | - | - | - |
Note: All fetched data is cached locally in SQLite. Subsequent visits serve from cache with no network round-trip. Label/tag search is powered by the local registry (
labels.toml+ built-in known addresses) with no external data source.
All data fetching starts here. Set APP_RPC_URL to any Starknet JSON-RPC v0.7+ endpoint (local node or hosted).
APP_RPC_URL=http://localhost:9545/rpc/v0_10
An optional WebSocket URL enables push-based block, txs and events
APP_WS_URL=ws://localhost:9545/ws
If you run a Pathfinder node, the bundled pf-query service exposes its SQLite database over HTTP. This unlocks fast data lookups, which power the address timeline.
See Setting up pf-query below.
APP_PATHFINDER_SERVICE_URL=http://localhost:8234
A Dune API key enables additional address history features:
- Account transaction history - full transaction list for an account, used as a fallback when Pathfinder is unavailable
- Contract call history - the Calls tab on an address shows all transactions that called that contract
- Activity range probe - detects the active block range for an address
DUNE_API_KEY=your_key_here
Connect to Voyager API to optionally get Voyager tags for addresses, classes etc
VOYAGER_API_KEY=your_key_here
pf-query is a lightweight HTTP service that exposes the Pathfinder SQLite database for fast nonce-history queries. It lives in crates/pf-query/.
# Build
cargo build --release --manifest-path crates/pf-query/Cargo.toml
# Run (point it at your Pathfinder database)
PF_DB_PATH=/var/lib/pathfinder/pathfinder.db ./target/release/pf-query
# Listening on 0.0.0.0:8234 by default| Variable | Default | Description |
|---|---|---|
PF_DB_PATH |
(required) | Path to the Pathfinder SQLite database |
PF_PORT |
8234 |
Port to listen on |
PF_HOST |
127.0.0.1 |
Host address to bind to |
If your Pathfinder node is on a different machine, run pf-query on that machine and set APP_PATHFINDER_SERVICE_URL in your snbeat .env to point at it:
APP_PATHFINDER_SERVICE_URL=http://192.168.1.10:8234
| Endpoint | Description |
|---|---|
GET /health |
Returns { "latest_block": N } |
GET /nonce-history/{address}?limit=N |
Returns ordered nonce update history (max 2000 entries) |
GET /class-history/{address} |
Returns class hash history for a contract |
GET /contracts-by-class/{class_hash} |
Returns contracts deployed with a given class hash |
GET /class-declaration/{class_hash} |
Returns declaration info for a class hash |
GET /tx-by-hash/{hash} |
Looks up a transaction by hash |
GET /block-txs/{block_number} |
Returns decoded transactions in a block |
GET /sender-txs/{address} |
Returns transactions sent by an address |
GET /contract-events/{address} |
Returns events emitted by a contract |
# Unit and integration tests (no network required)
cargo test
# RPC-dependent integration tests (requires APP_RPC_URL in .env)
cargo test -- --ignoredThis project is licensed under the MIT License.
- Fork the repository and create a feature branch.
- Run
cargo fmtandcargo clippybefore opening a PR. - Add tests for new behaviour where practical.
- Keep PRs focused; one feature or fix per PR.
Please open an issue first for significant changes to discuss the approach.



