-
Notifications
You must be signed in to change notification settings - Fork 301
feat(pyth-lazer-sdk): add funding rate #2423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
07181fd
7ef7b5d
06d1158
d29a68b
b55cf8e
1707f8c
bac763b
a0f999b
13f6207
bc9f03a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
|
||
use { | ||
super::router::{PriceFeedId, PriceFeedProperty, TimestampUs}, | ||
crate::router::{ChannelId, Price}, | ||
crate::router::{ChannelId, Price, Rate}, | ||
anyhow::bail, | ||
byteorder::{ByteOrder, ReadBytesExt, WriteBytesExt, BE, LE}, | ||
serde::{Deserialize, Serialize}, | ||
|
@@ -33,18 +33,22 @@ pub enum PayloadPropertyValue { | |
Price(Option<Price>), | ||
BestBidPrice(Option<Price>), | ||
BestAskPrice(Option<Price>), | ||
PublisherCount(Option<u16>), | ||
PublisherCount(u16), | ||
Exponent(i16), | ||
Confidence(Option<Price>), | ||
FundingRate(Option<Rate>), | ||
FundingTimestamp(Option<TimestampUs>), | ||
} | ||
|
||
#[derive(Debug, Clone, Default, Serialize, Deserialize)] | ||
pub struct AggregatedPriceFeedData { | ||
pub price: Option<Price>, | ||
pub best_bid_price: Option<Price>, | ||
pub best_ask_price: Option<Price>, | ||
pub publisher_count: Option<u16>, | ||
pub publisher_count: u16, | ||
pub confidence: Option<Price>, | ||
pub funding_rate: Option<Rate>, | ||
pub funding_timestamp: Option<TimestampUs>, | ||
} | ||
|
||
/// First bytes of a payload's encoding | ||
|
@@ -84,6 +88,12 @@ impl PayloadData { | |
PriceFeedProperty::Confidence => { | ||
PayloadPropertyValue::Confidence(feed.confidence) | ||
} | ||
PriceFeedProperty::FundingRate => { | ||
PayloadPropertyValue::FundingRate(feed.funding_rate) | ||
} | ||
PriceFeedProperty::FundingTimestamp => { | ||
PayloadPropertyValue::FundingTimestamp(feed.funding_timestamp) | ||
} | ||
}) | ||
.collect(), | ||
}) | ||
|
@@ -115,7 +125,7 @@ impl PayloadData { | |
} | ||
PayloadPropertyValue::PublisherCount(count) => { | ||
writer.write_u8(PriceFeedProperty::PublisherCount as u8)?; | ||
write_option_u16::<BO>(&mut writer, *count)?; | ||
writer.write_u16::<BO>(*count)?; | ||
} | ||
PayloadPropertyValue::Exponent(exponent) => { | ||
writer.write_u8(PriceFeedProperty::Exponent as u8)?; | ||
|
@@ -125,6 +135,14 @@ impl PayloadData { | |
writer.write_u8(PriceFeedProperty::Confidence as u8)?; | ||
write_option_price::<BO>(&mut writer, *confidence)?; | ||
} | ||
PayloadPropertyValue::FundingRate(rate) => { | ||
writer.write_u8(PriceFeedProperty::FundingRate as u8)?; | ||
write_option_rate::<BO>(&mut writer, *rate)?; | ||
} | ||
PayloadPropertyValue::FundingTimestamp(timestamp) => { | ||
writer.write_u8(PriceFeedProperty::FundingTimestamp as u8)?; | ||
write_option_timestamp::<BO>(&mut writer, *timestamp)?; | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -164,11 +182,17 @@ impl PayloadData { | |
} else if property == PriceFeedProperty::BestAskPrice as u8 { | ||
PayloadPropertyValue::BestAskPrice(read_option_price::<BO>(&mut reader)?) | ||
} else if property == PriceFeedProperty::PublisherCount as u8 { | ||
PayloadPropertyValue::PublisherCount(read_option_u16::<BO>(&mut reader)?) | ||
PayloadPropertyValue::PublisherCount(reader.read_u16::<BO>()?) | ||
} else if property == PriceFeedProperty::Exponent as u8 { | ||
PayloadPropertyValue::Exponent(reader.read_i16::<BO>()?) | ||
} else if property == PriceFeedProperty::Confidence as u8 { | ||
PayloadPropertyValue::Confidence(read_option_price::<BO>(&mut reader)?) | ||
} else if property == PriceFeedProperty::FundingRate as u8 { | ||
PayloadPropertyValue::FundingRate(read_option_rate::<BO>(&mut reader)?) | ||
} else if property == PriceFeedProperty::FundingTimestamp as u8 { | ||
PayloadPropertyValue::FundingTimestamp(read_option_timestamp::<BO>( | ||
&mut reader, | ||
)?) | ||
} else { | ||
bail!("unknown property"); | ||
}; | ||
|
@@ -196,14 +220,54 @@ fn read_option_price<BO: ByteOrder>(mut reader: impl Read) -> std::io::Result<Op | |
Ok(value.map(Price)) | ||
} | ||
|
||
fn write_option_u16<BO: ByteOrder>( | ||
fn write_option_rate<BO: ByteOrder>( | ||
mut writer: impl Write, | ||
value: Option<Rate>, | ||
) -> std::io::Result<()> { | ||
match value { | ||
Some(value) => { | ||
writer.write_u8(1)?; | ||
writer.write_i64::<BO>(value.0) | ||
} | ||
None => { | ||
writer.write_u8(0)?; | ||
Ok(()) | ||
} | ||
} | ||
} | ||
|
||
fn read_option_rate<BO: ByteOrder>(mut reader: impl Read) -> std::io::Result<Option<Rate>> { | ||
let present = reader.read_u8()? != 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is what you were discussing about the onchain payload right? We need to implement this function on contract? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, the contract needs to update to support this. |
||
if present { | ||
Ok(Some(Rate(reader.read_i64::<BO>()?))) | ||
} else { | ||
Ok(None) | ||
} | ||
} | ||
|
||
fn write_option_timestamp<BO: ByteOrder>( | ||
mut writer: impl Write, | ||
value: Option<u16>, | ||
value: Option<TimestampUs>, | ||
) -> std::io::Result<()> { | ||
writer.write_u16::<BO>(value.unwrap_or(0)) | ||
match value { | ||
Some(value) => { | ||
writer.write_u8(1)?; | ||
writer.write_u64::<BO>(value.0) | ||
} | ||
None => { | ||
writer.write_u8(0)?; | ||
Ok(()) | ||
} | ||
} | ||
} | ||
|
||
fn read_option_u16<BO: ByteOrder>(mut reader: impl Read) -> std::io::Result<Option<u16>> { | ||
let value = reader.read_u16::<BO>()?; | ||
Ok(Some(value)) | ||
fn read_option_timestamp<BO: ByteOrder>( | ||
mut reader: impl Read, | ||
) -> std::io::Result<Option<TimestampUs>> { | ||
let present = reader.read_u8()? != 0; | ||
if present { | ||
Ok(Some(TimestampUs(reader.read_u64::<BO>()?))) | ||
} else { | ||
Ok(None) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
//! eliminating WebSocket overhead. | ||
|
||
use { | ||
super::router::{Price, PriceFeedId, TimestampUs}, | ||
super::router::{Price, PriceFeedId, Rate, TimestampUs}, | ||
derive_more::derive::From, | ||
serde::{Deserialize, Serialize}, | ||
}; | ||
|
@@ -12,7 +12,33 @@ use { | |
/// from the publisher to the router. | ||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct PriceFeedData { | ||
pub struct PriceFeedDataV2 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wait, how will V1/V2 be communicated in the binary? Do we need to add a magic? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there are different urls in the relayers for these 2. |
||
pub price_feed_id: PriceFeedId, | ||
/// Timestamp of the last update provided by the source of the prices | ||
/// (like an exchange). If unavailable, this value is set to `publisher_timestamp_us`. | ||
pub source_timestamp_us: TimestampUs, | ||
/// Timestamp of the last update provided by the publisher. | ||
pub publisher_timestamp_us: TimestampUs, | ||
/// Last known value of the best executable price of this price feed. | ||
/// `None` if no value is currently available. | ||
pub price: Option<Price>, | ||
/// Last known value of the best bid price of this price feed. | ||
/// `None` if no value is currently available. | ||
pub best_bid_price: Option<Price>, | ||
/// Last known value of the best ask price of this price feed. | ||
/// `None` if no value is currently available. | ||
pub best_ask_price: Option<Price>, | ||
/// Last known value of the funding rate of this feed. | ||
/// `None` if no value is currently available. | ||
pub funding_rate: Option<Rate>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about funding timestamp? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I reused the source_timestamp as funding timestamp. |
||
} | ||
|
||
/// Old Represents a binary (bincode-serialized) stream update sent | ||
/// from the publisher to the router. | ||
/// Superseded by `PriceFeedData`. | ||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct PriceFeedDataV1 { | ||
pub price_feed_id: PriceFeedId, | ||
/// Timestamp of the last update provided by the source of the prices | ||
/// (like an exchange). If unavailable, this value is set to `publisher_timestamp_us`. | ||
|
@@ -33,6 +59,20 @@ pub struct PriceFeedData { | |
pub best_ask_price: Option<Price>, | ||
} | ||
|
||
impl From<PriceFeedDataV1> for PriceFeedDataV2 { | ||
fn from(v0: PriceFeedDataV1) -> Self { | ||
Self { | ||
price_feed_id: v0.price_feed_id, | ||
source_timestamp_us: v0.source_timestamp_us, | ||
publisher_timestamp_us: v0.publisher_timestamp_us, | ||
price: v0.price, | ||
best_bid_price: v0.best_bid_price, | ||
best_ask_price: v0.best_ask_price, | ||
funding_rate: None, | ||
} | ||
} | ||
} | ||
|
||
/// A response sent from the server to the publisher client. | ||
/// Currently only serde errors are reported back to the client. | ||
#[derive(Debug, Clone, Serialize, Deserialize, From)] | ||
|
@@ -49,7 +89,7 @@ pub struct UpdateDeserializationErrorResponse { | |
} | ||
|
||
#[test] | ||
fn price_feed_data_serde() { | ||
fn price_feed_data_v1_serde() { | ||
let data = [ | ||
1, 0, 0, 0, // price_feed_id | ||
2, 0, 0, 0, 0, 0, 0, 0, // source_timestamp_us | ||
|
@@ -59,7 +99,7 @@ fn price_feed_data_serde() { | |
6, 2, 0, 0, 0, 0, 0, 0, // best_ask_price | ||
]; | ||
|
||
let expected = PriceFeedData { | ||
let expected = PriceFeedDataV1 { | ||
price_feed_id: PriceFeedId(1), | ||
source_timestamp_us: TimestampUs(2), | ||
publisher_timestamp_us: TimestampUs(3), | ||
|
@@ -68,7 +108,7 @@ fn price_feed_data_serde() { | |
best_ask_price: Some(Price((2 * 256 + 6).try_into().unwrap())), | ||
}; | ||
assert_eq!( | ||
bincode::deserialize::<PriceFeedData>(&data).unwrap(), | ||
bincode::deserialize::<PriceFeedDataV1>(&data).unwrap(), | ||
expected | ||
); | ||
assert_eq!(bincode::serialize(&expected).unwrap(), data); | ||
|
@@ -81,16 +121,68 @@ fn price_feed_data_serde() { | |
0, 0, 0, 0, 0, 0, 0, 0, // best_bid_price | ||
0, 0, 0, 0, 0, 0, 0, 0, // best_ask_price | ||
]; | ||
let expected2 = PriceFeedData { | ||
let expected2 = PriceFeedDataV1 { | ||
price_feed_id: PriceFeedId(1), | ||
source_timestamp_us: TimestampUs(2), | ||
publisher_timestamp_us: TimestampUs(3), | ||
price: Some(Price(4.try_into().unwrap())), | ||
best_bid_price: None, | ||
best_ask_price: None, | ||
}; | ||
assert_eq!( | ||
bincode::deserialize::<PriceFeedDataV1>(&data2).unwrap(), | ||
expected2 | ||
); | ||
assert_eq!(bincode::serialize(&expected2).unwrap(), data2); | ||
} | ||
|
||
#[test] | ||
fn price_feed_data_v2_serde() { | ||
let data = [ | ||
1, 0, 0, 0, // price_feed_id | ||
2, 0, 0, 0, 0, 0, 0, 0, // source_timestamp_us | ||
3, 0, 0, 0, 0, 0, 0, 0, // publisher_timestamp_us | ||
1, 4, 0, 0, 0, 0, 0, 0, 0, // price | ||
1, 5, 0, 0, 0, 0, 0, 0, 0, // best_bid_price | ||
1, 6, 2, 0, 0, 0, 0, 0, 0, // best_ask_price | ||
0, // funding_rate | ||
]; | ||
|
||
let expected = PriceFeedDataV2 { | ||
price_feed_id: PriceFeedId(1), | ||
source_timestamp_us: TimestampUs(2), | ||
publisher_timestamp_us: TimestampUs(3), | ||
price: Some(Price(4.try_into().unwrap())), | ||
best_bid_price: Some(Price(5.try_into().unwrap())), | ||
best_ask_price: Some(Price((2 * 256 + 6).try_into().unwrap())), | ||
funding_rate: None, | ||
}; | ||
assert_eq!( | ||
bincode::deserialize::<PriceFeedDataV2>(&data).unwrap(), | ||
expected | ||
); | ||
assert_eq!(bincode::serialize(&expected).unwrap(), data); | ||
|
||
let data2 = [ | ||
1, 0, 0, 0, // price_feed_id | ||
2, 0, 0, 0, 0, 0, 0, 0, // source_timestamp_us | ||
3, 0, 0, 0, 0, 0, 0, 0, // publisher_timestamp_us | ||
1, 4, 0, 0, 0, 0, 0, 0, 0, // price | ||
0, // best_bid_price | ||
0, // best_ask_price | ||
1, 7, 3, 0, 0, 0, 0, 0, 0, // funding_rate | ||
]; | ||
let expected2 = PriceFeedDataV2 { | ||
price_feed_id: PriceFeedId(1), | ||
source_timestamp_us: TimestampUs(2), | ||
publisher_timestamp_us: TimestampUs(3), | ||
price: Some(Price(4.try_into().unwrap())), | ||
best_bid_price: None, | ||
best_ask_price: None, | ||
funding_rate: Some(Rate(3 * 256 + 7)), | ||
}; | ||
assert_eq!( | ||
bincode::deserialize::<PriceFeedData>(&data2).unwrap(), | ||
bincode::deserialize::<PriceFeedDataV2>(&data2).unwrap(), | ||
expected2 | ||
); | ||
assert_eq!(bincode::serialize(&expected2).unwrap(), data2); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this different from the timestamp of the update itself?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never mind this is answered above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.