Solana / SVM
Switchboard Randomness for Solana and SVM applications
Prerequisites
Basic understanding of Solana development and Anchor Framework.
A working Solana development environment (Solana tool suite, Anchor).
Solana CLI connected to the devnet.
Enough SOL tokens for devnet transactions.
Tooling:
Install Switchboard On-Demand in your program.
cargo add switchboard-on-demand
Install Switchboard On-Demand in your Javascript Client file:
npm i @switchboard-xyz/on-demand
Step 1: Coin Flip Program (Solana)
Define the Player State
Make sure to store the user's randomness_account in your program.
#[account] pub struct PlayerState { allowed_user: Pubkey, latest_flip_result: bool, randomness_account: Pubkey, current_guess: bool, wager: u64, bump: u8, }
Commit to a Future Slot
Commit to the game by locking the
randomness_account
.pub fn coin_flip(ctx: Context<CoinFlip>, randomness_account: Pubkey, guess: bool) -> Result<()> { // Load clock to check data from the future let clock = Clock::get()?; // Update player_state's randomness_account let randomness_data = RandomnessAccountData::parse(ctx.accounts.randomness_account_data.data.borrow()).unwrap(); if randomness_data.seed_slot != clock.slot - 1 { msg!("seed_slot: {}", randomness_data.seed_slot); msg!("slot: {}", clock.slot); return Err(ErrorCode::RandomnessAlreadyRevealed.into()); } // **IMPORTANT**: // Remember in Switchboard Randomness, game collateral MUST be taken upon randomness request, not on reveal. // *** transfer( ctx.accounts.system_program.to_account_info(), ctx.accounts.user.to_account_info(), ctx.accounts.escrow_account.to_account_info(), player_state.wager, None, )?; // Store flip commitment to the future slot byt referencing the randomness account player_state.randomness_account = randomness_account; }
Settle the Flip
Resolve randomness through the
settle_flip
function.pub fn settle_flip(ctx: Context<SettleFlip>) -> Result<()> { // Load clock to check data from the future let clock = Clock::get()?; // Parsing the oracle's scroll Call the switchboard on-demand parse function to get the randomness data let randomness_data = RandomnessAccountData::parse(ctx.accounts.randomness_account_data.data.borrow()).unwrap(); // Call the switchboard on-demand get_value function to get the revealed random value let revealed_random_value = randomness_data.get_value(&clock) .map_err(|_| ErrorCode::RandomnessNotResolved)?; }
Step 2: TypeScript Client
Setup and Environment Configuration:
import * as anchor from "@coral-xyz/anchor";
import {
Connection,
PublicKey,
Keypair,
Transaction,
SystemProgram,
VersionedTransaction,
} from "@solana/web3.js";
import {
AnchorUtils,
InstructionUtils,
Queue,
Randomness,
SB_ON_DEMAND_PID,
sleep,
} from "@switchboard-xyz/on-demand";
import dotenv from "dotenv";
import * as fs from "fs";
import reader from "readline-sync";
(async function () {
dotenv.config();
console.clear();
const { keypair, connection, provider, wallet } = await AnchorUtils.loadEnv();
const payer = wallet.payer;
// Switchboard sbQueue fixed
const sbQueue = new PublicKey("FfD96yeXs4cxZshoPPSKhSPgVQxLAJUT3gefgh84m1Di");
const sbProgramId = SB_ON_DEMAND_PID;
const sbProgram = await anchor.Program.at(sbProgramId, sbProgramId, provider);
const queueAccount = new Queue(sbProgram, sbQueue);
// setup
const path = "sb-randomness/target/deploy/sb_randomness-keypair.json";
const [_, myProgramKeypair] = await AnchorUtils.initWalletFromFile(path);
const coinFlipProgramId = myProgramKeypair.publicKey;
const coinFlipProgram = await myAnchorProgram(provider, coinFlipProgramId);
Creating a Randomness Account: Before flipping that coin, start with a clean
randomness
account.
const rngKp = Keypair.generate();
const [randomness, ix] = await Randomness.create(sbProgram, rngKp, sbQueue);
Committing to Randomness: Use randomness account to commit to the oracle.
const commitIx = await randomness.commitIx(sbQueue);
// Add this instruction to your coinFlip transaction and send it
Revealing the Vision: Once the slot is generated, invoke to generate randomness for the committed slot.
const revealIx = await randomness.revealIx();
// Execute the reveal instruction, followed by your program's settle_flip function
Note: here is an optional way to save the revealIx()
transaction .
randomness.serializeIxToFile(
[revealIx, SettleFlipIx],
"serializedIx.bin"
);
Settling the Flip: Finally, record the coin flip in the randomness account.
const settleFlipIx = await coinFlipProgram.instruction.settleFlip(
escrowBump,
{
accounts: {
playerState: playerStateAccount,
randomnessAccountData: randomness.pubkey,
escrowAccount: escrowAccount,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
}
);
// Add the revealIx and this instruction together and execute
You've just integrated randomness into your Solana application.
For more details, refer to the example repository
Last updated