Crate solana_randomness_service
source ·Expand description
The Solana Randomness Service uses a Switchboard SGX enabled oracle to provide randomness to any Solana program using a callback instruction.
Program ID: RANDMo5gFnqnXJW5Z52KNmd24sAo95KAd5VbiCtq5Rh
§Accounts:
- State: The program state account. Stores the cost per byte of randomness, the Switchboard service, and the wallet that will accrue rewards for fulfilling randomness requests.
- SimpleRandomnessV1Account: Stores the user’s request for randomness. This account is created on-chain and used by the off-chain oracle to fulfill the request. This account is automatically closed upon successful fulfillment.
§Admin Instructions:
- initialize: init the program state and set the program authority permitted to make global config changes.
- set_fees: Sets the randomness fee per byte of randomness requested. Doesnt include the 10_000 lamport base fee or the priority fees.
- set_switchboard_service: Sets the SwitchboardService that will fulfill the request.
- program_authority_withdraw: Allows the program authority to withdraw from the reward escrow.
- switchboard_service_withdraw: Allows the Switchboard service to withdraw funds to pay for future requests
§Simple Randomness V1 Instructions:
- simple_randomness_v1: Request randomness from the Switchboard service. This is the main instruction you will need to integrate.
- simple_randomness_v1_settle: Settles a randomness request and invokes the user’s callback.
- simple_randomness_v1_callback_error: Sets the error message for a randomness request. This provides visibility to the user that the request failed off-chain.
- simple_randomness_v1_callback_close: Allows the user to acknowledge the error message and close the request.
§Example Program
use solana_randomness_service::SimpleRandomnessV1Account;
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...");
// 1. Call the randomness service and request a new value
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, // Request 8 bytes of randomness
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(), // TODO: hardcode this discriminator [190,217,49,162,99,26,73,234]
},
Some(use solana_randomness_service::TransactionOptions {
compute_units: Some(1_000_000),
compute_unit_price: Some(100),
}),
)?;
// Here we can emit some event to index our requests
Ok(())
}
// 2. Build the instruction that will be called with the randomness bytes. The randomness bytes Vec will be appended to the end of the ixn data.
pub fn consume_randomness(
_ctx: Context<ConsumeRandomness>,
result: Vec<u8>,
) -> anchor_lang::prelude::Result<()> {
msg!("Randomness received: {:?}", result);
Ok(())
}
}
// The request_randomness macro breaks IDL generation. So we'll manually implement.
// #[request_randomness]
#[derive(Accounts)]
pub struct RequestRandomness<'info> {
//! The Solana Randomness Service program.
pub randomness_service: Program<'info, SolanaRandomnessService>,
//! The account that will be created on-chain to hold the randomness request.
//! Used by the off-chain oracle to pickup the request and fulfill it.
//! CHECK: todo
#[account(
mut,
signer,
owner = system_program.key(),
constraint = randomness_request.data_len() == 0 && randomness_request.lamports() == 0,
)]
pub randomness_request: AccountInfo<'info>,
//! The TokenAccount that will store the funds for the randomness request.
//! CHECK: todo
#[account(
mut,
owner = system_program.key(),
constraint = randomness_escrow.data_len() == 0 && randomness_escrow.lamports() == 0,
)]
pub randomness_escrow: AccountInfo<'info>,
//! The randomness service's state account. Responsible for storing the
//! reward escrow and the cost per random byte.
#[account(
seeds = [b"STATE"],
bump = randomness_state.bump,
seeds::program = randomness_service.key(),
)]
pub randomness_state: Box<Account<'info, solana_randomness_service::State>>,
//! The token mint to use for paying for randomness requests.
#[account(address = NativeMint::ID)]
pub randomness_mint: Account<'info, Mint>,
//! The account that will pay for the randomness request.
#[account(mut)]
pub payer: Signer<'info>,
//! The Solana System program. Used to allocate space on-chain for the randomness_request account.
pub system_program: Program<'info, System>,
//! The Solana Token program. Used to transfer funds to the randomness escrow.
pub token_program: Program<'info, Token>,
//! The Solana Associated Token program. Used to create the TokenAccount for the randomness escrow.
pub associated_token_program: Program<'info, AssociatedToken>,
}
#[derive(Accounts)]
pub struct ConsumeRandomness<'info> {
//! We need to make sure the randomness service signed this requests so it can only be invoked by a PDA and not a user.
#[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>>,
}
Re-exports§
pub use types::AccountMetaBorsh;
pub use types::Callback;
pub use types::TransactionOptions;
pub use impls::*;
pub use actions::*;
Modules§
- The Mint that represents the native token
- An Anchor generated module, providing a set of structs mirroring the structs deriving
Accounts
, where each field is aPubkey
. This is useful for specifying accounts for a client. - An Anchor generated module containing the program’s set of instructions, where each method handler in the
#[program]
mod is associated with a struct defining the input arguments to the method. These should be used directly, when one wants to serialize Anchor instruction data, for example, when speciying instructions on a client. - Module representing the program.
Structs§
- Initialize
Enums§
- Specifies the authority type for SetAuthority instructions
Constants§
- The const program ID.
Statics§
- The static program ID
Functions§
- Asserts that the current instruction is not a CPI call. This is to prevent re-entrancy from user provided callbacks.
- Confirms that a given pubkey is equivalent to the program ID
- The Anchor codegen exposes a programming model where a user defines a set of methods inside of a
#[program]
module in a way similar to writing RPC request handlers. The macro then generates a bunch of code wrapping these user defined methods into something that can be executed on Solana. - Returns the program ID