Pyth Network Client

repository·main·Indexed 41 days ago

https://github.com/pyth-network/pyth-client

Tools and client implementations for interacting with the Pyth Network decentralized oracle. Includes the pyth-oracle program (v2.35.0) for retrieving on-chain price data, the pythd off-chain client API, and the pyth-client-rs crate (v0.1.1). Documentation covers building BPF binaries for Solana, interpreting price and confidence data, and implementing batched price updates in C++ bindings (v2.10.1).

Tokens
8.6K
Snippets
29
Records
39
Agent score
52%

What's inside Pyth Network

  1. Understand the Pyth on-chain account structure

    main

    Pyth manages on-chain data using a hierarchical relationship between three main account types. This structure allows for efficient mapping of products to their respective price data.

    Account Hierarchy

    1. Mapping Accounts: A linked list of accounts that contain lists of product account IDs (public keys).
    2. Product Accounts: Contain reference attributes for a specific asset (e.g., BTC/USD). Attributes are stored as text key/value pairs (e.g., symbol, asset_type, quote_currency, tenor).
    3. Price Accounts: A linked list of accounts referenced by a product. Each price account represents a specific price_type (currently price, twap, or volatility).

    Relationship Diagram

    mapping $\rightarrow$ product $\rightarrow$ price (price/twap/volatility)

  2. Interpret Pyth price and confidence data

    main

    Pyth price data is stored in price accounts using specific formats and metadata:

    • Price Representation: Prices are stored as 64-bit integers. To get the human-readable value, you must apply the exponent field. For example, a price of 12276250 with an exponent of -5 represents 122.76250.
    • Confidence (conf): Represents the confidence interval on the price, which broadly corresponds to the bid-offer spread.
    • Status: Indicates the validity of the price. Valid prices must have a status of trading. Other possible values are halted, auction, or unknown.
    • Slots:
      • valid_slot: The Solana slot in which the aggregate price was computed.
      • publish_slot: The Solana slot in which the aggregate price was published.
  3. How Pyth derives aggregate prices

    main

    The Pyth aggregate price is derived from multiple contributing market "quoters" through a two-stage process:

    1. Submission: Individual quoters submit their prices along with the most recently confirmed Solana slot value.
    2. Aggregation: The program gathers the latest prices from each quoter, discards those that are too old or not in a valid trading state, and derives an aggregate price (currently using a simple median).

    Aggregation occurs as the Solana slot ticks forward. The program computes the aggregate price based on the current valid_slot and publishes it with a new publish_slot.

  4. Build pythd using Docker

    main

    If you are on a non-Linux machine (e.g., M1 Mac) or need to run BPF binaries, it is recommended to build inside a Linux Docker container. You can use a pre-configured image from the Pyth Foundation Docker Hub.

    Alternatively, you can use the provided helper script to start a build environment.

    # Option 1: Use the helper script
    ./scripts/start-docker-build-env.sh && cd pyth-client && cargo build --release
    
    # Option 2: Manual Docker run (requires setting PYTH_REPO and IMAGE env vars)
    docker run -it \
      --volume "${HOME}:/home/pyth/home" \
      --volume "${HOME}/.config:/home/pyth/.config" \
      --mount "type=bind,src=${PYTH_REPO},target=/home/pyth/pyth-client" \
      --userns=host \
      --user="$( id -ur ):$( id -gr )" \
      --platform linux/amd64 \
      $IMAGE \
      /bin/bash -l
  5. Test the Pyth Oracle Program

    main

    You can run several types of tests using cargo test. This includes unit tests, simulated transaction tests against the BPF binary on a Solana simulator, and randomized test batteries for core functionality. C tests are linked into the Rust binary and run automatically.

    Alternatively, you can use cargo test-bpf, which runs the same tests but is slower and has a different UX.

    # Run standard tests
    cargo test
    
    # Run BPF-specific tests
    cargo test-bpf
  6. Build the pythd off-chain client API

    main

    pythd (deprecated in favor of pyth-agent) provides a web API for interacting with the on-chain oracle program.

    To build it on a Linux system, you must install the following dependencies via apt:

    • libssl-dev (openssl)
    • zlib1g and zlib1g-dev (libz)
    • libzstd-dev (libzstd)
    • cmake (cmake)

    Once dependencies are installed, run the provided build script.

    # Install dependencies
    apt install libssl-dev zlib1g zlib1g-dev libzstd-dev cmake
    
    # Build (defaults to release build)
    ./scripts/build.sh
  7. Set up a VS Code development environment with Docker

    main

    To develop using VS Code with a Docker-based environment:

    1. Start a detached container using docker run with your project volume mounted.
    2. Adjust user permissions inside the container so your host user has access to the files (using usermod and groupadd).
    3. Use the VS Code 'Remote - Containers' (or 'Dev Containers') extension to 'Attach VS Code' to the running container.
    # 1. Start the container
    docker run --name pyth-dev -d \
      --volume "${HOME}:/home/pyth/home" \
      --volume "${HOME}/.config:/home/pyth/.config" \
      --volume "${HOME}/.ssh:/home/pyth/.ssh" \
      --mount "type=bind,src=${PYTH_REPO},target=/home/pyth/pyth-client" \
      --platform linux/amd64 \
      $IMAGE \
      /bin/bash -c "while [ true ]; do sleep 1000; done"
    
    # 2. Fix permissions (example IDs)
    docker exec -ti pyth-dev bash
    sudo su
    groupadd -g 1004 1004
    usermod -u 1002 -g 1004 -s /bin/bash pyth
  8. Debug a failing fuzz test case

    main

    If a fuzz test produces a crash, you can investigate the specific failing input by running the fuzz binary inside the docker container and redirecting the failing input file into it.

    # Example: running a specific crash file
    docker run -t \
      --platform linux/amd64 \
      -v "$(pwd)"/findings:/home/pyth/pyth-client/findings \
      pyth-fuzz \
      sh -c "./pyth-client/build/fuzz add < ./pyth-client/findings/crashes/id\:000000\,sig\:06\,src\:000000\,op\:flip1\,pos\:0"
  9. Run Fuzz tests for pythd

    main

    Fuzz testing is performed using a specialized Docker image.

    1. Build the fuzz image:
    docker build . --platform linux/amd64 -f docker/fuzz/Dockerfile -t pyth-fuzz
    1. Run a specific fuzz test (e.g., add) using the fuzz command-line program. Outputs are saved to a findings/ directory which is shared with the host.
    # Build the fuzz image
    docker build . --platform linux/amd64 -f docker/fuzz/Dockerfile -t pyth-fuzz
    
    # Run the 'add' fuzz test
    docker run -t \
      --platform linux/amd64 \
      -v "$(pwd)"/findings:/home/pyth/pyth-client/findings \
      pyth-fuzz \
      sh -c "./afl/afl-fuzz -i ./pyth-client/pyth/tests/fuzz/add/testcases -o ./pyth-client/findings ./pyth-client/build/fuzz add"
  10. Install pre-commit hooks

    main

    To ensure code follows consistent formatting (currently enforced in the program/ directory), you can install pre-commit hooks. You may need to install the nightly toolchain via rustup toolchain install nightly to run the formatting checks.

    pre-commit install
  11. Build the Pyth Oracle Program

    main

    The Pyth oracle program is located in the program/ directory and can be built using cargo. You must have the Solana tool suite installed, ensuring the version matches the one used in CI.

    Use cargo build to compile the program as a native binary, or cargo build-bpf to build the BPF binary for blockchain deployment. The BPF build produces the binary at target/deploy/pyth_oracle.so.

    # Build as native binary
    cargo build
    
    # Build BPF binary for blockchain deployment
    cargo build-bpf
  12. Update C++ publisher logic for version 2.10.1

    main

    For C++ developers, the transition to version 2.10.1 requires a change in how price updates are triggered.

    Old Pattern: Sending individual updates in price_sched callbacks. New Pattern: Calling pc::price::send with a vector of pc::price objects in response to on_slot_publish callbacks.

    Refer to ../pctest/test_publish.cpp in the repository for a concrete implementation example of the new approach.