Skip to main content

ore_api/state/
round.rs

1use serde::{Deserialize, Serialize};
2use steel::*;
3
4use crate::state::{round_pda, OreAccount};
5
6#[repr(C)]
7#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
8pub struct Round {
9    /// The round number.
10    pub id: u64,
11
12    /// The amount of SOL deployed in each square.
13    /// TODO: Rename to sol.
14    pub deployed: [u64; 25],
15
16    /// The amount of mass deployed in each square.
17    pub mass: [u64; 25],
18
19    /// The number of unique miners on each square.
20    /// TODO rename to miners.
21    pub count: [u64; 25],
22
23    /// The entropy value.
24    /// TODO: Rename to entropy.
25    pub slot_hash: [u8; 32],
26
27    /// The slot after which this account may be closed.
28    /// TODO: Rename to closes_at.
29    pub expires_at: u64,
30
31    /// The amount of ORE distributed as the motherlode reward.
32    pub motherlode: u64,
33
34    /// The account to which rent should be returned to when this account is closed.
35    pub rent_payer: Pubkey,
36
37    /// The amount of ORE to distribute to miners.
38    pub rewards: [u64; 25],
39
40    /// The total SOL collected by the protocol.
41    /// TODO: Rename to protocol_fee.
42    pub total_vaulted: u64,
43
44    /// The total SOL returned to miners.
45    /// TODO: Rename to total_returned.
46    pub total_winnings: u64,
47
48    /// The total number of unique miners that played in the round.
49    /// TODO rename to unique_miners.
50    pub total_miners: u64,
51
52    /// The winner of the solo reward.
53    /// TODO: Rename to winner.
54    pub top_miner: Pubkey,
55}
56
57impl Round {
58    pub fn pda(&self) -> (Pubkey, u8) {
59        round_pda(self.id)
60    }
61
62    pub fn rng(&self) -> Option<u64> {
63        if self.slot_hash == [0; 32] || self.slot_hash == [u8::MAX; 32] {
64            return None;
65        }
66        let r1 = u64::from_le_bytes(self.slot_hash[0..8].try_into().unwrap());
67        let r2 = u64::from_le_bytes(self.slot_hash[8..16].try_into().unwrap());
68        let r3 = u64::from_le_bytes(self.slot_hash[16..24].try_into().unwrap());
69        let r4 = u64::from_le_bytes(self.slot_hash[24..32].try_into().unwrap());
70        let r = r1 ^ r2 ^ r3 ^ r4;
71        Some(r)
72    }
73
74    pub fn winning_square(&self, rng: u64) -> usize {
75        (rng % 25) as usize
76    }
77
78    pub fn top_miner_sample(&self, rng: u64, winning_square: usize) -> u64 {
79        if self.deployed[winning_square] == 0 {
80            return 0;
81        }
82        rng.reverse_bits() % self.deployed[winning_square]
83    }
84
85    pub fn calculate_total_winnings(&self, winning_square: usize) -> u64 {
86        let mut total_winnings = 0;
87        for (i, &deployed) in self.deployed.iter().enumerate() {
88            if i != winning_square {
89                total_winnings += deployed;
90            }
91        }
92        total_winnings
93    }
94
95    pub fn is_split_reward(&self, rng: u64) -> bool {
96        // One out of four rounds get split rewards.
97        let rng = rng.reverse_bits().to_le_bytes();
98        let r1 = u16::from_le_bytes(rng[0..2].try_into().unwrap());
99        let r2 = u16::from_le_bytes(rng[2..4].try_into().unwrap());
100        let r3 = u16::from_le_bytes(rng[4..6].try_into().unwrap());
101        let r4 = u16::from_le_bytes(rng[6..8].try_into().unwrap());
102        let r = r1 ^ r2 ^ r3 ^ r4;
103        r % 2 == 0
104    }
105
106    pub fn did_hit_motherlode(&self, rng: u64) -> bool {
107        rng.reverse_bits() % 625 == 0
108    }
109
110    pub fn total_deployed(&self) -> u64 {
111        self.deployed.iter().sum()
112    }
113
114    pub fn top_miner_reward(&self) -> u64 {
115        self.rewards.iter().sum()
116    }
117}
118
119account!(OreAccount, Round);