← Articles
Pyth Lazer: pull prices on-chain in 2026 logo
pyth

Pyth Lazer: pull prices on-chain in 2026

· AUG 29, 2026 ·
Read
Share

Subscribe to Lazer, drop the signed payload in the trade tx, verify, then read price. That is what a program actually consumes.

devrels.xyz/a/270

Pyth Lazer is the low-latency pull oracle. You subscribe off-chain, get a signed price payload, and put that payload in the same transaction that needs the mark. Docs now call the product Pyth Pro. The SDK, crate, and program still say Lazer.

A program does not poll a live price account the way the old Solana push feed works. It verifies the message, then reads price, exponent, and timestamp from the payload. Drift already runs this path for live markets.

What the program consumes

Three pieces land in one transaction. Instruction 0 is the SVM Ed25519 precompile (it cannot be CPI'd). Instruction 1 is yours: CPI VerifyMessage into the Lazer program, then parse the little-endian payload. Accounts: payer, Lazer program, storage, treasury, System Program, instructions sysvar.

Core pull vs Lazer pull
PieceCore (Hermes)Lazer / Pro
Off-chainHermes client, Wormhole-signed VAA@pythnetwork/pyth-lazer-sdk, API key, WebSocket /v1/stream
On-chain programReceiver rec5EKMGg6MxZYaMdyBfgwp4d5rB9T1VQH5pJv5LtFJpytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt
What you readPriceUpdateV2 after post_updateVerified payload: feed id, price, exponent, timestampUs
CadenceHermes pull, typically hundreds of msChannels: real-time, 50 ms, 200 ms, 1000 ms

Schema and the older push account live in Pyth: the price layer. Product map: what the oracle is.

Subscribe, then attach

Request an API key from the Pro portal. Connect all three Douro WebSockets so a deploy on one host does not stall you. Ask for formats: ["solana"] so the binary is Ed25519, little-endian. Add feedUpdateTimestamp in production so you can tell a fresh tick from a carried-forward one.

typescript
import { PythLazerClient } from "@pythnetwork/pyth-lazer-sdk"

const client = await PythLazerClient.create({
  urls: [
    "wss://pyth-lazer-0.dourolabs.app/v1/stream",
    "wss://pyth-lazer-1.dourolabs.app/v1/stream",
    "wss://pyth-lazer-2.dourolabs.app/v1/stream",
  ],
  token: process.env.ACCESS_TOKEN!,
})

client.subscribe({
  type: "subscribe",
  subscriptionId: 1,
  priceFeedIds: [1, 2],
  properties: ["price", "exponent", "feedUpdateTimestamp"],
  formats: ["solana"],
  deliveryFormat: "json",
  channel: "fixed_rate@200ms",
  jsonBinaryEncoding: "hex",
})
// message.solana.data is the hex payload your tx must carry.

Server-to-server: send the long-lived key as Authorization: Bearer. Browsers get a short JWT from POST /auth/token so the raw key never leaves your backend.

Verify, then trust the bytes

Crate pyth-lazer-solana-contract with no-entrypoint. Storage 3rdJbqfnagQ4yx9HXJViD4zc4xpiSqmFsKpPuSCQVyQL. Treasury Gx4MBPb1vqZLJajZmsKLg8fGw9ErhoKsR8LeKcCKFyak. After verify, deserialize with PayloadData::deserialize_slice_le, match your feed id, reject a timestamp older than your staleness window (Drift uses 15 seconds lag / 60 seconds future), and skip a zero price.

rust
invoke(
    &ProgramInstruction::new_with_bytes(
        pyth_lazer_solana_contract::ID,
        &VerifyMessage {
            message_data: pyth_message.to_vec(),
            ed25519_instruction_index: 0,
            signature_index: 0,
        }
        .data(),
        vec![
            AccountMeta::new(*payer.key, true),
            AccountMeta::new_readonly(*storage.key, false),
            AccountMeta::new(*treasury.key, false),
            AccountMeta::new_readonly(*system_program.key, false),
            AccountMeta::new_readonly(*instructions_sysvar.key, false),
        ],
    ),
    accounts,
)?;
let data = PayloadData::deserialize_slice_le(verified.payload)?;

Example consumer: pyth-examples/lazer/solana. Feed catalog: Pro price feed IDs. SVM guide: Consume data on Solana and Fogo.

People and links

Keep reading

Get new articles in your inbox

Technical deep-dives on Solana tooling, infrastructure, and ecosystem. No noise.