blueshift-gg
doppler
Rust

The fastest oracle on Solana

Last updated Jun 17, 2026
166
Stars
35
Forks
5
Issues
0
Stars/day
Attention Score
87
Language breakdown
No language data available.
Files click to expand
README

A 21 CU Solana Oracle Program

Overview

Doppler is an ultra-optimized oracle program for Solana, achieving unparalleled performance at just 21 Compute Units (CUs) per update. Built with low-level optimizations and minimal overhead, Doppler sets the standard for high-frequency, low-latency price feeds on Solana.

Features

  • 21 CU Oracle Updates: The most efficient oracle implementation on Solana
  • Generic Payload Support: Flexible data structure supporting any payload type
  • Sequence-Based Updates: Built-in replay protection and ordering guarantees
  • Zero Dependencies: Pure no_std Rust implementation for minimal overhead
  • Direct Memory Operations: Optimized assembly-level exits for maximum efficiency

Installation

Add Doppler SDK and required Solana crates to your Cargo.toml:

[dependencies]
doppler-sdk = "0.1.0"
solana-instruction = "2.3.0"
solana-pubkey = "2.3.0"
solana-compute-budget-interface = "2.2.2"
solana-transaction = "2.3.0"
solana-keypair = "2.3.0"
solana-signer = "2.2.1"

Add other Solana crates as needed

Program ID

fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm

Architecture

Doppler uses a simple yet powerful architecture:

  • Admin Account: Controls oracle updates (hardcoded for security)
  • Oracle Account: Stores the sequence number and payload data
  • Sequence Validation: Ensures updates are monotonically increasing

Data Structure

pub struct Oracle<T> {
    pub sequence: u64,  // Timestamp, slot height, or auto-increment
    pub payload: T,     // Your custom data structure
}

Usage Guide

1. Setting Up Compute Budget

To achieve the 21 CU performance, configure your transaction with appropriate compute budget:

use solanacomputebudget_interface::ComputeBudgetInstruction;
use solana_instruction::Instruction;
use solana_transaction::Transaction;

// Request exactly the CUs needed (21 + overhead for other instructions) let computebudgetix = ComputeBudgetInstruction::setcomputeunitlimit(200000);

// Add to your transaction let mut instructions = vec![computebudgetix];

2. Setting Priority Fees

For high-frequency oracle updates, use priority fees to ensure timely inclusion:

// Set priority fee (price per compute unit in micro-lamports)
let priorityfeeix = ComputeBudgetInstruction::setcomputeunit_price(1000);

instructions.push(priorityfeeix);

3. Optimizing Account Data Size

Use setLoadedAccountsDataSizeLimit to optimize memory allocation:

// Set the maximum loaded account data size
// Calculate based on your oracle data structure size
let datasizelimitix = ComputeBudgetInstruction::setloadedaccountsdatasizelimit(
    32_768  // 32KB is usually sufficient for oracle operations
);

instructions.push(datasizelimit_ix);

4. Creating an Oracle Update

use dopplersdk::{Oracle, UpdateInstruction, ID as DOPPLERID};
use solana_instruction::Instruction;
use solana_pubkey::Pubkey;

// Define your payload structure #[derive(Clone, Copy)] pub struct PriceFeed { pub price: u64, }

// Create oracle update let oracle_update = Oracle { sequence: 1234567890, // Must be > current sequence payload: PriceFeed { price: 42000000, // $42.00 with 6 decimals }, };

// Create update instruction let update_ix: Instruction = UpdateInstruction { admin: admin_pubkey, oraclepubkey: oraclepubkey, oracle: oracle_update, }.into();

// Add to instructions instructions.push(update_ix);

5. Complete Transaction Example

use doppler_sdk::{Oracle, UpdateInstruction};
use solanaclient::rpcclient::RpcClient;
use solanacomputebudget_interface::ComputeBudgetInstruction;
use solana_instruction::Instruction;
use solana_keypair::Keypair;
use solana_signer::Signer;
use solana_transaction::Transaction;

