Using Pyth oracle data

Learn how to integrate real-time market price data from Pyth Network into your Clarity smart contracts.


What you'll learn

In this guide, you'll learn how to read real-time price data from Pyth Network's decentralized oracle in your Clarity contracts.

Understanding Pyth's pull-based oracle model
Reading and processing price feed data
Working with fixed-point price representation
Building USD-denominated smart contracts

Prerequisites

To follow this guide, you'll need:

Basic understanding of Clarity smart contracts
A Clarinet project set up for local development

Understanding Pyth oracles

Pyth Network uses a pull-based oracle model, different from traditional push-based oracles:

  • Push model: Oracle continuously sends price updates on-chain
  • Pull model: Users fetch and submit price updates when needed

This approach is more gas-efficient and allows for higher-frequency price updates.

Supported price feeds

The Pyth integration on Stacks currently supports these price feeds:

AssetPrice Feed IDExplorer Link
BTC/USD0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43View
STX/USD0xec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c17View
ETH/USD0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0aceView
USDC/USD0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94aView

Quickstart

Set up contract references

The Pyth oracle on Stacks consists of several contracts. Add these references to your contract:

;; Pyth oracle contract addresses (mainnet)
(define-constant PYTH-ORACLE-CONTRACT 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3)
(define-constant PYTH-STORAGE-CONTRACT 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3)
(define-constant PYTH-DECODER-CONTRACT 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-pnau-decoder-v2)
(define-constant WORMHOLE-CORE-CONTRACT 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.wormhole-core-v3)
;; Price feed IDs
(define-constant BTC-USD-FEED-ID 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43)

Update and read price data

Create a function that updates the price feed and reads the current price:

(define-public (get-btc-price (price-feed-bytes (buff 8192)))
(let (
;; Update the price feed with fresh data
(update-result (try! (contract-call? PYTH-ORACLE-CONTRACT
verify-and-update-price-feeds price-feed-bytes {
pyth-storage-contract: PYTH-STORAGE-CONTRACT,
pyth-decoder-contract: PYTH-DECODER-CONTRACT,
wormhole-core-contract: WORMHOLE-CORE-CONTRACT
})))
;; Read the updated price
(price-data (try! (contract-call? PYTH-ORACLE-CONTRACT
get-price
BTC-USD-FEED-ID
PYTH-STORAGE-CONTRACT)))
)
(ok price-data)
))

The update operation requires a small fee (typically 1 uSTX) to prevent spam.

Process price data

Price data uses fixed-point representation. Convert it to a usable format:

(define-read-only (process-price-data (price-data {
price-identifier: (buff 32),
price: int,
conf: uint,
expo: int,
ema-price: int,
ema-conf: uint,
publish-time: uint,
prev-publish-time: uint
}))
(let (
;; Calculate the denominator based on the exponent
;; For expo = -8, denominator = 10^8 = 100,000,000
(exponent (get expo price-data))
(denominator (pow u10 (to-uint (* exponent -1))))
;; Convert to standard decimal representation
;; If price = 10603557773590 and expo = -8
;; Actual price = 10603557773590 / 100,000,000 = 106,035.58
(adjusted-price (/ (to-uint (get price price-data)) denominator))
)
adjusted-price
))

Common patterns

USD-denominated NFT minting

Create an NFT that costs exactly $100 worth of sBTC:

contracts/benjamin-club.clar
;; Benjamin Club - $100 NFT minting contract
(define-constant BENJAMIN-COST u100) ;; $100 USD
(define-constant ERR-INSUFFICIENT-FUNDS (err u100))
(define-constant ERR-PRICE-UPDATE-FAILED (err u101))
(define-non-fungible-token benjamin-nft uint)
(define-data-var last-token-id uint u0)
(define-public (mint-for-hundred-dollars (price-feed-bytes (buff 8192)))
(let (
;; Update and get BTC price
(price-data (try! (get-btc-price price-feed-bytes)))
(btc-price-cents (process-price-data price-data))
;; Calculate required sBTC amount
;; $100 = 10,000 cents
(required-sbtc (/ (* u10000 u100000000) btc-price-cents))
;; Get user's sBTC balance
(user-balance (unwrap!
(contract-call? .sbtc-token get-balance tx-sender)
ERR-INSUFFICIENT-FUNDS))
)
;; Verify user has enough sBTC
(asserts! (>= user-balance required-sbtc) ERR-INSUFFICIENT-FUNDS)
;; Transfer sBTC and mint NFT
(try! (contract-call? .sbtc-token transfer
required-sbtc tx-sender (as-contract tx-sender) none))
;; Mint the NFT
(let ((token-id (+ (var-get last-token-id) u1)))
(try! (nft-mint? benjamin-nft token-id tx-sender))
(var-set last-token-id token-id)
(ok token-id))))

Price staleness protection

Ensure price data is recent enough for your use case:

(define-constant MAX-PRICE-AGE u300) ;; 5 minutes in seconds
(define-read-only (is-price-fresh (price-data (tuple)))
(let (
(current-time (unwrap-panic (get-block-info? time block-height)))
(publish-time (get publish-time price-data))
)
(<= (- current-time publish-time) MAX-PRICE-AGE))
)

Multi-asset price aggregation

Get multiple price feeds in one transaction:

(define-public (get-multiple-prices
(btc-vaa (buff 8192))
(eth-vaa (buff 8192))
(stx-vaa (buff 8192)))
(let (
;; Update all price feeds
(updates (try! (contract-call? PYTH-ORACLE-CONTRACT
verify-and-update-price-feeds
(concat btc-vaa (concat eth-vaa stx-vaa))
{ pyth-storage-contract: PYTH-STORAGE-CONTRACT,
pyth-decoder-contract: PYTH-DECODER-CONTRACT,
wormhole-core-contract: WORMHOLE-CORE-CONTRACT })))
;; Read all prices
(btc-price (try! (get-price-by-id BTC-USD-FEED-ID)))
(eth-price (try! (get-price-by-id ETH-USD-FEED-ID)))
(stx-price (try! (get-price-by-id STX-USD-FEED-ID)))
)
(ok { btc: btc-price, eth: eth-price, stx: stx-price }))
)

Testnet deployment

For testnet development, use these contract addresses:

;; Testnet addresses
(define-constant PYTH-ORACLE-CONTRACT-TESTNET 'ST3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3)
(define-constant PYTH-STORAGE-CONTRACT-TESTNET 'ST3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3)

Next steps