Expand description
§alea-sdk
CPI crate for Alea — the first production drand BN254 BLS verifier on Solana. Any Anchor program can receive verified on-chain randomness with a single CPI call.
§Quick Start
Add the mandatory constraints to your Accounts struct and call cpi::verify:
use alea_sdk::{self, AleaVerifier};
use anchor_lang::solana_program::sysvar::clock::Clock;
const MAX_BEACON_AGE_SECONDS: u64 = 30;
#[derive(Accounts)]
pub struct SettleMatch<'info> {
pub alea_program: Program<'info, AleaVerifier>,
#[account(
seeds = [b"config"],
bump,
seeds::program = alea_program.key(), // ← MANDATORY (ADR 0034)
)]
pub alea_config: Account<'info, alea_sdk::Config>,
pub payer: Signer<'info>,
pub clock: Sysvar<'info, Clock>,
}
pub fn settle_match(ctx: Context<SettleMatch>, round: u64, sig: [u8; 64]) -> Result<()> {
// MANDATORY: reject stale beacons before CPI
require!(
alea_sdk::is_round_recent(round, &ctx.accounts.alea_config, &ctx.accounts.clock, MAX_BEACON_AGE_SECONDS),
YourError::StaleBeacon,
);
// One-line CPI. Returns VerifiedRandomness (must_use wrapper).
let randomness = alea_sdk::cpi::verify(
ctx.accounts.alea_program.to_account_info(),
ctx.accounts.alea_config.to_account_info(),
ctx.accounts.payer.to_account_info(),
round, sig,
)?.into_inner();
// Read IMMEDIATELY — Solana return data is overwritten by any subsequent CPI
let random_value = u64::from_le_bytes(randomness[0..8].try_into().unwrap());
// … use randomness …
Ok(())
}§Security: Mandatory Constraints
Two constraints are MANDATORY for ANY consumer (omitting either ships an exploitable program):
-
seeds::program = alea_program.key()on thealea_configaccount. Without this, an attacker can substitute a fake Config PDA owned by a different program and feed attacker-controlled public keys to the pairing check. This is total compromise for any randomness consumer. (ADR 0034) -
is_round_recent()before trusting randomness. Without recency enforcement, an attacker can replay an old drand round whose randomness they already know to bias resolution.
§CPI Return Data Ordering Warning
Solana’s return data is single-slot — each CPI call overwrites the
previous value. Read cpi::verify’s result into a local variable
IMMEDIATELY, before any other CPI calls (token transfers, etc.).
§Compute Budget
Every transaction calling Alea MUST include a compute budget instruction of at least 900,000 CU (Solana default is 200K; Alea needs up to 454K + consumer headroom). The TypeScript SDK injects this automatically.
§Program IDs
| Cluster | Program ID |
|---|---|
| Devnet | ALEAydzHd4cN2EWcdHKp4hehAE4B88b16gqVtVqsck2U |
| Mainnet | Pending Phase 5 (same vanity ID — cluster binding is identical) |
Devnet-verified; mainnet deployment pending Phase 5. Cluster binding identical (vanity ID usable on both), mainnet traffic begins Phase 5.
§Maturity
See CAVEATS.md for maturity disclosures before integrating.
Re-exports§
pub use cpi::VerifiedRandomness;
Modules§
- accounts
- Account type re-exports for Alea CPI consumers.
- cpi
- CPI wrapper for Alea’s
verifyinstruction. - errors
- Error type re-exports for Alea CPI consumers.
Structs§
- Alea
Verifier - Type representing the program.
- Config
- drand evmnet configuration account.
Enums§
- Alea
Error - Alea error codes. Codes 6000-6009 are assigned in declaration order and are part of the v1 CPI interface per ADR 0028 — never renumber, never remove (reserved even if unreachable, see NoSquareRoot).
Constants§
- PROGRAM_
ID - Canonical Alea program ID. Vanity, frozen for the lifetime of the mainnet deployment per ADR 0028. Same ID used across localnet / devnet / mainnet by design — consumer SDKs do not need to branch per cluster.
Functions§
- config_
pda - Derive the Alea
ConfigPDA for a given program ID. - is_
round_ recent - Check that a drand round is recent relative to the current on-chain
clock. Returns
trueif the round’s emission timestamp is withinmax_age_secondsof the current slot’sunix_timestamp.