pyth-lazer-client 8.4.0

A Rust client for Pyth Lazer
Documentation

Pyth Lazer Rust Client

A high-performance Rust client for connecting to Pyth Lazer real-time data streams. This client provides reliable, low-latency access to Pyth's oracle price feeds with built-in redundancy and automatic failover.

Features

  • Multiple redundant WebSocket connections - Maintains 4 concurrent connections by default for high availability
  • Automatic message deduplication - Uses TTL-based caching to eliminate duplicate messages across connections
  • Exponential backoff reconnection - Automatically handles connection failures with configurable retry logic
  • Flexible subscription options - Support for multiple data formats (EVM, Solana, etc.) and delivery channels
  • History API client - Fetch symbol metadata and historical price information
  • Type-safe API - Strongly-typed Rust interface with comprehensive error handling

Installation

Add the following to your Cargo.toml:

[dependencies]
pyth-lazer-client = "8.2.2"
pyth-lazer-protocol = "0.16.0"
tokio = { version = "1", features = ["full"] }

Authentication

To use the Pyth Lazer client, you need an access token. Set your access token via the LAZER_ACCESS_TOKEN environment variable:

export LAZER_ACCESS_TOKEN="your_access_token_here"

Or provide it directly in your code:

use pyth_lazer_client::stream_client::PythLazerStreamClientBuilder;

let access_token = std::env::var("LAZER_ACCESS_TOKEN")
    .expect("LAZER_ACCESS_TOKEN not set");

let client = PythLazerStreamClientBuilder::new(access_token)
    .build()?;

Quick Start

Here's a minimal example to get started with streaming price feeds:

use pyth_lazer_client::stream_client::PythLazerStreamClientBuilder;
use pyth_lazer_protocol::api::{SubscribeRequest, SubscriptionParams, SubscriptionParamsRepr, Channel};
use pyth_lazer_protocol::{PriceFeedId, PriceFeedProperty};
use pyth_lazer_protocol::time::FixedRate;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Create and start the client
    let mut client = PythLazerStreamClientBuilder::new(
        std::env::var("LAZER_ACCESS_TOKEN")?
    ).build()?;

    let mut receiver = client.start().await?;

    // Subscribe to price feeds
    let subscribe_request = SubscribeRequest {
        subscription_id: pyth_lazer_protocol::api::SubscriptionId(1),
        params: SubscriptionParams::new(SubscriptionParamsRepr {
            price_feed_ids: Some(vec![PriceFeedId(1), PriceFeedId(2)]),
            symbols: None,
            properties: vec![PriceFeedProperty::Price, PriceFeedProperty::Exponent],
            formats: vec![pyth_lazer_protocol::api::Format::Solana],
            delivery_format: pyth_lazer_protocol::api::DeliveryFormat::Json,
            json_binary_encoding: pyth_lazer_protocol::api::JsonBinaryEncoding::Base64,
            parsed: true,
            channel: Channel::FixedRate(FixedRate::RATE_200_MS),
            ignore_invalid_feeds: false,
        })?,
    };

    client.subscribe(subscribe_request).await?;

    // Process incoming messages
    while let Some(response) = receiver.recv().await {
        println!("Received update: {:?}", response);
    }

    Ok(())
}

Configuration

The PythLazerStreamClientBuilder provides several configuration options:

Custom Endpoints

Override the default production endpoints:

let client = PythLazerStreamClientBuilder::new(access_token)
    .with_endpoints(vec![
        "wss://pyth-lazer-0.dourolabs.app/v1/stream".parse()?,
        "wss://pyth-lazer-1.dourolabs.app/v1/stream".parse()?,
    ])
    .build()?;

Number of Connections

Set the number of concurrent WebSocket connections (default: 4):

let client = PythLazerStreamClientBuilder::new(access_token)
    .with_num_connections(2)
    .build()?;

Connection Timeout

Configure the timeout for WebSocket operations (default: 5 seconds):

use std::time::Duration;

let client = PythLazerStreamClientBuilder::new(access_token)
    .with_timeout(Duration::from_secs(10))
    .build()?;

Exponential Backoff

Customize the reconnection backoff strategy:

use pyth_lazer_client::backoff::PythLazerExponentialBackoffBuilder;

let backoff = PythLazerExponentialBackoffBuilder::default()
    .build();

let client = PythLazerStreamClientBuilder::new(access_token)
    .with_backoff(backoff)
    .build()?;

Channel Capacity

Set the internal message buffer size (default: 1000):

let client = PythLazerStreamClientBuilder::new(access_token)
    .with_channel_capacity(5000)
    .build()?;

Subscription Options

Channels

Choose the update frequency for your price feeds:

  • Channel::RealTime - Receive updates as soon as they're available
  • Channel::FixedRate(FixedRate::RATE_50_MS) - Updates every 50ms
  • Channel::FixedRate(FixedRate::RATE_200_MS) - Updates every 200ms (recommended for most use cases)
  • Channel::FixedRate(FixedRate::RATE_1000_MS) - Updates every 1000ms
use pyth_lazer_protocol::api::Channel;
use pyth_lazer_protocol::time::FixedRate;

// Real-time updates
let channel = Channel::RealTime;

// Fixed rate updates
let channel = Channel::FixedRate(FixedRate::RATE_200_MS);

Formats

Specify the signature format for the price data:

  • Format::Evm - EVM-compatible format with secp256k1 signatures
  • Format::Solana - Solana-compatible format with Ed25519 signatures
  • Format::LeEcdsa - Little-endian ECDSA format
  • Format::LeUnsigned - Little-endian unsigned format
