1use serde::{Deserialize, Serialize};
2use steel::*;
3
4use crate::state::plot_pda;
5
6use super::OilAccount;
7
8#[repr(C)]
10#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
11pub struct Plot {
12 pub authority: Pubkey,
14
15 pub plot_level: u64,
17
18 pub fuel_capacity: u64,
20
21 pub current_fuel: u64,
23
24 pub max_rig_slots: u64,
26
27 pub total_mining_power: u64,
29
30 pub last_claim_block: u64,
32
33 pub last_fuel_update_block: u64,
35
36 pub last_upgrade_timestamp: i64,
38
39 pub total_fuel_requirement: u64,
41
42 pub num_rigs_staked: u64,
44
45 pub last_refinery_rewards_factor: Numeric,
47
48 pub buffer_a: u64,
51 pub buffer_b: u64,
53 pub buffer_c: u64,
55}
56
57impl Plot {
58 pub fn pda(&self) -> (Pubkey, u8) {
59 plot_pda(self.authority)
60 }
61
62 pub fn initialize(&mut self, authority: Pubkey, clock: &Clock) {
63 self.authority = authority;
64 self.plot_level = 0;
65 self.fuel_capacity = 20;
66 self.current_fuel = 20; self.max_rig_slots = 1;
68 self.total_mining_power = 0;
69 self.last_claim_block = clock.slot;
70 self.last_fuel_update_block = clock.slot;
71 self.last_upgrade_timestamp = 0; self.total_fuel_requirement = 0;
73 self.num_rigs_staked = 0;
74 self.last_refinery_rewards_factor = Numeric::ZERO;
75 self.buffer_a = 0; self.buffer_b = 0;
77 self.buffer_c = 0;
78 }
79
80 pub fn can_upgrade(&self, clock: &Clock) -> bool {
82 if self.last_upgrade_timestamp == 0 {
83 return true; }
85 let elapsed = clock.unix_timestamp - self.last_upgrade_timestamp;
86 elapsed >= 86400 }
88
89 pub fn get_upgrade_cost(&self) -> Option<u64> {
92 if self.plot_level >= 9 {
93 return None; }
95 let next_level = self.plot_level + 1;
96 Some(Self::upgrade_cost_for_level(next_level))
97 }
98
99 pub fn upgrade_cost_for_level(level: u64) -> u64 {
101 const UPGRADE_COSTS: [u64; 9] = [
102 210, 1_125, 888, 1_065, 1_275, 2_550, 5_000, 10_000, 20_000, ];
112 if level == 0 || level > 9 {
113 return 0;
114 }
115 UPGRADE_COSTS[(level - 1) as usize] * crate::consts::ONE_OIL
116 }
117
118 pub fn level_stats(level: u64) -> (u64, u64) {
120 const LEVEL_STATS: [(u64, u64); 10] = [
121 (1, 20), (1, 50), (2, 120), (2, 300), (3, 750), (3, 1_800), (4, 4_500), (4, 11_000), (5, 27_000), (5, 65_000), ];
132 if level > 9 {
133 return LEVEL_STATS[9];
134 }
135 LEVEL_STATS[level as usize]
136 }
137
138 pub fn upgrade(&mut self, clock: &Clock) -> Option<u64> {
141 if !self.can_upgrade(clock) {
142 return None; }
144 if self.plot_level >= 9 {
145 return None; }
147 let new_level = self.plot_level + 1;
148 let (slots, fuel_capacity) = Self::level_stats(new_level);
149 let cost = Self::upgrade_cost_for_level(new_level);
150
151 self.plot_level = new_level;
152 self.max_rig_slots = slots;
153 self.fuel_capacity = fuel_capacity;
154 self.last_upgrade_timestamp = clock.unix_timestamp;
155
156 Some(cost)
157 }
158
159 pub fn apply_fuel_consumption(&mut self, clock: &Clock) -> u64 {
162 let blocks_elapsed = clock.slot.saturating_sub(self.last_fuel_update_block);
163 if blocks_elapsed == 0 {
164 return if self.current_fuel > 0 {
165 self.total_mining_power
166 } else {
167 0
168 };
169 }
170 let total_rate = self.buffer_a; let total_consumption = (total_rate as u128)
172 .saturating_mul(blocks_elapsed as u128)
173 .checked_div(crate::consts::ONE_OIL as u128)
174 .unwrap_or(0) as u64;
175 self.current_fuel = self.current_fuel.saturating_sub(total_consumption);
176 self.last_fuel_update_block = clock.slot;
177 if self.current_fuel > 0 {
178 self.total_mining_power
179 } else {
180 0
181 }
182 }
183
184 pub fn update_fuel_consumption(
186 &mut self,
187 staked_rig_consumption_rates: &[u64],
188 clock: &Clock,
189 ) -> u64 {
190 let blocks_elapsed = clock.slot.saturating_sub(self.last_fuel_update_block);
191 if blocks_elapsed == 0 {
192 return if self.current_fuel > 0 {
193 self.total_mining_power
194 } else {
195 0
196 };
197 }
198 let mut total_consumption = 0u64;
199 for &consumption_rate in staked_rig_consumption_rates {
200 let rig_consumption = (consumption_rate as u128)
201 .checked_mul(blocks_elapsed as u128)
202 .and_then(|x| x.checked_div(crate::consts::ONE_OIL as u128))
203 .unwrap_or(0) as u64;
204 total_consumption = total_consumption.saturating_add(rig_consumption);
205 }
206 self.current_fuel = self.current_fuel.saturating_sub(total_consumption);
207 self.last_fuel_update_block = clock.slot;
208 if self.current_fuel > 0 {
209 self.total_mining_power
210 } else {
211 0
212 }
213 }
214}
215
216account!(OilAccount, Plot);