solana_kit_pyth 0.9.3 copy "solana_kit_pyth: ^0.9.3" to clipboard
solana_kit_pyth: ^0.9.3 copied to clipboard

Pyth Network Hermes client and price account codecs for Solana Kit Dart.

solana_kit_pyth #

pub package docs website CI coverage

Pyth Network client for the Solana Kit Dart SDK.

Covers the pull-model Pyth integration surface:

  • Hermes price service client — the official v2 REST API for price feeds and binary price updates
  • Binary update parsing — accumulator update blobs, Wormhole VAA envelopes, and wire-format price feed messages
  • On-chain account decoding — the classic Pyth price account layout and the push oracle PriceUpdateV2 price feed accounts
  • Update submission — instruction builders for the Pyth Solana Receiver program

Installation #

Installation #

Install the package directly:

dependencies:
  "solana_kit_pyth": ^

If your app uses several Solana Kit packages together, you can also depend on the umbrella package instead:

dart pub add solana_kit

Inside this monorepo, Dart workspace resolution uses the local package automatically.

Documentation #

For architecture notes, getting-started guides, and cross-package examples, start with the workspace docs site and then drill down into the package README and API reference.

Usage #

Fetch a price from Hermes #

import 'package:solana_kit_pyth/solana_kit_pyth.dart';

Future<void> main() async {
  final hermes = HermesClient(HermesConfig());

  // Discover feeds by symbol.
  final feeds = await hermes.getPriceFeeds(query: 'bitcoin');
  print(feeds.single.id); // hex price feed id

  // Latest update, with the parsed price included.
  final update = await hermes.getLatestPriceUpdates(
    [feeds.single.id],
    encoding: HermesEncoding.hex,
    parsed: true,
  );
  final feed = update.parsed!.single;
  final price = feed.price;
  print('${price.price} ± ${price.conf} * 10^${price.expo}');
}

Decode a binary update and post it on chain

The binary payload of a Hermes update is an accumulator update blob containing one Wormhole VAA plus merkle-committed price messages. Parse it, trim guardian signatures so the update fits in a single transaction, and submit it with the Pyth Solana Receiver's postUpdateAtomic instruction:

import 'package:solana_kit_codecs_strings/solana_kit_codecs_strings.dart';
import 'package:solana_kit_addresses/solana_kit_addresses.dart';
import 'package:solana_kit_pyth/solana_kit_pyth.dart';

Future<void> publish(
  HermesPriceUpdate update,
  Address payer,
  Address priceUpdateAccount,
) async {
  // Decode binary.data[0] (hex or base64 per the response encoding).
  // In Solana Kit, "encoders" turn encoded strings into raw bytes.
  final bytes = switch (update.binaryEncoding) {
    HermesEncoding.hex => getBase16Encoder().encode(update.binaryData.single),
    HermesEncoding.base64 => getBase64Encoder().encode(update.binaryData.single),
  };

  final accumulator = parseAccumulatorUpdateData(bytes);
  for (final message in accumulator.updates) {
    final priceFeed = parsePythPriceFeedMessage(message.message);
    print('feed 0x${priceFeed.feedIdHex}: '
        '${priceFeed.price} ± ${priceFeed.confidence} * 10^${priceFeed.exponent}');

    final instruction = await getPostUpdateAtomicInstruction(
      payer: payer,
      vaa: trimVaaSignatures(accumulator.vaa), // 5 signatures by default
      update: message,
      priceUpdateAccount: priceUpdateAccount,
    );
    // Add the instruction to a transaction message and send it.
    print('post $instruction');
  }
}

The receiver program also supports post_update, which consumes an encoded-VAA account that was already verified by the Wormhole program. On-chain price accounts decode with decodePythPriceAccount (classic layout) and decodePriceUpdateV2Account (push oracle PriceUpdateV2).

API overview #

Export Purpose
HermesClient / HermesConfig Hermes v2 REST client (price_feeds, latest/timestamped updates)
HermesEncoding, HermesAssetType Query enums: hex/base64 payloads, feed asset types
HermesPriceUpdate, HermesPriceFeed, HermesPriceFeedMetadata Typed models for Hermes responses (BigInt price and confidence)
parseAccumulatorUpdateData Split a Hermes binary blob into its VAA and merkle price updates
parseWormholeVaa, trimVaaSignatures Wormhole VAA (v1) envelope parsing and signature trimming
parsePythWormholeMessage, parsePythPriceFeedMessage Pythnet payload and wire-format price feed message parsing
decodePythPriceAccount Classic Pyth on-chain price account decoder
decodePriceUpdateV2Account Push oracle PriceUpdateV2 price feed account decoder
getPostUpdateAtomicInstruction, getPostUpdateInstruction Pyth Solana Receiver instruction builders
getPythConfigAddress, getPythTreasuryAddress, getGuardianSetAddress Receiver and Wormhole PDA helpers

Scope #

  • Hermes v2 REST endpoints only; the SSE streaming endpoint (/v2/updates/price/stream) is out of scope for v1.
  • Update submission targets the deployed Pyth Solana Receiver (post_update_atomic / post_update). The historical update_price_feeds-style receiver interface is not part of the current program and is not implemented.
  • The classic price account decoder targets layout version 2 (the @pythnetwork/client v2 layout).
  • No cryptographic signature verification happens client-side; guardian signatures are verified by the receiver program on chain.

Upstream reference #

Audited against:

HermesPrice.asDouble is a lossy floating-point conversion. Extreme exponents overflow to infinity or underflow to zero in bounded time; use the integer price and expo fields when exact decimal arithmetic is required.

On-chain confidence and slot fields are decoded as unsigned BigInt values across the full 64-bit range.