From b6f780a6c5f245288b5cbf4e6b2d39d8f5bd0a4d Mon Sep 17 00:00:00 2001 From: Tejas Badadare Date: Fri, 12 Sep 2025 15:18:31 -0700 Subject: [PATCH 1/7] feat(pyth-lazer-protocol)!: add optional symbols to request structs, make ids optional --- Cargo.lock | 2 +- lazer/sdk/rust/client/Cargo.toml | 2 +- .../client/examples/subscribe_price_feeds.rs | 18 +++++--- lazer/sdk/rust/protocol/src/api.rs | 41 +++++++++++++++---- 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b3f8301e96..65de83a9dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5692,7 +5692,7 @@ dependencies = [ [[package]] name = "pyth-lazer-client" -version = "7.0.0" +version = "8.0.0" dependencies = [ "alloy-primitives 0.8.25", "anyhow", diff --git a/lazer/sdk/rust/client/Cargo.toml b/lazer/sdk/rust/client/Cargo.toml index 029bb7d3f0..abf880f211 100644 --- a/lazer/sdk/rust/client/Cargo.toml +++ b/lazer/sdk/rust/client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyth-lazer-client" -version = "7.0.0" +version = "8.0.0" edition = "2021" description = "A Rust client for Pyth Lazer" license = "Apache-2.0" diff --git a/lazer/sdk/rust/client/examples/subscribe_price_feeds.rs b/lazer/sdk/rust/client/examples/subscribe_price_feeds.rs index 24eac797ae..d1856bc2d5 100644 --- a/lazer/sdk/rust/client/examples/subscribe_price_feeds.rs +++ b/lazer/sdk/rust/client/examples/subscribe_price_feeds.rs @@ -56,11 +56,12 @@ async fn main() -> anyhow::Result<()> { pin!(stream); let subscription_requests = vec![ - // Example subscription: Parsed JSON feed targeting Solana + // Example subscription: Parsed JSON feed targeting Solana, specified by price feed ids SubscribeRequest { subscription_id: SubscriptionId(1), params: SubscriptionParams::new(SubscriptionParamsRepr { - price_feed_ids: vec![PriceFeedId(1), PriceFeedId(2)], + price_feed_ids: Some(vec![PriceFeedId(1), PriceFeedId(2)]), + symbols: None, properties: vec![ PriceFeedProperty::Price, PriceFeedProperty::Exponent, @@ -72,15 +73,20 @@ async fn main() -> anyhow::Result<()> { json_binary_encoding: JsonBinaryEncoding::Base64, parsed: true, channel: Channel::FixedRate(FixedRate::RATE_200_MS), - ignore_invalid_feed_ids: false, + ignore_invalid_feeds: false, }) .expect("invalid subscription params"), }, - // Example subscription: binary feed targeting Solana and EVM + // Example subscription: binary feed targeting Solana and EVM, specified by price feed symbols SubscribeRequest { subscription_id: SubscriptionId(2), params: SubscriptionParams::new(SubscriptionParamsRepr { - price_feed_ids: vec![PriceFeedId(3), PriceFeedId(4)], + price_feed_ids: None, + symbols: Some(vec![ + "Crypto.BTC/USD".to_string(), + "Crypto.ETH/USD".to_string(), + "Crypto.PYTH/USD".to_string(), + ]), properties: vec![ PriceFeedProperty::Price, PriceFeedProperty::Exponent, @@ -92,7 +98,7 @@ async fn main() -> anyhow::Result<()> { json_binary_encoding: JsonBinaryEncoding::Base64, parsed: false, channel: Channel::FixedRate(FixedRate::RATE_50_MS), - ignore_invalid_feed_ids: false, + ignore_invalid_feeds: false, }) .expect("invalid subscription params"), }, diff --git a/lazer/sdk/rust/protocol/src/api.rs b/lazer/sdk/rust/protocol/src/api.rs index c5a17d21fc..195ed6ab8d 100644 --- a/lazer/sdk/rust/protocol/src/api.rs +++ b/lazer/sdk/rust/protocol/src/api.rs @@ -17,7 +17,8 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct LatestPriceRequest { - pub price_feed_ids: Vec, + pub price_feed_ids: Option>, + pub symbols: Option>, pub properties: Vec, // "chains" was renamed to "formats". "chains" is still supported for compatibility. #[serde(alias = "chains")] @@ -35,7 +36,9 @@ pub struct LatestPriceRequest { #[serde(rename_all = "camelCase")] pub struct PriceRequest { pub timestamp: TimestampUs, - pub price_feed_ids: Vec, + // Either price feed ids or symbols must be specified. + pub price_feed_ids: Option>, + pub symbols: Option>, pub properties: Vec, pub formats: Vec, #[serde(default)] @@ -181,7 +184,9 @@ impl<'de> Deserialize<'de> for Channel { #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SubscriptionParamsRepr { - pub price_feed_ids: Vec, + // Either price feed ids or symbols must be specified. + pub price_feed_ids: Option>, + pub symbols: Option>, pub properties: Vec, // "chains" was renamed to "formats". "chains" is still supported for compatibility. #[serde(alias = "chains")] @@ -195,8 +200,9 @@ pub struct SubscriptionParamsRepr { #[serde(default = "default_parsed")] pub parsed: bool, pub channel: Channel, - #[serde(default)] - pub ignore_invalid_feed_ids: bool, + // "ignoreInvalidFeedIds" was renamed to "ignoreInvalidFeeds". "ignoreInvalidFeedIds" is still supported for compatibility. + #[serde(default, alias = "ignoreInvalidFeedIds")] + pub ignore_invalid_feeds: bool, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)] @@ -215,12 +221,28 @@ impl<'de> Deserialize<'de> for SubscriptionParams { impl SubscriptionParams { pub fn new(value: SubscriptionParamsRepr) -> Result { - if value.price_feed_ids.is_empty() { - return Err("no price feed ids specified"); + if value.price_feed_ids.is_none() && value.symbols.is_none() { + return Err("either price feed ids or symbols must be specified"); + } + + if let Some(ref ids) = value.price_feed_ids { + if ids.is_empty() { + return Err("no price feed ids specified"); + } + if !ids.iter().all_unique() { + return Err("duplicate price feed ids specified"); + } } - if !value.price_feed_ids.iter().all_unique() { - return Err("duplicate price feed ids specified"); + + if let Some(ref symbols) = value.symbols { + if symbols.is_empty() { + return Err("no symbols specified"); + } + if !symbols.iter().all_unique() { + return Err("duplicate symbols specified"); + } } + if !value.formats.iter().all_unique() { return Err("duplicate formats or chains specified"); } @@ -442,6 +464,7 @@ pub struct SubscribedResponse { #[serde(rename_all = "camelCase")] pub struct InvalidFeedSubscriptionDetails { pub unknown_ids: Vec, + pub unknown_symbols: Vec, pub unsupported_channels: Vec, pub unstable: Vec, } From 9068d78366c99a3b93ba0f3154790b120a5eb2c4 Mon Sep 17 00:00:00 2001 From: Tejas Badadare Date: Fri, 12 Sep 2025 15:29:58 -0700 Subject: [PATCH 2/7] fix: remove InvalidFeedSubscriptionDetails.unknown_symbols --- lazer/sdk/rust/protocol/src/api.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lazer/sdk/rust/protocol/src/api.rs b/lazer/sdk/rust/protocol/src/api.rs index 195ed6ab8d..57bbe7a144 100644 --- a/lazer/sdk/rust/protocol/src/api.rs +++ b/lazer/sdk/rust/protocol/src/api.rs @@ -464,7 +464,6 @@ pub struct SubscribedResponse { #[serde(rename_all = "camelCase")] pub struct InvalidFeedSubscriptionDetails { pub unknown_ids: Vec, - pub unknown_symbols: Vec, pub unsupported_channels: Vec, pub unstable: Vec, } From ca1a33efccdedc8d86a6b5b86df240f81cfa728a Mon Sep 17 00:00:00 2001 From: Tejas Badadare Date: Mon, 15 Sep 2025 11:09:23 -0700 Subject: [PATCH 3/7] prevent specifying both price feed ids and symbols --- lazer/sdk/rust/protocol/src/api.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lazer/sdk/rust/protocol/src/api.rs b/lazer/sdk/rust/protocol/src/api.rs index 57bbe7a144..27aa8a83e1 100644 --- a/lazer/sdk/rust/protocol/src/api.rs +++ b/lazer/sdk/rust/protocol/src/api.rs @@ -224,6 +224,9 @@ impl SubscriptionParams { if value.price_feed_ids.is_none() && value.symbols.is_none() { return Err("either price feed ids or symbols must be specified"); } + if value.price_feed_ids.is_some() && value.symbols.is_some() { + return Err("either price feed ids or symbols must be specified, not both"); + } if let Some(ref ids) = value.price_feed_ids { if ids.is_empty() { From 54cfac6289e531d82a3dfd7b6b587e12aaacfb7e Mon Sep 17 00:00:00 2001 From: Tejas Badadare Date: Tue, 16 Sep 2025 16:02:59 -0700 Subject: [PATCH 4/7] feat(protocol): bump ver to 0.15.0, update dependents --- Cargo.lock | 28 ++++++++++++++-------------- lazer/publisher_sdk/rust/Cargo.toml | 2 +- lazer/sdk/rust/client/Cargo.toml | 2 +- lazer/sdk/rust/protocol/Cargo.toml | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a18c62692..a0755a0df6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5674,7 +5674,7 @@ dependencies = [ "hyper 1.6.0", "hyper-util", "protobuf", - "pyth-lazer-protocol 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pyth-lazer-protocol 0.14.0", "pyth-lazer-publisher-sdk 0.10.0", "reqwest 0.12.23", "serde", @@ -5711,7 +5711,7 @@ dependencies = [ "hex", "humantime-serde", "libsecp256k1 0.7.2", - "pyth-lazer-protocol 0.14.0", + "pyth-lazer-protocol 0.15.0", "reqwest 0.12.23", "serde", "serde_json", @@ -5726,22 +5726,17 @@ dependencies = [ [[package]] name = "pyth-lazer-protocol" version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91b3e69c264b2ad80b5943df86c606daae63b13f93062abcc008c09a9e2e621e" dependencies = [ - "alloy-primitives 0.8.25", "anyhow", - "assert_float_eq", - "bincode 1.3.3", - "bs58", "byteorder", "chrono", "derive_more 1.0.0", - "ed25519-dalek 2.1.1", "hex", "humantime", "humantime-serde", "itertools 0.13.0", - "libsecp256k1 0.7.2", - "mry", "protobuf", "rust_decimal", "serde", @@ -5751,18 +5746,23 @@ dependencies = [ [[package]] name = "pyth-lazer-protocol" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b3e69c264b2ad80b5943df86c606daae63b13f93062abcc008c09a9e2e621e" +version = "0.15.0" dependencies = [ + "alloy-primitives 0.8.25", "anyhow", + "assert_float_eq", + "bincode 1.3.3", + "bs58", "byteorder", "chrono", "derive_more 1.0.0", + "ed25519-dalek 2.1.1", "hex", "humantime", "humantime-serde", "itertools 0.13.0", + "libsecp256k1 0.7.2", + "mry", "protobuf", "rust_decimal", "serde", @@ -5780,7 +5780,7 @@ dependencies = [ "fs-err", "protobuf", "protobuf-codegen", - "pyth-lazer-protocol 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pyth-lazer-protocol 0.14.0", "serde_json", ] @@ -5792,7 +5792,7 @@ dependencies = [ "fs-err", "protobuf", "protobuf-codegen", - "pyth-lazer-protocol 0.14.0", + "pyth-lazer-protocol 0.15.0", "serde_json", ] diff --git a/lazer/publisher_sdk/rust/Cargo.toml b/lazer/publisher_sdk/rust/Cargo.toml index e2fdeb8753..14072639ce 100644 --- a/lazer/publisher_sdk/rust/Cargo.toml +++ b/lazer/publisher_sdk/rust/Cargo.toml @@ -7,7 +7,7 @@ license = "Apache-2.0" repository = "https://github.com/pyth-network/pyth-crosschain" [dependencies] -pyth-lazer-protocol = { version = "0.14.0", path = "../../sdk/rust/protocol" } +pyth-lazer-protocol = { version = "0.15.0", path = "../../sdk/rust/protocol" } anyhow = "1.0.98" protobuf = "3.7.2" serde_json = "1.0.140" diff --git a/lazer/sdk/rust/client/Cargo.toml b/lazer/sdk/rust/client/Cargo.toml index abf880f211..f93285720c 100644 --- a/lazer/sdk/rust/client/Cargo.toml +++ b/lazer/sdk/rust/client/Cargo.toml @@ -6,7 +6,7 @@ description = "A Rust client for Pyth Lazer" license = "Apache-2.0" [dependencies] -pyth-lazer-protocol = { path = "../protocol", version = "0.14.0" } +pyth-lazer-protocol = { path = "../protocol", version = "0.15.0" } tokio = { version = "1", features = ["full"] } tokio-tungstenite = { version = "0.20", features = ["native-tls"] } futures-util = "0.3" diff --git a/lazer/sdk/rust/protocol/Cargo.toml b/lazer/sdk/rust/protocol/Cargo.toml index 47232d91c2..c95e0e85c0 100644 --- a/lazer/sdk/rust/protocol/Cargo.toml +++ b/lazer/sdk/rust/protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyth-lazer-protocol" -version = "0.14.0" +version = "0.15.0" edition = "2021" description = "Pyth Lazer SDK - protocol types." license = "Apache-2.0" From 51e239b3dacf2fe5361d9ae5a01905b560904bf6 Mon Sep 17 00:00:00 2001 From: Tejas Badadare Date: Tue, 16 Sep 2025 16:08:12 -0700 Subject: [PATCH 5/7] chore(pyth-lazer-publisher-sdk): bump protocl version --- Cargo.lock | 2 +- lazer/publisher_sdk/rust/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0755a0df6..4d2251bf1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5786,7 +5786,7 @@ dependencies = [ [[package]] name = "pyth-lazer-publisher-sdk" -version = "0.11.0" +version = "0.12.0" dependencies = [ "anyhow", "fs-err", diff --git a/lazer/publisher_sdk/rust/Cargo.toml b/lazer/publisher_sdk/rust/Cargo.toml index 14072639ce..b1cbc3d1e5 100644 --- a/lazer/publisher_sdk/rust/Cargo.toml +++ b/lazer/publisher_sdk/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyth-lazer-publisher-sdk" -version = "0.11.0" +version = "0.12.0" edition = "2021" description = "Pyth Lazer Publisher SDK types." license = "Apache-2.0" From a0a96a9133c0ea2f23fddf5ed024fea1f48cf3ea Mon Sep 17 00:00:00 2001 From: Tejas Badadare Date: Tue, 16 Sep 2025 16:10:36 -0700 Subject: [PATCH 6/7] chore(pyth-lazer-solana-contract): bump protocol version --- .../solana/programs/pyth-lazer-solana-contract/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lazer/contracts/solana/programs/pyth-lazer-solana-contract/Cargo.toml b/lazer/contracts/solana/programs/pyth-lazer-solana-contract/Cargo.toml index 966801484b..04f19e0254 100644 --- a/lazer/contracts/solana/programs/pyth-lazer-solana-contract/Cargo.toml +++ b/lazer/contracts/solana/programs/pyth-lazer-solana-contract/Cargo.toml @@ -19,7 +19,7 @@ no-log-ix-name = [] idl-build = ["anchor-lang/idl-build"] [dependencies] -pyth-lazer-protocol = { path = "../../../../sdk/rust/protocol", version = "0.14.0" } +pyth-lazer-protocol = { path = "../../../../sdk/rust/protocol", version = "0.15.0" } anchor-lang = "0.31.1" bytemuck = { version = "1.20.0", features = ["derive"] } From cdee1dce8072aecb1076034f13e3e1089005dfec Mon Sep 17 00:00:00 2001 From: Tejas Badadare Date: Tue, 16 Sep 2025 16:21:03 -0700 Subject: [PATCH 7/7] chore(pyth-lazer-solana-contract): bump version --- .../solana/programs/pyth-lazer-solana-contract/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lazer/contracts/solana/programs/pyth-lazer-solana-contract/Cargo.toml b/lazer/contracts/solana/programs/pyth-lazer-solana-contract/Cargo.toml index 04f19e0254..11b7baf8a0 100644 --- a/lazer/contracts/solana/programs/pyth-lazer-solana-contract/Cargo.toml +++ b/lazer/contracts/solana/programs/pyth-lazer-solana-contract/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyth-lazer-solana-contract" -version = "0.6.0" +version = "0.7.0" edition = "2021" description = "Pyth Lazer Solana contract and SDK." license = "Apache-2.0"