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:

§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§

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 a Pubkey. 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§

Enums§

Constants§

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

Type Aliases§