Jelajahi Sumber

feat(pyth-lazer-protocol)!: enable subscribing by either price_feed_ids or symbols (#3049)

* feat(pyth-lazer-protocol)!: add optional symbols to request structs, make ids optional

* fix: remove InvalidFeedSubscriptionDetails.unknown_symbols

* prevent specifying both price feed ids and symbols

* feat(protocol): bump ver to 0.15.0, update dependents

* chore(pyth-lazer-publisher-sdk): bump protocl version

* chore(pyth-lazer-solana-contract): bump protocol version

* chore(pyth-lazer-solana-contract): bump version
Tejas Badadare 2 bulan lalu
induk
melakukan
6b1d6c61a3

+ 16 - 16
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",
@@ -5693,7 +5693,7 @@ dependencies = [
 
 [[package]]
 name = "pyth-lazer-client"
-version = "7.0.0"
+version = "8.0.0"
 dependencies = [
  "alloy-primitives 0.8.25",
  "anyhow",
@@ -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,19 +5780,19 @@ 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",
 ]
 
 [[package]]
 name = "pyth-lazer-publisher-sdk"
-version = "0.11.0"
+version = "0.12.0"
 dependencies = [
  "anyhow",
  "fs-err",
  "protobuf",
  "protobuf-codegen",
- "pyth-lazer-protocol 0.14.0",
+ "pyth-lazer-protocol 0.15.0",
  "serde_json",
 ]
 

+ 2 - 2
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"
@@ -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"] }

+ 2 - 2
lazer/publisher_sdk/rust/Cargo.toml

@@ -1,13 +1,13 @@
 [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"
 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"

+ 2 - 2
lazer/sdk/rust/client/Cargo.toml

@@ -1,12 +1,12 @@
 [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"
 
 [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"

+ 12 - 6
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"),
         },

+ 1 - 1
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"

+ 34 - 9
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<PriceFeedId>,
+    pub price_feed_ids: Option<Vec<PriceFeedId>>,
+    pub symbols: Option<Vec<String>>,
     pub properties: Vec<PriceFeedProperty>,
     // "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<PriceFeedId>,
+    // Either price feed ids or symbols must be specified.
+    pub price_feed_ids: Option<Vec<PriceFeedId>>,
+    pub symbols: Option<Vec<String>>,
     pub properties: Vec<PriceFeedProperty>,
     pub formats: Vec<Format>,
     #[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<PriceFeedId>,
+    // Either price feed ids or symbols must be specified.
+    pub price_feed_ids: Option<Vec<PriceFeedId>>,
+    pub symbols: Option<Vec<String>>,
     pub properties: Vec<PriceFeedProperty>,
     // "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,31 @@ impl<'de> Deserialize<'de> for SubscriptionParams {
 
 impl SubscriptionParams {
     pub fn new(value: SubscriptionParamsRepr) -> Result<Self, &'static str> {
-        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 !value.price_feed_ids.iter().all_unique() {
-            return Err("duplicate price feed ids 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() {
+                return Err("no price feed ids specified");
+            }
+            if !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");
         }