async fn update_oracle( client: &RpcClient, admin: &Keypair, oracle_pubkey: Pubkey, new_price: u64, sequence: u64, ) -> Result<(), Box<dyn std::error::Error>> { // Build all instructions let mut instructions = vec![ // 1. Set compute budget ComputeBudgetInstruction::setcomputeunitlimit(200000),

// 2. Set priority fee (1000 micro-lamports per CU) ComputeBudgetInstruction::setcomputeunitprice(1000),

// 3. Set loaded accounts data size limit ComputeBudgetInstruction::setloadedaccountsdatasizelimit(32768), ];

// 4. Add oracle update let oracle_update = Oracle { sequence, payload: PriceFeed { price: new_price }, };

let update_ix: Instruction = UpdateInstruction { admin: admin.pubkey(), oracle_pubkey, oracle: oracle_update, }.into();

instructions.push(update_ix);

// Create and send transaction let recentblockhash = client.getlatest_blockhash()?; let tx = Transaction::newsignedwith_payer( &instructions, Some(&admin.pubkey()), &[admin], recent_blockhash, );

let signature = client.sendandconfirm_transaction(&tx)?; println!("Oracle updated: {}", signature);

Ok(()) }

Performance Optimization Tips

1. Compute Budget Configuration

  • Exact CU Request: Request only what you need (21 CUs + overhead)
  • Priority Fees: Use dynamic priority fees based on network congestion
  • Account Data Size: Minimize loaded data to reduce memory overhead

2. Batching Updates

For multiple oracle updates, batch them efficiently:

// DON'T: Multiple transactions
for oracle in oracles {
    send_update(oracle)?;  // 21 CU each, but multiple transactions
}

// DO: Single transaction with multiple updates let mut instructions = vec![ ComputeBudgetInstruction::setcomputeunitlimit(200000), ComputeBudgetInstruction::setcomputeunitprice(1000), ComputeBudgetInstruction::setloadedaccountsdatasizelimit(65536), ];

for oracle in oracles { instructions.push(createupdateinstruction(oracle)); } // Single transaction with all updates

3. Network Optimization

// Use getRecentPrioritizationFees to determine optimal fee
let recentfees = client.getrecentprioritizationfees(&[oracle_pubkey])?;
let optimalfee = calculateoptimalfee(recentfees);

let priorityix = ComputeBudgetInstruction::setcomputeunitprice(optimal_fee);

Testing

Build

# Within root
cargo build-sbf --manifest-path program/Cargo.toml

Unit

Run the test suite:

# Run all tests
cargo test

E2E

chmod +X ./surfpool.sh
./surfpool.sh

cargo run --bin single-price-feed cargo run --bin multiple-price-feed

example of single price feed update response

Transaction executed in slot 131:
  Block Time: 2025-09-03T04:23:08+03:00
  Version: legacy
  Recent Blockhash: 89ZvpNezGugkfm9LnN99rhb6aTNaW1cLKkS2DDbr7NPA
  Signature 0: m14zQFvt1jU9YYM2QAmVSnMZUa5P2eKdtP21Shu9w9kEhxKLAfJoUyqZwiTt43hGwewhsahQJi5eLJ71NptUWDu
  Account 0: srw- admnz5UvRa93HM5nTrxXmsJ1rw2tvXMBFGauvCgzQhE (fee payer)
  Account 1: -rw- QUVF91dzXWYvE5FmFEc41JZxRDmNgx8S8P6sNDWYZiW
  Account 2: -r-x ComputeBudget111111111111111111111111111111
  Account 3: -r-x fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm
  Instruction 0
    Program:   ComputeBudget111111111111111111111111111111 (2)
    Data: [3, 232, 3, 0, 0, 0, 0, 0, 0]
  Instruction 1
    Program:   ComputeBudget111111111111111111111111111111 (2)
    Data: [2, 215, 1, 0, 0]
  Instruction 2
    Program:   ComputeBudget111111111111111111111111111111 (2)
    Data: [4, 127, 0, 0, 0]
  Instruction 3
    Program:   fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm (3)
    Account 0: admnz5UvRa93HM5nTrxXmsJ1rw2tvXMBFGauvCgzQhE (0)
    Account 1: QUVF91dzXWYvE5FmFEc41JZxRDmNgx8S8P6sNDWYZiW (1)
    Data: [159, 136, 1, 0, 0, 0, 0, 0, 64, 226, 1, 0, 0, 0, 0, 0, 160, 213, 119, 107, 1, 0, 0, 0]
  Status: Ok
    Fee: ◎0.000005001
    Account 0 balance: ◎9.999969996 -> ◎9.999964995
    Account 1 balance: ◎0.00100224
    Account 2 balance: ◎0.000000001
    Account 3 balance: ◎0.00114144
  Compute Units Consumed: 471
  Log Messages:
    Program ComputeBudget111111111111111111111111111111 invoke [1]
    Program ComputeBudget111111111111111111111111111111 success
    Program ComputeBudget111111111111111111111111111111 invoke [1]
    Program ComputeBudget111111111111111111111111111111 success
    Program ComputeBudget111111111111111111111111111111 invoke [1]
    Program ComputeBudget111111111111111111111111111111 success
    Program fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm invoke [1]
    Program fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm consumed 21 of 21 compute units
    Program fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm success

