Query Price Data on Berachain using Pyth Price Feeds

Beary Cucumber
Beary Cucumber
6 min read
Query Price Data on Berachain using Pyth Price Feeds

Building On-Demand Price Feeds with Pyth 🔼

Oracles help connect blockchains to the real world. The existing oracle paradigm revolves around oracle operators continuously pushing price updates on-chain (and paying associated fees). This can scale poorly with an increasing number of price feeds and supported chains.

Pyth Network aims to eliminate this tradeoff using on-demand price updates, where users pull on-chain prices only when needed — learn more here.

This article guides you through the process of creating a smart contract which leverages Pyth oracles to consume an ETH/USD price feed.

Overview of Interactions

📋 Requirements

Before we move on, make sure you have the following installed and setup on your computer.

Foundry

This guide requires Foundry to be installed. In a terminal window, run:

curl -L https://foundry.paradigm.xyz | bash; 
 
foundryup;  
# foundryup installs the 'forge' and 'cast' binaries, used later

For more installation instructions, see Foundry’s installation guide. For more details using Foundry on Berachain, see this guide.

Creating Our Pyth Oracle Project

First, we’re going to set up your development environment using Foundry.

Start by creating a new project folder and initializing Foundry:

forge init pyth-oracle --no-git --no-commit; 
 
cd pyth-oracle; 
 
# We observe the following basic layout 
# . 
# ├── foundry.toml 
# ├── script 
# │   └── Counter.s.sol 
# ├── src 
# │   └── Counter.sol 
# └── test 
#     └── Counter.t.sol

Installing Pyth Dependencies

We will be leveraging Pyth’s contracts, so you must first install Pyth’s contract interfaces as a node dependency:

# FROM: ./pyth-oracle 
 
pnpm init; 
pnpm add @pythnetwork/pyth-sdk-solidity;

Forge can remap dependencies to make imports more readable. So let’s remap our Pyth import:

# FROM: ./pyth-oracle 
 
echo "remappings = ['@pythnetwork/pyth-sdk-solidity/=node_modules/@pythnetwork/pyth-sdk-solidity']" >> ./foundry.toml

Writing the Oracle Consumer Contract

Now we’re ready to jump into the exciting part of consuming Pyth price feeds in a Solidity smart contract.

Create a new file ./src/ConsumerContract.sol and paste in the following:

// SPDX-License-Identifier: MIT 
pragma solidity ^0.8.0; 
 
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol"; 
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol"; 
 
contract ConsumerContract { 
    IPyth pyth; 
 
    /** 
     * Network: Berachain Artio (testnet) 
     * Address: 0x8D254a21b3C86D32F7179855531CE99164721933 
     */ 
    constructor() { 
        pyth = IPyth(0x8D254a21b3C86D32F7179855531CE99164721933); 
    } 
 
    function updatePrice(bytes[] calldata priceUpdateData) public payable { 
        uint fee = pyth.getUpdateFee(priceUpdateData); 
        pyth.updatePriceFeeds{value: fee}(priceUpdateData); 
    } 
     
    function getPrice() public view returns (PythStructs.Price memory) { 
        // ETH/USD priceID 
        bytes32 priceID = 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace; 
        return pyth.getPrice(priceID); 
    } 
 
    function getLatestPrice( 
        bytes[] calldata priceUpdateData 
    ) public payable returns (PythStructs.Price memory) { 
        updatePrice(priceUpdateData); 
        return getPrice(); 
    } 
}

Now let’s break this all down:

We first import IPyth and PythStructs, which provides interfaces for interacting with deployed Pyth oracle contracts. More information on working with the Pyth SDK.

In the constructor, we connect a pyth instance to the Pyth oracle at 0x8D254a21b3C86D32F7179855531CE99164721933, deployed on the Berachain Artio Testnet.

The updatePrice function takes an input of priceUpdateData which is a signed price update, which is streamed from Pyth off-chain (discussed below).

Following a price update, getPrice() may be called, which returns a Price object corresponding to the requested priceId. Here, we are using a priceId of 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace, which corresponds to Pyth’s ETH/USD price feed. See here for the full list of available feeds.

getLatestPrice simply aggregates the above two calls.

(You may deletesrc/Counter.sol , test/Counter.t.sol and script/Counter.s.sol which were generated with the project)

Deploying the smart contract

You’re almost ready to deploy your contract to Berachain. First, you will need to set up a wallet for deploying.

Setting up for Deployment

Run the following to import your wallet’s private key into Foundry’s keystore (with the deployeralias):

cast wallet import deployer --interactive; 
 
# [Example Output] 
# Enter private key: 
# Enter password:  
# `deployer` keystore was saved successfully. Address: <YOUR_WALLET_ADDRESS>

Confirm that your wallet was imported by running:

cast wallet list; 
 
# [Example Output] 
# deployer (Local)

Let’s load the Berachain RPC into our terminal session:

export BERACHAIN_ARTIO_RPC="https://rpc.ankr.com/berachain_testnet"

Deploying to Berachain Testnet

First, compile the smart contract:

# FROM: ./pyth-oracle 
 
forge build;

