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
4 changes: 2 additions & 2 deletions examples/cw-contract/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "example-cw-contract"
version = "0.1.0"
authors = ["Ali Behjati <[email protected]>"]
authors = ["Pyth Data Foundation"]
edition = "2018"

exclude = [
Expand Down Expand Up @@ -34,7 +34,7 @@ cosmwasm-storage = { version = "1.0.0" }
cw-storage-plus = "0.13.4"
schemars = "0.8"
serde = { version = "1.0", default-features = false, features = ["derive"] }
pyth-sdk-cw = { version = "0.1.0", path = "../../pyth-sdk-cw" } # Remove path and use version only when you use this example on your own.
pyth-sdk-cw = { version = "0.2.0", path = "../../pyth-sdk-cw" } # Remove path and use version only when you use this example on your own.

[dev-dependencies]
cosmwasm-schema = { version = "1.0.0" }
4 changes: 2 additions & 2 deletions pyth-sdk-cw/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pyth-sdk-cw"
version = "0.1.0"
version = "0.2.0"
authors = ["Pyth Data Foundation"]
edition = "2018"
license = "Apache-2.0"
Expand All @@ -14,7 +14,7 @@ readme = "README.md"
cosmwasm-std = { version = "1.0.0" }
cosmwasm-storage = { version = "1.0.0" }
serde = { version = "1.0.136", features = ["derive"] }
pyth-sdk = { path = "../pyth-sdk", version = "0.4.1" }
pyth-sdk = { path = "../pyth-sdk", version = "0.5.0" }
schemars = "0.8.1"

[dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions pyth-sdk-solana/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pyth-sdk-solana"
version = "0.4.2"
version = "0.5.0"
authors = ["Pyth Data Foundation"]
edition = "2018"
license = "Apache-2.0"
Expand All @@ -19,7 +19,7 @@ num-derive = "0.3"
num-traits = "0.2"
thiserror = "1.0"
serde = { version = "1.0.136", features = ["derive"] }
pyth-sdk = { path = "../pyth-sdk", version = "0.4.0" }
pyth-sdk = { path = "../pyth-sdk", version = "0.5.0" }

[dev-dependencies]
solana-client = "1.8.1, < 1.11"
Expand Down
1 change: 1 addition & 0 deletions pyth-sdk-solana/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use state::load_price_account;
pub use pyth_sdk::{
Price,
PriceFeed,
PriceIdentifier,
PriceStatus,
ProductIdentifier,
};
Expand Down
34 changes: 18 additions & 16 deletions pyth-sdk-solana/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,9 @@ pub use pyth_sdk::{
PriceStatus,
};

#[cfg(target_arch = "bpf")]
use solana_program::{
clock::Clock,
sysvar::Sysvar,
};
use solana_program::clock::Clock;
use solana_program::sysvar::Sysvar;

#[cfg(target_arch = "bpf")]
use crate::VALID_SLOT_PERIOD;

use crate::PythError;
Expand Down Expand Up @@ -348,14 +344,20 @@ unsafe impl Pod for PriceAccount {

impl PriceAccount {
pub fn to_price_feed(&self, price_key: &Pubkey) -> PriceFeed {
#[allow(unused_mut)]
let mut status = self.agg.status;

#[cfg(target_arch = "bpf")]
if matches!(status, PriceStatus::Trading)
&& Clock::get().unwrap().slot.saturating_sub(self.agg.pub_slot) > VALID_SLOT_PERIOD
{
status = PriceStatus::Unknown;
let mut prev_price = self.prev_price;
let mut prev_conf = self.prev_conf;
let mut prev_publish_time = self.prev_timestamp;

if let Ok(clock) = Clock::get() {
if matches!(status, PriceStatus::Trading)
&& clock.slot.saturating_sub(self.agg.pub_slot) > VALID_SLOT_PERIOD
{
status = PriceStatus::Unknown;
prev_price = self.agg.price;
prev_conf = self.agg.conf;
prev_publish_time = self.timestamp;
}
}

PriceFeed::new(
Expand All @@ -370,9 +372,9 @@ impl PriceAccount {
self.agg.conf,
self.ema_price.val,
self.ema_conf.val as u64,
self.prev_price,
self.prev_conf,
self.prev_timestamp,
prev_price,
prev_conf,
prev_publish_time,
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion pyth-sdk-solana/test-contract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ test-bpf = []
no-entrypoint = []

[dependencies]
pyth-sdk-solana = { path = "../", version = "0.4.0" }
pyth-sdk-solana = { path = "../", version = "0.5.0" }
solana-program = "1.8.1, < 1.11" # Currently latest Solana 1.11 crate can't build bpf: https://github.com/solana-labs/solana/issues/26188
bytemuck = "1.7.2"
borsh = "0.9"
Expand Down
4 changes: 0 additions & 4 deletions pyth-sdk-solana/test-contract/tests/stale_price.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,7 @@ async fn test_price_stale() {
price.agg.status = PriceStatus::Trading;
price.agg.pub_slot = 1000 - VALID_SLOT_PERIOD - 1;

#[cfg(feature = "test-bpf")] // Only in BPF the clock check is performed
let expected_status = PriceStatus::Unknown;

#[cfg(not(feature = "test-bpf"))]
let expected_status = PriceStatus::Trading;

test_instr_exec_ok(instruction::price_status_check(&price, expected_status)).await;
}
2 changes: 1 addition & 1 deletion pyth-sdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pyth-sdk"
version = "0.4.2"
version = "0.5.0"
authors = ["Pyth Data Foundation"]
edition = "2018"
license = "Apache-2.0"
Expand Down
76 changes: 61 additions & 15 deletions pyth-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ pub type ProductIdentifier = Identifier;
/// Jan 1970). It is a signed integer because it's the standard in Unix systems and allows easier
/// time difference.
pub type UnixTimestamp = i64;
pub type DurationInSeconds = u64;

/// Represents availability status of a price feed.
#[derive(
Expand Down Expand Up @@ -277,22 +278,67 @@ impl PriceFeed {
}
}

/// Get the "unchecked" previous price with Trading status,
/// along with the timestamp at which it was generated.
/// Get the latest available price, along with the timestamp when it was generated.
///
/// WARNING:
/// We make no guarantees about the unchecked price and confidence returned by
/// this function: it could differ significantly from the current price.
/// We strongly encourage you to use `get_current_price` instead.
pub fn get_prev_price_unchecked(&self) -> (Price, UnixTimestamp) {
(
Price {
price: self.prev_price,
conf: self.prev_conf,
expo: self.expo,
},
self.prev_publish_time,
)
/// This function returns the same price as `get_current_price` in the case where a price was
/// available at the time this `PriceFeed` was published (`publish_time`). However, if a
/// price was not available at that time, this function returns the price from the latest
/// time at which the price was available. The returned price can be from arbitrarily far in
/// the past; this function makes no guarantees that the returned price is recent or useful
/// for any particular application.
///
/// Users of this function should check the returned timestamp to ensure that the returned price
/// is sufficiently recent for their application. If you are considering using this
/// function, it may be safer / easier to use either `get_current_price` or
/// `get_latest_available_price_within_duration`.
///
/// Returns a struct containing the latest available price, confidence interval, and the
/// exponent for both numbers along with the timestamp when that price was generated.
pub fn get_latest_available_price_unchecked(&self) -> (Price, UnixTimestamp) {
match self.status {
PriceStatus::Trading => (
Price {
price: self.price,
conf: self.conf,
expo: self.expo,
},
self.publish_time,
),
_ => (
Price {
price: self.prev_price,
conf: self.prev_conf,
expo: self.expo,
},
self.prev_publish_time,
),
}
}

/// Get the latest price as long as it was updated within `duration` seconds of the
/// `current_time`.
///
/// This function is a sanity-checked version of `get_latest_available_price_unchecked` which is
/// useful in applications that require a sufficiently-recent price. Returns `None` if the
/// price wasn't updated sufficiently recently.
///
/// Returns a struct containing the latest available price, confidence interval and the exponent
/// for both numbers, or `None` if no price update occurred within `duration` seconds of the
/// `current_time`.
pub fn get_latest_available_price_within_duration(
&self,
current_time: UnixTimestamp,
duration: DurationInSeconds,
) -> Option<Price> {
let (price, publish_time) = self.get_latest_available_price_unchecked();

let time_diff_abs = (publish_time - current_time).abs() as u64;

if time_diff_abs > duration {
return None;
}

Some(price)
}
}
#[cfg(test)]
Expand Down