Need a crash course on Bitcoin layers?
→ READ OUR FREE GUIDE
Need a crash course on Bitcoin layers?
→ READ OUR FREE GUIDE
Need a crash course on Bitcoin layers?
→ READ OUR FREE GUIDE
Need a crash course on Bitcoin layers?
→ READ OUR FREE GUIDE
Need a crash course on Bitcoin layers?
→ READ OUR FREE GUIDE

Using Real-Time Price Data In Clarity

For DeFi smart contracts, it’s crucial to leverage trusted sources for asset pricing, which has profound implications for investor returns and trading strategies. With the Pyth integration on Stacks being fully supported, you can now leverage real-time market price data in your Clarity smart contract to give users confidence on how asset prices are being derived.

Type
Tutorial
Topic(s)
Clarity
Published
July 3, 2025
Author(s)
Developer Advocate
Using Real-Time Price Data In Clarity
Contents

Over a year ago, we announced the full integration of Pyth with Stacks. Earlier this year, Trust Machines took over the maintenance of this integration as it became clear that projects, specifically Granite, were in high need of a reputable decentralized oracle solution on Stacks.

But first, let’s summarize the purpose of oracles.

How Oracles Power Smart Contracts in Web3

In DeFi, oracles are essentially tools that provide off-chain data for smart contracts and apps. The most common type of off-chain data needed in DeFi are accurate price feeds, which enable use cases such as decentralized exchanges (DEX) and borrowing/lending applications.

That’s what oracles do, but the challenge with oracles is twofold: how can they ensure the information is accurate, and how can they provide that information in a secure and decentralized way? 

To solve that challenge, oracles often aggregate real-time price data from many sources, and Pyth takes a unique approach to that. Unlike other oracles, which push prices to users, the Pyth Network uses a low-latency pull oracle design. This means end users are also the participating parties that actually “pull” the necessary attestation data to then “push” on-chain.

In this design, some of the world’s largest exchanges, market makers, and financial services providers publish their proprietary price data on-chain for aggregation and distribution via Pyth to other smart contract applications.

In this post, we'll explain how to use real-time Pyth price data in a Clarity smart contract.

Getting BTC Price Data within Your Own Contract

The contract logic, that we’ll use for this example, will mint an NFT in exchange for $100 of sBTC. Our Clarity contract will read the price of BTC/USD from the Pyth integration contract to calculate the amount of sBTC required to mint the NFT.

Each Pyth Network price feed is referred to via a unique ID. The full list of price feeds is listed on the pyth.network website. To use a price feed on-chain, look up its ID, then store the feed ID in your program for price feed queries. Each price feed has its own unique id:

Available Pyth price feeds for Stacks:

To request more supported price feeds, open an issue in the Pyth maintained repo here.

Pyth Network uses a pull price update model that is slightly different from other oracles you may be more familiar with. Most oracles today use a push model, where the oracle runs an off-chain process that continuously sends transactions to update an on-chain price. In contrast, Pyth Network does not operate an off-chain process that pushes prices on-chain. Instead, it delegates this work to Pyth Network users.

The maintained Pyth integration contract for Stacks is called .pyth-oracle-v3. This contract serves as the main entry point for updating and getting price feed data. The Pyth protocol integration is available as a Beta on both testnet and mainnet networks, to help developers test, give feedback, and ensure the reliability and stability of the integration.

You'll notice in the Clarity snippet below we open up <code-rich-text>let<code-rich-text> bindings of our function to:

1. Verify and update the BTC price feed with its latest VAA message (more on how to pull the VAA later in this guide). This is a means of participating in the pull price update model.

2. Getting a fresh instance of the updated price data for BTC.


;; --snip--

(define-public (join-the-benjamin-club (price-feed-bytes (buff 8192)))
  (let (
      ;; To verify & update price feeds is to participate in the pull price model of Pyth's decentralization.
      ;; A VAA signed message is pulled from Wormhole via the Hermes API. This VAA signed message is what
      ;; gets passed into this function to verify & update the price data of a particular price feed.
      (update-status (try! (contract-call? 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3
        verify-and-update-price-feeds price-feed-bytes {
        pyth-storage-contract: 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3,
        pyth-decoder-contract: 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-pnau-decoder-v2,
        wormhole-core-contract: 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.wormhole-core-v3,
      })))
      ;; The price data returned will be fresh from the VAA signed message data we passed in above.
      (price-data (try! (contract-call? 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3
        get-price
        ;; The official BTC price feed id.
        0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43
        'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3
      )))

    ;; --snip--

After updating and verifying the price feed in question, and then getting the updated price feed data, we'll need to handle the price feed data and its properties.

The price feed data returned from invoking the <code-rich-text>`get-price`<code-rich-text> function of the <code-rich-text>`.pyth-oracle-v3`<code-rich-text> contract looks like the below:


{
  price-identifier: 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43,
  price: 10603557773590,
  conf: u3776653890,
  ema-price: 10602069900000,
  ema-conf: u4062895700,
  expo: -8,
  publish-time: u1750425711,
  prev-publish-time: u1750425710
}

With the latest price feed data returned, we can adjust the price based on the <code-rich-text>expo<code-rich-text> property. Price feeds represent numbers in a fixed-point format. Fixed-point numeric representation is a way of storing numbers with fractional parts using integers, where the decimal point is implicitly fixed at a certain position. So in the above returned price feed data, the returned price of <code-rich-text>10603557773590<code-rich-text> and given expo of <code-rich-text>-8<code-rich-text> should be formatted as <code-rich-text>106035<code-rich-text>. The same exponent is used for both the price and confidence interval.

We can then determine the USD amount of sBTC the user owns and decide if it is enough to mint a <code-rich-text>benjamin-nft<code-rich-text> for $100 worth of sBTC. Benjamin is in reference to Benjamin Franklin being the face of a one hundred dollar bill, get it?


;; --snip --

      ;; Price feeds represent numbers in a fixed-point format. The expo property tells us
      ;; at what certain position is the decimal point implicity fixed.
      (price-denomination (pow 10 (* (get expo price-data) -1)))
      ;; We'll adjust the price to its normal decimal representation.
      (adjusted-price (to-uint (/ (get price price-data) price-denomination)))
      ;; Get the user's current sBTC balance.
      (user-sbtc-balance (unwrap!
        (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
          get-balance-available tx-sender
        )
        ERR_READING_SBTC_BALANCE
      ))
    )
    ;; Determine if the user has at least $100 worth of sBTC to join the Benjamin Club.
    (if (> (/ (* user-sbtc-balance adjusted-price) (to-uint price-denomination))
        COST-OF-BENJAMIN-NFT
      )
      (let ((hundred-dollars-in-sbtc (/ (* COST-OF-BENJAMIN-NFT (to-uint price-denomination)) adjusted-price)))
        (try! (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
          transfer hundred-dollars-in-sbtc tx-sender (as-contract tx-sender)
          none
        ))
        (contract-call? .nft-contract mint tx-sender)
      )
      ERR_NOT_ENOUGH_SBTC
    )
  )
)

