Skip to content

feat(scanner): connect ChainProvider over WebSocket#31

Merged
obchain merged 5 commits intomainfrom
feat/06-chainprovider-ws
Apr 24, 2026
Merged

feat(scanner): connect ChainProvider over WebSocket#31
obchain merged 5 commits intomainfrom
feat/06-chainprovider-ws

Conversation

@obchain
Copy link
Copy Markdown
Owner

@obchain obchain commented Apr 21, 2026

Closes #7

Adds the per-chain RPC connection wrapper. WebSocket is required for subscribe_blocks / subscribe_logs — the scanner's hot path depends on push events, not polling.

  • ChainProvider::connect(name, &ChainConfig) — WS handshake via ProviderBuilder::on_ws(WsConnect::new(url))
  • Returns RootProvider<PubSubFrontend> usable by listener + adapters
  • test_connection() — lightweight get_block_number() health check
  • Errors carry chain name + URL context; no panics, no silent fallbacks

Verified against BSC mainnet: test-connection --chain bnb returns the latest block over a live wss:// endpoint.

Depends on #6 (feat/05-clap-cli).

obchain added 3 commits April 21, 2026 18:03
…command

First concrete network code. ChainProvider wraps a chain's HTTP RPC via
alloy's ProviderBuilder::on_builtin, which auto-selects the transport
from the URL scheme. Today it's HTTP-only — WebSocket follows in the
next chunk alongside the block listener.

Add `test-connection --chain <key>` subcommand that connects to the
named chain and prints its latest block number. Verified against the
public BSC endpoint:

    charon: connected — latest block chain=bnb block=93617402

charon-cli now depends on charon-scanner.

Refs #6
No functional change — purely rustfmt output. Lands ahead of upcoming
WebSocket provider work so subsequent commits diff cleanly against a
formatted baseline.
Swap the HTTP builder call for an explicit WebSocket handshake via
`ProviderBuilder::on_ws(WsConnect)`. WebSocket is required for the
upcoming block listener's `subscribe_blocks` — polling HTTP would waste
RPC quota and add latency to the hot path.

- `ChainProvider::connect` now performs a WS handshake against `ws_url`
- `provider()` exposes the pub-sub `RootProvider<PubSubFrontend>` for
  downstream consumers (block listener, scanner, executor)
- `test_connection()` remains a lightweight `get_block_number()` probe,
  now over WS
- Errors carry chain name + URL in context; no panics, no silent fallbacks

Verified against BSC mainnet: `test-connection --chain bnb` returns the
latest block number over a live wss endpoint.
…oviderT + Arc

- redact_url() strips the final path segment (typically the API-key slug)
  before any debug! or error includes the URL. WebSocket errors and boot
  debug logs now show 'wss://bsc-mainnet.nodereal.io/<redacted>' instead
  of the raw bearer-token URL. Three unit tests cover the redaction
  helper.
- connect() wraps ProviderBuilder::on_ws in tokio::time::timeout
  (DEFAULT_CONNECT_TIMEOUT = 10 s; connect_with_timeout exposes a
  caller-chosen deadline). Unreachable RPC no longer hangs the
  process forever.
- After the WS handshake, call eth_chainId and reject any endpoint whose
  chain id does not match config.chain_id. Misconfigured URLs pointing
  at the wrong network fail fast at boot, before any state-dependent
  call runs.
- Introduce ChainProviderT trait (Send + Sync) + MockChainProvider so
  downstream scanner logic can be unit-tested without a live node.
  Concrete ChainProvider implements the trait.
- connect() now returns Arc<Self>; a compile-time assertion pins
  ChainProvider: Send + Sync so accidental non-Send fields become
  compile errors rather than task-spawn failures.
- async-trait added to charon-scanner dependencies.

Closes #87 #88 #89 #90 #91
# Conflicts:
#	crates/charon-cli/src/main.rs
#	crates/charon-core/src/config.rs
#	crates/charon-core/src/traits.rs
#	crates/charon-scanner/Cargo.toml
@obchain obchain merged commit 649facf into main Apr 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[scanner] ChainProvider: alloy WebSocket connect + latest-block health check

1 participant