Smart Contract

Rust-based Solana programs handle ticket sales and winner selection. Open-source code ensures complete transparency and security.

circle-info

Please check official GitHub for most recent updates. Official Links

Overview

This smart contract implements a secure and transparent raffle system on the Solana blockchain. It enables us to create raffles where participants can purchase tickets for chances to win prizes. The system prioritizes security, fairness, and transparency throughout the entire process.

Core Components

1. Raffle Creation and Management

The system allows authorized users to create and manage raffles with customizable parameters.

For Users:

  • Set ticket prices (0.1 to 100 SOL)

  • Define minimum required ticket sales

  • Set optional maximum ticket limit

  • Set raffle duration (1 hour to 30 days)

  • Provide prize information via metadata URI

Technical Implementation:

#[account]
pub struct Raffle {
    pub authority: Pubkey, // Raffle creator/admin
    pub treasury: Pubkey, // Treasury PDA holding funds
    pub metadata_uri: String, // Prize metadata
    pub ticket_price: u64, // Price per ticket in lamports
    pub current_tickets: u64, // Total tickets sold
    pub min_tickets: u64, // Minimum required sales
    pub max_tickets: Option<u64>, // Optional maximum tickets limit
    pub current_entries: u64, // Number of purchase transactions
    pub creation_time: i64, // Unix timestamp of creation
    pub end_time: i64, // Unix timestamp of end
    pub raffle_state: RaffleState, // Current state
    pub winner_address: Option<Pubkey>, // Winner if drawn
    pub winning_ticket: Option<u64>, // The winning ticket that was drafted
}

pub fn create_raffle(
    ctx: Context<CreateRaffle>,
    metadata_uri: String,
    ticket_price: u64,
    end_time: i64,
    min_tickets: u64,
    max_tickets: Option<u64>,
) -> Result<()> {
    let current_time = Clock::get()?.unix_timestamp;

    // Validate inputs
    // URI format check - must start with one of the valid prefixes
    require!(
        VALID_URI_PREFIXES
            .iter()
            .any(|prefix| metadata_uri.starts_with(prefix)),
        RaffleError::InvalidMetadataUri
    );
    require!(metadata_uri.len() <= 256, RaffleError::MetadataUriTooLong);

    // Price checks
    require!(
        ticket_price >= MIN_TICKET_PRICE,
        RaffleError::TicketPriceTooLow
    );
    require!(
        ticket_price <= MAX_TICKET_PRICE,
        RaffleError::TicketPriceTooHigh
    );

    // Ticket count checks
    require!(min_tickets > 0, RaffleError::MinTicketsTooLow);
    require!(
        min_tickets <= MAX_MIN_TICKETS,
        RaffleError::MinTicketsTooHigh
    );

    // Check that max tickets is greater than or equal to min tickets
    if let Some(max_tickets) = max_tickets {
        require!(max_tickets >= min_tickets, RaffleError::MaxTicketsTooLow);
    }

    // Time checks
    require!(
        end_time > current_time.checked_add(MIN_DURATION).unwrap(),
        RaffleError::EndTimeTooClose
    );
    require!(
        end_time <= current_time.checked_add(MAX_DURATION).unwrap(),
        RaffleError::DurationTooLong
    );

    // Set inputs from transaction data
    ctx.accounts.raffle.metadata_uri = metadata_uri;
    ctx.accounts.raffle.ticket_price = ticket_price;
    ctx.accounts.raffle.min_tickets = min_tickets;
    ctx.accounts.raffle.end_time = end_time;
    ctx.accounts.raffle.treasury = ctx.accounts.treasury.key();
    ctx.accounts.treasury.bump = ctx.bumps.treasury;
    ctx.accounts.treasury.raffle = ctx.accounts.raffle.key();
    ctx.accounts.raffle.max_tickets = max_tickets;

    // Set default values
    ctx.accounts.raffle.current_tickets = 0;
    ctx.accounts.raffle.current_entries = 0;
    ctx.accounts.raffle.creation_time = current_time;
    ctx.accounts.raffle.raffle_state = RaffleState::Open;
    ctx.accounts.raffle.winner_address = None;
    ctx.accounts.raffle.winning_ticket = None;

    // Increment the raffle counter
    ctx.accounts.config.raffle_counter = ctx
        .accounts
        .config
        .raffle_counter
        .checked_add(1)
        .ok_or(RaffleError::Overflow)?;

    // Emit the raffle created event
    emit!(RaffleCreated {
        raffle: ctx.accounts.raffle.key(),
        metadata_uri: ctx.accounts.raffle.metadata_uri.clone(),
        ticket_price,
        min_tickets,
        end_time,
        creation_time: current_time,
    });

    Ok(())
}

2. Ticket System

The ticket system manages purchases, ownership tracking, and ticket distribution.

For Users:

  • Purchase multiple tickets in a single transaction

  • View ticket ownership and balance

  • Track all transactions on-chain

  • Secure entry tracking with randomized seeds

Technical Implementation:

3. Treasury Management

Secure handling of funds throughout the raffle lifecycle.

For Users:

  • Automatic fund collection in treasury

  • Secure withdrawal process

  • Automatic refunds for expired raffles

  • Protected balance tracking

  • Treasury can only be managed via program instructions (No associated private key)

Technical Implementation:

Winner Selection System

1. Randomness Implementation

The system uses a sophisticated multi-step process to ensure fair and verifiable winner selection.

For Users:

  • Transparent randomness source

  • Verifiable drawing process

  • Equal chance for every ticket

  • Resistant to manipulation

Technical Implementation:

2. Winner Determination

Once a winning ticket is drawn, the system identifies the owner and updates the raffle state.

Technical Implementation:

Security Features

1. Input Validation

The system implements strict validation for all inputs to prevent exploitation.

For Users:

  • Protected against invalid parameters

  • Secure price and ticket limits

  • Time constraint validation

  • URI format verification

Technical Implementation:

2. Access Control

Robust permission system ensuring only authorized operations.

For Users:

  • Secure account initialization

  • Protected administrative functions

  • PDAs only allow data modification through program instructions

  • PDAs have no private keys, so funds can only be moved via program instructions

Technical Implementation:

3. Financial Security

Multiple layers of protection for financial operations.

For Users:

  • Protected fund transfers

  • Balance verification

  • Overflow protection

  • Automated treasury management

Technical Implementation:

4. Enhanced Randomness

The system uses a sophisticated randomness implementation to ensure fair and unpredictable selection.

For Users:

  • Multiple entropy sources

  • Cryptographic mixing

  • Unbiased selection

  • Statistical fairness

Technical Implementation:

Refund System

The contract includes functionality for users to reclaim funds from expired raffles.

For Users:

  • Automatic eligibility for refunds if raffle expires

  • Full refund of ticket purchase amount

  • Secure transfer process

Technical Implementation:

State Management

Raffle States

The system maintains clear state transitions throughout the raffle lifecycle.

For Users:

  • Open: Active and accepting tickets

  • Drawing: Winning ticket drawn, looking for entry with ticket

  • Drawn: Winner selected

  • Expired: Failed to meet minimum tickets

  • Claimed: Prize claimed by winner

Technical Implementation:

Event System

The contract emits events for important actions to provide transparency and auditability.

Technical Implementation:

Error Handling System

Error Definitions

The contract implements comprehensive error handling for all operations.

For Users:

  • Clear error messages

  • Specific error types

  • Input validation errors

  • State transition errors

  • Financial operation errors

Technical Implementation:

Last updated