Pull VAA messages from Wormhole via the Hermes API

Wormhole is a decentralized attestation engine that leverages its network of guardians to trustlessly bridge information between the chains it supports. Wormhole has a simple, elegant, and pragmatic design that has enabled it to be the first real solution to ship to market and has received wide recognition and support from its member chains.

Hermes is a web service that listens to the Wormhole Network for signed and attested price updates, and serves them via a convenient web API. It provides Pyth's latest price update data format that is more cost-effective to verify and use on-chain. Hermes allows users to easily query for recent price updates via a REST API, or subscribe to a websocket for streaming updates. The Pyth Network also provides a Javascript SDK to connect to an instance of Hermes for fetching price updates.

In your front-end application code, you can install and use the methods brought by Pyth Network's Javascript SDK to fetch the latest price update, known as a VAA (Verified Action Approvals) message.


import { PriceServiceConnection } from "@pythnetwork/price-service-client"
import { Buffer } from "buffer"

// --snip--
async function handleFetchLatestVaa() {
  const connection = new PriceServiceConnection("https://hermes.pyth.network", {
    priceFeedRequestConfig: {
      binary: true
    }
  })

  const btcPriceId = ["0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"]

  const response = await connection.getLatestVaas(btcPriceId)
  let messageBuffer = Buffer.from(response[0], "base64")
  const hexString = messageBuffer.toString("hex")
  let latestVaaHex = `0x${hexString}`

  return latestVaaHex
}
// --snip--

For more info on what components make up a VAA (Verified Action Approvals), check out the Wormhole docs here.

“We will submit that price attestation (VAA) to the contract so it writes it on-chain. And then we will pull the latest on-chain price. And since those happen in sequence, the latest on-chain price is always updated with that previous transaction.”
- Blaize, Founding Contributor at Granite

The binary data returned from the Pyth SDK will be in base64 format which we'll need to convert to hexadecimal in order for the Pyth contract to properly ingest it. We'll then take this hexadecimal VAA message and pass it into our Clarity function as an argument.

Pass in VAA Message to Contract Function

Using Stacks connect, we'll open up a <code-rich-text>stx_callContract<code-rich-text> request and invoke our public function while passing in the <code-rich-text>latestVaaHex<code-rich-text> as the function argument.


let latestVaaHex = await handleFetchLatestVaa()

let postCond1 = Pc.principal("SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R").willSendLte(1).ustx()

const response = await request("stx_callContract", {
  contract: `SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R.benjamin-club`,
  functionName: "join-the-benjamin-club",
  functionArgs: [Cl.bufferFromHex(latestVaaHex)],
  network: "mainnet",
  postConditions: [postCond1],
  postConditionMode: "deny"
})

If you noticed, we set a post-condition statement of our user transferring less than or equal to 1 uSTX, which is 0.000001 STX. This is because the <code-rich-text>verify-and-update-price-feeds<code-rich-text> of the <code-rich-text>.pyth-oracle-v3<code-rich-text> contract applies a fee for this. Setting a separate post-condition statement on the actual sbtc token transfer in our example will also be needed. Beforehand, you could invoke the <code-rich-text>decode-price-feeds<code-rich-text> function with the <code-rich-text>latestVaaHex<code-rich-text> to simply have the contained price data decoded and returned. From there you could pre-determine the estimated amount of sbtc tokens to be transferred and set in a separate post-condition.

Check out the dedicated video tutorial on this, featuring Blaize Wallace, who shares how Granite uses Pyth:

Conclusion

DeFi applications on Stacks benefit when they have more options for real-time price feeds. Orderbooks, both spot and perpetual, depend on price feeds for setting stop limits and liquidation triggers. Borrowing and lending applications will rely on an oracle solution for accurately valuing collateral locked in pools.

By following this guide, you have successfully learned how to get real-time market prices of different crypto assets on-chain via Pyth.

Product updates & dev resources straight to your inbox
Your Email is in an invalid format
Checkbox is required.
Thanks for
subscribing.
Oops! Something went wrong while submitting the form.
Copy link
Mailbox
Hiro news & product updates straight to your inbox
Only relevant communications. We promise we won’t spam.

Related stories