Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"] }
Expand Down
4 changes: 2 additions & 2 deletions lazer/publisher_sdk/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
4 changes: 2 additions & 2 deletions lazer/sdk/rust/client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
18 changes: 12 additions & 6 deletions lazer/sdk/rust/client/examples/subscribe_price_feeds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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"),
},
Expand Down
2 changes: 1 addition & 1 deletion lazer/sdk/rust/protocol/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
43 changes: 34 additions & 9 deletions lazer/sdk/rust/protocol/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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)]
Expand Down Expand Up @@ -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>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a new field here. Shouldn't we add default value to None, to make sure api is backward compatible?
Maybe it's okay by default but I just wanna make sure if that's the case

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serde_json already defaults to None for an Option field if it's absent, so there is no need for #[serde(default)] in this case.

pub properties: Vec<PriceFeedProperty>,
// "chains" was renamed to "formats". "chains" is still supported for compatibility.
#[serde(alias = "chains")]
Expand All @@ -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,
Comment on lines +203 to +205
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this can be omitted altogether (defaulting to false) or one of ignoreInvalidFeedIds or ignoreInvalidFeeds can be specified? What about both?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this applies to the deserializer (e.g., from a JSON payload). Fields named ignoreInvalidFeedIds (the old name) will still be accepted, and will be aliased/renamed to the new ignoreInvalidFeeds. I'm actually not sure what happens if both are provided -- i assume serde would fail to deserialize the payload with some kind of "duplicate keys" error.

}

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
Expand All @@ -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");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should also forbid passing both feed ids and symbols at the same time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah makes sense. I was debating between this and union-ing them in the server. But yeah i'll go ahead forbid passing both feed ids and symbols at the same time.

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");
}
Expand Down