Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.inco.org/llms.txt

Use this file to discover all available pages before exploring further.

Program

This page documents the Private Raffle program’s instructions and account structures.

Account Structures

Lottery

The main raffle account:
#[account]
pub struct Lottery {
    pub authority: Pubkey,           // Raffle creator
    pub lottery_id: u64,             // Unique identifier
    pub ticket_price: u64,           // Price in lamports
    pub participant_count: u32,      // Number of tickets sold
    pub is_open: bool,               // Can still buy tickets?
    pub winning_number_handle: u128, // Encrypted winning number
    pub bump: u8,
}

Ticket

Each player’s ticket:
#[account]
pub struct Ticket {
    pub lottery: Pubkey,          // Associated raffle
    pub owner: Pubkey,            // Ticket owner
    pub guess_handle: u128,       // Encrypted guess (1-100)
    pub is_winner_handle: u128,   // Encrypted: guess == winning?
    pub prize_handle: u128,       // Encrypted prize amount
    pub claimed: bool,            // Has claimed?
    pub bump: u8,
}

Instructions

create_lottery

Creates a new raffle.
pub fn create_lottery(
    ctx: Context<CreateLottery>,
    lottery_id: u64,      // Unique raffle ID
    ticket_price: u64     // Price per ticket (lamports)
) -> Result<()>
Accounts:
#[derive(Accounts)]
#[instruction(lottery_id: u64)]
pub struct CreateLottery<'info> {
    #[account(mut)]
    pub authority: Signer<'info>,
    
    #[account(
        init, 
        payer = authority, 
        space = Lottery::SIZE,
        seeds = [b"lottery", lottery_id.to_le_bytes().as_ref()], 
        bump
    )]
    pub lottery: Account<'info, Lottery>,
    
    /// CHECK: vault PDA for holding prize pool
    #[account(mut, seeds = [b"vault", lottery.key().as_ref()], bump)]
    pub vault: AccountInfo<'info>,
    
    pub system_program: Program<'info, System>,
}

buy_ticket

Purchase a ticket with an encrypted guess.
pub fn buy_ticket(
    ctx: Context<BuyTicket>,
    encrypted_guess: Vec<u8>  // Encrypted guess (1-100)
) -> Result<()>
What it does:
  1. Transfers ticket price to vault
  2. Creates encrypted handle from guess
  3. Stores ticket with encrypted guess
  4. Allows buyer to decrypt their own guess
Accounts:
#[derive(Accounts)]
pub struct BuyTicket<'info> {
    #[account(mut)]
    pub buyer: Signer<'info>,
    
    #[account(mut)]
    pub lottery: Account<'info, Lottery>,
    
    #[account(
        init, 
        payer = buyer, 
        space = Ticket::SIZE,
        seeds = [b"ticket", lottery.key().as_ref(), buyer.key().as_ref()], 
        bump
    )]
    pub ticket: Account<'info, Ticket>,
    
    /// CHECK: vault PDA
    #[account(mut, seeds = [b"vault", lottery.key().as_ref()], bump)]
    pub vault: AccountInfo<'info>,
    
    pub system_program: Program<'info, System>,
    pub inco_lightning_program: Program<'info, IncoLightning>,
}

draw_winner

Authority sets the encrypted winning number and closes the raffle.
pub fn draw_winner(
    ctx: Context<DrawWinner>,
    encrypted_winning_number: Vec<u8>  // Encrypted winning number
) -> Result<()>
What it does:
  1. Verifies caller is authority
  2. Closes the raffle (is_open = false)
  3. Stores encrypted winning number

check_winner

Compares a ticket’s guess against the winning number (encrypted comparison).
pub fn check_winner(ctx: Context<CheckWinner>) -> Result<()>
What it does:
  1. Uses e_eq to compare guess_handle with winning_number_handle
  2. Stores encrypted boolean result in is_winner_handle
  3. Allows ticket owner to decrypt the result
Key encrypted operation:
// Encrypted comparison: guess == winning_number?
let is_winner: Ebool = cpi::e_eq(
    cpi_ctx,
    Euint128(ticket.guess_handle),
    Euint128(lottery.winning_number_handle),
    0,
)?;

ticket.is_winner_handle = is_winner.0;

claim_prize

Calculates the encrypted prize amount based on win/loss status.
pub fn claim_prize(ctx: Context<ClaimPrize>) -> Result<()>
What it does:
  1. Creates encrypted prize amount (vault balance)
  2. Creates encrypted zero
  3. Uses e_select: if winner, get prize; else get 0
  4. Stores encrypted prize amount
  5. Marks ticket as claimed
Key encrypted operation:
// e_select: if winner, get prize; else get 0
let actual_prize: Euint128 = e_select(
    cpi_ctx,
    Ebool(ticket.is_winner_handle),  // condition
    encrypted_prize,                  // if true
    zero,                             // if false
    0,
)?;

ticket.prize_handle = actual_prize.0;

withdraw_prize

Winner withdraws their prize with on-chain signature verification.
pub fn withdraw_prize(
    ctx: Context<WithdrawPrize>,
    handle: Vec<u8>,      // Prize handle as bytes
    plaintext: Vec<u8>    // Decrypted prize amount
) -> Result<()>
What it does:
  1. Verifies ticket owner and claimed status
  2. Verifies Ed25519 signature on-chain (proves decryption is valid)
  3. Parses plaintext to get prize amount
  4. Rejects if prize is 0 (not a winner)
  5. Transfers SOL from vault to winner
Key verification:
// Verify the decryption signature on-chain
cpi::is_validsignature(
    cpi_ctx,
    1,
    Some(vec![handle]),
    Some(vec![plaintext.clone()]),
)?;

// Parse verified plaintext
let prize_amount = parse_plaintext_to_u64(&plaintext)?;
require!(prize_amount > 0, LotteryError::NotWinner);

Error Codes

#[error_code]
pub enum LotteryError {
    #[msg("Lottery is closed")]
    LotteryClosed,
    #[msg("Lottery is still open")]
    LotteryStillOpen,
    #[msg("No winning number set")]
    NoWinningNumber,
    #[msg("No participants")]
    NoParticipants,
    #[msg("Not ticket owner")]
    NotOwner,
    #[msg("Already claimed")]
    AlreadyClaimed,
    #[msg("Ticket not checked yet")]
    NotChecked,
    #[msg("Not claimed yet")]
    NotClaimed,
    #[msg("Not the winner")]
    NotWinner,
    #[msg("Unauthorized")]
    Unauthorized,
}

PDA Seeds

AccountSeeds
Lottery["lottery", lottery_id]
Vault["vault", lottery_pubkey]
Ticket["ticket", lottery_pubkey, buyer_pubkey]

Next Steps

See Client Integration for TypeScript usage examples.