How to Fetch Real-Time Prices on Kaia Using Pyth
Introduction
Pyth is a decentralized oracle network that takes a distinct approach in an ecosystem largely powered by push-based oracles. Instead of pushing data to your contract at fixed intervals, Pyth allows you to pull real-world data on demand. This model gives developers more control and helps avoid unnecessary onchain updates. With this integration, developers can fetch real-time data and use a pay-per-use model where fees apply only when an update is requested.
In this guide, you will learn how to use Pyth’s real-time price feeds to read the value of IDR, a fiat currency. Your Solidity smart contract will retrieve the USD/IDR price from Pyth using the pyth-sdk-solidity, and you will update and fetch the latest price using the hermes-client.
For a quick start, you can find the complete code for this tutorial on GitHub. This provides a ready-to-use reference and helps you set up the project and installation more quickly.
Prerequisites
Before you begin, ensure you have the following:
-
Required to install dependencies and run development tools.
-
A wallet funded with KAIA testnet tokens.
You will need KAIA to pay for deployment and transaction gas fees on the Kairos testnet. You can request free testnet KAIA from the Kaia Faucet.
Setting up development environment
In this section, you will set up your development environment, compile your oracle contract, and prepare it for deployment using Hardhat.
1. Create the Hardhat Project
Create a new directory for your project and initialize Hardhat:
mkdir pyth-kaia-hardhat-example && cd pyth-kaia-hardhat-examplenpm init -ynpx hardhat@next --init
Accept the default responses when prompted. For this guide we will use the Mocha and Ethers template.
Verify your installation by checking the Hardhat version:
npx hardhat --version
2. Set Encrypted Secrets
You will now store your RPC URL and private key using Hardhat’s encrypted keystore.
Run the following commands:
npx hardhat keystore set KAIROS_RPC_URLnpx hardhat keystore set PRIVATE_KEY
Make sure to enter your password and value for each variable to keep them encrypted.
3. Reference Secrets in Your Configuration File
Open hardhat.config.ts and update the networks section to reference the encrypted secrets. If you used different secret names, update the keys accordingly.
import { configVariable } from "hardhat/config";module.exports = { networks: { kairos: { url: configVariable("KAIROS_RPC_URL"), accounts: [configVariable("PRIVATE_KEY")], }, },};
Create a Contract and Fetch Prices from Pyth Oracles
In this section, you will install the Pyth Solidity SDK, create a PriceConsumer contract, and deploy it using Hardhat. The contract will read a Pyth price feed, which you will later update using price data fetched from Hermes.
Install the Pyth SDK
Pyth provides a Solidity SDK that lets you interact with onchain Pyth price feed contracts. The SDK exposes the IPyth interface and related structs.
Install the SDK with npm:
npm install --save-dev @pythnetwork/pyth-sdk-solidity
Create the PriceConsumer contract
Create a new file at contracts/PriceConsumer.sol and add the following code:
// SPDX-License-Identifier: MITpragma solidity ^0.8.20;import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";contract PriceConsumer { IPyth public pyth; constructor(address pythContract) { pyth = IPyth(pythContract); } function updatePrice(bytes[] calldata priceUpdateData) external payable { // Pay the Pyth fee for receiving price updates uint fee = pyth.getUpdateFee(priceUpdateData); require(msg.value >= fee, "Not enough fee sent"); // Update the Pyth price state pyth.updatePriceFeeds{value: fee}(priceUpdateData); // Can fetch the price and use it as well //PythStructs.Price memory currentBasePrice = pyth.getPriceNoOlderThan(priceFeedId, 60); } function getLatestPrice(bytes32 priceFeedId) public view returns (int64, int32) { // Read the current price from a price feed if it is less than 60 seconds old. // Each price feed (e.g., USD/IDR) is identified by a price feed ID. // The complete list of feed IDs is available at https://docs.pyth.network/price-feeds/price-feeds PythStructs.Price memory currentBasePrice = pyth.getPriceNoOlderThan(priceFeedId, 60); // uint256 basePrice = PythUtils.convertToUint( // currentBasePrice.price, // currentBasePrice.expo, // 18 // ); return (currentBasePrice.price, currentBasePrice.expo); }}
Walk through
The PriceConsumer contract:
- Imports the Pyth interfaces and structs from
@pythnetwork/pyth-sdk-solidity. - Stores:
- The Pyth contract instance (pyth).
- The price feed ID for USD / IDR (usdIdrPriceId).
- Exposes
updateAndGetUsdIdrPrice, which:- Computes the update fee using IPyth.getUpdateFee.
- Call IPyth.updatePriceFeeds with the required fee.
- Call IPyth.getPriceNoOlderThan to read a fresh USD / IDR price.
- Returns the raw price, exponent, and publish time.
Later, your offchain Hermes client will build the priceUpdate bytes array and pass it into this function when you need fresh prices.
Compile the contract
Run the following command to compile your contracts:
npx hardhat compile
Deploy the Contract
To deploy PriceConsumer contract, you will create an Ignition module and then run the deployment command.
Create the Ignition module
Create a new file at ignition/modules/PriceConsumer.ts:
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";const pythContractAddress = "0x2880ab155794e7179c9ee2e38200202908c17b43"; export default buildModule("PriceConsumerModule", (m) => { const priceConsumer = m.contract("PriceConsumer", [pythContractAddress]); return { priceConsumer };});
Deploy the contract
Deploy the PriceConsumer contract to the Kairos testnet using your Ignition module:
npx hardhat ignition deploy --network kairos ignition/modules/PriceConsumer.ts
When prompted, enter the keystore password you configured earlier for your encrypted secrets.
Once this completes, your PriceConsumer.sol contract is deployed on the Kairos testnet and ready to consume real-time USD / IDR prices from Pyth.
Interact from TypeScript
In this final step, you will interact with your deployed PriceConsumer contract using TypeScript. This script will fetch the latest USD/IDR price by requesting Pyth price update data through the Hermes client and sending it onchain.
Install the Dependencies
Install the required packages:
npm install --save-dev tsx @pythnetwork/hermes-client @dotenv
Set up .env
Create a .env file in the root directory of your project and add your private key:
PRIVATE_KEY="0xDEAD....." // REPLACE WITH YOUR PRIVATE KEY
Create the Interaction Script
Create a new file at scripts/interact.ts and add the following:
import { HermesClient } from "@pythnetwork/hermes-client";import { ethers } from "ethers";import 'dotenv/config'// 1. Setupconst hermes = new HermesClient("https://hermes.pyth.network");const provider = new ethers.JsonRpcProvider( "https://public-en-kairos.node.kaia.io");const PK = process.env.PRIVATE_KEY; const wallet = new ethers.Wallet(PK, provider);// 2. Your deployed contractconst priceConsumerAddress = "0x91e89aa32224dEd5dA483a83a4de45bF4bE57caA"; // REPLACE WITH DEPLOYED PRICE CONSUMER CONTRACTconst priceConsumerAbi = [ "function updatePrice(bytes[] priceUpdateData) external payable", "function getLatestPrice(bytes32 priceId) public view returns(int64, int32)",];const priceConsumer = new ethers.Contract( priceConsumerAddress, priceConsumerAbi, wallet);// 3. Price feed IDsconst priceId = "0x6693afcd49878bbd622e46bd805e7177932cf6ab0b1c91b135d71151b9207433"; // FX.USD/IDR Beta Price Feed IDasync function run() { // Fetch Hermes price update binary const update = await hermes.getLatestPriceUpdates([priceId], { encoding: "hex", }); console.log(update); const priceUpdateData = ["0x" + update.binary.data]; // must be array of bytes console.log(priceUpdateData); // Estimate fee required by Pyth contract // EVM Network Price Feed Contract Addresses: https://docs.pyth.network/price-feeds/core/contract-addresses/evm const pythContractAddress = "0x2880ab155794e7179c9ee2e38200202908c17b43"; const pythAbi = [ "function getUpdateFee(bytes[] calldata data) external view returns(uint)", ]; console.log("Pyth contract address:", pythContractAddress); const pyth = new ethers.Contract(pythContractAddress, pythAbi, wallet); const fee = await pyth.getUpdateFee(priceUpdateData); console.log("Pyth fee:", fee.toString()); // Call your contract const tx = await priceConsumer.updatePrice(priceUpdateData, { value: fee, // pay the pyth update fee gasLimit: 500000, }); console.log("Tx sent:", tx.hash); const receipt = await tx.wait(); console.log("Tx confirmed"); console.log(receipt); // 4. Get latest price from contract try { console.log("=== Latest Price from Contract ==="); const [price, expo] = await priceConsumer.getLatestPrice(priceId); console.log("Price Value : " + price.toString()); console.log("Exponent Value : " + expo.toString()); } catch (error) { console.log(error); // @ts-ignore console.error("\nError calling getLatestPrice:", error.message); console.log( "This usually means the price is older than 60 seconds or hasn't been updated yet." ); console.log("Make sure updatePrice() was called successfully first."); }}run();
Run the Script
Execute the script with:
npx tsx scripts/interact.ts
Example Output
Tx sent: 0x79c5dcb7abd9605b070bf9062ba2e2382272d23d58f7b50446c3107b7784fc8eTx confirmed=== Latest Price from Contract ===Price Value : 1669784988Exponent Value : -5======== —— =========
Your transaction can be verified on the Kairos explorer by pasting the transaction hash into the search bar. This confirms that the update and read operations were successful.
Conclusion
In this tutorial, you created a Solidity contract that reads real-time prices from Pyth, deployed it to the Kairos testnet, and interacted with it using the Hermes client. You also learned how Pyth’s pull-based design gives you control over when and how price updates occur.
For more information, explore:
- EVM Contract Reference for the Pyth API
- Pyth Oracle AMM Examplefor a complete end-to-end implementation