Solana Randomness Service
The Solana Randomness Service uses a Switchboard SGX enabled oracle to provide randomness to any Solana program using a callback instruction.
Program ID: RANDMo5gFnqnXJW5Z52KNmd24sAo95KAd5VbiCtq5Rh
NOTE: This program ID is applicable for mainnet-beta and devnet.
Request Lifecycle
- User's program invokes the
simple_randomness_v1
instruction with a CPI call along with the number of randomness bytes, the custom callback instruction, and the priority fee config
- Creates a
SimpleRandomnessV1Account
account
- Sets the custom callback
- Wraps funds into an escrow to reward the oracle for fulfilling the request
- Off-chain SGX enabled oracle reads the request account
- Generates random bytes inside of the enclave
- Builds a txn with your callback and desired priority fees
- Simulates the txn. If successful, relays the txn on-chain. If error, relays an error instruction with the error message which is viewable in an explorer.
- Transaction relayed on-chain
- Oracle rewarded for fulfilling request
- Oracle invokes the users callback instruction
- Request account is closed and the rent-exemption is returned to the original payer
Usage
Add the solana_randomness_service to your Cargo.toml
solana-randomness-service = { version = "1", features = ["cpi"] }
See the example program below on how to integrate the Solana Randomness Service into your Anchor program.
- Call the
simple_randomness_v1
instruction with your payer, callback, and your desired priority fee config
- Build the callback isntruction that the randomness service will invoke with your requested randomness bytes
use solana_randomness_service::SimpleRandomnessV1Account;
use solana_randomness_service::TransactionOptions;
use solana_randomness_service::{
program::SolanaRandomnessService, ID as SolanaRandomnessServiceID,
};
use switchboard_solana::prelude::*;
use switchboard_solana::utils::get_ixn_discriminator;
declare_id!("39hMZgeiesFXMRFt8svuKVsdCW5geiYueSRx7dxhXN4f");
#[program]
pub mod solana_randomness_consumer {
use super::*;
pub fn request_randomness(ctx: Context<RequestRandomness>) -> anchor_lang::prelude::Result<()> {
msg!("Requesting randomness...");
solana_randomness_service::cpi::simple_randomness_v1(
CpiContext::new(
ctx.accounts.randomness_service.to_account_info(),
solana_randomness_service::cpi::accounts::SimpleRandomnessV1Request {
request: ctx.accounts.randomness_request.to_account_info(),
escrow: ctx.accounts.randomness_escrow.to_account_info(),
state: ctx.accounts.randomness_state.to_account_info(),
mint: ctx.accounts.randomness_mint.to_account_info(),
payer: ctx.accounts.payer.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
token_program: ctx.accounts.token_program.to_account_info(),
associated_token_program: ctx
.accounts
.associated_token_program
.to_account_info(),
},
),
8, solana_randomness_service::Callback {
program_id: ID,
accounts: vec![
AccountMeta::new_readonly(ctx.accounts.randomness_state.key(), true).into(),
AccountMeta::new_readonly(ctx.accounts.randomness_request.key(), false).into(),
],
ix_data: get_ixn_discriminator("consume_randomness").to_vec(), },
Some(TransactionOptions {
compute_units: Some(1_000_000),
compute_unit_price: Some(100),
}),
)?;
Ok(())
}
pub fn consume_randomness(
_ctx: Context<ConsumeRandomness>,
result: Vec<u8>,
) -> anchor_lang::prelude::Result<()> {
msg!("Randomness received: {:?}", result);
Ok(())
}
}
#[derive(Accounts)]
pub struct RequestRandomness<'info> {
pub randomness_service: Program<'info, SolanaRandomnessService>,
#[account(
mut,
signer,
owner = system_program.key(),
constraint = randomness_request.data_len() == 0 && randomness_request.lamports() == 0,
)]
pub randomness_request: AccountInfo<'info>,
#[account(
mut,
owner = system_program.key(),
constraint = randomness_escrow.data_len() == 0 && randomness_escrow.lamports() == 0,
)]
pub randomness_escrow: AccountInfo<'info>,
#[account(
seeds = [b"STATE"],
bump = randomness_state.bump,
seeds::program = randomness_service.key(),
)]
pub randomness_state: Box<Account<'info, solana_randomness_service::State>>,
#[account(address = NativeMint::ID)]
pub randomness_mint: Account<'info, Mint>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
pub token_program: Program<'info, Token>,
pub associated_token_program: Program<'info, AssociatedToken>,
}
#[derive(Accounts)]
pub struct ConsumeRandomness<'info> {
#[account(
signer,
seeds = [b"STATE"],
seeds::program = SolanaRandomnessServiceID,
bump = randomness_state.bump,
)]
pub randomness_state: Box<Account<'info, solana_randomness_service::State>>,
pub request: Box<Account<'info, SimpleRandomnessV1Account>>,
}