oil_api/state/whitelist.rs
1use serde::{Deserialize, Serialize};
2use solana_program::program_error::ProgramError;
3use solana_program::log::sol_log;
4use steel::*;
5
6use super::OilAccount;
7
8/// Whitelist tracks access codes for pre-mine phase.
9/// Each code can be used by multiple wallets - codes are shared.
10/// PDA includes only code_hash (not tied to specific wallets).
11#[repr(C)]
12#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
13pub struct Whitelist {
14 /// The code hash (first 32 bytes of keccak256 hash of the code string)
15 pub code_hash: [u8; 32],
16
17 /// Number of times this code has been used (optional tracking)
18 pub usage_count: u64,
19}
20
21impl Whitelist {
22 /// Derives the PDA for a Whitelist account.
23 /// PDA seeds: [b"whitelist", code_hash]
24 /// This allows the same code to be used by multiple wallets.
25 pub fn pda(code_hash: [u8; 32]) -> (Pubkey, u8) {
26 use crate::ID;
27 Pubkey::find_program_address(&[b"whitelist", &code_hash], &ID)
28 }
29
30 /// Validates and processes a premine access code for a new transaction.
31 ///
32 /// This function:
33 /// - Requires access code if pre-mine is active
34 /// - Validates the code account exists and matches the provided hash
35 /// - Increments the code's usage count
36 /// - Ignores access code if pre-mine is not active (not an error)
37 ///
38 /// Returns Ok(()) if validation passes or code is ignored, Err if validation fails.
39 pub fn validate_premine_code<'a>(
40 is_premine: bool,
41 has_access_code: bool,
42 access_code_hash: [u8; 32],
43 whitelist_info_opt: Option<&AccountInfo<'a>>,
44 ) -> Result<(), ProgramError> {
45 if is_premine {
46 if !has_access_code {
47 return Err(ProgramError::InvalidArgument); // Access code required during pre-mine
48 }
49
50 // Validate access code: check if Whitelist PDA exists
51 let whitelist_info = whitelist_info_opt.ok_or(ProgramError::NotEnoughAccountKeys)?;
52
53 // Derive PDA using code_hash only (not tied to wallet)
54 let (whitelist_pda, _) = Self::pda(access_code_hash);
55 whitelist_info.has_address(&whitelist_pda)?;
56
57 // Check if account exists (has data)
58 if whitelist_info.data_is_empty() {
59 return Err(ProgramError::InvalidArgument); // Access code not found
60 }
61
62 // Validate the code hash matches
63 let whitelist = whitelist_info.as_account::<Whitelist>(&crate::ID)?;
64 if whitelist.code_hash != access_code_hash {
65 return Err(ProgramError::InvalidArgument); // Access code hash mismatch
66 }
67
68 // Increment usage count (optional tracking)
69 let whitelist_mut = whitelist_info.as_account_mut::<Whitelist>(&crate::ID)?;
70 whitelist_mut.usage_count = whitelist_mut.usage_count.saturating_add(1);
71
72 sol_log(&format!(
73 "Pre-mine: access code validated, usage_count={}",
74 whitelist_mut.usage_count
75 ));
76 } else if has_access_code {
77 // Access code provided but not in pre-mine - ignore it (not an error)
78 sol_log("Access code provided but pre-mine is not active - ignoring");
79 }
80
81 Ok(())
82 }
83}
84
85account!(OilAccount, Whitelist);