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 total_fuel_consumption_rate: u64,
50 pub buffer_b: u64,
52 pub buffer_c: u64,
54}
55
56impl Plot {
57 pub fn pda(&self) -> (Pubkey, u8) {
58 plot_pda(self.authority)
59 }
60
61 pub fn initialize(&mut self, authority: Pubkey, clock: &Clock) {
62 self.authority = authority;
63 self.plot_level = 0;
64 self.fuel_capacity = 20;
65 self.current_fuel = 20; self.max_rig_slots = 1;
67 self.total_mining_power = 0;
68 self.last_claim_block = clock.slot;
69 self.last_fuel_update_block = clock.slot;
70 self.last_upgrade_timestamp = 0; self.total_fuel_requirement = 0;
72 self.num_rigs_staked = 0;
73 self.last_refinery_rewards_factor = Numeric::ZERO;
74 self.total_fuel_consumption_rate = 0;
75 self.buffer_b = 0;
76 self.buffer_c = 0;
77 }
78
79 pub fn can_upgrade(&self, clock: &Clock) -> bool {
81 if self.last_upgrade_timestamp == 0 {
82 return true; }
84 let elapsed = clock.unix_timestamp - self.last_upgrade_timestamp;
85 elapsed >= 86400 }
87
88 pub fn get_upgrade_cost(&self) -> Option<u64> {
91 if self.plot_level >= 9 {
92 return None; }
94 let next_level = self.plot_level + 1;
95 Some(Self::upgrade_cost_for_level(next_level))
96 }
97
98 pub fn upgrade_cost_for_level(level: u64) -> u64 {
100 const UPGRADE_COSTS: [u64; 9] = [
101 210, 1_125, 888, 1_065, 1_275, 2_550, 5_000, 10_000, 20_000, ];
111 if level == 0 || level > 9 {
112 return 0;
113 }
114 UPGRADE_COSTS[(level - 1) as usize] * crate::consts::ONE_OIL
115 }
116
117 pub fn level_stats(level: u64) -> (u64, u64) {
119 const LEVEL_STATS: [(u64, u64); 10] = [
120 (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), ];
131 if level > 9 {
132 return LEVEL_STATS[9];
133 }
134 LEVEL_STATS[level as usize]
135 }
136
137 pub fn upgrade(&mut self, clock: &Clock) -> Option<u64> {
140 if !self.can_upgrade(clock) {
141 return None; }
143 if self.plot_level >= 9 {
144 return None; }
146 let new_level = self.plot_level + 1;
147 let (slots, fuel_capacity) = Self::level_stats(new_level);
148 let cost = Self::upgrade_cost_for_level(new_level);
149
150 self.plot_level = new_level;
151 self.max_rig_slots = slots;
152 self.fuel_capacity = fuel_capacity;
153 self.last_upgrade_timestamp = clock.unix_timestamp;
154
155 Some(cost)
156 }
157
158 pub fn apply_fuel_consumption(&mut self, clock: &Clock) -> u64 {
163 let blocks_elapsed = clock.slot.saturating_sub(self.last_fuel_update_block);
164 if blocks_elapsed == 0 {
165 return if self.current_fuel > 0 {
166 self.total_mining_power
167 } else {
168 0
169 };
170 }
171 let total_rate = self.total_fuel_consumption_rate; if total_rate == 0 {
173 return self.total_mining_power;
175 }
176 let fuel_supported_blocks = {
180 let max_fuel_blocks = (self.current_fuel as u128)
181 .saturating_mul(crate::consts::ONE_OIL as u128)
182 .checked_div(total_rate as u128)
183 .unwrap_or(u64::MAX as u128) as u64;
184 blocks_elapsed.min(max_fuel_blocks)
185 };
186 let consumed = (total_rate as u128)
188 .saturating_mul(fuel_supported_blocks as u128)
189 .checked_div(crate::consts::ONE_OIL as u128)
190 .unwrap_or(0) as u64;
191 self.current_fuel = self.current_fuel.saturating_sub(consumed);
192 self.last_fuel_update_block = self.last_fuel_update_block.saturating_add(fuel_supported_blocks);
193 if fuel_supported_blocks == 0 {
195 return 0;
196 }
197 ((self.total_mining_power as u128) * (fuel_supported_blocks as u128) / (blocks_elapsed as u128)) as u64
198 }
199
200 pub fn update_fuel_consumption(
203 &mut self,
204 staked_rig_consumption_rates: &[u64],
205 clock: &Clock,
206 ) -> u64 {
207 let blocks_elapsed = clock.slot.saturating_sub(self.last_fuel_update_block);
208 if blocks_elapsed == 0 {
209 return if self.current_fuel > 0 {
210 self.total_mining_power
211 } else {
212 0
213 };
214 }
215 let total_rate: u64 = staked_rig_consumption_rates.iter().copied().sum();
216 if total_rate == 0 {
217 return self.total_mining_power;
218 }
219 let fuel_supported_blocks = {
220 let max_fuel_blocks = (self.current_fuel as u128)
221 .saturating_mul(crate::consts::ONE_OIL as u128)
222 .checked_div(total_rate as u128)
223 .unwrap_or(u64::MAX as u128) as u64;
224 blocks_elapsed.min(max_fuel_blocks)
225 };
226 let mut consumed = 0u64;
227 for &consumption_rate in staked_rig_consumption_rates {
228 let rig_consumption = (consumption_rate as u128)
229 .saturating_mul(fuel_supported_blocks as u128)
230 .checked_div(crate::consts::ONE_OIL as u128)
231 .unwrap_or(0) as u64;
232 consumed = consumed.saturating_add(rig_consumption);
233 }
234 self.current_fuel = self.current_fuel.saturating_sub(consumed);
235 self.last_fuel_update_block = self.last_fuel_update_block.saturating_add(fuel_supported_blocks);
236 if fuel_supported_blocks == 0 {
237 return 0;
238 }
239 ((self.total_mining_power as u128) * (fuel_supported_blocks as u128) / (blocks_elapsed as u128)) as u64
240 }
241}
242
243account!(OilAccount, Plot);