This tutorial demonstrates how to build an ultra-optimized Switchboard oracle integration using the Pinocchio framework, achieving ~90% reduction in compute units compared to the basic Anchor implementation.
Why Optimize Compute Units?
On Solana, compute units directly impact transaction costs. Every CU saved means lower fees for your users and operators. This matters especially for:
High-frequency oracle updates: If you're cranking prices every few seconds, those CUs add up fast
Production DeFi protocols: Lower costs improve margins and user experience
MEV-sensitive operations: Smaller, faster transactions have competitive advantages
Metric
Basic (Anchor)
Advanced (Pinocchio)
Savings
Compute Units
~2,000 CU
~190 CU
~90%
Framework Overhead
High
Minimal
-
Transaction Size
Standard
Optimized with LUTs
~90%
What You'll Build
An ultra-optimized oracle program with:
Admin authorization for trusted crankers
Four modular instructions: init_state, init_oracle, crank, read
Direct oracle writes bypassing expensive verification (for authorized users)
QuoteVerifier for secure reads with staleness checks
Pinocchio is a zero-abstraction Solana program framework that provides:
Direct syscall access without runtime overhead
Zero-allocation account parsing
#[inline(always)] instruction dispatch
~100 CU framework overhead vs ~2,000 CU for Anchor
Admin Authorization Pattern
Instead of verifying every oracle update cryptographically (expensive), this pattern:
Stores an authorized signer in a state account
Only allows that signer to write oracle data
Uses write_from_ix_unchecked for direct writes
This trades decentralization for efficiency—suitable when you control the cranker.
Modular Account Initialization
The program separates account creation into dedicated instructions:
init_state: Creates the authorization state account
init_oracle: Creates the quote storage account
This allows conditional initialization only when needed.
The On-Chain Program
Program Structure
Use the current Pinocchio and Switchboard dependencies:
The instruction discriminator is a single byte—no Anchor discriminator overhead.
Instruction 0: crank
The crank instruction writes oracle data for authorized signers:
Key points:
Uses AccountView accessors for low-overhead account reads and writes
write_from_ix_unchecked directly writes oracle data without Ed25519 verification
Only ~90 CU for the entire operation
Instruction 1: read
The read instruction verifies and displays oracle data:
Key points:
Uses QuoteVerifier for secure verification
max_age(30) ensures data freshness (30 slots ≈ 12 seconds)
Iterates through all feeds in the quote
Instruction 2: init_state
Initializes the authorization state account:
The state account stores a single 32-byte pubkey—the authorized cranker.
Instruction 3: init_oracle
Initializes the oracle quote account:
Account Initialization Helpers
The utils.rs module handles PDA derivation and account creation:
Quote Account Initialization
State Account Initialization
The TypeScript Client
The client handles initialization, quote fetching, and transaction building:
OracleQuote.decode(...) parses the Ed25519 quote instruction payload. Stored quote-program accounts are variable-length; read them with the Rust SwitchboardQuote/PackedFeedInfo account types instead of fixed offsets. See Quote Program Accounts.
Running the Example
1. Clone the Examples Repository
2. Install Dependencies
3. Build and Deploy the Program
Unlike the basic example, the advanced program must be deployed:
4. Run the Example
Expected Output
When to Use This Pattern
Use the advanced pattern when:
Scenario
Recommendation
High-frequency cranking (sub-second)
Advanced
Compute-sensitive DeFi protocols
Advanced
You control the cranker infrastructure
Advanced
Learning / prototyping
Basic
Decentralized, trustless updates
Basic
Simple integrations
Basic
Security Considerations
write_from_ix_unchecked Risks
This function bypasses cryptographic verification. Only use it when:
You control the signer: The authorized cranker is your infrastructure
You trust all transaction accounts: Malicious accounts could corrupt data
You have external monitoring: Detect and respond to anomalies
[dependencies]
switchboard-on-demand = { version = "0.13.0", features = ["pinocchio", "devnet"] }
pinocchio = { version = "0.11.2", features = ["cpi"] }
solana-msg = "2.2.1"
use pinocchio::{entrypoint, AccountView, Address, ProgramResult};
use pinocchio::error::ProgramError;
use solana_msg::msg;
use switchboard_on_demand::{
check_pubkey_eq, get_slot, Instructions, OracleQuote, QuoteVerifier
};
mod utils;
use utils::{init_quote_account_if_needed, init_state_account_if_needed};
entrypoint!(process_instruction);
pub fn process_instruction(
program_id: &Address,
accounts: &mut [AccountView],
instruction_data: &[u8],
) -> ProgramResult {
match instruction_data[0] {
0 => crank(program_id, accounts)?, // Write oracle data
1 => read(program_id, accounts)?, // Read and verify
2 => init_state(program_id, accounts)?, // Initialize state
3 => init_oracle(program_id, accounts)?, // Initialize oracle account
_ => return Err(ProgramError::InvalidInstructionData),
}
Ok(())
}
pub fn crank(program_id: &Address, accounts: &mut [AccountView]) -> ProgramResult {
let [quote, queue, state, payer, instructions_sysvar, _clock_sysvar]: &mut [AccountView; 6] =
accounts.try_into().map_err(|_| ProgramError::NotEnoughAccountKeys)?;
// Validate state account belongs to this program
if !is_state_account(state, program_id) {
msg!("Invalid state account");
return Err(ProgramError::Custom(2));
}
// Check payer matches authorized signer stored in state
let state_data = state.try_borrow()?;
if !check_pubkey_eq(&state_data[..32], payer.address()) {
return Err(ProgramError::Custom(1)); // UnauthorizedSigner
}
// DANGER: Only use this if you trust the signer!
// Bypasses cryptographic verification for speed
OracleQuote::write_from_ix_unchecked(&*instructions_sysvar, quote, queue.address(), 0);
Ok(())
}
pub fn read(_program_id: &Address, accounts: &mut [AccountView]) -> ProgramResult {
let [quote, queue, clock_sysvar, slothashes_sysvar, instructions_sysvar]: &mut [AccountView; 5] =
accounts.try_into().map_err(|_| ProgramError::NotEnoughAccountKeys)?;
let slot = get_slot(&*clock_sysvar);
// Verify the oracle data with staleness check
let quote_data = QuoteVerifier::new()
.slothash_sysvar(&*slothashes_sysvar)
.ix_sysvar(&*instructions_sysvar)
.clock_slot(slot)
.queue(&*queue)
.max_age(30) // Reject data older than 30 slots (~12 seconds)
.verify_account(queue.address(), quote)
.unwrap();
msg!("Quote slot: {}", quote_data.slot());
// Display each feed's data
for (index, feed_info) in quote_data.feeds().iter().enumerate() {
msg!("Feed #{}: {}", index + 1, feed_info.hex_id());
msg!("Value: {}", feed_info.value());
}
Ok(())
}
pub fn init_state(program_id: &Address, accounts: &mut [AccountView]) -> ProgramResult {
let [state, payer, system_program]: &mut [AccountView; 3] =
accounts.try_into().map_err(|_| ProgramError::NotEnoughAccountKeys)?;
// Create the state account PDA
init_state_account_if_needed(program_id, state, payer, system_program)?;
// Store the authorized signer (payer becomes the admin)
state.try_borrow_mut()?[..32].copy_from_slice(payer.address().as_ref());
Ok(())
}
pub fn init_oracle(program_id: &Address, accounts: &mut [AccountView]) -> ProgramResult {
let [quote, queue, payer, system_program, instructions_sysvar]: &mut [AccountView; 5] =
accounts.try_into().map_err(|_| ProgramError::NotEnoughAccountKeys)?;
// Parse feed info from the Ed25519 instruction data
let quote_data = Instructions::parse_ix_data_unverified(&*instructions_sysvar, 0)
.map_err(|_| ProgramError::InvalidInstructionData)?;
// Create the quote account as a PDA derived from queue + feed IDs
init_quote_account_if_needed(
program_id,
quote,
queue,
payer,
system_program,
"e_data,
)?;
Ok(())
}
pub const ORACLE_ACCOUNT_SIZE: usize = 8 + 32 + 1024; // discriminator + queue + data
pub fn init_quote_account_if_needed(
program_id: &Address,
oracle_account: &mut AccountView,
queue_account: &mut AccountView,
payer: &mut AccountView,
system_program: &mut AccountView,
oracle_quote: &OracleQuote,
) -> Result<(), ProgramError> {
// Skip if already initialized
if oracle_account.lamports() != 0 {
msg!("Oracle account already initialized");
return Ok(());
}
// Derive PDA from queue + feed IDs
let feed_ids = oracle_quote.feed_ids();
let mut seeds: Vec<&[u8]> = Vec::with_capacity(feed_ids.len() + 1);
seeds.push(queue_account.address().as_ref());
for feed_id in &feed_ids {
seeds.push(feed_id.as_ref());
}
let (canonical_address, bump) = Address::find_program_address(&seeds, program_id);
if canonical_address != *oracle_account.address() {
return Err(ProgramError::InvalidArgument);
}
// Create account via CPI to system program
// ... (invoke_signed with seeds)
}
pub const STATE_ACCOUNT_SIZE: usize = 32; // Single pubkey
pub fn init_state_account_if_needed(
program_id: &Address,
state_account: &mut AccountView,
payer: &mut AccountView,
system_program: &mut AccountView,
) -> Result<(), ProgramError> {
if state_account.lamports() != 0 {
return Ok(());
}
// Derive PDA from "state" seed
let (expected_key, bump) = Address::find_program_address(&[b"state"], program_id);
if state_account.address() != &expected_key {
return Err(ProgramError::InvalidArgument);
}
// Create account via CPI
// ... (invoke_signed with ["state", bump] seeds)
}
import * as sb from "@switchboard-xyz/on-demand";
import { OracleQuote } from "@switchboard-xyz/on-demand";
import { PublicKey } from "@solana/web3.js";
const FEED_ID = "4cd1cad962425681af07b9254b7d804de3ca3446fbfd1371bb258d2c75059812";
async function main() {
// Step 1: Load environment (auto-detects network)
const { keypair, connection, crossbar, queue } = await sb.AnchorUtils.loadEnv();
// Load your deployed program ID
const advancedProgramId = new PublicKey("YOUR_PROGRAM_ID");
// Step 2: Derive the canonical quote account
// Note: includes program ID for program-owned accounts
const [quoteAccount] = OracleQuote.getCanonicalPubkey(
queue.pubkey,
[FEED_ID],
advancedProgramId // Program ID for custom derivation
);
// Step 3: Fetch the Ed25519 quote instruction
const quoteIx = await queue.fetchQuoteIx(crossbar, [FEED_ID], {
variableOverrides: {},
});
// Step 4: Decode and inspect the quote
const decodedQuote = OracleQuote.decode(quoteIx.data);
console.log("Quote slot:", decodedQuote.slot.toString());
console.log("Feeds:", decodedQuote.feeds.length);
decodedQuote.feeds.forEach((feed, idx) => {
console.log(` Feed ${idx}: ${feed.value}`);
});
// Step 5: Check if accounts need initialization
const [stateAccount] = PublicKey.findProgramAddressSync(
[Buffer.from("state")],
advancedProgramId
);
const instructions = [quoteIx];
// Add init_state if needed
const stateInfo = await connection.getAccountInfo(stateAccount);
if (!stateInfo) {
instructions.push(createInitStateIx(advancedProgramId, keypair.publicKey));
}
// Add init_oracle if needed
const quoteInfo = await connection.getAccountInfo(quoteAccount);
if (!quoteInfo) {
instructions.push(createInitOracleIx(
advancedProgramId, quoteAccount, queue.pubkey, keypair.publicKey
));
}
// Step 6: Add crank and read instructions
instructions.push(createCrankIx(
advancedProgramId, quoteAccount, queue.pubkey, stateAccount, keypair.publicKey
));
instructions.push(createReadIx(advancedProgramId, quoteAccount, queue.pubkey));
// Step 7: Build V0 transaction with priority fees
const tx = await sb.asV0Tx({
connection,
ixs: instructions,
signers: [keypair],
computeUnitPrice: 10_000,
computeUnitLimitMultiple: 1.1,
});
// Step 8: Simulate and send
const sim = await connection.simulateTransaction(tx);
console.log(sim.value.logs?.join("\n"));
if (!sim.value.err) {
const sig = await connection.sendTransaction(tx);
console.log("Transaction:", sig);
}
}
git clone https://github.com/switchboard-xyz/sb-on-demand-examples
cd sb-on-demand-examples/solana/feeds/advanced
npm install
# Build the Pinocchio program
cargo build-sbf --manifest-path programs/advanced-oracle-example/Cargo.toml
# Deploy to devnet
solana program deploy target/deploy/advanced_oracle_example.so
# Using default BTC/USD feed
npm run update
# Using a custom feed ID
npm run update -- --feedId YOUR_FEED_ID