diff --git a/README.md b/README.md index 2602eaa..2c1f01d 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Key features of this SDK include: * Get the current price of over [50 products](https://pyth.network/markets/), including cryptocurrencies, US equities, forex and more. * Combine listed products to create new price feeds, e.g., for baskets of tokens or non-USD quote currencies. -* Consume prices in Solana programs, CosmWasm smart contracts, or off-chain applications. +* Consume prices in Solana programs or off-chain applications. Please see the [pyth.network documentation](https://docs.pyth.network/) for more information about pyth.network. @@ -18,7 +18,6 @@ This repository is divided into several crates focused on specific use cases: 1. [Pyth SDK](pyth-sdk) provides common data types and interfaces for that are shared across different blockchains. 2. [Pyth SDK Solana](pyth-sdk-solana) provides an interface for reading Pyth price feeds in Solana programs. This crate may also be used in off-chain applications that read prices from the Solana blockchain. -3. [Pyth SDK CosmWasm](pyth-sdk-cw) provides an interface for reading Pyth price feeds in on-chain CosmWasm contracts. Please see the documentation for the relevant crate to get started using Pyth Network. diff --git a/pyth-sdk-solana/README.md b/pyth-sdk-solana/README.md index db3318a..9616ee9 100644 --- a/pyth-sdk-solana/README.md +++ b/pyth-sdk-solana/README.md @@ -26,7 +26,7 @@ Applications can obtain the content of these accounts in two different ways: * On-chain programs should pass these accounts to the instructions that require price feeds. * Off-chain programs can access these accounts using the Solana RPC client (as in the [eth price example program](examples/eth_price.rs)). -The pyth.network website can be used to identify the public keys of the various Pyth Network accounts (e.g., [Crypto.BTC/USD accounts](https://pyth.network/markets/#Crypto.BTC/USD)). +The [pyth.network](https://pyth.network/developers/price-feed-ids#solana-mainnet-beta) website can be used to identify the public keys of each price feed's price account (e.g. Crypto.BTC/USD). ### On-chain @@ -37,15 +37,19 @@ The `load_price_feed_from_account_info` function will construct a `PriceFeed` st ```rust use pyth_sdk_solana::{load_price_feed_from_account_info, PriceFeed}; +const STALENESS_THRESHOLD : u64 = 60; // staleness threshold in seconds let price_account_info: AccountInfo = ...; let price_feed: PriceFeed = load_price_feed_from_account_info( &price_account_info ).unwrap(); -let current_price: Price = price_feed.get_current_price().unwrap(); -println!("price: ({} +- {}) x 10^{}", current_price.price, current_price.conf, current_price.expo); +let current_timestamp = Clock::get()?.unix_timestamp; +let current_price: Price = price_feed.get_price_no_older_than(current_timestamp, STALENESS_THRESHOLD).unwrap(); +msg!("price: ({} +- {}) x 10^{}", current_price.price, current_price.conf, current_price.expo); ``` The `PriceFeed` object returned by `load_price_feed_from_account_info` contains all currently-available pricing information about the product. This struct also has some useful functions for manipulating and combining prices; see the [common SDK documentation](../pyth-sdk) for more details. +The function `get_price_no_older_than` takes in an `age` in seconds. If the current on-chain aggregate is older than `current_timestamp - age`, `get_price_no_older_than` will return `None`. + Note that your application should also validate the address of the passed-in price account before using it. Otherwise, an attacker could pass in a different account and set the price to an arbitrary value. @@ -58,10 +62,16 @@ The `load_price_feed_from_account` function will construct a `PriceFeed` struct ```rust use pyth_sdk_solana::{load_price_feed_from_account, PriceFeed}; +const STALENESS_THRESHOLD : u64 = 60; // staleness threshold in seconds +let current_time = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() as i64; + let price_key: Pubkey = ...; -let mut price_account: Account = ...; +let mut price_account: Account = clnt.get_account(&price_key).unwrap(); let price_feed: PriceFeed = load_price_feed_from_account( &price_key, &mut price_account ).unwrap(); -let current_price: Price = price_feed.get_current_price().unwrap(); +let current_price: Price = price_feed.get_price_no_older_than(current_time, STALENESS_THRESHOLD).unwrap(); println!("price: ({} +- {}) x 10^{}", current_price.price, current_price.conf, current_price.expo); ``` diff --git a/pyth-sdk-solana/src/state.rs b/pyth-sdk-solana/src/state.rs index cee0ce1..2874416 100644 --- a/pyth-sdk-solana/src/state.rs +++ b/pyth-sdk-solana/src/state.rs @@ -220,16 +220,14 @@ unsafe impl Pod for ProductAccount { #[repr(C)] pub struct PriceInfo { /// the current price. - /// For the aggregate price use price.get_current_price() whenever possible. It has more checks - /// to make sure price is valid. + /// For the aggregate price use `get_price_no_older_than()` whenever possible. Accessing fields + /// directly might expose you to stale or invalid prices. pub price: i64, /// confidence interval around the price. - /// For the aggregate confidence use price.get_current_price() whenever possible. It has more - /// checks to make sure price is valid. + /// For the aggregate confidence use `get_price_no_older_than()` whenever possible. Accessing + /// fields directly might expose you to stale or invalid prices. pub conf: u64, - /// status of price (Trading is valid). - /// For the aggregate status use price.get_current_status() whenever possible. - /// Price data can sometimes go stale and the function handles the status in such cases. + /// status of price (Trading is valid) pub status: PriceStatus, /// notification of any corporate action pub corp_act: CorpAction, diff --git a/pyth-sdk/README.md b/pyth-sdk/README.md index 1135567..a0588db 100644 --- a/pyth-sdk/README.md +++ b/pyth-sdk/README.md @@ -1,7 +1,7 @@ # Pyth Network Common Rust SDK This crate contains Pyth Network data structures that are shared across all Rust-based consumers of Pyth Network data. -This crate is typically used in combination with a platform-specific crate such as [pyth-sdk-solana](../pyth-sdk-solana) or [pyth-sdk-cw](../pyth-sdk-cw). +This crate is typically used in combination with a platform-specific crate such as [pyth-sdk-solana](../pyth-sdk-solana). ## Usage @@ -19,7 +19,9 @@ Once you have a `PriceFeed`, you can call one of the methods below to get the pr Get the current price of the product from its `PriceFeed`: ```rust -let current_price: Price = price_feed.get_current_price().ok_or(StdError::not_found("Current price is not available"))?; +const STALENESS_THRESHOLD : u64 = 60; // staleness threshold in seconds +let current_timestamp = ...; +let current_price: Price = price_feed.get_price_no_older_than(current_timestamp, STALENESS_THRESHOLD).ok_or(StdError::not_found("Current price is not available"))?; println!("price: ({} +- {}) x 10^{}", current_price.price, current_price.conf, current_price.expo); ``` @@ -35,7 +37,9 @@ Please see the [consumer best practices guide](https://docs.pyth.network/consume The EMA price can be retrieved as follows: ```rust -let ema_price: Price = price_feed.get_ema_price().ok_or(StdError::not_found("EMA price is not available"))?; +const STALENESS_THRESHOLD : u64 = 60; // staleness threshold in seconds +let current_timestamp = ...; +let ema_price: Price = price_feed.get_ema_price_no_older_than(current_timestamp, STALENESS_THRESHOLD).ok_or(StdError::not_found("EMA price is not available"))?; println!("price: ({} +- {}) x 10^{}", ema_price.price, ema_price.conf, ema_price.expo); ```