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#[repr(C)]
12#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
13pub struct Referral {
14    /// The authority (wallet) of this referrer.
15    pub authority: Pubkey,
16
17    /// Total number of miners referred by this referrer.
18    pub total_referred: u64,
19
20    /// Total SOL earned from referrals (lifetime, for stats).
21    pub total_sol_earned: u64,
22
23    /// Total OIL earned from referrals (lifetime, for stats).
24    pub total_oil_earned: u64,
25
26    /// Pending SOL rewards to claim.
27    pub pending_sol: u64,
28
29    /// Pending OIL rewards to claim.
30    pub pending_oil: u64,
31}
32
33impl Referral {
34    /// Claims pending SOL rewards.
35    pub fn claim_sol(&mut self) -> u64 {
36        let amount = self.pending_sol;
37        self.pending_sol = 0;
38        amount
39    }
40
41    /// Claims pending OIL rewards.
42    pub fn claim_oil(&mut self) -> u64 {
43        let amount = self.pending_oil;
44        self.pending_oil = 0;
45        amount
46    }
47
48    /// Calculates and credits SOL referral bonus (0.5% of total amount).
49    pub fn credit_sol_referral(&mut self, total_amount: u64) -> u64 {
50        // Calculate referral bonus (0.5% of total claim)
51        let referral_amount = if total_amount > 0 {
52            total_amount / 200 // 0.5% = 1/200
53        } else {
54            0
55        };
56
57        // Credit referral account
58        if referral_amount > 0 {
59            self.pending_sol += referral_amount;
60            self.total_sol_earned += referral_amount;
61        }
62
63        referral_amount
64    }
65
66    /// Calculates and credits OIL referral bonus (0.5% of total amount).
67    pub fn credit_oil_referral(&mut self, total_amount: u64) -> u64 {
68        // Calculate referral bonus (0.5% of total claim)
69        let referral_amount = if total_amount > 0 {
70            total_amount / 200 // 0.5% = 1/200
71        } else {
72            0
73        };
74
75        // Credit referral account
76        if referral_amount > 0 {
77            self.pending_oil += referral_amount;
78            self.total_oil_earned += referral_amount;
79        }
80
81        referral_amount
82    }
83
84    /// Validates and increments referral count for a new miner.
85    pub fn process_new_miner_referral<'a>(
86        referral_info_opt: Option<&AccountInfo<'a>>,
87        referrer: Pubkey,
88        authority: Pubkey,
89    ) -> Result<(), ProgramError> {
90        // Only process if referrer is valid (not default and not self-referral)
91        if referrer == Pubkey::default() || referrer == authority {
92            return Ok(());
93        }
94        
95        // Referral account must be provided
96        let referral_info = referral_info_opt.ok_or(ProgramError::NotEnoughAccountKeys)?;
97        
98        // Validate referral account
99        referral_info
100            .is_writable()?
101            .has_seeds(&[REFERRAL, &referrer.to_bytes()], &crate::ID)?;
102        
103        // Referral account must exist
104        if referral_info.data_is_empty() {
105            return Err(ProgramError::InvalidAccountData);
106        }
107        
108        // Increment total_referred
109        let referral = referral_info.as_account_mut::<Referral>(&crate::ID)?;
110        referral.total_referred += 1;
111        sol_log(&format!("Referral: {} now has {} referrals", referrer, referral.total_referred));
112        
113        Ok(())
114    }
115}
116
117account!(OilAccount, Referral);