329 lines
9.4 KiB
Plaintext
329 lines
9.4 KiB
Plaintext
---
|
|
sidebar_position: 40
|
|
title: Randomness
|
|
---
|
|
|
|
import { Box, Typography, Grid } from "@mui/material";
|
|
import Link from "@docusaurus/Link";
|
|
import Tabs from "@theme/Tabs";
|
|
import TabItem from "@theme/TabItem";
|
|
|
|
<!--
|
|
<Typography variant="h4">
|
|
<Link to="https://github.com/switchboard-xyz/switchboard-v2/tree/main/programs/anchor-vrf-parser">
|
|
# anchor-vrf-parser
|
|
</Link>
|
|
</Typography>
|
|
-->
|
|
|
|
## Reading a VRF Account
|
|
|
|
<Tabs>
|
|
<TabItem value="Rust" label="Rust" default>
|
|
|
|
```rust
|
|
use switchboard_v2::VrfAccountData;
|
|
|
|
let vrf = VrfAccountData::new(vrf_account_info)?;
|
|
let result_buffer = vrf.get_result()?;
|
|
if result_buffer == [0u8; 32] {
|
|
msg!("vrf buffer empty");
|
|
return Ok(());
|
|
}
|
|
|
|
let value: &[u128] = bytemuck::cast_slice(&result_buffer[..]);
|
|
let result = value[0] % 256000 as u128;
|
|
```
|
|
|
|
</TabItem>
|
|
<TabItem value="Typescript" label="Typescript">
|
|
|
|
```ts
|
|
import * as anchor from "@project-serum/anchor";
|
|
import { Keypair } from "@solana/web3.js";
|
|
import {
|
|
loadSwitchboardProgram,
|
|
VrfAccount,
|
|
} from "@switchboard-xyz/switchboard-v2";
|
|
|
|
let payer: Keypair;
|
|
const program = await loadSwitchboardProgram("devnet", undefined, payer);
|
|
|
|
const vrfAccount = new VrfAccount({
|
|
program,
|
|
publicKey: vrfKey,
|
|
});
|
|
const vrf = await vrfAccount.loadData();
|
|
console.log(vrf.currentRound.result);
|
|
```
|
|
|
|
</TabItem>
|
|
<TabItem value="CLI" label="CLI">
|
|
|
|
```bash
|
|
sbv2 vrf print VRFKEY
|
|
# OR
|
|
sbv2 vrf watch VRFKEY
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
## Creating a VRF Account
|
|
|
|
- `vrfInit`
|
|
|
|
<Tabs>
|
|
<TabItem value="Typescript" label="Typescript" default>
|
|
|
|
```ts
|
|
import * as anchor from "@project-serum/anchor";
|
|
import { Keypair } from "@solana/web3.js";
|
|
import {
|
|
loadSwitchboardProgram,
|
|
OracleQueueAccount,
|
|
PermissionAccount,
|
|
SwitchboardPermission,
|
|
VrfAccount,
|
|
} from "@switchboard-xyz/switchboard-v2";
|
|
|
|
let payer: Keypair;
|
|
const program = await loadSwitchboardProgram("devnet", undefined, payer);
|
|
const queueAccount = new queueAccount({ program, publicKey: queueKey });
|
|
const queue = await queueAccount.loadData();
|
|
|
|
// load client program used for callback
|
|
const vrfClientProgram = anchor.workspace
|
|
.AnchorVrfParser as anchor.Program<AnchorVrfParser>;
|
|
const vrfSecret = anchor.web3.Keypair.generate();
|
|
|
|
const vrfIxCoder = new anchor.BorshInstructionCoder(vrfClientProgram.idl);
|
|
const vrfClientCallback: Callback = {
|
|
programId: vrfClientProgram.programId,
|
|
accounts: [
|
|
// ensure all accounts in updateResult are populated
|
|
{ pubkey: vrfClientKey, isSigner: false, isWritable: true },
|
|
{ pubkey: vrfSecret.publicKey, isSigner: false, isWritable: false },
|
|
],
|
|
ixData: vrfIxCoder.encode("updateResult", ""), // pass any params for instruction here
|
|
};
|
|
|
|
// create VRF
|
|
const vrfAccount = await VrfAccount.create(program, {
|
|
queue: queueAccount,
|
|
callback: vrfClientCallback,
|
|
authority: vrfClientKey, // vrf authority
|
|
keypair: vrfSecret,
|
|
});
|
|
|
|
// create permission
|
|
const permissionAccount = await PermissionAccount.create(program, {
|
|
authority: queue.authority,
|
|
granter: queue.publicKey,
|
|
grantee: vrfAccount.publicKey,
|
|
});
|
|
|
|
// if queue has not enabled unpermissionedVrfEnabled, queue will need to grant permission
|
|
let queueAuthority: Keypair;
|
|
await permissionAccount.set({
|
|
authority: queueAuthority,
|
|
permission: SwitchboardPermission.PERMIT_VRF_REQUESTS,
|
|
enable: true,
|
|
});
|
|
```
|
|
|
|
</TabItem>
|
|
<TabItem value="CLI" label="CLI">
|
|
|
|
```bash
|
|
sbv2 vrf create QUEUEKEY \
|
|
--keypair "path/to/payer/keypair.json" \
|
|
--authority "path/to/authority/keypair.json" \
|
|
--queueAuthority "path/to/queue-authority/keypair.json" \
|
|
--vrfKeypair vrf-keypair.json \
|
|
--callbackPid CALLBACKPROGRAMID \
|
|
--ixData "[145,72,9,94,61,97,126,106]" \
|
|
-a "{\"pubkey\": \"C7PVykaTT8xDt8D1bugNvAJP1mJnfK5h12PuqgghmCDZ\",\"isSigner\": false,\"isWritable\": true}" \
|
|
-a "{\"pubkey\": \"BV7TXpXtnzYCFjoQCeGx8iMgfi9WB38tmirrDkun8P38\",\"isSigner\": false,\"isWritable\": false}" \
|
|
--enable \
|
|
--verbose
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
|
|
## Request Randomness
|
|
|
|
- `vrfRequestRandomness`
|
|
|
|
<Tabs>
|
|
<TabItem value="Rust" label="Rust" default>
|
|
|
|
```rust
|
|
use crate::*;
|
|
use anchor_lang::prelude::*;
|
|
pub use switchboard_v2::{VrfAccountData, VrfRequestRandomness};
|
|
use anchor_spl::token::Token;
|
|
use anchor_lang::solana_program::clock;
|
|
|
|
#[derive(Accounts)]
|
|
#[instruction(params: RequestResultParams)] // rpc parameters hint
|
|
pub struct RequestResult<'info> {
|
|
#[account(
|
|
mut,
|
|
seeds = [
|
|
STATE_SEED,
|
|
vrf.key().as_ref(),
|
|
authority.key().as_ref(),
|
|
],
|
|
bump = state.load()?.bump,
|
|
has_one = vrf,
|
|
has_one = authority
|
|
)]
|
|
pub state: AccountLoader<'info, VrfClient>,
|
|
#[account(signer)]
|
|
pub authority: AccountInfo<'info>,
|
|
#[account(constraint = switchboard_program.executable == true)]
|
|
pub switchboard_program: AccountInfo<'info>,
|
|
#[account(mut, constraint = vrf.owner.as_ref() == switchboard_program.key().as_ref())]
|
|
pub vrf: AccountInfo<'info>,
|
|
#[account(mut, constraint = oracle_queue.owner.as_ref() == switchboard_program.key().as_ref())]
|
|
pub oracle_queue: AccountInfo<'info>,
|
|
pub queue_authority: UncheckedAccount<'info>,
|
|
#[account(constraint = data_buffer.owner.as_ref() == switchboard_program.key().as_ref())]
|
|
pub data_buffer: AccountInfo<'info>,
|
|
#[account(mut, constraint = permission.owner.as_ref() == switchboard_program.key().as_ref())]
|
|
pub permission: AccountInfo<'info>,
|
|
#[account(mut, constraint = escrow.owner == program_state.key())]
|
|
pub escrow: Account<'info, TokenAccount>,
|
|
#[account(mut, constraint = payer_wallet.owner == payer_authority.key())]
|
|
pub payer_wallet: Account<'info, TokenAccount>,
|
|
#[account(signer)]
|
|
pub payer_authority: AccountInfo<'info>,
|
|
#[account(address = solana_program::sysvar::recent_blockhashes::ID)]
|
|
pub recent_blockhashes: AccountInfo<'info>,
|
|
#[account(constraint = program_state.owner.as_ref() == switchboard_program.key().as_ref())]
|
|
pub program_state: AccountInfo<'info>,
|
|
#[account(address = anchor_spl::token::ID)]
|
|
pub token_program: Program<'info, Token>,
|
|
}
|
|
|
|
#[derive(Clone, AnchorSerialize, AnchorDeserialize)]
|
|
pub struct RequestResultParams {
|
|
pub permission_bump: u8,
|
|
pub switchboard_state_bump: u8,
|
|
}
|
|
|
|
impl RequestResult<'_> {
|
|
pub fn validate(&self, _ctx: &Context<Self>, _params: &RequestResultParams) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
|
|
pub fn actuate(ctx: &Context<Self>, params: &RequestResultParams) -> Result<()> {
|
|
let client_state = ctx.accounts.state.load()?;
|
|
let bump = client_state.bump.clone();
|
|
let max_result = client_state.max_result.clone();
|
|
drop(client_state);
|
|
|
|
let switchboard_program = ctx.accounts.switchboard_program.to_account_info();
|
|
|
|
let vrf_request_randomness = VrfRequestRandomness {
|
|
authority: ctx.accounts.state.to_account_info(),
|
|
vrf: ctx.accounts.vrf.to_account_info(),
|
|
oracle_queue: ctx.accounts.oracle_queue.to_account_info(),
|
|
queue_authority: ctx.accounts.queue_authority.to_account_info(),
|
|
data_buffer: ctx.accounts.data_buffer.to_account_info(),
|
|
permission: ctx.accounts.permission.to_account_info(),
|
|
escrow: ctx.accounts.escrow.clone(),
|
|
payer_wallet: ctx.accounts.payer_wallet.clone(),
|
|
payer_authority: ctx.accounts.payer_authority.to_account_info(),
|
|
recent_blockhashes: ctx.accounts.recent_blockhashes.to_account_info(),
|
|
program_state: ctx.accounts.program_state.to_account_info(),
|
|
token_program: ctx.accounts.token_program.to_account_info(),
|
|
};
|
|
|
|
let vrf_key = ctx.accounts.vrf.key.clone();
|
|
let authority_key = ctx.accounts.authority.key.clone();
|
|
|
|
msg!("bump: {}", bump);
|
|
msg!("authority: {}", authority_key);
|
|
msg!("vrf: {}", vrf_key);
|
|
|
|
let state_seeds: &[&[&[u8]]] = &[&[
|
|
&STATE_SEED,
|
|
vrf_key.as_ref(),
|
|
authority_key.as_ref(),
|
|
&[bump],
|
|
]];
|
|
msg!("requesting randomness");
|
|
vrf_request_randomness.invoke_signed(
|
|
switchboard_program,
|
|
params.switchboard_state_bump,
|
|
params.permission_bump,
|
|
state_seeds,
|
|
)?;
|
|
|
|
emit!(RequestingRandomness{
|
|
vrf_client: ctx.accounts.state.key(),
|
|
max_result: max_result,
|
|
timestamp: clock::Clock::get().unwrap().unix_timestamp
|
|
});
|
|
|
|
msg!("randomness requested successfully");
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
```
|
|
|
|
</TabItem>
|
|
<TabItem value="Typescript" label="Typescript">
|
|
|
|
```ts
|
|
import * as anchor from "@project-serum/anchor";
|
|
import { Keypair } from "@solana/web3.js";
|
|
import {
|
|
loadSwitchboardProgram,
|
|
VrfAccount,
|
|
} from "@switchboard-xyz/switchboard-v2";
|
|
|
|
let payer: Keypair;
|
|
let authority: Keypair;
|
|
const program = await loadSwitchboardProgram("devnet", undefined, payer);
|
|
|
|
const vrfAccount = new VrfAccount({
|
|
program,
|
|
publicKey: vrfKey,
|
|
});
|
|
const vrf = await vrfAccount.loadData();
|
|
|
|
const queueAccount = new OracleQueueAccount({
|
|
program,
|
|
publicKey: vrf.queuePubkey,
|
|
});
|
|
const queue = await queueAccount.loadData();
|
|
const mint = await queueAccount.loadMint();
|
|
|
|
const payerTokenWallet = = (
|
|
await mint.getOrCreateAssociatedAccountInfo(payer.publicKey)
|
|
).address;
|
|
|
|
const signature = await vrfAccount.requestRandomness({
|
|
authority,
|
|
payer: payerTokenWallet,
|
|
payerAuthority: payer,
|
|
});
|
|
```
|
|
|
|
</TabItem>
|
|
<TabItem value="CLI" label="CLI">
|
|
|
|
```bash
|
|
sbv2 vrf request VRFKEY \
|
|
--keypair "path/to/payer/keypair.json"
|
|
```
|
|
|
|
</TabItem>
|
|
</Tabs>
|
|
```
|