use pyth_lazer_protocol::api::Format;

let formats = vec![Format::Evm, Format::Solana];

Delivery Format

Choose how messages are delivered:

  • DeliveryFormat::Json - Receive updates as JSON text messages (default)
  • DeliveryFormat::Binary - Receive updates as binary messages (more efficient)
use pyth_lazer_protocol::api::DeliveryFormat;

let delivery_format = DeliveryFormat::Binary;

Properties

Select which price feed properties to receive:

  • PriceFeedProperty::Price - Current price
  • PriceFeedProperty::BestBidPrice - Best bid price
  • PriceFeedProperty::BestAskPrice - Best ask price
  • PriceFeedProperty::PublisherCount - Number of contributing publishers
  • PriceFeedProperty::Exponent - Price exponent
  • PriceFeedProperty::Confidence - Confidence interval
  • PriceFeedProperty::FundingRate - Funding rate (for perpetual markets)
  • PriceFeedProperty::FundingTimestamp - Funding rate timestamp
  • PriceFeedProperty::FundingRateInterval - Funding rate update interval
use pyth_lazer_protocol::PriceFeedProperty;

let properties = vec![
    PriceFeedProperty::Price,
    PriceFeedProperty::Exponent,
    PriceFeedProperty::Confidence,
];

Identifying Price Feeds

Subscribe to feeds using either price feed IDs or symbols:

// By price feed ID
let params = SubscriptionParamsRepr {
    price_feed_ids: Some(vec![PriceFeedId(1), PriceFeedId(2)]),
    symbols: None,
    // ... other fields
};

// By symbol
let params = SubscriptionParamsRepr {
    price_feed_ids: None,
    symbols: Some(vec![
        "Crypto.BTC/USD".to_string(),
        "Crypto.ETH/USD".to_string(),
    ]),
    // ... other fields
};

Examples

Comprehensive Streaming Example

See examples/subscribe_price_feeds.rs for a complete example demonstrating:

  • Client configuration with multiple connections
  • Subscribing to price feeds with different formats
  • Processing JSON and binary updates
  • Verifying message signatures
  • Unsubscribing from feeds

Run the example:

cargo run --example subscribe_price_feeds

Symbol Metadata

Fetch symbol metadata using the history client:

use pyth_lazer_client::history_client::{PythLazerHistoryClient, PythLazerHistoryClientConfig};

let client = PythLazerHistoryClient::new(
    PythLazerHistoryClientConfig::default()
);

// Get all symbol metadata
let symbols = client.all_symbols_metadata().await?;

// Or get an auto-updating handle
let handle = client.all_symbols_metadata_handle().await?;
let symbols = handle.symbols();

See examples/symbols.rs and examples/symbols_stream.rs for complete examples.

History Client

The PythLazerHistoryClient provides access to symbol metadata and historical price information:

use pyth_lazer_client::history_client::{PythLazerHistoryClient, PythLazerHistoryClientConfig};
use std::time::Duration;

let config = PythLazerHistoryClientConfig {
    urls: vec!["https://history.pyth-lazer.dourolabs.app/".parse()?],
    update_interval: Duration::from_secs(30),
    request_timeout: Duration::from_secs(15),
    cache_dir: Some("/tmp/pyth-lazer-cache".into()),
    channel_capacity: 1000,
};

let client = PythLazerHistoryClient::new(config);

// Fetch symbol metadata once
let symbols = client.all_symbols_metadata().await?;

// Or get an auto-updating handle that refreshes in the background
let handle = client.all_symbols_metadata_handle().await?;

// Or get a stream of updates
let mut stream = client.all_symbols_metadata_stream().await?;
while let Some(symbols) = stream.recv().await {
    println!("Updated symbols: {} feeds", symbols.len());
}

The history client supports:

  • One-time fetches - Get current data with all_symbols_metadata()
  • Auto-updating handles - Background updates with all_symbols_metadata_handle()
  • Streaming updates - Receive updates via channels with all_symbols_metadata_stream()
  • Local caching - Optional disk cache for offline access
  • Fault tolerance - Graceful fallback to cached data on network failures

API Reference

Main Types

  • PythLazerStreamClient - The main client for streaming price updates
  • PythLazerStreamClientBuilder - Builder for configuring the stream client
  • PythLazerHistoryClient - Client for fetching symbol metadata
  • SubscribeRequest - Subscription configuration
  • SubscriptionParams - Subscription parameters wrapper
  • AnyResponse - Enum for JSON or binary responses

Core Methods

PythLazerStreamClient

  • start() -> Result<Receiver<AnyResponse>> - Start the client and return message receiver
  • subscribe(request: SubscribeRequest) -> Result<()> - Subscribe to price feeds
  • unsubscribe(id: SubscriptionId) -> Result<()> - Unsubscribe from a feed

PythLazerHistoryClient

  • all_symbols_metadata() -> Result<Vec<SymbolMetadata>> - Fetch all symbols once
  • all_symbols_metadata_handle() -> Result<SymbolMetadataHandle> - Get auto-updating handle
  • all_symbols_metadata_stream() -> Result<Receiver<Vec<SymbolMetadata>>> - Get update stream

For complete API documentation, visit docs.rs/pyth-lazer-client.

License

This project is licensed under the Apache-2.0 License. See the LICENSE file for details.

Resources