Skip to content
This repository was archived by the owner on Jun 30, 2022. It is now read-only.
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: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ no-entrypoint = []
solana-program = "1.8.1"
borsh = "0.9"
borsh-derive = "0.9.0"
bytemuck = "1.7.2"
num-derive = "0.3"
num-traits = "0.2"
thiserror = "1.0"

[dev-dependencies]
solana-program-test = "1.8.1"
Expand Down
47 changes: 40 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
# pyth-client-rs
# Pyth Client

A rust API for describing on-chain pyth account structures. A primer on pyth accounts can be found at https://github.com/pyth-network/pyth-client/blob/main/doc/aggregate_price.md
A Rust library for consuming price feeds from the [pyth.network](https://pyth.network/) oracle on the Solana network.
This package includes a library for on-chain programs and an example program for printing product reference data.

Contains a library for use in on-chain program development and an off-chain example program for loading and printing product reference data and aggregate prices from all devnet pyth accounts.
Key features of this library 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 on-chain Solana programs or off-chain applications.

Please see the [pyth.network documentation](https://docs.pyth.network/) for more information about pyth.network.

## Usage

Add a dependency to your Cargo.toml:

```toml
[dependencies]
pyth-client="<version>"
```

See [pyth-client on crates.io](https://crates.io/crates/pyth-client/) to get the latest version of the library.

### Running the Example

Expand Down Expand Up @@ -38,9 +57,23 @@ product_account .. 6MEwdxe4g1NeAF9u6KDG14anJpFsVEa2cvr5H6iriFZ8
twac ......... 2259870
```

## Development

This library can be built for either your native platform or in BPF (used by Solana programs).
Use `cargo build` / `cargo test` to build and test natively.
Use `cargo build-bpf` / `cargo test-bpf` to build in BPF for Solana; these commands require you to have installed the [Solana CLI tools](https://docs.solana.com/cli/install-solana-cli-tools).

The BPF tests will also run an instruction count program that logs the resource consumption
of various library functions.
This program can also be run on its own using `cargo test-bpf --test instruction_count`.

### Releases

### Development
To release a new version of this package, perform the following steps:

Run `cargo test-bpf` to build in BPF and run the unit tests.
This command will also build an instruction count program that logs the resource consumption
of various functions.
1. Increment the version number in `Cargo.toml`.
You may use a version number with a `-beta.x` suffix such as `0.0.1-beta.0` to create opt-in test versions.
2. Merge your change into `main` on github.
3. Create and publish a new github release.
The name of the release should be the version number, and the tag should be the version number prefixed with `v`.
Publishing the release will trigger a github action that will automatically publish the [pyth-client](https://crates.io/crates/pyth-client) rust crate to `crates.io`.
31 changes: 6 additions & 25 deletions examples/get_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@
// bootstrap all product and pricing accounts from root mapping account

use pyth_client::{
AccountType,
Mapping,
Product,
Price,
PriceType,
PriceStatus,
CorpAction,
cast,
MAGIC,
VERSION_2,
load_mapping,
load_product,
load_price,
PROD_HDR_SIZE
};
use solana_client::{
Expand Down Expand Up @@ -71,24 +67,14 @@ fn main() {
loop {
// get Mapping account from key
let map_data = clnt.get_account_data( &akey ).unwrap();
let map_acct = cast::<Mapping>( &map_data );
assert_eq!( map_acct.magic, MAGIC, "not a valid pyth account" );
assert_eq!( map_acct.atype, AccountType::Mapping as u32,
"not a valid pyth mapping account" );
assert_eq!( map_acct.ver, VERSION_2,
"unexpected pyth mapping account version" );
let map_acct = load_mapping( &map_data ).unwrap();

// iget and print each Product in Mapping directory
let mut i = 0;
for prod_akey in &map_acct.products {
let prod_pkey = Pubkey::new( &prod_akey.val );
let prod_data = clnt.get_account_data( &prod_pkey ).unwrap();
let prod_acct = cast::<Product>( &prod_data );
assert_eq!( prod_acct.magic, MAGIC, "not a valid pyth account" );
assert_eq!( prod_acct.atype, AccountType::Product as u32,
"not a valid pyth product account" );
assert_eq!( prod_acct.ver, VERSION_2,
"unexpected pyth product account version" );
let prod_acct = load_product( &prod_data ).unwrap();

// print key and reference data for this Product
println!( "product_account .. {:?}", prod_pkey );
Expand All @@ -106,13 +92,8 @@ fn main() {
let mut px_pkey = Pubkey::new( &prod_acct.px_acc.val );
loop {
let pd = clnt.get_account_data( &px_pkey ).unwrap();
let pa = cast::<Price>( &pd );
let pa = load_price( &pd ).unwrap();

assert_eq!( pa.magic, MAGIC, "not a valid pyth account" );
assert_eq!( pa.atype, AccountType::Price as u32,
"not a valid pyth price account" );
assert_eq!( pa.ver, VERSION_2,
"unexpected pyth price account version" );
println!( " price_account .. {:?}", px_pkey );

let maybe_price = pa.get_current_price();
Expand Down
25 changes: 25 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use num_derive::FromPrimitive;
use solana_program::program_error::ProgramError;
use thiserror::Error;

/// Errors that may be returned by Pyth.
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
pub enum PythError {
// 0
/// Invalid account data -- either insufficient data, or incorrect magic number
#[error("Failed to convert account into a Pyth account")]
InvalidAccountData,
/// Wrong version number
#[error("Incorrect version number for Pyth account")]
BadVersionNumber,
/// Tried reading an account with the wrong type, e.g., tried to read
/// a price account as a product account.
#[error("Incorrect account type")]
WrongAccountType,
}

impl From<PythError> for ProgramError {
fn from(e: PythError) -> Self {
ProgramError::Custom(e as u32)
}
}
2 changes: 1 addition & 1 deletion src/instruction.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Program instructions, used for end-to-end testing and instruction counts
//! Program instructions for end-to-end testing and instruction counts

use {
crate::id,
Expand Down
Loading