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:
- Transfers ticket price to vault
- Creates encrypted handle from guess
- Stores ticket with encrypted guess
- 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:
- Verifies caller is authority
- Closes the raffle (
is_open = false)
- 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:
- Uses
e_eq to compare guess_handle with winning_number_handle
- Stores encrypted boolean result in
is_winner_handle
- 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:
- Creates encrypted prize amount (vault balance)
- Creates encrypted zero
- Uses
e_select: if winner, get prize; else get 0
- Stores encrypted prize amount
- 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:
- Verifies ticket owner and claimed status
- Verifies Ed25519 signature on-chain (proves decryption is valid)
- Parses plaintext to get prize amount
- Rejects if prize is 0 (not a winner)
- 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
| Account | Seeds |
|---|
| Lottery | ["lottery", lottery_id] |
| Vault | ["vault", lottery_pubkey] |
| Ticket | ["ticket", lottery_pubkey, buyer_pubkey] |
Next Steps
See Client Integration for TypeScript usage examples.