Skip to main content

oil_api/state/
well.rs

1use serde::{Deserialize, Serialize};
2use steel::*;
3
4use crate::state::well_pda;
5
6use super::{OilAccount, Auction};
7
8/// Well account (one per well)
9#[repr(C)]
10#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
11pub struct Well {
12    /// Well ID (0-3) - which well this is for
13    pub well_id: u64,
14    
15    /// Current epoch ID (increments each auction: 0, 1, 2, 3, etc.)
16    pub epoch_id: u64,
17    
18    /// Current bidder/owner (Pubkey::default() if unowned)
19    pub current_bidder: Pubkey,
20    
21    /// Initial price for current epoch (in lamports)
22    pub init_price: u64,
23    
24    /// Mining per second (MPS) - current mining rate (OIL per second, in atomic units)
25    pub mps: u64,
26    
27    /// Epoch start time (timestamp when current epoch started)
28    pub epoch_start_time: u64,
29    
30    /// Accumulated OIL mined by current owner (not yet claimed)
31    pub accumulated_oil: u64,
32    
33    /// Last time accumulated_oil was updated
34    pub last_update_time: u64,
35    
36    /// Number of halvings that have occurred (for rate calculation)
37    pub halving_count: u64,
38    
39    /// Total OIL ever mined from this well (lifetime)
40    pub lifetime_oil_mined: u64,
41    
42    /// Total OIL mined by current operator (doesn't reset when claimed, only when ownership changes)
43    pub operator_total_oil_mined: u64,
44    
45    /// Buffer field (for future use) - previously is_pool_owned
46    pub buffer_c: u64,
47    
48    /// Buffer field (for future use)
49    pub buffer_d: u64,
50    
51    /// Buffer field (for future use)
52    pub buffer_e: u64,
53}
54
55impl Well {
56    pub fn pda(well_id: u64) -> (Pubkey, u8) {
57        well_pda(well_id)
58    }
59
60    /// Calculate current price for this epoch (Dutch auction - price decreases over time)
61    pub fn current_price(&self, auction: &Auction, clock: &Clock) -> u64 {
62        use crate::consts::AUCTION_FLOOR_PRICE;
63        
64        // If well has no owner (never been bid on), show starting price
65        use solana_program::pubkey::Pubkey;
66        if self.current_bidder == Pubkey::default() {
67            return self.init_price; // Return starting price for unowned wells
68        }
69        
70        let elapsed = clock.unix_timestamp.saturating_sub(self.epoch_start_time as i64);
71        let duration = auction.auction_duration_seconds as i64;
72        
73        if elapsed >= duration {
74            return AUCTION_FLOOR_PRICE; // Auction expired, price is at floor
75        }
76        
77        // Linear decay: price = floor + (init_price - floor) * (remaining / duration)
78        let remaining = duration - elapsed;
79        let price_range = self.init_price.saturating_sub(AUCTION_FLOOR_PRICE);
80        let decayed_amount = (price_range as u128 * remaining as u128 / duration as u128) as u64;
81        AUCTION_FLOOR_PRICE + decayed_amount
82    }
83
84    /// Update accumulated OIL for this epoch state
85    pub fn update_accumulated_oil(&mut self, clock: &Clock) {
86        // Skip if no owner
87        use solana_program::pubkey::Pubkey;
88        if self.current_bidder == Pubkey::default() {
89            return;
90        }
91        
92        let last_update = self.last_update_time as i64;
93        let elapsed = clock.unix_timestamp.saturating_sub(last_update);
94        if elapsed <= 0 {
95            return;
96        }
97        
98        // Calculate OIL mined: rate * time
99        let oil_mined = self.mps
100            .checked_mul(elapsed as u64)
101            .unwrap_or(0);
102        
103        self.accumulated_oil = self.accumulated_oil
104            .checked_add(oil_mined)
105            .unwrap_or(u64::MAX);
106        
107        self.lifetime_oil_mined = self.lifetime_oil_mined
108            .checked_add(oil_mined)
109            .unwrap_or(u64::MAX);
110        
111        // Track total mined by current operator (persists even after claiming)
112        self.operator_total_oil_mined = self.operator_total_oil_mined
113            .checked_add(oil_mined)
114            .unwrap_or(u64::MAX);
115        
116        self.last_update_time = clock.unix_timestamp as u64;
117    }
118
119    /// Check and apply halving if needed, updating mining rate
120    pub fn check_and_apply_halving(&mut self, auction: &mut Auction, clock: &Clock) {
121        // Check if we should apply halvings based on current time
122        let current_time = clock.unix_timestamp as u64;
123        let halvings_to_apply = auction.should_apply_halving(current_time);
124        
125        if halvings_to_apply > 0 {
126            // Apply halvings: 50% reduction (multiply by 0.5) per halving
127            for _ in 0..halvings_to_apply {
128                self.mps = self.mps / 2; // 50% reduction (multiply by 0.5)
129                self.halving_count += 1;
130            }
131            
132            // Update auction last_halving_time to current time
133            auction.last_halving_time = current_time;
134        }
135    }
136}
137
138account!(OilAccount, Well);
139