|
| 1 | +--- |
| 2 | +description: Consume Pyth Network prices in Starknet applications |
| 3 | +--- |
| 4 | + |
| 5 | +import { Callout, Tabs } from "nextra/components"; |
| 6 | + |
| 7 | +# How to Use Real-Time Data in Starknet Contracts |
| 8 | + |
| 9 | +This guide explains how to use real-time Pyth data in Starknet contracts. |
| 10 | + |
| 11 | +## Install the Pyth SDK |
| 12 | + |
| 13 | +Use the following dependency in your `Scarb.toml` file to use the latest Pyth Starkenet package: |
| 14 | + |
| 15 | +```toml copy |
| 16 | +[dependencies] |
| 17 | +pyth = { git = "https://github.com/pyth-network/pyth-crosschain.git", tag = "pyth-starknet-contract-v0.1.0"} |
| 18 | +``` |
| 19 | + |
| 20 | +Pyth also provides a javascript SDK to interact with the Pyth contract on Starknet. You can install it using the following command: |
| 21 | + |
| 22 | +<Tabs items={["npm", "yarn"]}> |
| 23 | + <Tabs.Tab> |
| 24 | + ```sh copy |
| 25 | + npm install --save @pythnetwork/pyth-starknet-js |
| 26 | + ``` |
| 27 | + </Tabs.Tab> |
| 28 | + <Tabs.Tab> |
| 29 | + ```sh copy |
| 30 | + yarn add @pythnetwork/pyth-starknet-js |
| 31 | + ``` |
| 32 | + </Tabs.Tab> |
| 33 | +</Tabs> |
| 34 | + |
| 35 | +## Write Contract Code |
| 36 | + |
| 37 | +The code snippet below provides an example module fetching the STRK/USD price from Pyth price feeds: |
| 38 | + |
| 39 | +```rust {2,17,47,55,64,71-73} copy |
| 40 | +use starknet::ContractAddress; |
| 41 | +use pyth::ByteBuffer; |
| 42 | + |
| 43 | +#[starknet::interface] |
| 44 | +pub trait IExampleContract<T> { |
| 45 | + // pyth_price_update is the price update data from Pyth to update the price feeds. |
| 46 | + // It should be passed as a ByteBuffer. |
| 47 | + fn example_method( |
| 48 | + ref self: T, pyth_price_update: ByteBuffer |
| 49 | + ); |
| 50 | +} |
| 51 | + |
| 52 | +#[starknet::contract] |
| 53 | +mod example_contract { |
| 54 | + use core::panic_with_felt252; |
| 55 | + use starknet::{ContractAddress, get_caller_address, get_contract_address}; |
| 56 | + use pyth::{ByteBuffer, IPythDispatcher, IPythDispatcherTrait, UnwrapWithFelt252}; |
| 57 | + use openzeppelin::token::erc20::interface::{IERC20CamelDispatcherTrait, IERC20CamelDispatcher}; |
| 58 | + |
| 59 | + const MAX_PRICE_AGE: u64 = 3600; // 1 hour |
| 60 | + // Storage to store the Pyth contract address, the ERC20 contract address representing ETH, and the ETH/USD price feed ID. |
| 61 | + #[storage] |
| 62 | + struct Storage { |
| 63 | + pyth_address: ContractAddress, |
| 64 | + strk_erc20_address: ContractAddress, |
| 65 | + } |
| 66 | + |
| 67 | + // Constructor to initialize the contract storage. |
| 68 | + // * @param pyth_address: The address of the Pyth contract on Starknet. |
| 69 | + // * @param strk_erc20_address: The address of the ERC20 contract representing STRK on Starknet. |
| 70 | + #[constructor] |
| 71 | + fn constructor( |
| 72 | + ref self: ContractState, |
| 73 | + pyth_address: ContractAddress, |
| 74 | + strk_erc20_address: ContractAddress, |
| 75 | + ) { |
| 76 | + self.pyth_address.write(pyth_address); |
| 77 | + self.strk_erc20_address.write(strk_erc20_address); |
| 78 | + } |
| 79 | + |
| 80 | + #[abi(embed_v0)] |
| 81 | + impl ExampleContract of super::IExampleContract<ContractState> { |
| 82 | + fn example_method( |
| 83 | + ref self: ContractState, |
| 84 | + pyth_price_update: ByteBuffer |
| 85 | + ) { |
| 86 | + let pyth = IPythDispatcher { contract_address: self.pyth_address.read() }; |
| 87 | + let strk_erc20 = IERC20CamelDispatcher { |
| 88 | + contract_address: self.strk_erc20_address.read() |
| 89 | + }; |
| 90 | + let caller = get_caller_address(); |
| 91 | + let contract = get_contract_address(); |
| 92 | + |
| 93 | + // Get the fee required to update the Pyth price feeds. |
| 94 | + let pyth_fee = pyth.get_update_fee(pyth_price_update.clone(), strk_erc20.contract_address); |
| 95 | + if !strk_erc20.transferFrom(caller, contract, pyth_fee) { |
| 96 | + panic_with_felt252('insufficient allowance for fee'); |
| 97 | + } |
| 98 | + if !strk_erc20.approve(pyth.contract_address, pyth_fee) { |
| 99 | + panic_with_felt252('approve failed'); |
| 100 | + } |
| 101 | +
|
| 102 | + // Submit a pyth_price_update to the Pyth contract to update the on-chain price. |
| 103 | + pyth.update_price_feeds(pyth_price_update); |
| 104 | +
|
| 105 | + // Read the current price from a price feed. |
| 106 | + // STRK/USD price feed ID |
| 107 | + // The complete list of feed IDs is available at https://pyth.network/developers/price-feed-ids |
| 108 | + let strk_usd_price_id = |
| 109 | + 0x6a182399ff70ccf3e06024898942028204125a819e519a335ffa4579e66cd870; |
| 110 | + let price = pyth |
| 111 | + .get_price_no_older_than(strk_usd_price_id, MAX_PRICE_AGE) |
| 112 | + .unwrap_with_felt252(); |
| 113 | + let _: u64 = price.price.try_into().unwrap(); // Price in u64 |
| 114 | + } |
| 115 | + } |
| 116 | +} |
| 117 | +``` |
| 118 | +
|
| 119 | +The pyth_price_update argument contains verified prices from Pyth. |
| 120 | +Calling pyth.update_price_feeds with this value updates the on-chain Pyth price and ensures your application has recent price data. |
| 121 | +The pyth_price_update 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 pyth_price_update. |
| 122 | +
|
| 123 | +<Callout type="info" emoji="ℹ️"> |
| 124 | + Unlike Ethereum, there is no native token on Starknet. You cannot pass tokens |
| 125 | + implicitly when calling functions. Moreover, there is no concept of a |
| 126 | + designated payer account, unlike Solana. In Starknet, all token transfers must |
| 127 | + be performed explicitly by calling functions on the token's ERC20 contract. |
| 128 | + Regarding the Pyth contract on Starknet, the caller must approve the fee |
| 129 | + transfer before calling `update_price_feeds` or using similar methods. You can |
| 130 | + use **STRK** or **ETH** to pay the fee, but STRK is preferred. The fee is |
| 131 | + currently set to the minimum possible value (1e-18 STRK, 1 WEI). |
| 132 | +</Callout> |
| 133 | + |
| 134 | +The code snippet above does the following things: |
| 135 | + |
| 136 | +1. Call `pyth.get_update_fee` to get the fee required to update the Pyth price feeds. |
| 137 | +1. Call `pyth.update_price_feeds` and pass `pyth_price_update` to update the Pyth price feeds. |
| 138 | +1. Call `pyth.get_price_no_older_than` to read the price, providing the [price feed ID](https://pyth.network/developers/price-feed-ids) you wish to read. |
| 139 | + |
| 140 | +### Write Client Code |
| 141 | + |
| 142 | +The code snippet below provides an example of how to fetch price updates and convert to `ByteBuffer` for Starknet using the `pyth-starknet-js` in JavaScript: |
| 143 | + |
| 144 | +```ts {16} copy |
| 145 | +import { PriceServiceConnection } from "@pythnetwork/price-service-client"; |
| 146 | +import { ByteBuffer } from "@pythnetwork/pyth-starknet-js"; |
| 147 | +// The URL below is a public Hermes instance operated by the Pyth Data Association. |
| 148 | +// Hermes is also available from several third-party providers listed here: |
| 149 | +// https://docs.pyth.network/price-feeds/api-instances-and-providers/hermes |
| 150 | +const connection = new PriceServiceConnection("https://hermes.pyth.network", { |
| 151 | + priceFeedRequestConfig: { |
| 152 | + binary: true, |
| 153 | + }, |
| 154 | +}); |
| 155 | + |
| 156 | +const priceId = |
| 157 | + "0x6a182399ff70ccf3e06024898942028204125a819e519a335ffa4579e66cd870"; // STRK/USD |
| 158 | + |
| 159 | +// Get the latest values of the price feeds as json objects. |
| 160 | +const currentPrices = await connection.getLatestPriceFeeds([priceId]); |
| 161 | + |
| 162 | +// Convert the price update to Starknet format. |
| 163 | +const pythUpdate = ByteBuffer.fromHex(currentPrices[0].vaa); |
| 164 | +``` |
| 165 | + |
| 166 | +<Callout type="info" emoji="ℹ️"> |
| 167 | + Price updates must be converted to `ByteBuffer` before being passed on to the |
| 168 | + Pyth contract on Starknet. Use the `ByteBuffer` type from |
| 169 | + `@pythnetwork/pyth-starknet-js` package as shown above. |
| 170 | +</Callout> |
| 171 | + |
| 172 | +## Additional Resources |
| 173 | + |
| 174 | +You may find these additional resources helpful for developing your Starknet application. |
| 175 | + |
| 176 | +### Interface |
| 177 | + |
| 178 | +The [Starknet Interface](https://github.com/pyth-network/pyth-crosschain/blob/main/target_chains/starknet/contracts/src/pyth/interface.cairo#L9) provides a list of functions that can be called on the Pyth contract deployed on Starknet. |
| 179 | + |
| 180 | +### Example Applications |
| 181 | + |
| 182 | +- [Send-USD](https://github.com/pyth-network/pyth-examples/tree/main/price_feeds/starknet), which updates and consumes STRK/USD price feeds on Starknet to send USD to a recipient. |
0 commit comments