Finalized

Fully fledged tx requires: 471 CU + 111 bytes

example of multiple price feed update response

Transaction executed in slot 218:
  Block Time: 2025-09-06T13:06:05+03:00
  Version: legacy
  Recent Blockhash: AeCvWYJjrx6Yxjknh6ndTTaTYsHkPQgr9iMURRN8Ah4S
  Signature 0: 3MLXk7YCsqEoMiYiGT4RYKa3Js2QJ6acM1BQstKGNbXsUJ6rNaySmUzzqNRDnFd7St1XTpPngAbcnf3ZxD2Lj9Jr
  Account 0: srw- admnz5UvRa93HM5nTrxXmsJ1rw2tvXMBFGauvCgzQhE (fee payer)
  Account 1: -rw- QUVF91dzXWYvE5FmFEc41JZxRDmNgx8S8P6sNDWYZiW
  Account 2: -rw- 6uQ848roY5vumz43QeQguE7xCyBSmgZbwNdJMTrs2Xhy
  Account 3: -rw- 9bA7GPqPpZ5aLbwb8E6cKvUPM8pcHXXTqLpf5zLAqHP5
  Account 4: -r-x ComputeBudget111111111111111111111111111111
  Account 5: -r-x fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm
  Instruction 0
    Program:   ComputeBudget111111111111111111111111111111 (4)
    Data: [3, 232, 3, 0, 0, 0, 0, 0, 0]
  Instruction 1
    Program:   ComputeBudget111111111111111111111111111111 (4)
    Data: [4, 175, 0, 0, 0]
  Instruction 2
    Program:   ComputeBudget111111111111111111111111111111 (4)
    Data: [2, 1, 2, 0, 0]
  Instruction 3
    Program:   fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm (5)
    Account 0: admnz5UvRa93HM5nTrxXmsJ1rw2tvXMBFGauvCgzQhE (0)
    Account 1: QUVF91dzXWYvE5FmFEc41JZxRDmNgx8S8P6sNDWYZiW (1)
    Data: [2, 0, 0, 0, 0, 0, 0, 0, 180, 134, 1, 0, 0, 0, 0, 0]
  Instruction 4
    Program:   fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm (5)
    Account 0: admnz5UvRa93HM5nTrxXmsJ1rw2tvXMBFGauvCgzQhE (0)
    Account 1: 9bA7GPqPpZ5aLbwb8E6cKvUPM8pcHXXTqLpf5zLAqHP5 (3)
    Data: [1, 0, 0, 0, 0, 0, 0, 0, 170, 134, 1, 0, 0, 0, 0, 0]
  Instruction 5
    Program:   fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm (5)
    Account 0: admnz5UvRa93HM5nTrxXmsJ1rw2tvXMBFGauvCgzQhE (0)
    Account 1: 6uQ848roY5vumz43QeQguE7xCyBSmgZbwNdJMTrs2Xhy (2)
    Data: [1, 0, 0, 0, 0, 0, 0, 0, 170, 134, 1, 0, 0, 0, 0, 0]
  Status: Ok
    Fee: ◎0.000005001
    Account 0 balance: ◎0.999994999 -> ◎0.999989998
    Account 1 balance: ◎0.00100224
    Account 2 balance: ◎0.00100224
    Account 3 balance: ◎0.00100224
    Account 4 balance: ◎0.000000001
    Account 5 balance: ◎0.00114144
  Compute Units Consumed: 513
  Log Messages:
    Program ComputeBudget111111111111111111111111111111 invoke [1]
    Program ComputeBudget111111111111111111111111111111 success
    Program ComputeBudget111111111111111111111111111111 invoke [1]
    Program ComputeBudget111111111111111111111111111111 success
    Program ComputeBudget111111111111111111111111111111 invoke [1]
    Program ComputeBudget111111111111111111111111111111 success
    Program fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm invoke [1]
    Program fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm consumed 21 of 63 compute units
    Program fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm success
    Program fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm invoke [1]
    Program fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm consumed 21 of 42 compute units
    Program fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm success
    Program fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm invoke [1]
    Program fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm consumed 21 of 21 compute units
    Program fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm success

