Skip to main content

ore_api/state/
miner.rs

1use serde::{Deserialize, Serialize};
2use steel::*;
3
4use crate::state::{miner_pda, OreAccountV4, Treasury, TreasuryV1, TreasuryV4};
5
6use super::OreAccountV1;
7
8#[repr(C)]
9#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
10pub struct MinerV1 {
11    /// The authority of this miner account.
12    pub authority: Pubkey,
13
14    /// The miner's prospects in the current round.
15    pub deployed: [u64; 25],
16
17    /// The cumulative amount of SOL deployed on each square prior to this miner's move.
18    pub cumulative: [u64; 25],
19
20    /// SOL witheld in reserve to pay for checkpointing.
21    pub checkpoint_fee: u64,
22
23    /// The last round that this miner checkpointed.
24    pub checkpoint_id: u64,
25
26    /// The last time this miner claimed ORE rewards.
27    pub last_claim_ore_at: i64,
28
29    /// The last time this miner claimed SOL rewards.
30    pub last_claim_sol_at: i64,
31
32    /// The rewards factor last time rewards were updated on this miner account.
33    pub rewards_factor: Numeric,
34
35    /// The amount of SOL this miner can claim.
36    pub rewards_sol: u64,
37
38    /// The amount of ORE this miner can claim.
39    pub rewards_ore: u64,
40
41    /// The amount of ORE this miner has earned from claim fees.
42    pub refined_ore: u64,
43
44    /// The ID of the round this miner last played in.
45    pub round_id: u64,
46
47    /// The total amount of SOL this miner has mined across all blocks.
48    pub lifetime_rewards_sol: u64,
49
50    /// The total amount of ORE this miner has mined across all blocks.
51    pub lifetime_rewards_ore: u64,
52
53    /// The total amount of SOL this miner has deployed across all rounds.
54    pub lifetime_deployed: u64,
55}
56
57#[repr(C)]
58#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
59pub struct MinerV4 {
60    /// The authority of this miner account.
61    pub authority: Pubkey,
62
63    /// Whether or not to auto-return SOL winnings to the miner's wallet.
64    pub auto_return: u64,
65
66    /// The checkpoint ID.
67    pub checkpoint_id: u64,
68
69    /// SOL witheld in reserve to pay for checkpointing.
70    pub checkpoint_fee: u64,
71
72    /// The amount of SOL deployed on each square.
73    pub sol: [u64; 25],
74
75    /// The amount of SOL deployed on each square, weighted by the time remaining when deployed.
76    pub mass: [u64; 25],
77
78    /// The cumulative mass on each square prior to this miner's deployment on that square.
79    pub mass_cumulative: [u64; 25],
80
81    /// The round ID.
82    pub round_id: u64,
83
84    /// The rewards factor last time rewards were updated on this miner account.
85    pub rewards_factor: Numeric,
86
87    /// The amount of SOL this miner has had returned and may claim.
88    pub sol_returned: u64,
89
90    /// The amount of ORE this miner has earned from refining fees and may claim.
91    pub ore_refined: u64,
92
93    /// The amount of ORE this miner has mined and may claim.
94    pub ore_unrefined: u64,
95
96    /// The last time this miner claimed ORE rewards.
97    pub last_claim_ore_at: i64,
98
99    /// The last time this miner claimed SOL rewards.
100    pub last_claim_sol_at: i64,
101
102    /// The total amount of ORE this miner has mined across all blocks.
103    pub lifetime_rewards: u64,
104
105    /// The total amount of SOL this miner has deployed across all rounds.
106    pub lifetime_deployed: u64,
107
108    /// The total amount of SOL this miner has mined across all blocks.
109    pub lifetime_returned_sol: u64,
110}
111
112impl MinerV1 {
113    pub fn pda(&self) -> (Pubkey, u8) {
114        miner_pda(self.authority)
115    }
116
117    pub fn claim_ore_v1(&mut self, clock: &Clock, treasury: &mut TreasuryV1) -> u64 {
118        self.update_rewards_v1(treasury);
119        let refined_ore = self.refined_ore;
120        let rewards_ore = self.rewards_ore;
121        let mut amount = refined_ore + rewards_ore;
122        self.refined_ore = 0;
123        self.rewards_ore = 0;
124        treasury.total_unclaimed -= rewards_ore;
125        treasury.total_refined -= refined_ore;
126        self.last_claim_ore_at = clock.unix_timestamp;
127
128        // Charge a 10% fee and share with miners who haven't claimed yet.
129        if treasury.total_unclaimed > 0 {
130            let fee = rewards_ore / 10;
131            amount -= fee;
132            treasury.miner_rewards_factor += Numeric::from_fraction(fee, treasury.total_unclaimed);
133            treasury.total_refined += fee;
134            self.lifetime_rewards_ore -= fee;
135        }
136
137        amount
138    }
139
140    pub fn claim_ore_v4(&mut self, clock: &Clock, treasury: &mut TreasuryV4) -> u64 {
141        self.update_rewards_v4(treasury);
142        let refined_ore = self.refined_ore;
143        let rewards_ore = self.rewards_ore;
144        let mut amount = refined_ore + rewards_ore;
145        self.refined_ore = 0;
146        self.rewards_ore = 0;
147        treasury.total_unrefined -= rewards_ore;
148        treasury.total_refined -= refined_ore;
149        self.last_claim_ore_at = clock.unix_timestamp;
150
151        // Charge a 10% fee and share with miners who haven't claimed yet.
152        if treasury.total_unrefined > 0 {
153            let fee = rewards_ore / 10;
154            amount -= fee;
155            treasury.rewards_factor += Numeric::from_fraction(fee, treasury.total_unrefined);
156            treasury.total_refined += fee;
157            self.lifetime_rewards_ore -= fee;
158        }
159
160        amount
161    }
162
163    pub fn claim_sol(&mut self, clock: &Clock) -> u64 {
164        let amount = self.rewards_sol;
165        self.rewards_sol = 0;
166        self.last_claim_sol_at = clock.unix_timestamp;
167        amount
168    }
169
170    pub fn update_rewards_v1(&mut self, treasury: &TreasuryV1) {
171        // Accumulate rewards, weighted by stake balance.
172        if treasury.miner_rewards_factor > self.rewards_factor {
173            let accumulated_rewards = treasury.miner_rewards_factor - self.rewards_factor;
174            if accumulated_rewards < Numeric::ZERO {
175                panic!("Accumulated rewards is negative");
176            }
177            let personal_rewards = accumulated_rewards * Numeric::from_u64(self.rewards_ore);
178            self.refined_ore += personal_rewards.to_u64();
179            self.lifetime_rewards_ore += personal_rewards.to_u64();
180        }
181
182        // Update this miner account's last seen rewards factor.
183        self.rewards_factor = treasury.miner_rewards_factor;
184    }
185
186    pub fn update_rewards_v4(&mut self, treasury: &TreasuryV4) {
187        // Accumulate rewards, weighted by stake balance.
188        if treasury.rewards_factor > self.rewards_factor {
189            let accumulated_rewards = treasury.rewards_factor - self.rewards_factor;
190            if accumulated_rewards < Numeric::ZERO {
191                panic!("Accumulated rewards is negative");
192            }
193            let personal_rewards = accumulated_rewards * Numeric::from_u64(self.rewards_ore);
194            self.refined_ore += personal_rewards.to_u64();
195            self.lifetime_rewards_ore += personal_rewards.to_u64();
196        }
197
198        // Update this miner account's last seen rewards factor.
199        self.rewards_factor = treasury.rewards_factor;
200    }
201}
202
203impl MinerV4 {
204    pub fn pda(&self) -> (Pubkey, u8) {
205        miner_pda(self.authority)
206    }
207
208    pub fn claim_ore_v1(&mut self, clock: &Clock, treasury: &mut TreasuryV1) -> u64 {
209        self.update_rewards_v1(treasury);
210        let refined_ore = self.ore_refined;
211        let rewards_ore = self.ore_unrefined;
212        let mut amount = refined_ore + rewards_ore;
213        self.ore_refined = 0;
214        self.ore_unrefined = 0;
215        treasury.total_unclaimed -= rewards_ore;
216        treasury.total_refined -= refined_ore;
217        self.last_claim_ore_at = clock.unix_timestamp;
218
219        // Charge a 10% fee and share with miners who haven't claimed yet.
220        if treasury.total_unclaimed > 0 {
221            let fee = rewards_ore / 10;
222            amount -= fee;
223            treasury.miner_rewards_factor += Numeric::from_fraction(fee, treasury.total_unclaimed);
224            treasury.total_refined += fee;
225            self.lifetime_rewards -= fee;
226        }
227
228        amount
229    }
230
231    pub fn claim_ore_v4(&mut self, clock: &Clock, treasury: &mut TreasuryV4) -> u64 {
232        self.update_rewards_v4(treasury);
233        let refined_ore = self.ore_refined;
234        let rewards_ore = self.ore_unrefined;
235        let mut amount = refined_ore + rewards_ore;
236        self.ore_refined = 0;
237        self.ore_unrefined = 0;
238        treasury.total_unrefined -= rewards_ore;
239        treasury.total_refined -= refined_ore;
240        self.last_claim_ore_at = clock.unix_timestamp;
241
242        // Charge a 10% fee and share with miners who haven't claimed yet.
243        if treasury.total_unrefined > 0 {
244            let fee = rewards_ore / 10;
245            amount -= fee;
246            treasury.rewards_factor += Numeric::from_fraction(fee, treasury.total_unrefined);
247            treasury.total_refined += fee;
248            self.lifetime_rewards -= fee;
249        }
250
251        amount
252    }
253
254    pub fn claim_sol(&mut self, clock: &Clock) -> u64 {
255        let amount = self.sol_returned;
256        self.sol_returned = 0;
257        self.last_claim_sol_at = clock.unix_timestamp;
258        amount
259    }
260
261    pub fn update_rewards_v4(&mut self, treasury: &TreasuryV4) {
262        // Accumulate rewards, weighted by stake balance.
263        if treasury.rewards_factor > self.rewards_factor {
264            let accumulated_rewards = treasury.rewards_factor - self.rewards_factor;
265            if accumulated_rewards < Numeric::ZERO {
266                panic!("Accumulated rewards is negative");
267            }
268            let personal_rewards = accumulated_rewards * Numeric::from_u64(self.ore_unrefined);
269            self.ore_refined += personal_rewards.to_u64();
270            self.lifetime_rewards += personal_rewards.to_u64();
271        }
272
273        // Update this miner account's last seen rewards factor.
274        self.rewards_factor = treasury.rewards_factor;
275    }
276
277    pub fn update_rewards_v1(&mut self, treasury: &TreasuryV1) {
278        // Accumulate rewards, weighted by stake balance.
279        if treasury.miner_rewards_factor > self.rewards_factor {
280            let accumulated_rewards = treasury.miner_rewards_factor - self.rewards_factor;
281            if accumulated_rewards < Numeric::ZERO {
282                panic!("Accumulated rewards is negative");
283            }
284            let personal_rewards = accumulated_rewards * Numeric::from_u64(self.ore_unrefined);
285            self.ore_refined += personal_rewards.to_u64();
286            self.lifetime_rewards += personal_rewards.to_u64();
287        }
288
289        // Update this miner account's last seen rewards factor.
290        self.rewards_factor = treasury.miner_rewards_factor;
291    }
292}
293
294account!(OreAccountV1, MinerV1);
295account!(OreAccountV4, MinerV4);
296
297#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
298pub enum Miner {
299    V1(MinerV1),
300    V4(MinerV4),
301}
302
303impl Miner {
304    pub fn authority(&self) -> Pubkey {
305        match self {
306            Miner::V1(miner) => miner.authority,
307            Miner::V4(miner) => miner.authority,
308        }
309    }
310
311    pub fn deployed(&self) -> [u64; 25] {
312        match self {
313            Miner::V1(miner) => miner.deployed,
314            Miner::V4(miner) => miner.sol,
315        }
316    }
317
318    pub fn cumulative(&self) -> [u64; 25] {
319        match self {
320            Miner::V1(miner) => miner.cumulative,
321            Miner::V4(miner) => miner.mass_cumulative,
322        }
323    }
324
325    pub fn checkpoint_fee(&self) -> u64 {
326        match self {
327            Miner::V1(miner) => miner.checkpoint_fee,
328            Miner::V4(miner) => miner.checkpoint_fee,
329        }
330    }
331
332    pub fn checkpoint_id(&self) -> u64 {
333        match self {
334            Miner::V1(miner) => miner.checkpoint_id,
335            Miner::V4(miner) => miner.checkpoint_id,
336        }
337    }
338
339    pub fn last_claim_ore_at(&self) -> i64 {
340        match self {
341            Miner::V1(miner) => miner.last_claim_ore_at,
342            Miner::V4(miner) => miner.last_claim_ore_at,
343        }
344    }
345
346    pub fn last_claim_sol_at(&self) -> i64 {
347        match self {
348            Miner::V1(miner) => miner.last_claim_sol_at,
349            Miner::V4(miner) => miner.last_claim_sol_at,
350        }
351    }
352    pub fn rewards_factor(&self) -> Numeric {
353        match self {
354            Miner::V1(miner) => miner.rewards_factor,
355            Miner::V4(miner) => miner.rewards_factor,
356        }
357    }
358
359    pub fn rewards_sol(&self) -> u64 {
360        match self {
361            Miner::V1(miner) => miner.rewards_sol,
362            Miner::V4(miner) => miner.sol_returned,
363        }
364    }
365
366    pub fn rewards_ore(&self) -> u64 {
367        match self {
368            Miner::V1(miner) => miner.rewards_ore,
369            Miner::V4(miner) => miner.ore_unrefined,
370        }
371    }
372
373    pub fn refined_ore(&self) -> u64 {
374        match self {
375            Miner::V1(miner) => miner.refined_ore,
376            Miner::V4(miner) => miner.ore_refined,
377        }
378    }
379
380    pub fn round_id(&self) -> u64 {
381        match self {
382            Miner::V1(miner) => miner.round_id,
383            Miner::V4(miner) => miner.round_id,
384        }
385    }
386
387    pub fn lifetime_rewards_sol(&self) -> u64 {
388        match self {
389            Miner::V1(miner) => miner.lifetime_rewards_sol,
390            Miner::V4(miner) => miner.lifetime_returned_sol,
391        }
392    }
393
394    pub fn lifetime_rewards_ore(&self) -> u64 {
395        match self {
396            Miner::V1(miner) => miner.lifetime_rewards_ore,
397            Miner::V4(miner) => miner.lifetime_rewards,
398        }
399    }
400
401    pub fn lifetime_deployed(&self) -> u64 {
402        match self {
403            Miner::V1(miner) => miner.lifetime_deployed,
404            Miner::V4(miner) => miner.lifetime_deployed,
405        }
406    }
407
408    pub fn pda(&self) -> (Pubkey, u8) {
409        match self {
410            Miner::V1(miner) => miner.pda(),
411            Miner::V4(miner) => miner.pda(),
412        }
413    }
414
415    pub fn claim_ore(&mut self, clock: &Clock, treasury: &mut Treasury) -> u64 {
416        match self {
417            Miner::V1(miner) => match treasury {
418                Treasury::V1(treasury) => miner.claim_ore_v1(clock, treasury),
419                Treasury::V4(treasury) => miner.claim_ore_v4(clock, treasury),
420            },
421            Miner::V4(miner) => match treasury {
422                Treasury::V1(treasury) => miner.claim_ore_v1(clock, treasury),
423                Treasury::V4(treasury) => miner.claim_ore_v4(clock, treasury),
424            },
425        }
426    }
427
428    pub fn claim_sol(&mut self, clock: &Clock) -> u64 {
429        match self {
430            Miner::V1(miner) => miner.claim_sol(clock),
431            Miner::V4(miner) => miner.claim_sol(clock),
432        }
433    }
434
435    pub fn update_rewards(&mut self, treasury: &Treasury) {
436        match self {
437            Miner::V1(miner) => match treasury {
438                Treasury::V1(treasury) => miner.update_rewards_v1(treasury),
439                Treasury::V4(treasury) => miner.update_rewards_v4(treasury),
440            },
441            Miner::V4(miner) => match treasury {
442                Treasury::V1(treasury) => miner.update_rewards_v1(treasury),
443                Treasury::V4(treasury) => miner.update_rewards_v4(treasury),
444            },
445        }
446    }
447}