Rust
A Rust library to interact with Switchboard V2 accounts.
To get started, add the switchboard-v2 crate to your manifest.
[dependencies]
switchboard-v2 = { version = "~0.1.19" }
Features​
Feature | Description |
---|---|
devnet | The devnet feature enables using the Switchboard Devnet Program ID instead of the Mainnet Program ID. |
Enable it in your Cargo.toml
[dependencies]
switchboard-v2 = { version = "~0.1.19", features = ["devnet"] }
Define Your Own Devnet Feature​
You can also define your own devnet feature to dynamically swap the program IDs.
[features]
default = []
devnet = ["switchboard-v2/devnet"]
This allows you to build your program with a feature flag to automate devnet and mainnet builds.
# Build with Mainnet Switchboard Program ID
cargo build-bpf
# Build with Devnet Switchboard Program ID
cargo build-bpf --features devnet
Data Feeds​
Read Latest Result​
- Preview
- Full
use anchor_lang::solana_program::clock;
use std::convert::TryInto;
use switchboard_v2::{AggregatorAccountData, SwitchboardDecimal, SWITCHBOARD_PROGRAM_ID};
// check feed owner
let owner = *aggregator.owner;
if owner != SWITCHBOARD_PROGRAM_ID {
return Err(error!(ErrorCode::InvalidSwitchboardAccount));
}
// deserialize account info
let feed = ctx.accounts.aggregator.load()?;
// OR
let feed = AggregatorAccountData::new(feed_account_info)?;
// get result
let decimal: f64 = feed.get_result()?.try_into()?;
// check if feed has been updated in the last 5 minutes
feed.check_staleness(clock::Clock::get().unwrap().unix_timestamp, 300)?;
// check if feed exceeds a confidence interval of +/i $0.80
feed.check_confidence_interval(SwitchboardDecimal::from_f64(0.80))?;
use anchor_lang::prelude::*;
use anchor_lang::solana_program::clock;
use std::convert::TryInto;
pub use switchboard_v2::{AggregatorAccountData, SwitchboardDecimal, SWITCHBOARD_PROGRAM_ID};
declare_id!("FnsPs665aBSwJRu2A8wGv6ZT76ipR41kHm4hoA3B1QGh");
#[derive(Accounts)]
#[instruction(params: ReadResultParams)]
pub struct ReadResult<'info> {
pub aggregator: AccountLoader<'info, AggregatorAccountData>,
}
#[derive(Clone, AnchorSerialize, AnchorDeserialize)]
pub struct ReadResultParams {
pub max_confidence_interval: Option<f64>,
}
#[program]
pub mod anchor_feed_parser {
use super::*;
pub fn read_result(
ctx: Context<ReadResult>,
params: ReadResultParams,
) -> anchor_lang::Result<()> {
let feed = &ctx.accounts.aggregator.load()?;
// get result
let val: f64 = feed.get_result()?.try_into()?;
// check whether the feed has been updated in the last 300 seconds
feed.check_staleness(clock::Clock::get().unwrap().unix_timestamp, 300)
.map_err(|_| error!(FeedErrorCode::StaleFeed))?;
// check feed does not exceed max_confidence_interval
if let Some(max_confidence_interval) = params.max_confidence_interval {
feed.check_confidence_interval(SwitchboardDecimal::from_f64(max_confidence_interval))
.map_err(|_| error!(FeedErrorCode::ConfidenceIntervalExceeded))?;
}
msg!("Current feed result is {}!", val);
Ok(())
}
}
#[error_code]
#[derive(Eq, PartialEq)]
pub enum FeedErrorCode {
#[msg("Not a valid Switchboard account")]
InvalidSwitchboardAccount,
#[msg("Switchboard feed has not been updated in 5 minutes")]
StaleFeed,
#[msg("Switchboard feed exceeded provided confidence interval")]
ConfidenceIntervalExceeded,
}
Example(s):
Read Aggregator History​
Note: The Aggregator must have a history buffer initialized before using
- Preview
- Full
let history_buffer = AggregatorHistoryBuffer::new(&ctx.accounts.history_buffer)?;
let timestamp: i64 = Clock::get()?.unix_timestamp - 3600;
let value_at_timestamp: f64 = history_buffer
.lower_bound(timestamp)
.unwrap()
.value
.try_into()?;
msg!("Result {:?}!", value_at_timestamp);
use anchor_lang::prelude::*;
use std::convert::TryInto;
pub use switchboard_v2::{
AggregatorAccountData, AggregatorHistoryBuffer, SwitchboardDecimal, SWITCHBOARD_PROGRAM_ID,
};
declare_id!("FnsPs665aBSwJRu2A8wGv6ZT76ipR41kHm4hoA3B1QGh");
#[derive(Accounts)]
#[instruction(params: ReadHistoryParams)]
pub struct ReadHistory<'info> {
#[account(
has_one = history_buffer @ ErrorCode::InvalidHistoryBuffer
)]
pub aggregator: AccountLoader<'info, AggregatorAccountData>,
/// CHECK: verified in the aggregator has_one check
pub history_buffer: AccountInfo<'info>,
}
#[derive(Clone, AnchorSerialize, AnchorDeserialize)]
pub struct ReadHistoryParams {
pub timestamp: Option<i64>,
}
#[program]
pub mod anchor_history_parser {
use super::*;
pub fn read_history(
ctx: Context<ReadHistory>,
params: ReadHistoryParams,
) -> anchor_lang::Result<()> {
let history_buffer = AggregatorHistoryBuffer::new(&ctx.accounts.history_buffer)?;
let timestamp: i64 = Clock::get()?.unix_timestamp - 3600;
let value_at_timestamp: f64 = history_buffer
.lower_bound(timestamp)
.unwrap()
.value
.try_into()?;
msg!("Result {:?}!", value_at_timestamp);
Ok(())
}
}
#[error_code]
#[derive(Eq, PartialEq)]
pub enum ErrorCode {
#[msg("Not a valid Switchboard account")]
InvalidSwitchboardAccount,
#[msg("History buffer mismatch")]
InvalidHistoryBuffer,
}
Example(s):
VRF Account​
Read Latest Result​
use switchboard_v2::VrfAccountData;
// deserialize the account info
let vrf = ctx.accounts.vrf.load()?;
// OR
let vrf = VrfAccountData::new(vrf_account_info)?;
// read the result
let result_buffer = vrf.get_result()?;
let value: &[u128] = bytemuck::cast_slice(&result_buffer[..]);
let result = value[0] % 256000 as u128;
Example:
RequestRandomness CPI​
pub use switchboard_v2::{VrfAccountData, VrfRequestRandomness};
let switchboard_program = ctx.accounts.switchboard_program.to_account_info();
let vrf_request_randomness = VrfRequestRandomness {
authority: ctx.accounts.state.to_account_info(),
vrf: ctx.accounts.vrf.to_account_info(),
oracle_queue: ctx.accounts.oracle_queue.to_account_info(),
queue_authority: ctx.accounts.queue_authority.to_account_info(),
data_buffer: ctx.accounts.data_buffer.to_account_info(),
permission: ctx.accounts.permission.to_account_info(),
escrow: ctx.accounts.escrow.clone(),
payer_wallet: ctx.accounts.payer_wallet.clone(),
payer_authority: ctx.accounts.payer_authority.to_account_info(),
recent_blockhashes: ctx.accounts.recent_blockhashes.to_account_info(),
program_state: ctx.accounts.program_state.to_account_info(),
token_program: ctx.accounts.token_program.to_account_info(),
};
let vrf_key = ctx.accounts.vrf.key.clone();
let authority_key = ctx.accounts.authority.key.clone();
let state_seeds: &[&[&[u8]]] = &[&[
&STATE_SEED,
vrf_key.as_ref(),
authority_key.as_ref(),
&[bump],
]];
msg!("requesting randomness");
vrf_request_randomness.invoke_signed(
switchboard_program,
params.switchboard_state_bump,
params.permission_bump,
state_seeds,
)?;
Example:
Buffer Relayer Account​
Read Latest Result​
use anchor_lang::solana_program::clock;
use std::convert::TryInto;
use switchboard_v2::{BufferRelayerAccountData, SWITCHBOARD_PROGRAM_ID};
// check feed owner
let owner = *aggregator.owner;
if owner != SWITCHBOARD_PROGRAM_ID {
return Err(error!(ErrorCode::InvalidSwitchboardAccount));
}
// deserialize account info
let buffer = BufferRelayerAccountData::new(feed_account_info)?;
// get result
let buffer_result = buffer.get_result();
// check if feed has been updated in the last 5 minutes
buffer.check_staleness(clock::Clock::get().unwrap().unix_timestamp, 300)?;
// convert buffer to a string
let result_string = String::from_utf8(buffer.result)
.map_err(|_| error!(ErrorCode::StringConversionFailed))?;
msg!("Buffer string {:?}!", result_string);
Example: