oil_api/state/
referral.rs

1use serde::{Deserialize, Serialize};
2use solana_program::pubkey::Pubkey;
3use solana_program::program_error::ProgramError;
4use solana_program::log::sol_log;
5use steel::*;
6
7use super::OilAccount;
8use crate::consts::REFERRAL;
9
10/// Referral account tracks a referrer's stats and pending rewards.
11/// 
12/// Anyone can create a Referral account to become a referrer.
13/// When a new miner signs up with their referral link, 0.5% of that
14/// miner's claimed rewards from each mode (block-based and auction-based)
15/// are credited to the referrer's pending balance, totaling 1% across both modes.
16#[repr(C)]
17#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
18pub struct Referral {
19    /// The authority (wallet) of this referrer.
20    pub authority: Pubkey,
21
22    /// Total number of miners referred by this referrer.
23    pub total_referred: u64,
24
25    /// Total SOL earned from referrals (lifetime, for stats).
26    pub total_sol_earned: u64,
27
28    /// Total OIL earned from referrals (lifetime, for stats).
29    pub total_oil_earned: u64,
30
31    /// Pending SOL rewards to claim.
32    pub pending_sol: u64,
33
34    /// Pending OIL rewards to claim.
35    pub pending_oil: u64,
36}
37
38impl Referral {
39    /// Claims pending SOL rewards.
40    /// Returns the amount of SOL that can be claimed and resets pending_sol to 0.
41    pub fn claim_sol(&mut self) -> u64 {
42        let amount = self.pending_sol;
43        self.pending_sol = 0;
44        amount
45    }
46
47    /// Claims pending OIL rewards.
48    /// Returns the amount of OIL that can be claimed and resets pending_oil to 0.
49    pub fn claim_oil(&mut self) -> u64 {
50        let amount = self.pending_oil;
51        self.pending_oil = 0;
52        amount
53    }
54
55    /// Calculates and credits SOL referral bonus (0.5% of total amount).
56    /// Returns the referral amount that should be transferred to this referral account.
57    /// 
58    /// This function handles:
59    /// - Calculating 0.5% referral bonus
60    /// - Crediting pending_sol
61    /// - Updating total_sol_earned
62    pub fn credit_sol_referral(&mut self, total_amount: u64) -> u64 {
63        // Calculate referral bonus (0.5% of total claim)
64        let referral_amount = if total_amount > 0 {
65            total_amount / 200 // 0.5% = 1/200
66        } else {
67            0
68        };
69
70        // Credit referral account
71        if referral_amount > 0 {
72            self.pending_sol += referral_amount;
73            self.total_sol_earned += referral_amount;
74        }
75
76        referral_amount
77    }
78
79    /// Calculates and credits OIL referral bonus (0.5% of total amount).
80    /// Returns the referral amount that should be transferred to this referral account.
81    /// 
82    /// This function handles:
83    /// - Calculating 0.5% referral bonus
84    /// - Crediting pending_oil
85    /// - Updating total_oil_earned
86    pub fn credit_oil_referral(&mut self, total_amount: u64) -> u64 {
87        // Calculate referral bonus (0.5% of total claim)
88        let referral_amount = if total_amount > 0 {
89            total_amount / 200 // 0.5% = 1/200
90        } else {
91            0
92        };
93
94        // Credit referral account
95        if referral_amount > 0 {
96            self.pending_oil += referral_amount;
97            self.total_oil_earned += referral_amount;
98        }
99
100        referral_amount
101    }
102
103    /// Validates and increments referral count for a new miner.
104    /// 
105    /// This function:
106    /// - Validates the referrer is not default and not self-referral
107    /// - Validates the referral account exists and matches the referrer
108    /// - Increments the referral's total_referred count
109    /// 
110    /// Returns Ok(()) if referral was processed, or Err if validation fails.
111    /// If referrer is invalid (default or self), returns Ok(()) without processing.
112    pub fn process_new_miner_referral<'a>(
113        referral_info_opt: Option<&AccountInfo<'a>>,
114        referrer: Pubkey,
115        authority: Pubkey,
116    ) -> Result<(), ProgramError> {
117        // Only process if referrer is valid (not default and not self-referral)
118        if referrer == Pubkey::default() || referrer == authority {
119            return Ok(());
120        }
121        
122        // Referral account must be provided
123        let referral_info = referral_info_opt.ok_or(ProgramError::NotEnoughAccountKeys)?;
124        
125        // Validate referral account
126        referral_info
127            .is_writable()?
128            .has_seeds(&[REFERRAL, &referrer.to_bytes()], &crate::ID)?;
129        
130        // Referral account must exist
131        if referral_info.data_is_empty() {
132            return Err(ProgramError::InvalidAccountData);
133        }
134        
135        // Increment total_referred
136        let referral = referral_info.as_account_mut::<Referral>(&crate::ID)?;
137        referral.total_referred += 1;
138        sol_log(&format!("Referral: {} now has {} referrals", referrer, referral.total_referred));
139        
140        Ok(())
141    }
142}
143
144account!(OilAccount, Referral);