Skip to main content

ore_api/state/
miner.rs

1use serde::{Deserialize, Serialize};
2use steel::*;
3
4use crate::state::{miner_pda, OreAccountV4, Treasury};
5
6use super::OreAccount;
7
8#[repr(C)]
9#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
10pub struct Miner {
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 Miner {
113    pub fn pda(&self) -> (Pubkey, u8) {
114        miner_pda(self.authority)
115    }
116
117    pub fn claim_ore(&mut self, clock: &Clock, treasury: &mut Treasury) -> u64 {
118        self.update_rewards(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_sol(&mut self, clock: &Clock) -> u64 {
141        let amount = self.rewards_sol;
142        self.rewards_sol = 0;
143        self.last_claim_sol_at = clock.unix_timestamp;
144        amount
145    }
146
147    pub fn update_rewards(&mut self, treasury: &Treasury) {
148        // Accumulate rewards, weighted by stake balance.
149        if treasury.miner_rewards_factor > self.rewards_factor {
150            let accumulated_rewards = treasury.miner_rewards_factor - self.rewards_factor;
151            if accumulated_rewards < Numeric::ZERO {
152                panic!("Accumulated rewards is negative");
153            }
154            let personal_rewards = accumulated_rewards * Numeric::from_u64(self.rewards_ore);
155            self.refined_ore += personal_rewards.to_u64();
156            self.lifetime_rewards_ore += personal_rewards.to_u64();
157        }
158
159        // Update this miner account's last seen rewards factor.
160        self.rewards_factor = treasury.miner_rewards_factor;
161    }
162}
163
164impl MinerV4 {
165    pub fn pda(&self) -> (Pubkey, u8) {
166        miner_pda(self.authority)
167    }
168
169    pub fn claim_ore(&mut self, clock: &Clock, treasury: &mut Treasury) -> u64 {
170        self.update_rewards(treasury);
171        let refined_ore = self.ore_refined;
172        let rewards_ore = self.ore_unrefined;
173        let mut amount = refined_ore + rewards_ore;
174        self.ore_refined = 0;
175        self.ore_unrefined = 0;
176        treasury.total_unclaimed -= rewards_ore;
177        treasury.total_refined -= refined_ore;
178        self.last_claim_ore_at = clock.unix_timestamp;
179
180        // Charge a 10% fee and share with miners who haven't claimed yet.
181        if treasury.total_unclaimed > 0 {
182            let fee = rewards_ore / 10;
183            amount -= fee;
184            treasury.miner_rewards_factor += Numeric::from_fraction(fee, treasury.total_unclaimed);
185            treasury.total_refined += fee;
186            self.lifetime_rewards -= fee;
187        }
188
189        amount
190    }
191
192    pub fn claim_sol(&mut self, clock: &Clock) -> u64 {
193        let amount = self.sol_returned;
194        self.sol_returned = 0;
195        self.last_claim_sol_at = clock.unix_timestamp;
196        amount
197    }
198
199    pub fn update_rewards(&mut self, treasury: &Treasury) {
200        // Accumulate rewards, weighted by stake balance.
201        if treasury.miner_rewards_factor > self.rewards_factor {
202            let accumulated_rewards = treasury.miner_rewards_factor - self.rewards_factor;
203            if accumulated_rewards < Numeric::ZERO {
204                panic!("Accumulated rewards is negative");
205            }
206            let personal_rewards = accumulated_rewards * Numeric::from_u64(self.ore_unrefined);
207            self.ore_refined += personal_rewards.to_u64();
208            self.lifetime_rewards += personal_rewards.to_u64();
209        }
210
211        // Update this miner account's last seen rewards factor.
212        self.rewards_factor = treasury.miner_rewards_factor;
213    }
214}
215
216account!(OreAccount, Miner);
217account!(OreAccountV4, MinerV4);
218
219pub enum MinerAccount {
220    Miner(Miner),
221    MinerV4(MinerV4),
222}
223
224impl MinerAccount {
225    pub fn authority(&self) -> Pubkey {
226        match self {
227            MinerAccount::Miner(miner) => miner.authority,
228            MinerAccount::MinerV4(miner) => miner.authority,
229        }
230    }
231
232    pub fn deployed(&self) -> [u64; 25] {
233        match self {
234            MinerAccount::Miner(miner) => miner.deployed,
235            MinerAccount::MinerV4(miner) => miner.sol,
236        }
237    }
238
239    pub fn cumulative(&self) -> [u64; 25] {
240        match self {
241            MinerAccount::Miner(miner) => miner.cumulative,
242            MinerAccount::MinerV4(miner) => miner.mass_cumulative,
243        }
244    }
245
246    pub fn checkpoint_fee(&self) -> u64 {
247        match self {
248            MinerAccount::Miner(miner) => miner.checkpoint_fee,
249            MinerAccount::MinerV4(miner) => miner.checkpoint_fee,
250        }
251    }
252
253    pub fn checkpoint_id(&self) -> u64 {
254        match self {
255            MinerAccount::Miner(miner) => miner.checkpoint_id,
256            MinerAccount::MinerV4(miner) => miner.checkpoint_id,
257        }
258    }
259
260    pub fn last_claim_ore_at(&self) -> i64 {
261        match self {
262            MinerAccount::Miner(miner) => miner.last_claim_ore_at,
263            MinerAccount::MinerV4(miner) => miner.last_claim_ore_at,
264        }
265    }
266
267    pub fn last_claim_sol_at(&self) -> i64 {
268        match self {
269            MinerAccount::Miner(miner) => miner.last_claim_sol_at,
270            MinerAccount::MinerV4(miner) => miner.last_claim_sol_at,
271        }
272    }
273    pub fn rewards_factor(&self) -> Numeric {
274        match self {
275            MinerAccount::Miner(miner) => miner.rewards_factor,
276            MinerAccount::MinerV4(miner) => miner.rewards_factor,
277        }
278    }
279
280    pub fn rewards_sol(&self) -> u64 {
281        match self {
282            MinerAccount::Miner(miner) => miner.rewards_sol,
283            MinerAccount::MinerV4(miner) => miner.sol_returned,
284        }
285    }
286
287    pub fn rewards_ore(&self) -> u64 {
288        match self {
289            MinerAccount::Miner(miner) => miner.rewards_ore,
290            MinerAccount::MinerV4(miner) => miner.ore_unrefined,
291        }
292    }
293
294    pub fn refined_ore(&self) -> u64 {
295        match self {
296            MinerAccount::Miner(miner) => miner.refined_ore,
297            MinerAccount::MinerV4(miner) => miner.ore_refined,
298        }
299    }
300
301    pub fn round_id(&self) -> u64 {
302        match self {
303            MinerAccount::Miner(miner) => miner.round_id,
304            MinerAccount::MinerV4(miner) => miner.round_id,
305        }
306    }
307
308    pub fn lifetime_rewards_sol(&self) -> u64 {
309        match self {
310            MinerAccount::Miner(miner) => miner.lifetime_rewards_sol,
311            MinerAccount::MinerV4(miner) => miner.lifetime_returned_sol,
312        }
313    }
314
315    pub fn lifetime_rewards_ore(&self) -> u64 {
316        match self {
317            MinerAccount::Miner(miner) => miner.lifetime_rewards_ore,
318            MinerAccount::MinerV4(miner) => miner.lifetime_rewards,
319        }
320    }
321
322    pub fn lifetime_deployed(&self) -> u64 {
323        match self {
324            MinerAccount::Miner(miner) => miner.lifetime_deployed,
325            MinerAccount::MinerV4(miner) => miner.lifetime_deployed,
326        }
327    }
328
329    pub fn pda(&self) -> (Pubkey, u8) {
330        match self {
331            MinerAccount::Miner(miner) => miner.pda(),
332            MinerAccount::MinerV4(miner) => miner.pda(),
333        }
334    }
335
336    pub fn claim_ore(&mut self, clock: &Clock, treasury: &mut Treasury) -> u64 {
337        match self {
338            MinerAccount::Miner(miner) => miner.claim_ore(clock, treasury),
339            MinerAccount::MinerV4(miner) => miner.claim_ore(clock, treasury),
340        }
341    }
342
343    pub fn claim_sol(&mut self, clock: &Clock) -> u64 {
344        match self {
345            MinerAccount::Miner(miner) => miner.claim_sol(clock),
346            MinerAccount::MinerV4(miner) => miner.claim_sol(clock),
347        }
348    }
349
350    pub fn update_rewards(&mut self, treasury: &Treasury) {
351        match self {
352            MinerAccount::Miner(miner) => miner.update_rewards(treasury),
353            MinerAccount::MinerV4(miner) => miner.update_rewards(treasury),
354        }
355    }
356}