You will notice a number of build outputs appears in the ./out directory.

We will be leveraging the forge create command for deploying our new contract on Berachain Testnet (read more about the command here):

# FROM: ./pyth-oracle 
 
forge create ./src/ConsumerContract.sol:ConsumerContract --rpc-url $BERACHAIN_ARTIO_RPC --account deployer 
 
# [Expected Similar Output]: 
# Enter keystore password: 
# Deployer: 0x529CA3A690E1bB4e9F04d132bd99D4398f626A44 
# Deployed to: 0x9106b2041C896224Af2142ea9C7349aa283Df7C6 
# Transaction hash: 0xc8efdd3132080491b42c469fb2219bc6f0432981a46cdd3f6ae73b9e834ff4e4

Take note of the deployed contract address, you will need it in the following section.

You will be prompted for the keystore password you set earlier. You are required to have $BERA to pay for the deployment fees. Faucet funds are available at https://artio.faucet.berachain.com/.

Interacting with your Smart Contract

Finally, we’re going to fetch the ETH/USD price from within your deployed smart contract.

Recall that we had to obtainpriceUpdateData before calling updatePrice. We obtain this payload by using Hermes, a web service which listens to the Pyth Network for price updates and serves them via REST API.

Make a request to fetch the priceUpdateData with our ETH/USD priceId:

# FROM: ./pyth-oracle 
 
curl -s "https://hermes.pyth.network/v2/updates/price/latest?&ids[]=0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace" | jq -r ".binary.data[0]" > price_update.txt

The jq utility is used for writing the output to a price_update.txt file, which makes for easy retrieval later on.

Once we have this data, we can invoke updatePrice with the following command, passing in the payload we received from Pyth (replacing the placeholder with your deployed contract):

# FROM: ./pyth-oracle 
 
cast send <YOUR_DEPLOYED_CONTRACT> --rpc-url $BERACHAIN_ARTIO_RPC "updatePrice(bytes[])"  "[0x`cat price_update.txt`]" --account deployer --value 0.0001ether  
 
# [Expected Similar Output]: 
# blockHash               0xf00e38ea8197d088973dc51502b9fb62d089ac31b6fe01002e83a969e9c05f93 
# blockNumber             1037572 
# contractAddress 
# cumulativeGasUsed       208351 
# effectiveGasPrice       3000000017 
# from                    0x529CA3A690E1bB4e9F04d132bd99D4398f626A44 
# ...

Next, we query the price with getPrice():

cast call <YOUR_DEPLOYED_CONTRACT> --rpc-url $BERACHAIN_ARTIO_RPC "getPrice()" 
 
# [Expected Similar Output] 
# 0x0000000000000000000000000000000000000000000000000000005eb4cf6d2800000000000000000000000000000000000000000000000000000000130c6cd8fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000065ef9853

This provides us the ETH/USD price in terms of the Solidity Price struct (from ./pyth-oracle/lib/pyth-sdk-solidity/PythStructs.sol):

struct Price { 
        // Price 
        int64 price; 
        // Confidence interval around the price 
        uint64 conf; 
        // Price exponent 
        int32 expo; 
        // Unix timestamp describing when the price was published 
        uint publishTime; 
    }

To decode the hexadecimal output to a human readable format, you can use the abi-decode command below:

# FROM: ./pyth-oracle 
 
cast abi-decode "getPrice()(int64,uint64,int32,uint)"  <YOUR_GETPRICE_OUTPUT> 
 
# [Example Decoded Output of ETH/USD Feed] 
# 406760418600 [4.067e11] 
# 319581400 [3.195e8] 
# -8 
# 1710200915 [1.71e9]

Troubleshooting

  • If you don’t act quick enough, you may encounter the error 0x19abf40e representing a StalePrice error. This means that the price_update.txt was too old to be used by the contract. Re-run the sequence of commands in this subsection to retry.
  • The error code 0x025dbdd4 represents an InsufficientFee error. Try raising the value of $BERA in the updatePrice call e.g. 0.0005ether.

Recap

Congratulations, you’ve successfully deployed a smart contract which pulls price data from Pyth oracles on the Berachain Testnet 🎉


đŸ» Full Code Repository

If you want to see the final code and see other guides, check out Berachain Pyth Guide Code.

guides/apps/pyth-oracle at main · berachain/guides
A demonstration of different contracts, languages, and libraries that work with Berachain EVM. â€Š

đŸ› ïž Want To Build More?

Want to build more on Berachain and see more examples. Take a look at our Berachain GitHub Guides Repo for a wide variety of implementations that include NextJS, Hardhat, Viem, Foundry, and more.

GitHub - berachain/guides: A demonstration of different contracts, languages, and libraries that

A demonstration of different contracts, languages, and libraries that work with Berachain EVM. - berachain/guides

If you’re looking to dive deeper into the details, take a look at our Berachain Docs.

Berachain Docs
Berachain protocol core docs for learning how the blockchain works, developer guides, and understanding how to manage


Looking For Dev Support?

Make sure to join our Berachain Discord server and check out our developer channels to ask questions.

❀ Don’t forget to show some love for this article đŸ‘đŸŒ