Skip to content

Commit 13b554e

Browse files
devin-ai-integration[bot]keyvan
andauthored
docs: add comprehensive README for Pyth Lazer Rust client (#3097)
* docs: add comprehensive README for Pyth Lazer Rust client Co-Authored-By: keyvan <[email protected]> * chore: bump pyth-lazer-client version to 8.2.2 Co-Authored-By: keyvan <[email protected]> * docs: update README version to 8.2.2 Co-Authored-By: keyvan <[email protected]> * chore: update Cargo.lock for version 8.2.2 Co-Authored-By: keyvan <[email protected]> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: keyvan <[email protected]>
1 parent 2ff48dd commit 13b554e

File tree

3 files changed

+369
-2
lines changed

3 files changed

+369
-2
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lazer/sdk/rust/client/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pyth-lazer-client"
3-
version = "8.2.1"
3+
version = "8.2.2"
44
edition = "2021"
55
description = "A Rust client for Pyth Lazer"
66
license = "Apache-2.0"

lazer/sdk/rust/client/README.md

Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
# Pyth Lazer Rust Client
2+
3+
A high-performance Rust client for connecting to [Pyth Lazer](https://pyth.network/) real-time data streams. This client provides reliable, low-latency access to Pyth's oracle price feeds with built-in redundancy and automatic failover.
4+
5+
## Features
6+
7+
- **Multiple redundant WebSocket connections** - Maintains 4 concurrent connections by default for high availability
8+
- **Automatic message deduplication** - Uses TTL-based caching to eliminate duplicate messages across connections
9+
- **Exponential backoff reconnection** - Automatically handles connection failures with configurable retry logic
10+
- **Flexible subscription options** - Support for multiple data formats (EVM, Solana, etc.) and delivery channels
11+
- **History API client** - Fetch symbol metadata and historical price information
12+
- **Type-safe API** - Strongly-typed Rust interface with comprehensive error handling
13+
14+
## Installation
15+
16+
Add the following to your `Cargo.toml`:
17+
18+
```toml
19+
[dependencies]
20+
pyth-lazer-client = "8.2.2"
21+
pyth-lazer-protocol = "0.16.0"
22+
tokio = { version = "1", features = ["full"] }
23+
```
24+
25+
## Authentication
26+
27+
To use the Pyth Lazer client, you need an access token. Set your access token via the `LAZER_ACCESS_TOKEN` environment variable:
28+
29+
```bash
30+
export LAZER_ACCESS_TOKEN="your_access_token_here"
31+
```
32+
33+
Or provide it directly in your code:
34+
35+
```rust
36+
use pyth_lazer_client::stream_client::PythLazerStreamClientBuilder;
37+
38+
let access_token = std::env::var("LAZER_ACCESS_TOKEN")
39+
.expect("LAZER_ACCESS_TOKEN not set");
40+
41+
let client = PythLazerStreamClientBuilder::new(access_token)
42+
.build()?;
43+
```
44+
45+
## Quick Start
46+
47+
Here's a minimal example to get started with streaming price feeds:
48+
49+
```rust
50+
use pyth_lazer_client::stream_client::PythLazerStreamClientBuilder;
51+
use pyth_lazer_protocol::api::{SubscribeRequest, SubscriptionParams, SubscriptionParamsRepr, Channel};
52+
use pyth_lazer_protocol::{PriceFeedId, PriceFeedProperty};
53+
use pyth_lazer_protocol::time::FixedRate;
54+
55+
#[tokio::main]
56+
async fn main() -> anyhow::Result<()> {
57+
// Create and start the client
58+
let mut client = PythLazerStreamClientBuilder::new(
59+
std::env::var("LAZER_ACCESS_TOKEN")?
60+
).build()?;
61+
62+
let mut receiver = client.start().await?;
63+
64+
// Subscribe to price feeds
65+
let subscribe_request = SubscribeRequest {
66+
subscription_id: pyth_lazer_protocol::api::SubscriptionId(1),
67+
params: SubscriptionParams::new(SubscriptionParamsRepr {
68+
price_feed_ids: Some(vec![PriceFeedId(1), PriceFeedId(2)]),
69+
symbols: None,
70+
properties: vec![PriceFeedProperty::Price, PriceFeedProperty::Exponent],
71+
formats: vec![pyth_lazer_protocol::api::Format::Solana],
72+
delivery_format: pyth_lazer_protocol::api::DeliveryFormat::Json,
73+
json_binary_encoding: pyth_lazer_protocol::api::JsonBinaryEncoding::Base64,
74+
parsed: true,
75+
channel: Channel::FixedRate(FixedRate::RATE_200_MS),
76+
ignore_invalid_feeds: false,
77+
})?,
78+
};
79+
80+
client.subscribe(subscribe_request).await?;
81+
82+
// Process incoming messages
83+
while let Some(response) = receiver.recv().await {
84+
println!("Received update: {:?}", response);
85+
}
86+
87+
Ok(())
88+
}
89+
```
90+
91+
## Configuration
92+
93+
The `PythLazerStreamClientBuilder` provides several configuration options:
94+
95+
### Custom Endpoints
96+
97+
Override the default production endpoints:
98+
99+
```rust
100+
let client = PythLazerStreamClientBuilder::new(access_token)
101+
.with_endpoints(vec![
102+
"wss://pyth-lazer-0.dourolabs.app/v1/stream".parse()?,
103+
"wss://pyth-lazer-1.dourolabs.app/v1/stream".parse()?,
104+
])
105+
.build()?;
106+
```
107+
108+
### Number of Connections
109+
110+
Set the number of concurrent WebSocket connections (default: 4):
111+
112+
```rust
113+
let client = PythLazerStreamClientBuilder::new(access_token)
114+
.with_num_connections(2)
115+
.build()?;
116+
```
117+
118+
### Connection Timeout
119+
120+
Configure the timeout for WebSocket operations (default: 5 seconds):
121+
122+
```rust
123+
use std::time::Duration;
124+
125+
let client = PythLazerStreamClientBuilder::new(access_token)
126+
.with_timeout(Duration::from_secs(10))
127+
.build()?;
128+
```
129+
130+
### Exponential Backoff
131+
132+
Customize the reconnection backoff strategy:
133+
134+
```rust
135+
use pyth_lazer_client::backoff::PythLazerExponentialBackoffBuilder;
136+
137+
let backoff = PythLazerExponentialBackoffBuilder::default()
138+
.build();
139+
140+
let client = PythLazerStreamClientBuilder::new(access_token)
141+
.with_backoff(backoff)
142+
.build()?;
143+
```
144+
145+
### Channel Capacity
146+
147+
Set the internal message buffer size (default: 1000):
148+
149+
```rust
150+
let client = PythLazerStreamClientBuilder::new(access_token)
151+
.with_channel_capacity(5000)
152+
.build()?;
153+
```
154+
155+
## Subscription Options
156+
157+
### Channels
158+
159+
Choose the update frequency for your price feeds:
160+
161+
- **`Channel::RealTime`** - Receive updates as soon as they're available
162+
- **`Channel::FixedRate(FixedRate::RATE_50_MS)`** - Updates every 50ms
163+
- **`Channel::FixedRate(FixedRate::RATE_200_MS)`** - Updates every 200ms (recommended for most use cases)
164+
- **`Channel::FixedRate(FixedRate::RATE_1000_MS)`** - Updates every 1000ms
165+
166+
```rust
167+
use pyth_lazer_protocol::api::Channel;
168+
use pyth_lazer_protocol::time::FixedRate;
169+
170+
// Real-time updates
171+
let channel = Channel::RealTime;
172+
173+
// Fixed rate updates
174+
let channel = Channel::FixedRate(FixedRate::RATE_200_MS);
175+
```
176+
177+
### Formats
178+
179+
Specify the signature format for the price data:
180+
181+
- **`Format::Evm`** - EVM-compatible format with secp256k1 signatures
182+
- **`Format::Solana`** - Solana-compatible format with Ed25519 signatures
183+
- **`Format::LeEcdsa`** - Little-endian ECDSA format
184+
- **`Format::LeUnsigned`** - Little-endian unsigned format
185+
186+
```rust
187+
use pyth_lazer_protocol::api::Format;
188+
189+
let formats = vec![Format::Evm, Format::Solana];
190+
```
191+
192+
### Delivery Format
193+
194+
Choose how messages are delivered:
195+
196+
- **`DeliveryFormat::Json`** - Receive updates as JSON text messages (default)
197+
- **`DeliveryFormat::Binary`** - Receive updates as binary messages (more efficient)
198+
199+
```rust
200+
use pyth_lazer_protocol::api::DeliveryFormat;
201+
202+
let delivery_format = DeliveryFormat::Binary;
203+
```
204+
205+
### Properties
206+
207+
Select which price feed properties to receive:
208+
209+
- `PriceFeedProperty::Price` - Current price
210+
- `PriceFeedProperty::BestBidPrice` - Best bid price
211+
- `PriceFeedProperty::BestAskPrice` - Best ask price
212+
- `PriceFeedProperty::PublisherCount` - Number of contributing publishers
213+
- `PriceFeedProperty::Exponent` - Price exponent
214+
- `PriceFeedProperty::Confidence` - Confidence interval
215+
- `PriceFeedProperty::FundingRate` - Funding rate (for perpetual markets)
216+
- `PriceFeedProperty::FundingTimestamp` - Funding rate timestamp
217+
- `PriceFeedProperty::FundingRateInterval` - Funding rate update interval
218+
219+
```rust
220+
use pyth_lazer_protocol::PriceFeedProperty;
221+
222+
let properties = vec![
223+
PriceFeedProperty::Price,
224+
PriceFeedProperty::Exponent,
225+
PriceFeedProperty::Confidence,
226+
];
227+
```
228+
229+
### Identifying Price Feeds
230+
231+
Subscribe to feeds using either price feed IDs or symbols:
232+
233+
```rust
234+
// By price feed ID
235+
let params = SubscriptionParamsRepr {
236+
price_feed_ids: Some(vec![PriceFeedId(1), PriceFeedId(2)]),
237+
symbols: None,
238+
// ... other fields
239+
};
240+
241+
// By symbol
242+
let params = SubscriptionParamsRepr {
243+
price_feed_ids: None,
244+
symbols: Some(vec![
245+
"Crypto.BTC/USD".to_string(),
246+
"Crypto.ETH/USD".to_string(),
247+
]),
248+
// ... other fields
249+
};
250+
```
251+
252+
## Examples
253+
254+
### Comprehensive Streaming Example
255+
256+
See [`examples/subscribe_price_feeds.rs`](examples/subscribe_price_feeds.rs) for a complete example demonstrating:
257+
258+
- Client configuration with multiple connections
259+
- Subscribing to price feeds with different formats
260+
- Processing JSON and binary updates
261+
- Verifying message signatures
262+
- Unsubscribing from feeds
263+
264+
Run the example:
265+
266+
```bash
267+
cargo run --example subscribe_price_feeds
268+
```
269+
270+
### Symbol Metadata
271+
272+
Fetch symbol metadata using the history client:
273+
274+
```rust
275+
use pyth_lazer_client::history_client::{PythLazerHistoryClient, PythLazerHistoryClientConfig};
276+
277+
let client = PythLazerHistoryClient::new(
278+
PythLazerHistoryClientConfig::default()
279+
);
280+
281+
// Get all symbol metadata
282+
let symbols = client.all_symbols_metadata().await?;
283+
284+
// Or get an auto-updating handle
285+
let handle = client.all_symbols_metadata_handle().await?;
286+
let symbols = handle.symbols();
287+
```
288+
289+
See [`examples/symbols.rs`](examples/symbols.rs) and [`examples/symbols_stream.rs`](examples/symbols_stream.rs) for complete examples.
290+
291+
## History Client
292+
293+
The `PythLazerHistoryClient` provides access to symbol metadata and historical price information:
294+
295+
```rust
296+
use pyth_lazer_client::history_client::{PythLazerHistoryClient, PythLazerHistoryClientConfig};
297+
use std::time::Duration;
298+
299+
let config = PythLazerHistoryClientConfig {
300+
urls: vec!["https://history.pyth-lazer.dourolabs.app/".parse()?],
301+
update_interval: Duration::from_secs(30),
302+
request_timeout: Duration::from_secs(15),
303+
cache_dir: Some("/tmp/pyth-lazer-cache".into()),
304+
channel_capacity: 1000,
305+
};
306+
307+
let client = PythLazerHistoryClient::new(config);
308+
309+
// Fetch symbol metadata once
310+
let symbols = client.all_symbols_metadata().await?;
311+
312+
// Or get an auto-updating handle that refreshes in the background
313+
let handle = client.all_symbols_metadata_handle().await?;
314+
315+
// Or get a stream of updates
316+
let mut stream = client.all_symbols_metadata_stream().await?;
317+
while let Some(symbols) = stream.recv().await {
318+
println!("Updated symbols: {} feeds", symbols.len());
319+
}
320+
```
321+
322+
The history client supports:
323+
324+
- **One-time fetches** - Get current data with `all_symbols_metadata()`
325+
- **Auto-updating handles** - Background updates with `all_symbols_metadata_handle()`
326+
- **Streaming updates** - Receive updates via channels with `all_symbols_metadata_stream()`
327+
- **Local caching** - Optional disk cache for offline access
328+
- **Fault tolerance** - Graceful fallback to cached data on network failures
329+
330+
## API Reference
331+
332+
### Main Types
333+
334+
- **`PythLazerStreamClient`** - The main client for streaming price updates
335+
- **`PythLazerStreamClientBuilder`** - Builder for configuring the stream client
336+
- **`PythLazerHistoryClient`** - Client for fetching symbol metadata
337+
- **`SubscribeRequest`** - Subscription configuration
338+
- **`SubscriptionParams`** - Subscription parameters wrapper
339+
- **`AnyResponse`** - Enum for JSON or binary responses
340+
341+
### Core Methods
342+
343+
#### PythLazerStreamClient
344+
345+
- `start() -> Result<Receiver<AnyResponse>>` - Start the client and return message receiver
346+
- `subscribe(request: SubscribeRequest) -> Result<()>` - Subscribe to price feeds
347+
- `unsubscribe(id: SubscriptionId) -> Result<()>` - Unsubscribe from a feed
348+
349+
#### PythLazerHistoryClient
350+
351+
- `all_symbols_metadata() -> Result<Vec<SymbolMetadata>>` - Fetch all symbols once
352+
- `all_symbols_metadata_handle() -> Result<SymbolMetadataHandle>` - Get auto-updating handle
353+
- `all_symbols_metadata_stream() -> Result<Receiver<Vec<SymbolMetadata>>>` - Get update stream
354+
355+
For complete API documentation, visit [docs.rs/pyth-lazer-client](https://docs.rs/pyth-lazer-client).
356+
357+
## License
358+
359+
This project is licensed under the Apache-2.0 License. See the [LICENSE](../../../../LICENSE) file for details.
360+
361+
## Resources
362+
363+
- [Pyth Network Documentation](https://docs.pyth.network/)
364+
- [Pyth Lazer Overview](https://docs.pyth.network/price-feeds/lazer)
365+
- [API Reference](https://docs.rs/pyth-lazer-client)
366+
- [Examples](examples/)
367+
- [GitHub Repository](https://github.com/pyth-network/pyth-crosschain)

0 commit comments

Comments
 (0)