WS Provider

The Ws provider establishes an WebSocket connection with a node, allowing you to send JSON-RPC requests to the node to fetch data, simulate calls, send transactions and much more. The Ws provider can be used with any Ethereum node that supports WebSocket connections. This allows programs to interact with the network in real-time without the need for HTTP polling for things like new block headers and filter logs.

Initializing a Ws Provider

The recommended way of initializing a Ws provider is by using the on_ws method on the ProviderBuilder with a WsConnect configuration.

//! Example of creating an WS provider using the `on_ws` method on the `ProviderBuilder`.

use alloy::providers::{Provider, ProviderBuilder, WsConnect};
use eyre::Result;

#[tokio::main]
async fn main() -> eyre::Result<()> {
    // Set up the WS transport which is consumed by the RPC client.
    let rpc_url = "wss://eth-mainnet.g.alchemy.com/v2/your-api-key";

    // Create the provider.
    let ws = WsConnect::new(rpc_url);
    let provider = ProviderBuilder::new().on_ws(ws).await?;

    Ok(())
}

An alternative way of initializing is to use the on_builtin method on the ProviderBuilder. This method will automatically determine the connection type (Http, Ws or Ipc) depending on the format of the URL. This method is particularly useful if you need a boxed transport.

//! Example of creating an WS provider using the `on_builtin` method on the `ProviderBuilder`.

use alloy::providers::{Provider, ProviderBuilder};
use eyre::Result;

#[tokio::main]
async fn main() -> eyre::Result<()> {
    // Create a provider with the WS transport.
    let provider = ProviderBuilder::new().on_builtin("wss://eth-mainnet.g.alchemy.com/v2/your-api-key").await?;

    Ok(())
}

Similar to the other providers, you can also establish an authorized connection with a node via websockets.

Example: ws

Example

To run this example:

  • Clone the examples repository: git clone git@github.com:alloy-rs/examples.git
  • Run: cargo run --example ws
//! Example of using the WS provider to subscribe to new blocks.

use alloy::providers::{Provider, ProviderBuilder, WsConnect};
use eyre::Result;
use futures_util::StreamExt;

#[tokio::main]
async fn main() -> Result<()> {
    // Set up the WS transport which is consumed by the RPC client.
    let rpc_url = "wss://eth-mainnet.g.alchemy.com/v2/your-api-key";

    // Create the provider.
    let ws = WsConnect::new(rpc_url);
    let provider = ProviderBuilder::new().on_ws(ws).await?;

    // Subscribe to new blocks.
    let sub = provider.subscribe_blocks().await?;

    // Wait and take the next 4 blocks.
    let mut stream = sub.into_stream().take(4);

    println!("Awaiting blocks...");

    // Take the stream and print the block number upon receiving a new block.
    let handle = tokio::spawn(async move {
        while let Some(block) = stream.next().await {
            println!(
                "Latest block number: {}",
                block.header.number.expect("Failed to get block number")
            );
        }
    });

    handle.await?;

    Ok(())
}

Find the source code on Github here.

Example: ws_with_auth

Example

To run this example:

  • Clone the examples repository: git clone git@github.com:alloy-rs/examples.git
  • Run: cargo run --example ws_with_auth
//! Example of using the WS provider with auth to subscribe to new blocks.

use alloy::{
    providers::{Provider, ProviderBuilder, WsConnect},
    transports::Authorization,
};
use eyre::Result;
use futures_util::StreamExt;

#[tokio::main]
async fn main() -> Result<()> {
    // Set up the WS transport which is consumed by the RPC client.
    let rpc_url = "wss://your-ws-endpoint.com/";

    // Create authorization methods.
    let auth = Authorization::basic("username", "password");
    let auth_bearer = Authorization::bearer("bearer-token");

    // Create the WS connection object with authentication.
    let ws_basic = WsConnect::with_auth(rpc_url, Some(auth));
    let ws_bearer = WsConnect::with_auth(rpc_url, Some(auth_bearer));

    // Create the provider.
    let provider_basic = ProviderBuilder::new().on_ws(ws_basic).await?;
    let provider_bearer = ProviderBuilder::new().on_ws(ws_bearer).await?;

    // Subscribe to new blocks.
    let sub_basic = provider_basic.subscribe_blocks();
    let sub_bearer = provider_bearer.subscribe_blocks();

    // Wait and take the next 4 blocks.
    let mut stream_basic = sub_basic.await?.into_stream().take(4);
    let mut stream_bearer = sub_bearer.await?.into_stream().take(4);

    println!("Awaiting blocks...");

    // Take the basic stream and print the block number upon receiving a new block.
    let basic_handle = tokio::spawn(async move {
        while let Some(block) = stream_basic.next().await {
            println!(
                "Latest block number (basic): {}",
                block.header.number.expect("Failed to get block number")
            );
        }
    });

    // Take the bearer stream and print the block number upon receiving a new block.
    let bearer_handle = tokio::spawn(async move {
        while let Some(block) = stream_bearer.next().await {
            println!(
                "Latest block number (bearer): {}",
                block.header.number.expect("Failed to get block number")
            );
        }
    });

    // Wait for both tasks to complete.
    let _ = tokio::try_join!(basic_handle, bearer_handle)?;

    Ok(())
}

Find the source code on Github here.