Oracles: Pyth and Switchboard
Programs can't fetch off-chain data, so oracles publish it into accounts the program reads. A Pyth consumer passes a price update account into the instruction and checks freshness (for example a maximum age) and confidence before using the price. Never trust an oracle account without verifying its owner and feed ID.
Reference: Pyth on Solana · Switchboard docs
Real code, line by line
5 excerpts from different projects. Each caption links the exact lines at the fork's pinned commit. The notes below each excerpt explain the lines that matter.
decoders/kamino-lending-decoder/src/instructions/refresh_reserve.rs L43–64 @ bebd108 · MIT · original let mut iter = accounts.iter(); let reserve = next_account(&mut iter)?; let lending_market = next_account(&mut iter)?; let pyth_oracle = next_account(&mut iter); let switchboard_price_oracle = next_account(&mut iter); let switchboard_twap_oracle = next_account(&mut iter); let scope_prices = next_account(&mut iter); let remaining = iter.as_slice(); Some(RefreshReserveInstructionAccounts { reserve, lending_market, pyth_oracle, switchboard_price_oracle, switchboard_twap_oracle, scope_prices, remaining: remaining.to_vec(), }) }}src/components/winisol/winisol-data-access.tsx L715–739 @ 1930ba0 · MIT · original } const sbRandomnessPubkey = res.data.randomnessKeys.sbRandomnessPubKey; const sbQueuePubkey = res.data.randomnessKeys.sbQueuePubKey; const isdevnet = SOLANA_ENVIRONMENT === 'devnet'; const switchboardIDL = await Program.fetchIdl( isdevnet ? sb.ON_DEMAND_DEVNET_PID : sb.ON_DEMAND_MAINNET_PID, { connection: new web3.Connection(CONNECTION_ENDPOINT) } ) as Idl; const switchboardProgram = new Program(switchboardIDL, provider); // const queueAccount = new sb.Queue(switchboardProgram, new web3.PublicKey(sbQueuePubkey)); const randomnessAccount = new sb.Randomness(switchboardProgram, new web3.PublicKey(sbRandomnessPubkey)); const queueAccount = new sb.Queue(switchboardProgram, new web3.PublicKey(sbQueuePubkey)); try { await queueAccount.loadData(); } catch (error) { console.log(error); process.exit(1); } const sbRevealIx = await randomnessAccount.revealIx(); const revealWinnerIx = await program.methods.revealWinner(lottery_id).accounts({ randomnessAccount: randomnessAccount.pubkey }).instruction();oracles/pyth/anchor/programs/pythexample/src/lib.rs L1–24 @ de09019 · MIT · originaluse pyth_solana_receiver_sdk::price_update::PriceUpdateV2;use anchor_lang::prelude::*; declare_id!("GUkjQmrLPFXXNK1bFLKt8XQi6g3TjxcHVspbjDoHvMG2"); #[program]pub mod anchor_test { use super::*; pub fn read_price(ctx: Context<ReadPrice>) -> Result<()> { let price_update = &ctx.accounts.price_update; msg!("Price feed id: {:?}", price_update.price_message.feed_id); msg!("Price: {:?}", price_update.price_message.price); msg!("Confidence: {:?}", price_update.price_message.conf); msg!("Exponent: {:?}", price_update.price_message.exponent); msg!("Publish Time: {:?}", price_update.price_message.publish_time); Ok(()) }} #[derive(Accounts)]pub struct ReadPrice<'info> { pub price_update: Account<'info, PriceUpdateV2>,}- Line 1
use pyth_solana_receiver_sdk::price_update::PriceUpdateV2;: Reads a Pyth price and enforces a maximum age. - Line 12
msg!("Price feed id: {:?}", price_update.price_message.feed_id);: Logs to the program log, which is visible in explorers and the test output.
ts/packages/spl-token-lending/program/lib.rs L129–154 @ 541295d · Apache-2.0 · original #[account(mut)] reserve_collateral_mint: AccountInfo<'info>, #[account(mut)] reserve_collateral_supply: AccountInfo<'info>, pyth_product: AccountInfo<'info>, pyth_price: AccountInfo<'info>, lending_market: AccountInfo<'info>, lending_market_authority: AccountInfo<'info>, lending_market_owner: Signer<'info>, user_transfer_authority: Signer<'info>, clock: Sysvar<'info, Clock>, rent: Sysvar<'info, Rent>, token_program: Program<'info, Token>,} #[derive(Accounts)]pub struct RefreshReserve<'info> { #[account(mut)] reserve: AccountInfo<'info>, reserve_liquidity_oracle: AccountInfo<'info>, clock: Sysvar<'info, Clock>,} #[derive(Accounts)]pub struct DepositReserveLiquidity<'info> { #[account(mut)]- Line 137
lending_market_owner: Signer<'info>,: Requires this account to have signed the transaction.
storage-bigtable/proto/google.api.rs L613–638 @ 5770743 · Apache-2.0 · original pub common: ::core::option::Option<CommonLanguageSettings>, /// Experimental features to be included during client library generation. #[prost(message, optional, tag = "2")] pub experimental_features: ::core::option::Option< python_settings::ExperimentalFeatures, >,}/// Nested message and enum types in `PythonSettings`.pub mod python_settings { /// Experimental features to be included during client library generation. /// These fields will be deprecated once the feature graduates and is enabled /// by default. #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct ExperimentalFeatures { /// Enables generation of asynchronous REST clients if `rest` transport is /// enabled. By default, asynchronous REST clients will not be generated. /// This feature will be enabled by default 1 month after launching the /// feature in preview packages. #[prost(bool, tag = "1")] pub rest_async_io_enabled: bool, /// Enables generation of protobuf code using new types that are more /// Pythonic which are included in `protobuf>=5.29.x`. This feature will be /// enabled by default 1 month after launching the feature in preview /// packages. #[prost(bool, tag = "2")] pub protobuf_pythonic_types_enabled: bool,Go further
- carbon: explore the full project
- winisol: explore the full project
- program-examples: explore the full project
- anchor: explore the full project
- agave: explore the full project
- Next lesson: Solana Pay
Related lessons
- Escrow patterns
- Staking and reward accrual
- Program Derived Addresses (PDAs)
- Cross-Program Invocations (CPIs)
- SPL Tokens: mints, accounts and transfers
Topics: DeFi · Learning path · Glossary