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);