npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@switchboard-xyz/solana-randomness-service

v1.0.0

Published

A Typescript client to interact with the Solana Randomness Service via Switchboard

Downloads

19

Readme

Solana Randomness Service

A Typescript client to interact with the 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

  1. 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
  2. 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.
  3. 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 package to your project.

npm i @switchboard-xyz/solana-randomness-service

The randomness service client can be initialized from an anchor provider.

Switchboard Labs provides a set of off-chain oracles to fulfill any requests on devnet and mainnet.

For localnet, the RandomnessService will initialize itself and create its own Switchboard infrastructure. When you await a request to be settled, the randomness service will check and fulfill the request itself.

import * as anchor from "@coral-xyz/anchor";
import { RandomnessService } from "@switchboard-xyz/solana-randomness-service";

const provider = anchor.AnchorProvider.env();
const randomnessService = await RandomnessService.fromProvider(provider);

// Create a keypair for our request account. This account will be automatically closed on settlement and
// the rent will be returned to the original payer.
const requestKeypair = anchor.web3.Keypair.generate();

// Start watching for the settled event before triggering the request.
// If on localnet this will fulfill the randomness request for you in the background.
const settledRandomnessEventPromise = randomnessService.awaitSettledEvent(
  requestKeypair.publicKey
);

// your program makes a CPI request to the RandomnessService
const signature = await program.methods
  .requestRandomness()
  .accounts({
    randomnessService: randomnessService.programId,
    randomnessRequest: requestKeypair.publicKey,
    randomnessEscrow: anchor.utils.token.associatedAddress({
      mint: randomnessService.accounts.mint,
      owner: requestKeypair.publicKey,
    }),
    randomnessState: randomnessService.accounts.state,
    randomnessMint: randomnessService.accounts.mint,
    payer: provider.wallet.publicKey,
  })
  .signers([requestKeypair])
  .rpc();

// Await the response from the Switchboard Service
const [settledRandomnessEvent, settledSlot] =
  await settledRandomnessEventPromise;

Example Anchor Program

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.

  1. Call the simple_randomness_v1 instruction with your payer, callback, and your desired priority fee config
  2. 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...");

        // 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(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>>,
}