Finalized

Expected Priority Score

based on the Anza's blog post and the code from single price feed update example

let's assume we are going to update a single oracle:

  • 1 signature
  • 0 write locks
  • Requested compute-budget-limit to 21 (with compute-budget instructions 321 and 471 respectively) CUs
  • Paying priority fee: 1.00 lamports per CU
| Metric | Without Instruction | With 111 byte Limit | | ------------------------------ | -------------------------------- | --------------------------------- | | Loaded Account Data Size Limit | 64M | 111 bytes | | Data Size Cost Calculation | 64M x (8/32K) | 111 bytes x (8/32K) | | Data Size Cost (CUs) | 16,000 | 0.02775 | | Reward to Leader Calculation | (1 x 5000 + 1 x 321)/2 | (1 x 5000 + 1 x 471)/2 | | Reward to Leader (lamports) | 2,660.5 | 2,735.5 | | Transaction Cost Formula | 1 x 720 + 0 x 300 + 321 + 16,000 | 1 x 720 + 0 x 300 + 471 + 0.02775 | | Transaction Cost (CUs) | 17,041 | 1,141.02775 | | Priority Score | 0.156 | 2.397 |

Building

Build the on-chain program:

# Build for Solana BPF
cargo build-sbf

Deploy

solana program deploy target/deploy/doppler.so

Security Considerations

  • Admin Key: The admin key is hardcoded in the program for security
  • Sequence Validation: Prevents replay attacks and ensures ordering
  • No External Dependencies: Reduces attack surface
  • Direct Memory Operations: Eliminates unnecessary abstraction layers

Benchmarks

| Operation | Compute Units | | ------------------ | ------------- | | Oracle Update | 21 | | Sequence Check | 5 | | Payload Write | 10 | | Admin Verification | 6 |

Example Payloads

Simple Price Feed

#[derive(Clone, Copy)]
pub struct PriceFeed {
    pub price: u64,
}

AMM Oracle

#[derive(Clone, Copy)]
pub struct PropAMM {
    pub bid: u64,
    pub ask: u64,
}

Complex Market Data

#[derive(Clone, Copy)]
pub struct MarketData {
    pub price: u64,
    pub volume: u64,
    pub confidence: u32,
}

FAQ

Q: Why only 21 CUs? A: Doppler uses direct memory operations, inline assembly optimizations, and zero-overhead abstractions to achieve minimal compute usage.

Q: Can I use custom payload types? A: Yes! Doppler is generic over any Copy type. Define your structure and use it with the SDK.

Q: How do I handle oracle account creation? A: However you like, but if you use Solana's createaccountwith_seed instruction with the admin as the base key it's cheaper!

Q: What's the maximum update frequency? A: Limited only by Solana's throughput. With 21 CUs, you can update as fast as you land.

Support

For issues, questions, or contributions:

License

Licensed under MIT.

© 2026 GitRepoTrend · blueshift-gg/doppler · Updated daily from GitHub