|
| 1 | +--- |
| 2 | +description: Consume Pyth Network prices in Fuel applications |
| 3 | +--- |
| 4 | + |
| 5 | +import { Callout, Tabs } from "nextra/components"; |
| 6 | + |
| 7 | +# How to Use Real-Time Data in Fuel Contracts |
| 8 | + |
| 9 | +This guide explains how to use real-time Pyth data in Fuel contracts. |
| 10 | + |
| 11 | +## Install the Pyth SDK |
| 12 | + |
| 13 | +Use the following dependency in your `Forc.toml` file to use the latest Pyth Fuel package: |
| 14 | + |
| 15 | +```toml copy |
| 16 | +[dependencies] |
| 17 | +pyth_interface = { git = "https://github.com/pyth-network/pyth-crosschain", tag = "pyth-fuel-contract-v0.5.0" } |
| 18 | +``` |
| 19 | + |
| 20 | +Pyth also provides a javascript SDK to interact with the Pyth contract on Fuel. You can install it using the following command: |
| 21 | + |
| 22 | +<Tabs items={["npm", "yarn"]}> |
| 23 | + <Tabs.Tab>```npm install --save @pythnetwork/pyth-fuel-js ```</Tabs.Tab> |
| 24 | + <Tabs.Tab>```yarn add @pythnetwork/pyth-fuel-js ```</Tabs.Tab> |
| 25 | +</Tabs> |
| 26 | + |
| 27 | +## Write Contract Code |
| 28 | + |
| 29 | +The code snippet below provides an example module fetching the ETH/USD price from Pyth price feeds: |
| 30 | + |
| 31 | +```sway copy |
| 32 | +contract; |
| 33 | +
|
| 34 | +use pyth_interface::{data_structures::price::{Price, PriceFeedId}, PythCore}; |
| 35 | +
|
| 36 | +use std::bytes::Bytes; |
| 37 | +
|
| 38 | +abi UpdatePrice { |
| 39 | + fn valid_time_period() -> u64; |
| 40 | + fn get_price(price_feed_id: PriceFeedId) -> Price; |
| 41 | + fn get_price_unsafe(price_feed_id: PriceFeedId) -> Price; |
| 42 | + fn update_fee(update_data: Vec<Bytes>) -> u64; |
| 43 | + #[payable] |
| 44 | + fn update_price_feeds(update_fee: u64, update_data: Vec<Bytes>); |
| 45 | +} |
| 46 | +
|
| 47 | +const PYTH_CONTRACT_ID = 0x1ab91bc1402a187055d3e827017ace566a103ce2a4126517da5d656d6a436aea; // Testnet Contract |
| 48 | +const FUEL_ETH_BASE_ASSET_ID = 0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07; |
| 49 | +
|
| 50 | +impl UpdatePrice for Contract { |
| 51 | + fn valid_time_period() -> u64 { |
| 52 | + let pyth_contract = abi(PythCore, PYTH_CONTRACT_ID); |
| 53 | + let period = pyth_contract.valid_time_period(); |
| 54 | + period |
| 55 | + } |
| 56 | + fn get_price(price_feed_id: PriceFeedId) -> Price { |
| 57 | + let pyth_contract = abi(PythCore, PYTH_CONTRACT_ID); |
| 58 | + let price = pyth_contract.price(price_feed_id); |
| 59 | + price |
| 60 | + } |
| 61 | + fn get_price_unsafe(price_feed_id: PriceFeedId) -> Price { |
| 62 | + let pyth_contract = abi(PythCore, PYTH_CONTRACT_ID); |
| 63 | + let price = pyth_contract.price_unsafe(price_feed_id); |
| 64 | + price |
| 65 | + } |
| 66 | + fn update_fee(update_data: Vec<Bytes>) -> u64 { |
| 67 | + let pyth_contract = abi(PythCore, PYTH_CONTRACT_ID); |
| 68 | + let fee = pyth_contract.update_fee(update_data); |
| 69 | + fee |
| 70 | + } |
| 71 | + #[payable] |
| 72 | + fn update_price_feeds(update_fee: u64, update_data: Vec<Bytes>) { |
| 73 | + let pyth_contract = abi(PythCore, PYTH_CONTRACT_ID); |
| 74 | + pyth_contract |
| 75 | + .update_price_feeds { |
| 76 | + asset_id: FUEL_ETH_BASE_ASSET_ID, |
| 77 | + coins: update_fee, |
| 78 | + }(update_data); |
| 79 | + } |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +The `update_data` argument contains verified prices from Pyth. |
| 84 | +Calling `pyth_contract.update_price_feeds` with this value updates the on-chain Pyth price and ensures your application has recent price data. |
| 85 | +The `update_data` can be fetched from Hermes; Consult [Fetch Price Updates](https://docs.pyth.network/price-feeds/fetch-price-updates) for more information on how to fetch the `update_data`. |
| 86 | + |
| 87 | +<Callout type="info" emoji="ℹ️"> |
| 88 | + Regarding the Pyth contract on Fuel, the caller must pay the fee in the base |
| 89 | + asset for functions like `update_price_feeds`. The fee is currently set to the |
| 90 | + minimum possible value (1 wei). |
| 91 | +</Callout> |
| 92 | + |
| 93 | +The code snippet above does the following things: |
| 94 | + |
| 95 | +1. Defines an `UpdatePrice` ABI with functions to interact with the Pyth contract. |
| 96 | +2. Implements the `UpdatePrice` ABI for the contract, providing the following functionality: |
| 97 | + |
| 98 | + - `valid_time_period()`: Retrieves the valid time period from the Pyth contract. |
| 99 | + - `get_price(price_feed_id)`: Gets the price for a given price feed ID. |
| 100 | + - `get_price_unsafe(price_feed_id)`: Gets the price for a given price feed ID without staleness checks. |
| 101 | + - `update_fee(update_data)`: Calculates the fee required to update the price feeds. |
| 102 | + - `update_price_feeds(update_fee, update_data)`: Updates the price feeds with the provided data and fee. |
| 103 | + |
| 104 | +3. Uses the `PYTH_CONTRACT_ID` constant to interact with the Pyth contract on testnet. |
| 105 | +4. Uses the `FUEL_ETH_BASE_ASSET_ID` constant as the asset ID for paying update fees. |
| 106 | + |
| 107 | +To use this contract, you would typically: |
| 108 | + |
| 109 | +1. Call `update_fee()` to get the required fee for updating price feeds. |
| 110 | +2. Call `update_price_feeds()` with the fee and update data to refresh the price feeds. |
| 111 | +3. Use `get_price()` or `get_price_unsafe()` to read the updated prices. |
| 112 | + |
| 113 | +### Write Client Code |
| 114 | + |
| 115 | +The code snippet below provides an example of how to fetch price updates using NextJS, a full example can be found [here](https://github.com/pyth-network/pyth-examples/tree/main/price_feeds/fuel/fetch-and-update-btc-price). |
| 116 | + |
| 117 | +```ts copy |
| 118 | +import { TestContractAbi__factory } from "@/sway-api"; |
| 119 | +import PYTH_CONTRACT_ABI from "../abi/pyth-contract-abi.json"; |
| 120 | +import { arrayify, Contract, hexlify } from "fuels"; |
| 121 | +import { HermesClient } from "@pythnetwork/hermes-client"; |
| 122 | + |
| 123 | +const HERMES_ENDPOINT = "https://hermes.pyth.network/"; |
| 124 | +const FUEL_ETH_BASE_ASSET_ID = |
| 125 | + "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07"; |
| 126 | +const ETH_USD_PRICE_FEED_ID = |
| 127 | + "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"; // ETH/USD |
| 128 | + |
| 129 | +const contractId = |
| 130 | + CURRENT_ENVIRONMENT === "local" |
| 131 | + ? contractIds.testContract |
| 132 | + : (process.env.NEXT_PUBLIC_TESTNET_CONTRACT_ID as string); // Testnet Contract ID |
| 133 | +const pythContractId = process.env |
| 134 | + .NEXT_PUBLIC_PYTH_TESTNET_CONTRACT_ID as string; // Testnet Contract ID |
| 135 | + |
| 136 | +async function updateAndGetPrice() { |
| 137 | + const fetchPriceUpdateData = async () => { |
| 138 | + const connection = new HermesClient(HERMES_ENDPOINT); |
| 139 | + |
| 140 | + // Latest price updates |
| 141 | + const priceUpdates = await connection.getLatestPriceUpdates([ |
| 142 | + ETH_USD_PRICE_FEED_ID, |
| 143 | + ]); |
| 144 | + |
| 145 | + const buffer = Buffer.from(priceUpdates.binary.data[0], "hex"); |
| 146 | + return buffer; |
| 147 | + }; |
| 148 | + |
| 149 | + const updateData = await fetchPriceUpdateData(); |
| 150 | + |
| 151 | + const { waitForResult: waitForResultFee } = await contract.functions |
| 152 | + .update_fee([arrayify(updateData)]) |
| 153 | + .addContracts([pythContract]) |
| 154 | + .call(); |
| 155 | + const { value: fee } = await waitForResultFee(); |
| 156 | + |
| 157 | + await contract.functions |
| 158 | + .update_price_feeds(fee, [arrayify(updateData)]) |
| 159 | + .addContracts([pythContract]) |
| 160 | + .callParams({ |
| 161 | + forward: [fee, hexlify(FUEL_ETH_BASE_ASSET_ID)], |
| 162 | + }) |
| 163 | + .call(); |
| 164 | + |
| 165 | + const { value: price } = await contract.functions |
| 166 | + .get_price(hexlify(PRICE_FEED_ID)) |
| 167 | + .addContracts([pythContract]) |
| 168 | + .get(); |
| 169 | + |
| 170 | + console.log("Latest ETH/USD price after update:", price); |
| 171 | + return price; |
| 172 | +} |
| 173 | + |
| 174 | +updateAndGetPrice().catch(console.error); |
| 175 | +``` |
| 176 | + |
| 177 | +## Additional Resources |
| 178 | + |
| 179 | +You may find these additional resources helpful for developing your Fuel application. |
| 180 | + |
| 181 | +### Interface |
| 182 | + |
| 183 | +The [Fuel Interface](https://github.com/pyth-network/pyth-crosschain/tree/main/target_chains/fuel/contracts/pyth-interface/src) directory contains multiple files that define the functions and structures for interacting with the Pyth contract deployed on Fuel. |
| 184 | + |
| 185 | +### Example Applications |
| 186 | + |
| 187 | +- [fetch-and-update-btc-price](https://github.com/pyth-network/pyth-examples/tree/main/price_feeds/fuel/fetch-and-update-btc-price), which fetches the latest price update from Hermes and updates the Pyth price feeds on Fuel. |
0 commit comments