Skip to content

Commit 1ba5a39

Browse files
committed
added Stacks guide
1 parent 0808ed9 commit 1ba5a39

File tree

2 files changed

+173
-0
lines changed

2 files changed

+173
-0
lines changed

pages/price-feeds/use-real-time-data/_meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"solana": "in Solana and SVM Programs",
44
"starknet": "in Starknet Contracts",
55
"fuel": "in Fuel Contracts",
6+
"stacks": "in Stacks Contracts",
67
"aptos": "in Aptos Contracts",
78
"sui": "in Sui Contracts",
89
"iota": "in IOTA Contracts",
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
---
2+
description: Consume Pyth Network prices in Stacks applications
3+
---
4+
5+
import { Callout } from "nextra/components";
6+
7+
# How to Use Real-Time Data in Stacks applications
8+
9+
[Stacks](https://www.stacks.co/) is a layer 2 network on Bitcoin. It brings programmability and decentralized applications (dApps) to Bitcoin without modifying Bitcoin itself. Stacks lets developers build apps, smart contracts, NFTs, and DeFi on top of Bitcoin — while using Bitcoin as the settlement and security layer.
10+
11+
This guide explains how to use real-time Pyth data in [Clarity](https://clarity-lang.org/) smart contracts on Stacks.
12+
13+
## Write Contract Code
14+
15+
The Pyth protocol integration for Stacks 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.
16+
Unlike other smart contract languages, there is no importing modules into Clarity smart contracts for certain security reasons. So the Pyth integration on Stacks is implemented as its own set of Clarity contracts where developers will invoke a `contract-call?` to the main Pyth Clarity [contract](https://explorer.hiro.so/txid/SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3?chain=mainnet).
17+
18+
<Callout type="info" emoji="ℹ️">
19+
Currently, the Pyth protocol integration is currently maintained by Trust Machines. It currently supports real-time price feeds for BTC, STX, ETH, and USDC. To request more price feeds, open an issue in Trust Machine's Pyth maintained repo [here](https://github.com/Trust-Machines/stacks-pyth-bridge).
20+
</Callout>
21+
22+
### Read BTC prices from a Clarity smart contract
23+
24+
For our example, we will imagine a contract that mints an NFT in exchange for $100 of sBTC. In order to determine the USD value of a user's sBTC amount, we'll need to use Pyth. Since market pricing for sBTC isn't supported currently, we'll use the price data from the BTC/USD price feed.
25+
26+
<Callout type="info" emoji="ℹ️">
27+
The maintained Pyth integration contract for Stacks is called [`.pyth-oracle-v3`](https://explorer.hiro.so/txid/0x745a0e07ef9487ebb2190da515bda60f1531299553420750b33b3ba4a97729e1?chain=mainnet). This contract serves as the main entry point for updating and getting price feed data.
28+
</Callout>
29+
30+
You'll notice in the Clarity snippet below we open up `let` bindings of our function to:
31+
32+
1. Verify & 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.
33+
2. Getting a fresh instance of the updated price data for BTC.
34+
35+
```clarity
36+
;; --snip--
37+
(define-public (join-the-benjamin-club (price-feed-bytes (buff 8192)))
38+
(let (
39+
;; To verify & update price feeds is to participate in the pull price model of Pyth's decentralization.
40+
;; A VAA signed message is pulled from Wormhole via the Hermes API. This VAA signed message is what
41+
;; gets passed into this function to verify & update the price data of a particular price feed.
42+
(update-status (try! (contract-call? 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3
43+
verify-and-update-price-feeds price-feed-bytes {
44+
pyth-storage-contract: 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3,
45+
pyth-decoder-contract: 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-pnau-decoder-v2,
46+
wormhole-core-contract: 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.wormhole-core-v3,
47+
})))
48+
;; The price data returned will be fresh from the VAA signed message data we passed in above.
49+
(price-data (try! (contract-call? 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3
50+
get-price
51+
;; The official BTC price feed id.
52+
0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43
53+
'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3
54+
)))
55+
;; --snip--
56+
```
57+
58+
After updating & 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.
59+
The price feed data returned from invoking the `get-price` function of the `.pyth-oracle-v3` contract looks like the below:
60+
61+
```bash
62+
{
63+
price-identifier: 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43,
64+
price: 10603557773590,
65+
conf: u3776653890,
66+
ema-price: 10602069900000,
67+
ema-conf: u4062895700,
68+
expo: -8,
69+
publish-time: u1750425711,
70+
prev-publish-time: u1750425710
71+
}
72+
```
73+
74+
With the price feed data, we can adjust the price based on the `expo` property. Price feeds represent numbers in a fixed-point format. So in the above returned price feed data, the price of `10603557773590` and given `expo` of `-8` should be formatted as `106035`. The same exponent is used for both the price and confidence interval.
75+
76+
We can then determine the USD amount of sBTC the user owns and decide if it is enough to mint a `benjamin-nft` for $100 worth of sBTC. Benjamin is in reference to Benjamin Franklin being the face of a one hundred dollar bill, get it?
77+
78+
```clarity
79+
;; --snip --
80+
;; Price feeds represent numbers in a fixed-point format. The expo property tells us
81+
;; at what certain position is the decimal point implicity fixed.
82+
(price-denomination (pow 10 (* (get expo price-data) -1)))
83+
;; We'll adjust the price to its normal decimal representation.
84+
(adjusted-price (to-uint (/ (get price price-data) price-denomination)))
85+
;; Get the user's current sBTC balance.
86+
(user-sbtc-balance (unwrap!
87+
(contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
88+
get-balance-available tx-sender
89+
)
90+
ERR_READING_SBTC_BALANCE
91+
))
92+
)
93+
;; Determine if the user has at least $100 worth of sBTC to join the Benjamin Club.
94+
(if (> (/ (* user-sbtc-balance adjusted-price) (to-uint price-denomination))
95+
COST-OF-BENJAMIN-NFT
96+
)
97+
(let ((hundred-dollars-in-sbtc (/ (* COST-OF-BENJAMIN-NFT (to-uint price-denomination)) adjusted-price)))
98+
(try! (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
99+
transfer hundred-dollars-in-sbtc tx-sender (as-contract tx-sender)
100+
none
101+
))
102+
(contract-call? .nft-contract mint tx-sender)
103+
)
104+
ERR_NOT_ENOUGH_SBTC
105+
)
106+
)
107+
)
108+
```
109+
110+
<Callout type="warning" emoji="⚠️">
111+
The `verify-and-update-price-feeds` of the `.pyth-oracle-v3` contract applies a fee of 1 uSTX, or 1 micro-stx, which is 0.000001 STX.
112+
</Callout>
113+
114+
Alternatively, developers can just invoke the `read-price-feed` public function of the `pyth-oracle-v3.clar` contract. This will simply return the price feed from the last updated feed. But it's always encouraged to determine if a staleness check is viable for your application.
115+
116+
## Write Front-End Code
117+
118+
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.
119+
120+
```javascript copy
121+
import { PriceServiceConnection } from "@pythnetwork/price-service-client"
122+
import { Buffer } from "buffer"
123+
124+
// --snip--
125+
async function handleFetchLatestVaa() {
126+
const connection = new PriceServiceConnection("https://hermes.pyth.network", {
127+
priceFeedRequestConfig: {
128+
binary: true
129+
}
130+
})
131+
132+
const btcPriceId = ["0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"]
133+
134+
const response = await connection.getLatestVaas(btcPriceId)
135+
let messageBuffer = Buffer.from(response[0], "base64")
136+
const hexString = messageBuffer.toString("hex")
137+
let latestVaaHex = `0x${hexString}`
138+
139+
return latestVaaHex
140+
}
141+
// --snip--
142+
```
143+
144+
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.
145+
146+
Using Stacks Connect of the [stacks.js](https://github.com/hirosystems/stacks.js) monorepo, we'll open up a stx_callContract request and invoke our public function while passing in the latestVaaHex as the function argument.
147+
148+
```javascript copy
149+
let latestVaaHex = await handleFetchLatestVaa()
150+
151+
let postCond1 = Pc.principal("SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R").willSendLte(1).ustx()
152+
153+
const response = await request("stx_callContract", {
154+
contract: `SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R.benjamin-club`,
155+
functionName: "join-the-benjamin-club",
156+
functionArgs: [Cl.bufferFromHex(latestVaaHex)],
157+
network: "mainnet",
158+
postConditions: [postCond1],
159+
postConditionMode: "deny"
160+
})
161+
```
162+
163+
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 `verify-and-update-price-feeds` of the `.pyth-oracle-v3` 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 `decode-price-feeds` function with the `latestVaaHex` 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.
164+
165+
## Additional Resources
166+
167+
You may find these additional resources helpful for developing your Stacks application with Pyth.
168+
169+
- **[Hiro Docs](https://docs.hiro.so/resources/guides/using-pyth-price-feeds):** Check out the dedicated guide for using Pyth in Stacks applications in Hiro's documentation. In this guide you'll see a visual architecture overview of using Pyth in Stacks, how you can test your implementation, how to fetch VAAs on the front-end, learn best practices, and more.
170+
- **[Hiro How-To Tutorial](https://youtu.be/eybqQVRh_hw?si=KNfUp3RS3CnaST91):** Watch the dedicated video tutorial on using Pyth in Stacks and learn how a major Stacks DeFi app, Granite, is using Pyth.
171+
- **[Trust Machine's Pyth Github](https://github.com/Trust-Machines/stacks-pyth-bridge):** Check out the open-source repo for the Pyth integration Clarity contracts.
172+
- **[pyth-oracle-v3.clar](https://explorer.hiro.so/txid/SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3?chain=mainnet):** The latest Pyth integration contract on Stacks' mainnet.

0 commit comments

Comments
 (0)