For the complete documentation index, see llms.txt. This page is also available as Markdown.

Price Feeds Tutorial

Example Code: The complete working example for this tutorial is available at sb-on-demand-examples/evm/price-feeds

This tutorial walks you through integrating Switchboard oracle price feeds into your EVM smart contracts. You'll learn how to fetch oracle data, submit updates to your contract, and read verified prices.

Version source of truth: SDK Version Matrix

What You'll Build

A Solidity smart contract that:

  • Receives and stores verified oracle price updates

  • Validates price freshness and deviation

  • Provides helper functions for DeFi use cases (collateral ratios, liquidations)

Plus a TypeScript client that fetches oracle data and submits it to your contract.

Prerequisites

  • Foundry for Solidity development (forge, cast)

  • Bun or Node.js 20+

  • Native tokens for gas (MON, ETH, etc.)

  • Basic understanding of Solidity and ethers.js

Key Concepts

How Switchboard On-Demand Works on EVM

Switchboard uses an on-demand model where:

  1. Your client fetches signed price data from Crossbar (Switchboard's gateway)

  2. Your contract submits the signed data to the Switchboard contract for verification

  3. Switchboard verifies the oracle signatures and stores the data

  4. Your contract reads the verified data via latestUpdate()

This pattern ensures prices are cryptographically verified on-chain while keeping gas costs low.

The CrossbarClient

The CrossbarClient from @switchboard-xyz/common is your interface to fetch oracle data for the v2 feed-hash flow used by current Monad integrations and Feed Builder feeds:

If you are using a bytes32 feed ID from Explorer or Feed Builder, this is the path you want. Legacy fetchEVMResults() and /updates/evm/... routes remain available for older aggregator-based integrations, but they are not the primary flow for custom feeds.

Fee Handling

Some networks require a fee for oracle updates. Always check before submitting:

The Smart Contract

Here's a complete example contract that integrates Switchboard price feeds:

Contract Walkthrough

State Variables

  • switchboard - Reference to the deployed Switchboard contract

  • prices - Maps feed IDs to their latest price data

  • maxPriceAge - Maximum acceptable age for price data (5 minutes default)

  • maxDeviationBps - Maximum price change allowed (10% default, prevents manipulation)

The updatePrices Function

This is the main entry point for updating prices:

  1. Check fee - Ensure caller sent enough to cover the oracle update fee

  2. Submit to Switchboard - Call updateFeeds() which verifies oracle signatures

  3. Read verified data - Call latestUpdate() to get the verified price

  4. Store locally - Save the price in your contract's storage

  5. Refund excess - Return any overpayment to the caller

Reading Prices

Always check freshness before using a price:

The TypeScript Client

Here's a complete client that fetches oracle data and submits it to your contract:

Client Walkthrough

Step 1: Fetch Oracle Data

The fetchV2Update call returns:

  • medianResponses with one consensus value per feed

  • timestamp for the signed oracle consensus

  • oracleResponses for per-oracle detail

  • encoded, the EVM payload you wrap into bytes[] for getFee and updateFeeds

Step 2: Submit to Contract

Your contract receives the encoded data, submits it to Switchboard for verification, then stores the result.

Step 3-5: Confirm and Read

After confirmation, you can:

  • Parse PriceUpdated events from the receipt

  • Read the stored price directly from your contract

Deployment

The packaged example now uses one network switch for both deploys and runtime:

  • NETWORK=monad-testnet or NETWORK=monad-mainnet

  • RPC_URL is optional and overrides the default RPC for the selected network

  • PRIVATE_KEY is required

  • SWITCHBOARD_ADDRESS is an advanced override only

Defaults:

  • NETWORK=monad-testnet

  • Testnet RPC: https://testnet-rpc.monad.xyz

  • Mainnet RPC: https://rpc.monad.xyz

The packaged deploy flow validates the selected network before broadcast:

  • the RPC chain ID must match NETWORK

  • the resolved Switchboard address must have deployed bytecode

  • Monad SWITCHBOARD_ADDRESS overrides must match the canonical address for the selected network

Use the packaged wrapper from the example repo:

If you want raw Foundry instead of the wrapper, keep the same env contract:

Running the Example

1. Clone the Examples Repository

2. Install Dependencies

3. Configure Environment

Security: Never use export PRIVATE_KEY=...—it appears in shell history. Use a .env file instead.

Create a .env file (add it to .gitignore):

4. Run the Example

If CONTRACT_ADDRESS is unset, the script deploys a fresh consumer contract before it fetches the v2 update and submits it on-chain:

Switch to Monad mainnet without changing the script:

For Feed Builder or custom feeds, it is useful to preflight before sending a transaction:

Expected Output

Adding to Your Project

1. Install the Switchboard Interfaces

Copy the interface files from the examples repo:

Or install via npm:

2. Import and Use

Example: DeFi Business Logic

The example contract includes helper functions for common DeFi patterns:

Calculate Collateral Ratio

Check Liquidation

Switchboard Contract Addresses

Network
Chain ID
Switchboard Contract

Monad Testnet

10143

0x6724818814927e057a693f4e3A172b6cC1eA690C

Monad Mainnet

143

0xB7F03eee7B9F56347e32cC71DaD65B303D5a0E67

HyperEVM Mainnet

999

0xcDb299Cb902D1E39F83F54c7725f54eDDa7F3347

Arbitrum One

42161

0xAd9b8604b6B97187CDe9E826cDeB7033C8C37198

Arbitrum Sepolia

421614

0xA2a0425fA3C5669d384f4e6c8068dfCf64485b3b

Core Mainnet

1116

0x33A5066f65f66161bEb3f827A3e40fce7d7A2e6C

Available Feeds

Find available price feeds at the Switchboard Explorer.

Popular feeds include:

  • BTC/USD: 0x4cd1cad962425681af07b9254b7d804de3ca3446fbfd1371bb258d2c75059812

  • ETH/USD: 0xa0950ee5ee117b2e2c30f154a69e17bfb489a7610c508dc5f67eb2a14616d8ea

  • SOL/USD: 0x822512ee9add93518eca1c105a38422841a76c590db079eebb283deb2c14caa9

Troubleshooting

Error
Solution

InsufficientFee

Query switchboard.getFee(updates) and send that amount as msg.value

PriceDeviationTooHigh

Normal during high volatility; adjust maxDeviationBps if needed

PriceTooOld

Fetch fresh data from Crossbar; adjust maxPriceAge if needed

InvalidFeedId

Ensure the feed ID exists and has been updated at least once

ORACLE_UNAVAILABLE

If simulateFeed works but fetchV2Update fails, it is not a missing deployment step. Check oracle/gateway availability and oracle-side validation errors such as RangeExceeded, especially if raw v2 maxJobRangePct was not scaled by 1e9; see Feed Parameter Units

Build errors

Bootstrap forge-std in ../randomness/coin-flip, then rerun forge build

Next Steps

Last updated