diff --git a/components/LazerPriceIdTable.tsx b/components/LazerPriceIdTable.tsx new file mode 100644 index 00000000..8d8f0110 --- /dev/null +++ b/components/LazerPriceIdTable.tsx @@ -0,0 +1,98 @@ +import { useEffect, useState } from "react"; +import { StyledTd } from "./Table"; +import { Spinner } from "./Spinner"; +const fetchLazerPriceIdMetadata = async () => { + const response = await fetch( + "https://pyth-lazer-staging.dourolabs.app/history/v1/symbols" + ); + const data = await response.json(); + return data; +}; + +type LazerPriceIdMetadata = { + asset_type: string; + description: string; + exponent: number; + name: string; + pyth_lazer_id: number; + symbol: string; +}; + +enum LazerPriceIdStateType { + NotLoaded, + Loading, + Loaded, + Error, +} + +const LazerPriceIdState = { + NotLoaded: () => ({ type: LazerPriceIdStateType.NotLoaded as const }), + Loading: () => ({ type: LazerPriceIdStateType.Loading as const }), + Loaded: (priceFeeds: LazerPriceIdMetadata[]) => ({ + type: LazerPriceIdStateType.Loaded as const, + priceFeeds, + }), + Error: (error: unknown) => ({ + type: LazerPriceIdStateType.Error as const, + error, + }), +}; + +type LazerPriceIdState = ReturnType< + typeof LazerPriceIdState[keyof typeof LazerPriceIdState] +>; + +const useLazerPriceIdState = () => { + const [state, setState] = useState( + LazerPriceIdState.NotLoaded() + ); + + useEffect(() => { + setState(LazerPriceIdState.Loading()); + fetchLazerPriceIdMetadata() + .then((priceFeeds) => setState(LazerPriceIdState.Loaded(priceFeeds))) + .catch((error) => setState(LazerPriceIdState.Error(error))); + }, []); + + return state; +}; + +export function LazerPriceIdTable() { + const lazerPriceIdState = useLazerPriceIdState(); + + switch (lazerPriceIdState.type) { + case LazerPriceIdStateType.NotLoaded: + return
Loading...
; + case LazerPriceIdStateType.Loading: + return ; + case LazerPriceIdStateType.Loaded: + return ( + + + + + + + + + + + + + {lazerPriceIdState.priceFeeds.map((priceFeed) => ( + + {priceFeed.asset_type} + {priceFeed.description} + {priceFeed.name} + {priceFeed.symbol} + {priceFeed.pyth_lazer_id} + {priceFeed.exponent} + + ))} + +
Asset TypeDescriptionNameSymbolPyth Lazer IdExponent
+ ); + case LazerPriceIdStateType.Error: + return
Error
; + } +} diff --git a/components/Spinner.tsx b/components/Spinner.tsx new file mode 100644 index 00000000..9134da8e --- /dev/null +++ b/components/Spinner.tsx @@ -0,0 +1,23 @@ +import { SVGProps } from "react"; +export const Spinner = (props: SVGProps) => ( +
+ + Loading... +
+); diff --git a/images/lazer/Pyth_Laser_table.png b/images/lazer/Pyth_Laser_table.png new file mode 100644 index 00000000..9f010ff5 Binary files /dev/null and b/images/lazer/Pyth_Laser_table.png differ diff --git a/pages/_meta.json b/pages/_meta.json index 2518e2ca..604a1d83 100644 --- a/pages/_meta.json +++ b/pages/_meta.json @@ -9,6 +9,11 @@ "type": "page" }, + "lazer": { + "title": "Lazer", + "type": "page" + }, + "benchmarks": { "title": "Benchmarks", "type": "page" diff --git a/pages/home/_meta.json b/pages/home/_meta.json index cb30232f..b67ff895 100644 --- a/pages/home/_meta.json +++ b/pages/home/_meta.json @@ -11,6 +11,11 @@ "href": "/price-feeds" }, + "Lazer": { + "title": "Lazer →", + "href": "/lazer" + }, + "Benchmarks": { "title": "Benchmarks →", "href": "/benchmarks" diff --git a/pages/home/index.mdx b/pages/home/index.mdx index edc7353b..c6a2c16b 100644 --- a/pages/home/index.mdx +++ b/pages/home/index.mdx @@ -8,6 +8,7 @@ Pyth Network offers several products for developers: - [Price Feeds](../price-feeds) provide real-time prices for 500+ assets on 55+ blockchain ecosystems, including Solana, many EVM chains, Aptos, Sui, NEAR, and several Cosmos chains. +- [Lazer](../lazer) [TODO] - [Benchmarks](../benchmarks) provides historical Pyth prices for both on- and off-chain use. - [Express Relay](../express-relay/) enables protocols to eliminate their MEV while gaining access to active searchers and liquidators. - [Entropy](../entropy) allows developers to generate secure random numbers on the blockchain. diff --git a/pages/lazer/_meta.json b/pages/lazer/_meta.json new file mode 100644 index 00000000..5c98e5e7 --- /dev/null +++ b/pages/lazer/_meta.json @@ -0,0 +1,46 @@ +{ + "documentation-home": { + "title": "← Documentation Home", + "href": "/home" + }, + + "-- Lazer": { + "title": "Lazer", + "type": "separator" + }, + "index": "Introduction", + "getting-started": "Getting Started", + + "-- How-to Guides": { + "title": "How-To Guides", + "type": "separator" + }, + + "subscribe-price-updates": "Subscribe to Price Updates", + "integrate-as-consumer": "Integrate as a Consumer", + "integrate-as-publisher": "Integrate as a Publisher", + + "-- Reference Material": { + "title": "Reference Material", + "type": "separator" + }, + "price-feeds-ids": "Price Feed IDs", + + "websocket-api-reference": { + "title": "Websocket API Reference ↗", + "href": "https://pyth-lazer.dourolabs.app/docs", + "newWindow": true + }, + + "examples": { + "title": "Example Applications ↗", + "href": "https://github.com/pyth-network/pyth-examples/tree/main/lazer", + "newWindow": true + }, + "-- Understand Lazer": { + "title": "Understanding Lazer", + "type": "separator" + }, + + "how-lazer-works": "How Lazer Works" +} diff --git a/pages/lazer/getting-started.mdx b/pages/lazer/getting-started.mdx new file mode 100644 index 00000000..da529c2b --- /dev/null +++ b/pages/lazer/getting-started.mdx @@ -0,0 +1,3 @@ +# Getting Started with Pyth Lazer + +Please refer to the how-to guides to get started. diff --git a/pages/lazer/how-lazer-works.mdx b/pages/lazer/how-lazer-works.mdx new file mode 100644 index 00000000..ea314c0e --- /dev/null +++ b/pages/lazer/how-lazer-works.mdx @@ -0,0 +1,5 @@ +# How Pyth Lazer works + +Pyth Lazer is a permissioned service that provides ultra-low-latency price and market data to highly latency-sensitive users. + +We are working on writing a detailed technical overview how Lazer works. Details will be added here as they are completed. diff --git a/pages/lazer/index.mdx b/pages/lazer/index.mdx new file mode 100644 index 00000000..e9819a7f --- /dev/null +++ b/pages/lazer/index.mdx @@ -0,0 +1,20 @@ +import Image from "next/image"; + +# Pyth Lazer + +Pyth Lazer is a low latency, highly customizable price oracle. +It offers a customizable set of price feeds, target chains (EVM or Solana) and channels (real time or fixed rate): + +- Real time channels send updates as frequently as they become available; +- Fixed rate channels send updates at fixed time intervals (you can choose between 50 ms or 200 ms). + +The table below shows the difference between Pyth Core and Pyth Lazer: + +| | **Pyth Core** | **Pyth Lazer** | +| ----------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| **Solution Type** | Stable, secure, and decentralized price data source for a broad spectrum of DeFi or TradFi applications. | **Permissioned** service focused on **ultra-low-latency** price and market data for highly latency-sensitive users. | +| **Frequency** | 400ms on Pythnet appchain with support for risk mitigation via Benchmarks and confidence intervals. | **1ms** (**real-time**), 50ms, and 200ms channels, **customizable** frequencies, and throttling support to address different needs. | +| **Data Types** | Aggregate price and confidence intervals. | Aggregate price, bid/ask price, and **customizable** market data (market depth and more). | +| **Fees** | On-chain fee per signed cross-chain price update. | On-chain fee per signed cross-chain price update. | +| **Update Costs** | >1,000-byte proofs and complex signature verification. | **100-byte proofs** and simple signature verification. | +| **Integration Process** | Open and permissionless integration for any Web3 or Web2 protocol. | **Specialized** and **permissioned** solution for protocols prioritizing performance over some elements of decentralization. | diff --git a/pages/lazer/integrate-as-consumer.mdx b/pages/lazer/integrate-as-consumer.mdx new file mode 100644 index 00000000..94185d3d --- /dev/null +++ b/pages/lazer/integrate-as-consumer.mdx @@ -0,0 +1,11 @@ +# How to Integrate Pyth Lazer as a Consumer + +The following guides demonstrate how to integrate Pyth Lazer as a consumer in your application. + +Lazer is available in Solana and EVM. Please consult the following guides to get started: + +- [Solana](integrate-as-consumer/svm) +- [EVM](integrate-as-consumer/evm) + +Lazer price feeds can also be used in off-chain applications. See the +[subscription guide](subscribe-price-updates) for more information. diff --git a/pages/lazer/integrate-as-consumer/_meta.json b/pages/lazer/integrate-as-consumer/_meta.json new file mode 100644 index 00000000..f78d3668 --- /dev/null +++ b/pages/lazer/integrate-as-consumer/_meta.json @@ -0,0 +1,4 @@ +{ + "evm": "on EVM chains", + "svm": "on Solana" +} diff --git a/pages/lazer/integrate-as-consumer/evm.mdx b/pages/lazer/integrate-as-consumer/evm.mdx new file mode 100644 index 00000000..77626091 --- /dev/null +++ b/pages/lazer/integrate-as-consumer/evm.mdx @@ -0,0 +1,160 @@ +import { Callout, Tabs, Steps } from "nextra/components"; + +# Integrate Pyth Lazer as a Consumer on EVM chains + +This guide is intended to serve users who wants to consume prices from the Pyth Lazer on **EVM chains**. + +Integrating with Pyth Lazer in smart contracts as a consumer is a three-step process: + +1. **Use** Pyth Lazer SDK into EVM smart contracts to parse the price updates. +2. **Subscribe** to Pyth Lazer websocket to receive price updates on backend or frontend. +3. **Include** the price updates into the smart contract transactions. + + + +### Use Pyth Lazer SDK into smart contracts + +Pyth Lazer provides a [solidity SDK](https://github.com/pyth-network/pyth-crosschain/tree/main/lazer/contracts/evm), which allows consumers to parse the price updates. + +```bash copy +forge install pythnet/pyth-crosschain +``` + +Add the following to `requirements.txt{:js}` file: + +```bash copy +pyth-lazer-sdk/=lib/pythnet/pyth-crosschain/lazer/contracts/evm +``` + +Once the SDK is installed, one can import the sdk into smart contracts: + +```solidity copy +import { PythLazer } from "pyth-lazer/PythLazer.sol"; +import { PythLazerLib } from "pyth-lazer/PythLazerLib.sol"; + +``` + +After importing the SDK, initialize the [`PythLazer`](https://github.com/pyth-network/pyth-crosschain/blob/main/lazer/contracts/evm/src/PythLazer.sol#L7) contract and set up state varables to store prices and timestamps: + +```solidity copy +contract ExampleConsumer { + // Example state. + PythLazer pythLazer; + uint64 public price; + uint64 public timestamp; + + //... + + constructor(address pythLazerAddress) { + pythLazer = PythLazer(pythLazerAddress); + } +} + +``` + +Add an argument of type `bytes calldata{:solidity}` to the method which will receive the Pyth Lazer price udpate: + +```solidity copy +function updatePrice(bytes calldata priceUpdate) public payable { + uint256 verification_fee = pythLazer.verification_fee(); + (bytes calldata payload, ) = verifyUpdate{ value: verification_fee }(update); + //... +} + +``` + +The `verifyUpdate` function will verify the price update and return the payload and the verification fee. This call takes a fee which can be queried from [`verification_fee(){:solidity}`](https://github.com/pyth-network/pyth-crosschain/blob/main/lazer/contracts/evm/src/PythLazer.sol#L9) function and passed to the `verifyUpdate` call. This fee is used to cover the cost of verifying the price update. + +This SDK provides [`parsePayloadHeader`](https://github.com/pyth-network/pyth-crosschain/blob/main/lazer/contracts/evm/src/PythLazerLib.sol#L21) method to retrieve the values from the payload header. + +```solidity copy +(uint64 _timestamp, Channel channel, uint8 feedsLen, uint16 pos) = parsePayloadHeader(payload); +``` + +This method returns: + +- `_timestamp`: The timestamp of the price update. +- `channel`: The channel of the price update. +- `feedsLen`: The number of feeds in the price update. +- `pos`: The cursor position of the payload. + +One can iterate over all the feeds and properties present within the price update, modifying the state variables as necessary. + +Here is an example of how to iterate over the feeds and properties: + +```solidity copy +for (uint8 i = 0; i < feedsLen; i++) { + uint32 feedId; + uint8 num_properties; + (feedId, num_properties, pos) = parseFeedHeader(payload, pos); + for (uint8 j = 0; j < num_properties; j++) { + PriceFeedProperty property; + (property, pos) = parseFeedProperty(payload, pos); + if (property == PriceFeedProperty.Price) { + uint64 _price; + (_price, pos) = parseFeedValueUint64(payload, pos); + if (feedId == 2 && _timestamp > timestamp) { + price = _price; + timestamp = _timestamp; + } + } else if (property == PriceFeedProperty.BestBidPrice) { + uint64 _price; + (_price, pos) = parseFeedValueUint64(payload, pos); + } else if (property == PriceFeedProperty.BestAskPrice) { + uint64 _price; + (_price, pos) = parseFeedValueUint64(payload, pos); + } else { + revert("unknown property"); + } + } +} +``` + + + Make sure to pass the `pos` variable to every parsing call and assign the + returned `pos` value to the same variable. Failure to do so will cause + incorrect parsing results. + + + + When calling these parse functions, you must not skip price feeds or + properties. Every parsing function will modify your `pos` variable, so + skipping a call of `parseFeedHeader`, `parseFeedProperty`, or + `parseFeedValueUint64` will lead to incorrect parsing results. Keep in mind + that you can customize the set of price feeds and properties when requesting + price updates via WebSocket. This will be explained in the next step. + + +### Subscribe to Pyth Lazer to receive Price Updates + +Pyth Lazer provides a websocket endpoint to receive price updates. Moreover, Pyth Lazer also provides a [typescript SDK](https://github.com/pyth-network/pyth-crosschain/tree/main/lazer/sdk/js) to subscribe to the websocket endpoint. + +Consult [How to fetch price updates from Pyth Lazer](../fetch-price-updates.mdx) for a complete step-by-step guide. + +### Include the price updates into smart contract transactions + +Now that you have the price updates, and your smart contract is able to parse the price updates, you can include the price updates into the smart contract transactions by passing the price updates received from the previous step as an argument to the `updatePrice` method of your smart contract. + + + +## Additional Resources + +You may find these additional resources helpful for integrating Pyth Lazer into your EVM smart contracts. + +### Price Feed IDs + +Pyth Lazer supports a wide range of price feeds. Consult the [Price Feed IDs](../price-feeds-ids.mdx) page for a complete list of supported price feeds. + +### Examples + +[Pyth-lazer-example-evm](https://github.com/pyth-network/pyth-examples/tree/main/lazer/evm) is a simple example contract that parses and consumes price updates from Pyth Lazer. + +[pyth-lazer-example-js](https://github.com/pyth-network/pyth-examples/tree/main/lazer/js) is a simple example for subscribing to the Pyth Lazer websocket. + +### API Reference + +TODO: + +### Error Codes + +TODO: Add error codes for EVM. diff --git a/pages/lazer/integrate-as-consumer/svm.mdx b/pages/lazer/integrate-as-consumer/svm.mdx new file mode 100644 index 00000000..b103768e --- /dev/null +++ b/pages/lazer/integrate-as-consumer/svm.mdx @@ -0,0 +1,196 @@ +import { Callout, Tabs, Steps } from "nextra/components"; + +# Integrate Pyth Lazer as a Consumer on Solana + +This guide is intended to serve users who wants to consume prices from the Pyth Lazer on **Solana**. + +Integrating with Pyth Lazer in smart contracts as a consumer is a three-step process: + +1. **Use** Pyth Lazer SDK into Solana smart contracts to parse the price updates. +2. **Subscribe** to Pyth Lazer websocket to receive price updates on backend or frontend. +3. **Include** the price updates into the smart contract transactions. + + + +### Use Pyth Lazer SDK into smart contracts + +Pyth Lazer provides a [Rust SDK](https://github.com/pyth-network/pyth-crosschain/tree/main/lazer/sdk/rust), which allows consumers to parse the price updates. + +Add the following to your `Cargo.toml` file: + +```toml copy +[dependencies] +pyth-lazer-sdk = 0.1.0 +``` + +Now you can create an instruction or multiple instructions that will receive Pyth Lazer messages. +The instruction data sent to your program should include a byte array containing the Pyth Lazer message. The instruction data can also contain any other parameters your contracts may need. + +In order to successfully validate the Pyth Lazer message, the instruction needs to receive the standard Solana sysvar account and Pyth Lazer storage account (`3rdJbqfnagQ4yx9HXJViD4zc4xpiSqmFsKpPuSCQVyQL`). You may also add any other accounts you need. + +The following code can be used to set up a new instruction within a Solana contract: + +```rust copy +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive)] +pub enum Instruction { + //... + /// Update price. + /// Data: `UpdateArgs` followed by a signed Pyth Lazer update. + /// Accounts: + /// 1. sysvar account [readonly] - required for Pyth Lazer + /// 2. data account [writable] - needed by our example contract + /// 3. pyth storage account [readonly] - required for Pyth Lazer + Update = 1, +} + +/// Inputs to the `Update` instruction. `UpdateArgs` must be followed by a signed Pyth Lazer message. +#[derive(Debug, Clone, Copy, Zeroable, Pod)] +#[repr(C, packed)] +pub struct UpdateArgs { + /// Example argument + pub hello: u64, +} + +/// Program entrypoint's implementation. +pub fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + // In our example contract, the first byte is the ID of the instruction. + let instruction = *instruction_data + .first() + .ok_or(ProgramError::InvalidInstructionData)?; + let instruction = + Instruction::from_u8(instruction).ok_or(ProgramError::InvalidInstructionData)?; + let instruction_args = &instruction_data[1..]; + + match instruction { + Instruction::Initialize => { + process_initialize_instruction(program_id, accounts, instruction_args) + } + Instruction::Update => process_update_instruction(program_id, accounts, instruction_args), + } +} + +pub fn process_update_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_args: &[u8], +) -> ProgramResult { + // Verify accounts passed to the instruction. + if accounts.len() != 3 { + return Err(ProgramError::NotEnoughAccountKeys); + } + let sysvar_account = &accounts[0]; + let data_account = &accounts[1]; + let pyth_storage_account = &accounts[2]; + // See below for next steps... +} +``` + +Call `pyth_lazer_sdk::verify_message{:rust}` function with appropriate arguments to validate the Pyth Lazer signature of the message. + +```rust copy +// Offset of pyth message within the original instruction_data. +// 1 byte is the instruction id. +let pyth_message_total_offset = size_of::() + 1; +// We expect the instruction to the built-in ed25519 program to be +// the first instruction within the transaction. +let ed25519_instruction_index = 0; +// We expect our signature to be the first (and only) signature to be checked +// by the built-in ed25519 program within the transaction. +let signature_index = 0; +// Check signature verification. +let verified = pyth_lazer_sdk::verify_message( + pyth_storage_account, + sysvar_account, + pyth_message, + ed25519_instruction_index, + signature_index, + pyth_message_total_offset.try_into().unwrap(), +)?; +``` + + + Note: When using native ed25519 signatures on Solana, we must use the built-in + ed25519 program provided by the Solana runtime. This program can't be invoked + from another contract. Instead, it must be called in an explicit instruction + within the submitted transaction. This means that the sender of the + transaction must include that instruction in the transaction. Our SDK + leverages Solana runtime capabilities to ensure the ed25519 program has been + correctly called in the transaction. + + +Now Parse the Pyth Lazer message. + +```rust copy +// Deserialize and use the payload. +let data = PayloadData::deserialize_slice_le(verified.payload) + .map_err(|_| ProgramError::InvalidInstructionData)?; + +if data.feeds.is_empty() || data.feeds[0].properties.is_empty() { + return Err(ProgramError::InvalidInstructionData); +} +``` + +Now you can update the state accourding to the contract's logic. + +```rust copy +// Read the data PDA of our example contract. +let mut state_data = data_account.data.borrow_mut(); +let state = + try_from_bytes_mut::(*state_data).map_err(|_| ProgramError::InvalidAccountData)?; + +if state.price_feed != data.feeds[0].feed_id.0 { + return Err(ProgramError::InvalidInstructionData); +} +if data.channel_id != Channel::RealTime.id() { + return Err(ProgramError::InvalidInstructionData); +} +if data.timestamp_us.0 <= state.latest_timestamp { + return Err(ProgramError::AccountAlreadyInitialized); +} +let PayloadPropertyValue::Price(Some(price)) = data.feeds[0].properties[0] else { + return Err(ProgramError::InvalidInstructionData); +}; +state.latest_price = price.into_inner().into(); +state.latest_timestamp = data.timestamp_us.0; +``` + +### Subscribe to Pyth Lazer to receive Price Updates + +Pyth Lazer provides a websocket endpoint to receive price updates. Moreover, Pyth Lazer also provides a [typescript SDK](https://github.com/pyth-network/pyth-crosschain/tree/main/lazer/sdk/js) to subscribe to the websocket endpoint. + +Consult [How to fetch price updates from Pyth Lazer](../fetch-price-updates.mdx) for a complete step-by-step guide. + +### Include the price updates into smart contract transactions + +Now that you have the price updates, and your smart contract is able to parse the price updates, you can include the price updates into the smart contract transactions by passing the price updates received from the previous step as an argument to the `update_price` method of your smart contract. + + + +## Additional Resources + +You may find these additional resources helpful for integrating Pyth Lazer into your Solana smart contracts. + +### Price Feed IDs + +Pyth Lazer supports a wide range of price feeds. Consult the [Price Feed IDs](../price-feeds.mdx) page for a complete list of supported price feeds. + +### Examples + +[pyth-lazer-example-solana](https://github.com/pyth-network/pyth-examples/tree/main/lazer/solana) is a simple example contract that parses and consumes price updates from Pyth Lazer. + +[pyth-lazer-example-js](https://github.com/pyth-network/pyth-examples/tree/main/lazer/js) is a simple example for subscribing to the Pyth Lazer websocket. + +### API Reference + +TODO: + +### Error Codes + +TODO: Add error codes for Solana. diff --git a/pages/lazer/integrate-as-publisher.mdx b/pages/lazer/integrate-as-publisher.mdx new file mode 100644 index 00000000..25784d01 --- /dev/null +++ b/pages/lazer/integrate-as-publisher.mdx @@ -0,0 +1,3 @@ +# How to Integrate Pyth Lazer as a Publisher + +We are working on this guide. Please check back later or contact us here if you wish to publish data on Pyth Lazer. diff --git a/pages/lazer/price-feeds-ids.mdx b/pages/lazer/price-feeds-ids.mdx new file mode 100644 index 00000000..9ac2fb3a --- /dev/null +++ b/pages/lazer/price-feeds-ids.mdx @@ -0,0 +1,5 @@ +import { LazerPriceIdTable } from "../../components/LazerPriceIdTable"; + +# Lazer Price Feeds + + diff --git a/pages/lazer/subscribe-price-updates.mdx b/pages/lazer/subscribe-price-updates.mdx new file mode 100644 index 00000000..6c1633c9 --- /dev/null +++ b/pages/lazer/subscribe-price-updates.mdx @@ -0,0 +1,131 @@ +import { Callout, Steps } from "nextra/components"; + +# How to Subscribe to Price Updates from Pyth Lazer + +This guide explains how to subscribe to price updates from Pyth Lazer. This guide will also explain various properties and channels that one can use to customize the price updates. + +Subscribing to price updates is a three-step process: + +1. **Acquire** an access token. +2. **Adjust** subscription parameters. +3. **Subscribe** to the price updates via [websocket API](https://pyth-lazer.dourolabs.app/docs). + +The websocket server is available at `wss://pyth-lazer.dourolabs.app/v1/stream{:bash}`. + + + +### 1. Acquire an access token + +Please fill out [this form](https://tally.so/r/nP2lG5) to contact the Pyth team and get the access token. + +Use the access token to authenticate the websocket connection by passing it as an `Authorization{:bash}` header with the value `Bearer {token}{:bash}`. + +### 2. Adjust subscription parameters + +One can configure the request/subscription parameters to customize the received price updates. A sample request is shown below: + +```js +client.send({ + type: "subscribe", + subscriptionId: 1, + priceFeedIds: [1, 2], + properties: ["price"], + chains: ["solana"], + channel: "fixed_rate@200ms", +}); +``` + +Here: + +- `subscriptionId` is an arbitrary numeric identifier one can choose for a subscription. It will be returned back in response by the server. It doesn not affect the signed payload. +- `priceFeedIds` is the list of price feeds one like to receive. Data for all price feeds will be present in the signed price updates generated. Refer to the [Price Feed IDs list](../price-feeds-ids.mdx) for the supported price feeds. +- `properties` is the list of properties one can request, such as **price**, **bestBidPrice**, **bestAskPrice**, etc. +- `chains` is the list of chains for which one need a signed payload, such as **evm**, **solana**, etc. +- `channel` allows to configure the update rate: updates in the **real_time** channel are sent as frequently as possible, while **fixed_rate@200ms** and **fixed_rate@50ms** channels are updated at fixed rates. + +There are also a few other parameters one may use. Refer to the [API documentation](https://pyth-lazer.dourolabs.app/docs) for more details. + +### 3. Subscribe to the price updates + +To subscribe to the price updates, one needs to send the request to the websocket server. The server will respond with a signed price update. + +1. Pyth Lazer provides a [SDK](https://github.com/pyth-network/pyth-crosschain/tree/main/lazer/sdk/js) to seamlessly integrate the websocket API into your application. + It can be installed using the following command: + +```bash +npm install --save @pythnetwork/pyth-lazer-sdk +``` + +2. Then create a [`PythLazerClient`](https://github.com/pyth-network/pyth-crosschain/blob/main/lazer/sdk/js/src/client.ts#L32) object using the URL and the access token requested from the Pyth team in the first step. + +```js +import { PythLazerClient } from "@pythnetwork/pyth-lazer-sdk"; + +const client = new PythLazerClient( + "wss://pyth-lazer.dourolabs.app/v1/stream", + "ctoken1" +); +``` + +3. After the client is created, one can adjust the subscription parameters and subscribe to the price updates. + +```js +client.ws.addEventListener("open", () => { + client.send({ + type: "subscribe", + subscriptionId: 1, + priceFeedIds: [1, 2], + properties: ["price"], + chains: ["solana"], + channel: "fixed_rate@200ms", + }); +}); +``` + +4. One the connection is established, the server will start sending the price updates to the client. + +```js +client.addMessageListener((message) => { + console.log(message); +}); +``` + +By default, price updates contain the `parsed` field that one can use to easily interpret the price update in their backend or frontend, as well as `evm` and/or `solana` fields that contain data that one should include in the on-chain transaction: + +```json +{ + "type": "streamUpdated", + "subscriptionId": 1, + "parsed": { + "timestampUs": "1730986152400000", + "priceFeeds": [ + { + "priceFeedId": 1, + "price": "1006900000000" + }, + { + "priceFeedId": 2, + "price": "2006900000000" + } + ] + }, + "solana": { + "encoding": "hex", + "data": "b9011a82d239c094c52016990d6ca2b261dbb1157ad503cbd3ea0679493316150cf3457624d19ec3f6e0a0e94373ab0971e39d939beda15cc02eb3c5454eb700f1f7310df65210bee4fcf5b1cee1e537fabcfd95010297653b94af04d454fc473e94834f2a0075d3c7938094b99e52260600030201000000010000b5ea6fea00000002000000010000c58f44d3010000" + } +} +``` + + + +## Additional Resources + +You may find these additional resources helpful for subscribing to price updates from Pyth Lazer. + +### Price Feed IDs + +Pyth Lazer supports a wide range of price feeds. Consult the [Price Feed IDs](../price-feeds-ids.mdx) page for a complete list of supported price feeds. + +### Examples + +[pyth-lazer-example-js](https://github.com/pyth-network/pyth-examples/tree/main/lazer/js) is a simple example for subscribing to the Pyth Lazer websocket.