|
1 |
| -# pyth-client-rs |
| 1 | +# Pyth Client |
2 | 2 |
|
3 |
| -A rust API for desribing 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 |
| 3 | +This crate provides utilities for reading price feeds from the [pyth.network](https://pyth.network/) oracle on the Solana network. |
| 4 | +The crate includes a library for on-chain programs and an off-chain example program. |
4 | 5 |
|
| 6 | +Key features of this library include: |
5 | 7 |
|
6 |
| -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. |
| 8 | +* Get the current price of over [50 products](https://pyth.network/markets/), including cryptocurrencies, |
| 9 | + US equities, forex and more. |
| 10 | +* Combine listed products to create new price feeds, e.g., for baskets of tokens or non-USD quote currencies. |
| 11 | +* Consume prices in on-chain Solana programs or off-chain applications. |
7 | 12 |
|
8 |
| -### Running the Example |
| 13 | +Please see the [pyth.network documentation](https://docs.pyth.network/) for more information about pyth.network. |
| 14 | + |
| 15 | +## Installation |
| 16 | + |
| 17 | +Add a dependency to your Cargo.toml: |
| 18 | + |
| 19 | +```toml |
| 20 | +[dependencies] |
| 21 | +pyth-client="<version>" |
| 22 | +``` |
| 23 | + |
| 24 | +See [pyth-client on crates.io](https://crates.io/crates/pyth-client/) to get the latest version of the library. |
| 25 | + |
| 26 | +## Usage |
| 27 | + |
| 28 | +Pyth Network stores its price feeds in a collection of Solana accounts. |
| 29 | +This crate provides utilities for interpreting and manipulating the content of these accounts. |
| 30 | +Applications can obtain the content of these accounts in two different ways: |
| 31 | +* On-chain programs should pass these accounts to the instructions that require price feeds. |
| 32 | +* Off-chain programs can access these accounts using the Solana RPC client (as in the [example program](examples/get_accounts.rs)). |
| 33 | + |
| 34 | +In both cases, the content of the account will be provided to the application as a binary blob (`Vec<u8>`). |
| 35 | +The examples below assume that the user has already obtained this account data. |
| 36 | + |
| 37 | +### Parse account data |
| 38 | + |
| 39 | +Pyth Network has several different types of accounts: |
| 40 | +* Price accounts store the current price for a product |
| 41 | +* Product accounts store metadata about a product, such as its symbol (e.g., "BTC/USD"). |
| 42 | +* Mapping accounts store a listing of all Pyth accounts |
| 43 | + |
| 44 | +For more information on the different types of Pyth accounts, see the [account structure documentation](https://docs.pyth.network/how-pyth-works/account-structure). |
| 45 | +The pyth.network website also lists the public keys of the accounts (e.g., [BTC/USD accounts](https://pyth.network/markets/#BTC/USD)). |
| 46 | + |
| 47 | +This library provides several `load_*` methods that translate the binary data in each account into an appropriate struct: |
| 48 | + |
| 49 | +```rust |
| 50 | +// replace with account data, either passed to on-chain program or from RPC node |
| 51 | +let price_account_data: Vec<u8> = ...; |
| 52 | +let price_account: Price = load_price( &price_account_data ).unwrap(); |
| 53 | + |
| 54 | +let product_account_data: Vec<u8> = ...; |
| 55 | +let product_account: Product = load_product( &product_account_data ).unwrap(); |
| 56 | + |
| 57 | +let mapping_account_data: Vec<u8> = ...; |
| 58 | +let mapping_account: Mapping = load_mapping( &mapping_account_data ).unwrap(); |
| 59 | +``` |
| 60 | + |
| 61 | +### Get the current price |
| 62 | + |
| 63 | +Read the current price from a `Price` account: |
| 64 | + |
| 65 | +```rust |
| 66 | +let price: PriceConf = price_account.get_current_price().unwrap(); |
| 67 | +println!("price: ({} +- {}) x 10^{}", price.price, price.conf, price.expo); |
| 68 | +``` |
| 69 | + |
| 70 | +The price is returned along with a confidence interval that represents the degree of uncertainty in the price. |
| 71 | +Both values are represented as fixed-point numbers, `a * 10^e`. |
| 72 | +The method will return `None` if the price is not currently available. |
| 73 | + |
| 74 | +### Non-USD prices |
| 75 | + |
| 76 | +Most assets in Pyth are priced in USD. |
| 77 | +Applications can combine two USD prices to price an asset in a different quote currency: |
| 78 | + |
| 79 | +```rust |
| 80 | +let btc_usd: Price = ...; |
| 81 | +let eth_usd: Price = ...; |
| 82 | +// -8 is the desired exponent for the result |
| 83 | +let btc_eth: PriceConf = btc_usd.get_price_in_quote(ð_usd, -8); |
| 84 | +println!("BTC/ETH price: ({} +- {}) x 10^{}", price.price, price.conf, price.expo); |
| 85 | +``` |
| 86 | + |
| 87 | +### Price a basket of assets |
| 88 | + |
| 89 | +Applications can also compute the value of a basket of multiple assets: |
| 90 | + |
| 91 | +```rust |
| 92 | +let btc_usd: Price = ...; |
| 93 | +let eth_usd: Price = ...; |
| 94 | +// Quantity of each asset in fixed-point a * 10^e. |
| 95 | +// This represents 0.1 BTC and .05 ETH. |
| 96 | +// -8 is desired exponent for result |
| 97 | +let basket_price: PriceConf = Price::price_basket(&[ |
| 98 | + (btc_usd, 10, -2), |
| 99 | + (eth_usd, 5, -2) |
| 100 | + ], -8); |
| 101 | +println!("0.1 BTC and 0.05 ETH are worth: ({} +- {}) x 10^{} USD", |
| 102 | + basket_price.price, basket_price.conf, basket_price.expo); |
| 103 | +``` |
| 104 | + |
| 105 | +This function additionally propagates any uncertainty in the price into uncertainty in the value of the basket. |
| 106 | + |
| 107 | +### Off-chain example program |
9 | 108 |
|
10 | 109 | The example program prints the product reference data and current price information for Pyth on Solana devnet.
|
11 | 110 | Run the following commands to try this example program:
|
@@ -37,4 +136,25 @@ product_account .. 6MEwdxe4g1NeAF9u6KDG14anJpFsVEa2cvr5H6iriFZ8
|
37 | 136 | publish_slot . 91340925
|
38 | 137 | twap ......... 7426390900
|
39 | 138 | twac ......... 2259870
|
40 |
| -``` |
| 139 | +``` |
| 140 | + |
| 141 | +## Development |
| 142 | + |
| 143 | +This library can be built for either your native platform or in BPF (used by Solana programs). |
| 144 | +Use `cargo build` / `cargo test` to build and test natively. |
| 145 | +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). |
| 146 | + |
| 147 | +The BPF tests will also run an instruction count program that logs the resource consumption |
| 148 | +of various library functions. |
| 149 | +This program can also be run on its own using `cargo test-bpf --test instruction_count`. |
| 150 | + |
| 151 | +### Releases |
| 152 | + |
| 153 | +To release a new version of this package, perform the following steps: |
| 154 | + |
| 155 | +1. Increment the version number in `Cargo.toml`. |
| 156 | + You may use a version number with a `-beta.x` suffix such as `0.0.1-beta.0` to create opt-in test versions. |
| 157 | +2. Merge your change into `main` on github. |
| 158 | +3. Create and publish a new github release. |
| 159 | + The name of the release should be the version number, and the tag should be the version number prefixed with `v`. |
| 160 | + 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`. |
0 commit comments