1use serde::{Deserialize, Serialize};
2use steel::*;
3
4use crate::state::well_pda;
5
6use super::{OilAccount, Auction};
7
8#[repr(C)]
10#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
11pub struct Well {
12 pub well_id: u64,
14
15 pub epoch_id: u64,
17
18 pub current_bidder: Pubkey,
20
21 pub init_price: u64,
23
24 pub mps: u64,
26
27 pub epoch_start_time: u64,
29
30 pub accumulated_oil: u64,
32
33 pub last_update_time: u64,
35
36 pub halving_count: u64,
38
39 pub lifetime_oil_mined: u64,
41
42 pub operator_total_oil_mined: u64,
44
45 pub buffer_c: u64,
47
48 pub total_contributed: u64,
52
53 pub pool_bid_cost: u64,
57}
58
59impl Well {
60 pub fn pda(well_id: u64) -> (Pubkey, u8) {
61 well_pda(well_id)
62 }
63
64 pub fn current_price(&self, auction: &Auction, clock: &Clock) -> u64 {
65 use crate::consts::AUCTION_FLOOR_PRICE;
66
67 use solana_program::pubkey::Pubkey;
69 if self.current_bidder == Pubkey::default() {
70 return self.init_price; }
72
73 let elapsed = clock.unix_timestamp.saturating_sub(self.epoch_start_time as i64);
74 let duration = auction.auction_duration_seconds as i64;
75
76 if elapsed >= duration {
77 return AUCTION_FLOOR_PRICE; }
79
80 let remaining = duration - elapsed;
82 let price_range = self.init_price.saturating_sub(AUCTION_FLOOR_PRICE);
83 let decayed_amount = (price_range as u128 * remaining as u128 / duration as u128) as u64;
84 AUCTION_FLOOR_PRICE + decayed_amount
85 }
86
87 pub fn update_accumulated_oil(&mut self, auction: &Auction, clock: &Clock) {
88 use solana_program::pubkey::Pubkey;
90 if self.current_bidder == Pubkey::default() {
91 return;
92 }
93
94 let last_update = self.last_update_time as i64;
95 let current_time = clock.unix_timestamp as u64;
96 let elapsed = clock.unix_timestamp.saturating_sub(last_update);
97 if elapsed <= 0 {
98 return;
99 }
100
101 let oil_mined = if auction.halving_count == 0 {
103 let first_halving_time = auction.last_halving_time + Auction::FIRST_HALVING_PERIOD_SECONDS;
105 if current_time >= first_halving_time && (last_update as u64) < first_halving_time {
106 let time_before_halving = first_halving_time.saturating_sub(last_update as u64);
108 let time_after_halving = current_time.saturating_sub(first_halving_time);
109 let rate_before = self.mps;
110 let rate_after = self.mps / 2; rate_before.checked_mul(time_before_halving).unwrap_or(0)
112 .checked_add(rate_after.checked_mul(time_after_halving).unwrap_or(0))
113 .unwrap_or(u64::MAX)
114 } else {
115 self.mps.checked_mul(elapsed as u64).unwrap_or(0)
117 }
118 } else {
119 let next_halving_time = auction.last_halving_time + auction.halving_period_seconds;
121 if current_time >= next_halving_time && (last_update as u64) < next_halving_time {
122 let mut total_oil = 0u64;
124 let mut segment_start = last_update as u64;
125 let mut current_rate = self.mps;
126 let mut last_halving = auction.last_halving_time;
127
128 while segment_start < current_time {
130 let next_halving = last_halving + auction.halving_period_seconds;
131 if next_halving > current_time {
132 let remaining_time = current_time.saturating_sub(segment_start);
134 total_oil = total_oil.checked_add(
135 current_rate.checked_mul(remaining_time).unwrap_or(0)
136 ).unwrap_or(u64::MAX);
137 break;
138 } else {
139 let segment_time = next_halving.saturating_sub(segment_start);
141 total_oil = total_oil.checked_add(
142 current_rate.checked_mul(segment_time).unwrap_or(0)
143 ).unwrap_or(u64::MAX);
144 current_rate = (current_rate * 3) / 4;
146 segment_start = next_halving;
147 last_halving = next_halving;
148 }
149 }
150 total_oil
151 } else {
152 self.mps.checked_mul(elapsed as u64).unwrap_or(0)
154 }
155 };
156
157 self.accumulated_oil = self.accumulated_oil
158 .checked_add(oil_mined)
159 .unwrap_or(u64::MAX);
160
161 self.lifetime_oil_mined = self.lifetime_oil_mined
162 .checked_add(oil_mined)
163 .unwrap_or(u64::MAX);
164
165 self.operator_total_oil_mined = self.operator_total_oil_mined
167 .checked_add(oil_mined)
168 .unwrap_or(u64::MAX);
169
170 self.last_update_time = current_time;
171 }
172
173 pub fn check_and_apply_halving(&mut self, auction: &mut Auction, clock: &Clock) {
174 let current_time = clock.unix_timestamp as u64;
176 let (halvings_to_apply, is_first_halving) = auction.should_apply_halving(current_time);
177
178 if halvings_to_apply > 0 {
179 if is_first_halving {
180 self.mps = self.mps / 2; self.halving_count += 1;
183 auction.halving_count += 1;
184 auction.last_halving_time = current_time;
185 } else {
186 for _ in 0..halvings_to_apply {
188 self.mps = (self.mps * 3) / 4; self.halving_count += 1;
190 auction.halving_count += 1;
191 }
192 auction.last_halving_time = current_time;
194 }
195 }
196 }
197}
198
199account!(OilAccount, Well);
200