Doppler
Concepts

Compute Units

Understand what Doppler's low compute unit usage does and does not include.

Doppler benchmarks at 21 compute units (CUs) per oracle update when using the default payload schema with a single u64 field. That number measures only what the generated program instruction consumes — not the full transaction.

Surfpool logs for a single price-feed update show the program line explicitly:

Program fastRQJt3nLdY3QA7n8eZ8ETEVefy56ryfUGVkfZokm consumed 21 of 21 compute units

The same transaction reports 471 CUs total once compute-budget instructions, signature verification, and account loading are included.

Default Payload: 21 CU Breakdown

The generator's default schema is a single u64 price field:

payload: {
  price: "u64",
}

That produces an oracle account of 16 bytes:

8 bytes   sequence   little-endian u64
8 bytes   price      little-endian u64

The generated SDKs model program CU cost as:

sequence check      5 CUs
admin verification  6 CUs
payload write       6 CUs
account data term   floor(oracle account size / 4)

For the default payload, floor(16 / 4) = 4, so:

5 + 6 + 6 + 4 = 21 CUs
ComponentCUsWhat the program does
Sequence check5Reads the stored sequence from the oracle account and the new sequence from instruction data, then rejects the update if new_sequence <= current_sequence.
Admin verification6Confirms the admin account is a non-duplicate signer and matches the hardcoded admin pubkey embedded in the program bytecode.
Payload write6Reads the new payload from instruction data and writes the updated sequence and payload bytes into the oracle account.
Account data term4Solana charges CUs proportional to writable account data touched. For a 16-byte oracle account, floor(16 / 4) = 4.

On-Chain Update Path

Each update instruction passes two accounts and fixed-size instruction data:

AccountRoleAccess
AdminReadonly signerRead
OracleWritableRead/write

Instruction data for the default payload is 16 bytes:

offset 0   sequence   u64   8 bytes
offset 8   price      u64   8 bytes

The program entrypoint runs two steps:

  1. Admin::check — verify the signer is the embedded admin.
  2. Oracle::check_and_update — validate monotonic sequence, then write sequence and payload.

There is no deserialization, no heap allocation, and no external CPI. The program reads and writes at fixed offsets using direct memory operations.

Scaling With Larger Payloads

The account data term grows with oracle account size. Larger fixed-size payloads cost more than 21 CUs, but the first three terms stay constant.

Payload schemaOracle sizeAccount data termTotal CUs
{ price: "u64" } (default)16 bytes421
{ bid: "u64", ask: "u64" }24 bytes623
{ price: "u64", volume: "u64", confidence: "u32" }28 bytes724

The SDK helper oracleUpdateComputeUnits(serializer) applies the same formula for any generated schema.

Program CUs vs Transaction CUs

Use the 21 CU figure precisely: it is the Doppler instruction cost, not the cost of landing a transaction.

A production update transaction typically includes compute-budget instructions and normal Solana overhead:

  • transaction signature verification
  • account loading
  • SetComputeUnitLimit
  • SetLoadedAccountsDataSizeLimit
  • SetComputeUnitPrice when you pass a priority fee
  • any other instructions in the transaction

A single price-feed update with optimized compute-budget settings consumes roughly 471 CUs end to end while the Doppler program itself still reports 21 CUs.

Setting SetLoadedAccountsDataSizeLimit to the actual loaded account data size (for example, 111 bytes for a minimal single-feed transaction) avoids paying the default 64 MB data-size penalty and materially improves priority score. See Anza's CU optimization guide for the scheduling math.

Compute Budget Guidance

The generated SDK transaction builders add:

  • a loaded accounts data size limit instruction
  • a compute unit limit instruction
  • a compute unit price instruction when you pass a priority fee

Request enough CUs for the full transaction, not just the 21 CU program path. The builders start from compute-budget instruction overhead and add oracleUpdateComputeUnits(serializer) per update.

For high-frequency updates, set a priority fee when timely inclusion matters:

await client.updateOracle(
  oracleAddress,
  {
    sequence: BigInt(Date.now()),
    payload: { price: 42_000_000n },
  },
  serializer,
  1_000n,
);

Tune the fee and batching strategy against current cluster conditions rather than hard-coding one number permanently.

Batching Multiple Updates

Each Doppler update in a transaction still costs 21 CUs at the program level (for the default payload). Batching multiple oracle updates into one transaction amortizes signature verification and compute-budget overhead across feeds:

3 updates in one transaction → 3 × 21 = 63 program CUs
same 3 updates as separate transactions → 3 × 21 program CUs, but ~3× transaction overhead

The SDK TransactionBuilder accumulates CU and loaded-data-size estimates as you append updates.

On this page