All articles
solanaswitchboardoraclevrf

Switchboard: the customisable oracle, queues, jobs, and aggregators

Switchboard is Solana's customisable oracle network. Queues, oracles, jobs, aggregators, randomness — here's the on-chain shape and how to wire each up.

Switchboard is Solana's other major oracle. Pyth handles a curated list of canonical asset prices; Switchboard lets you publish a feed of anything by writing a job definition. Sports scores, custom CEX prices, weather data, election results, on-chain randomness — same primitive across all of them.

The four account types

text
Queue          → the oracle network that processes update requests
Oracle         → a participant in a queue, runs the off-chain worker
Aggregator     → the on-chain feed account holding the latest aggregated value
Job            → a definition of how to fetch + transform raw data
                 (referenced by Aggregators; each Aggregator can have N Jobs)
Crank          → a permissionless turn-the-handle account that triggers
                 due updates

Switchboard's permissionless design: anyone can create an Aggregator with their own Jobs, pay into a queue, and start receiving updates from the queue's oracles. The on-chain program enforces validation; the off-chain oracles compete to publish.

What a Job looks like

Jobs are serialized protobuf describing a pipeline of tasks: HTTP fetch → JSON path extract → math transform. Each oracle in the queue runs the same job, posts its result, and the aggregator takes the median (with outlier rejection).

json
{
  "tasks": [
    {
      "httpTask": {
        "url": "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=solana"
      }
    },
    {
      "jsonParseTask": {
        "path": "$[0].current_price"
      }
    }
  ]
}

That's the entire definition for a SOL/USD feed. Switch solana to bitcoin and you have BTC/USD. Replace the URL with a sports API and JSON path with a score field, and you have a live game score feed.

The Aggregator account

rust
pub struct AggregatorAccountData {
    pub name:                 [u8; 32],
    pub metadata:             [u8; 128],
    pub author_wallet:        Pubkey,
    pub queue_pubkey:         Pubkey,                  // which queue this feed lives in
    pub oracle_request_batch_size: u32,                // how many oracles must respond
    pub min_oracle_results:   u32,                     // minimum valid responses for an update
    pub min_job_results:      u32,
    pub min_update_delay_seconds: u32,                 // throttle — minimum seconds between updates
    pub start_after:          i64,
    pub variance_threshold:   SwitchboardDecimal,      // % change required to trigger an update
    pub force_report_period:  i64,
    pub expiration:           i64,
    pub consecutive_failure_count: u64,
    pub next_allowed_update_time: i64,
    pub is_locked:            bool,
    pub crank_pubkey:         Pubkey,
    pub latest_confirmed_round: AggregatorRound {
        num_success:         u32,
        num_error:           u32,
        is_closed:           bool,
        round_open_slot:     u64,
        round_open_timestamp: i64,
        result:              SwitchboardDecimal,      // the actual aggregated value
        std_deviation:       SwitchboardDecimal,
        min_response:        SwitchboardDecimal,
        max_response:        SwitchboardDecimal,
        oracle_pubkeys_data: [Pubkey; 16],
        medians_data:        [SwitchboardDecimal; 16],
        current_payout:      [i64; 16],
        errors_fulfilled:    [bool; 16],
        medians_fulfilled:   [bool; 16],
    },
    pub current_round:        AggregatorRound,
    pub job_pubkeys_data:     [Pubkey; 16],
    pub job_hashes:           [Hash; 16],
    pub job_pubkeys_size:     u32,
    pub jobs_checksum:        [u8; 32],
    pub authority:            Pubkey,
    pub history_buffer:       Pubkey,                  // optional ring-buffer of past results
    pub previous_confirmed_round_result: SwitchboardDecimal,
    pub previous_confirmed_round_slot: u64,
    pub disable_crank:        bool,
    pub job_weights:          [u8; 16],
    pub creation_timestamp:   i64,
    pub _ebuf:                [u8; 130],
}

Reading a Switchboard feed

rust
use anchor_lang::prelude::*;
use switchboard_solana::AggregatorAccountData;

#[derive(Accounts)]
pub struct UseSwitchboard<'info> {
    /// CHECK: validated by Switchboard
    pub aggregator: AccountLoader<'info, AggregatorAccountData>,
}

pub fn read_price(ctx: Context<UseSwitchboard>) -> Result<()> {
    let agg = ctx.accounts.aggregator.load()?;

    // Returns SwitchboardDecimal { mantissa: i128, scale: u32 }
    let result = agg.get_result()?;
    let price_f64: f64 = result.try_into()?;

    // Reject if stale (>5 minutes old)
    agg.check_staleness(Clock::get()?.unix_timestamp, 300)?;

    // Reject if confidence too wide (variance > 1%)
    agg.check_confidence_interval(SwitchboardDecimal::from_f64(0.01))?;

    msg!("price: {}", price_f64);
    Ok(())
}

Switchboard randomness (VRF)

Beyond price feeds, Switchboard publishes a verifiable random function — on-chain randomness backed by an oracle's cryptographic proof. Useful for games, fair draws, mint ordering.

rust
use switchboard_solana::VrfAccountData;

pub fn use_random(ctx: Context<UseVrf>) -> Result<()> {
    let vrf = ctx.accounts.vrf_account.load()?;
    let randomness = vrf.get_result()?;        // [u8; 32]

    // Use first 8 bytes as u64
    let n = u64::from_le_bytes(randomness[..8].try_into().unwrap());
    let dice = (n % 6) + 1;

    msg!("rolled: {}", dice);
    Ok(())
}

Switchboard vs Pyth

text
                  Pyth                       Switchboard
─────────────────────────────────────────────────────────────────
Model             Push (account)             Push (account)
Custom feeds      No — curated symbols       Yes — any job definition
Setup cost        Free (read the account)    Pay queue lease + per-update
Update cadence    Every slot                 Configurable (variance + delay)
Confidence        First-class (1-sigma)      Variance + std deviation
Randomness        No                         Yes (VRF)
Best for          Canonical asset prices     Custom data, long-tail symbols

References

Pyth for canonical asset prices, Switchboard for everything else. Both run in production on every major Solana DeFi protocol's read path.

Switchboard: the customisable oracle, queues, jobs, and aggregators | devrels.xyz