How to Use Real-Time Data in Fuel Contracts
This guide explains how to use real-time Pyth data in Fuel contracts.
Install the Pyth SDK
Use the following dependency in your Forc.toml
file to use the latest Pyth Fuel package:
[dependencies]
pyth_interface = { git = "https://github.com/pyth-network/pyth-crosschain", tag = "pyth-fuel-contract-v0.5.0" }
Pyth also provides a javascript SDK to interact with the Pyth contract on Fuel. You can install it using the following command:
npm install --save @pythnetwork/pyth-fuel-js
Write Contract Code
The code snippet below provides an example module fetching the ETH/USD price from Pyth price feeds:
contract;
use pyth_interface::{data_structures::price::{Price, PriceFeedId}, PythCore};
use std::bytes::Bytes;
abi UpdatePrice {
fn valid_time_period() -> u64;
fn get_price(price_feed_id: PriceFeedId) -> Price;
fn get_price_unsafe(price_feed_id: PriceFeedId) -> Price;
fn update_fee(update_data: Vec<Bytes>) -> u64;
#[payable]
fn update_price_feeds(update_fee: u64, update_data: Vec<Bytes>);
}
const PYTH_MAINNET_CONTRACT_ID = 0x1c86fdd9e0e7bc0d2ae1bf6817ef4834ffa7247655701ee1b031b52a24c523da; // Mainnet Contract
const PYTH_TESTNET_CONTRACT_ID = 0x25146735b29d4216639f7f8b1d7b921ff87a1d3051de62d6cceaacabeb33b8e7; // Testnet Contract
const FUEL_ETH_BASE_ASSET_ID = 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07;
impl UpdatePrice for Contract {
fn valid_time_period() -> u64 {
let pyth_contract = abi(PythCore, PYTH_MAINNET_CONTRACT_ID);
let period = pyth_contract.valid_time_period();
period
}
fn get_price(price_feed_id: PriceFeedId) -> Price {
let pyth_contract = abi(PythCore, PYTH_MAINNET_CONTRACT_ID);
let price = pyth_contract.price(price_feed_id);
price
}
fn get_price_unsafe(price_feed_id: PriceFeedId) -> Price {
let pyth_contract = abi(PythCore, PYTH_MAINNET_CONTRACT_ID);
let price = pyth_contract.price_unsafe(price_feed_id);
price
}
fn update_fee(update_data: Vec<Bytes>) -> u64 {
let pyth_contract = abi(PythCore, PYTH_MAINNET_CONTRACT_ID);
let fee = pyth_contract.update_fee(update_data);
fee
}
#[payable]
fn update_price_feeds(update_fee: u64, update_data: Vec<Bytes>) {
let pyth_contract = abi(PythCore, PYTH_MAINNET_CONTRACT_ID);
pyth_contract
.update_price_feeds {
asset_id: FUEL_ETH_BASE_ASSET_ID,
coins: update_fee,
}(update_data);
}
}
The update_data
argument contains verified prices from Pyth.
Calling pyth_contract.update_price_feeds
with this value updates the on-chain Pyth price and ensures your application has recent price data.
The update_data
can be fetched from Hermes; Consult Fetch Price Updates (opens in a new tab) for more information on how to fetch the update_data
.
Regarding the Pyth contract on Fuel, the caller must pay the fee in the base
asset for functions like update_price_feeds
. The fee is currently set to the
minimum possible value (1 wei).
The code snippet above does the following things:
-
Defines an
UpdatePrice
ABI with functions to interact with the Pyth contract. -
Implements the
UpdatePrice
ABI for the contract, providing the following functionality:valid_time_period()
: Retrieves the valid time period from the Pyth contract.get_price(price_feed_id)
: Gets the price for a given price feed ID.get_price_unsafe(price_feed_id)
: Gets the price for a given price feed ID without staleness checks.update_fee(update_data)
: Calculates the fee required to update the price feeds.update_price_feeds(update_fee, update_data)
: Updates the price feeds with the provided data and fee.
-
Uses the
PYTH_MAINNET_CONTRACT_ID
constant to interact with the Pyth contract on testnet. -
Uses the
FUEL_ETH_BASE_ASSET_ID
constant as the asset ID for paying update fees.
To use this contract, you would typically:
- Call
update_fee()
to get the required fee for updating price feeds. - Call
update_price_feeds()
with the fee and update data to refresh the price feeds. - Use
get_price()
orget_price_unsafe()
to read the updated prices.
Write Client Code
The code snippet below provides an example of how to fetch price updates using NextJS, a full example can be found here (opens in a new tab).
import { TestContractAbi__factory } from "@/sway-api";
import PYTH_CONTRACT_ABI from "../abi/pyth-contract-abi.json";
import { arrayify, Contract, hexlify } from "fuels";
import { HermesClient } from "@pythnetwork/hermes-client";
const HERMES_ENDPOINT = "https://hermes.pyth.network/";
const FUEL_ETH_BASE_ASSET_ID =
"0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07";
const ETH_USD_PRICE_FEED_ID =
"0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"; // ETH/USD
const contractId =
CURRENT_ENVIRONMENT === "local"
? contractIds.testContract
: (process.env.NEXT_PUBLIC_TESTNET_CONTRACT_ID as string); // Testnet Contract ID
const pythContractId = process.env
.NEXT_PUBLIC_PYTH_TESTNET_CONTRACT_ID as string; // Testnet Contract ID
async function updateAndGetPrice() {
const fetchPriceUpdateData = async () => {
const connection = new HermesClient(HERMES_ENDPOINT);
// Latest price updates
const priceUpdates = await connection.getLatestPriceUpdates([
ETH_USD_PRICE_FEED_ID,
]);
const buffer = Buffer.from(priceUpdates.binary.data[0], "hex");
return buffer;
};
const updateData = await fetchPriceUpdateData();
const { waitForResult: waitForResultFee } = await contract.functions
.update_fee([arrayify(updateData)])
.addContracts([pythContract])
.call();
const { value: fee } = await waitForResultFee();
await contract.functions
.update_price_feeds(fee, [arrayify(updateData)])
.addContracts([pythContract])
.callParams({
forward: [fee, hexlify(FUEL_ETH_BASE_ASSET_ID)],
})
.call();
const { value: price } = await contract.functions
.get_price(hexlify(PRICE_FEED_ID))
.addContracts([pythContract])
.get();
console.log("Latest ETH/USD price after update:", price);
return price;
}
updateAndGetPrice().catch(console.error);
Additional Resources
You may find these additional resources helpful for developing your Fuel application.
Interface
The Fuel Interface (opens in a new tab) directory contains multiple files that define the functions and structures for interacting with the Pyth contract deployed on Fuel.
Example Applications
- fetch-and-update-btc-price (opens in a new tab), which fetches the latest price update from Hermes and updates the Pyth price feeds on Fuel.