init4_bin_base/utils/
calc.rs1use crate::utils::from_env::FromEnv;
2
3#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Deserialize, FromEnv)]
6#[from_env(crate)]
7pub struct SlotCalculator {
8 #[from_env(
10 var = "START_TIMESTAMP",
11 desc = "The start timestamp of the chain in seconds"
12 )]
13 start_timestamp: u64,
14
15 #[from_env(
22 var = "SLOT_OFFSET",
23 desc = "The number of the slot containing the start timestamp"
24 )]
25 slot_offset: u64,
26
27 #[from_env(
29 var = "SLOT_DURATION",
30 desc = "The slot duration of the chain in seconds"
31 )]
32 slot_duration: u64,
33}
34
35impl SlotCalculator {
36 pub const fn new(start_timestamp: u64, slot_offset: u64, slot_duration: u64) -> Self {
38 Self {
39 start_timestamp,
40 slot_offset,
41 slot_duration,
42 }
43 }
44
45 pub const fn holesky() -> Self {
47 Self {
51 start_timestamp: 1695902424,
52 slot_offset: 2,
53 slot_duration: 12,
54 }
55 }
56
57 pub const fn pecorino_host() -> Self {
59 Self {
60 start_timestamp: 1740681556,
61 slot_offset: 0,
62 slot_duration: 12,
63 }
64 }
65
66 pub const fn mainnet() -> Self {
68 Self {
69 start_timestamp: 1663224179,
70 slot_offset: 4700013,
71 slot_duration: 12,
72 }
73 }
74
75 pub const fn calculate_slot(&self, timestamp: u64) -> u64 {
78 let elapsed = timestamp - self.start_timestamp;
79 let slots = elapsed.div_ceil(self.slot_duration);
80 slots + self.slot_offset
81 }
82
83 pub const fn calculate_timepoint_within_slot(&self, timestamp: u64) -> u64 {
85 (timestamp - self.slot_utc_offset()) % self.slot_duration
86 }
87
88 pub const fn calculate_slot_window(&self, slot_number: u64) -> (u64, u64) {
90 let end_of_slot =
91 ((slot_number - self.slot_offset) * self.slot_duration) + self.start_timestamp;
92 let start_of_slot = end_of_slot - self.slot_duration;
93 (start_of_slot, end_of_slot)
94 }
95
96 pub fn current_slot(&self) -> u64 {
98 self.calculate_slot(chrono::Utc::now().timestamp() as u64)
99 }
100
101 pub fn current_timepoint_within_slot(&self) -> u64 {
103 self.calculate_timepoint_within_slot(chrono::Utc::now().timestamp() as u64)
104 }
105
106 pub const fn start_timestamp(&self) -> u64 {
108 self.start_timestamp
109 }
110
111 pub const fn slot_offset(&self) -> u64 {
113 self.slot_offset
114 }
115
116 pub const fn slot_duration(&self) -> u64 {
118 self.slot_duration
119 }
120
121 const fn slot_utc_offset(&self) -> u64 {
123 self.start_timestamp % self.slot_duration
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn test_basic_slot_calculations() {
133 let calculator = SlotCalculator::new(0, 0, 12);
134 assert_eq!(calculator.calculate_slot(0), 0);
135
136 assert_eq!(calculator.calculate_slot(1), 1);
137 assert_eq!(calculator.calculate_slot(11), 1);
138 assert_eq!(calculator.calculate_slot(12), 1);
139
140 assert_eq!(calculator.calculate_slot(13), 2);
141 assert_eq!(calculator.calculate_slot(23), 2);
142 assert_eq!(calculator.calculate_slot(24), 2);
143
144 assert_eq!(calculator.calculate_slot(25), 3);
145 assert_eq!(calculator.calculate_slot(35), 3);
146 assert_eq!(calculator.calculate_slot(36), 3);
147 }
148
149 #[test]
150 fn test_holesky_slot_calculations() {
151 let calculator = SlotCalculator::holesky();
152 assert_eq!(calculator.calculate_slot(1695902424), 2);
155 assert_eq!(calculator.calculate_slot(1695902425), 3);
157
158 assert_eq!(calculator.calculate_slot(1742931924), 3919127);
161 assert_eq!(calculator.calculate_slot(1742931925), 3919128);
163 }
164
165 #[test]
166 fn test_holesky_slot_timepoint_calculations() {
167 let calculator = SlotCalculator::holesky();
168 assert_eq!(calculator.calculate_timepoint_within_slot(1695902424), 0);
170 assert_eq!(calculator.calculate_timepoint_within_slot(1695902425), 1);
171 assert_eq!(calculator.calculate_timepoint_within_slot(1695902435), 11);
172 assert_eq!(calculator.calculate_timepoint_within_slot(1695902436), 0);
173 }
174
175 #[test]
176 fn test_holesky_slot_window() {
177 let calculator = SlotCalculator::holesky();
178 assert_eq!(
180 calculator.calculate_slot_window(2),
181 (1695902412, 1695902424)
182 );
183 assert_eq!(
184 calculator.calculate_slot_window(3),
185 (1695902424, 1695902436)
186 );
187 }
188
189 #[test]
190 fn test_mainnet_slot_calculations() {
191 let calculator = SlotCalculator::mainnet();
192 assert_eq!(calculator.calculate_slot(1663224179), 4700013);
193 assert_eq!(calculator.calculate_slot(1663224180), 4700014);
194
195 assert_eq!(calculator.calculate_slot(1738863035), 11003251);
196 assert_eq!(calculator.calculate_slot(1738866239), 11003518);
197 assert_eq!(calculator.calculate_slot(1738866227), 11003517);
198 }
199
200 #[test]
201 fn test_mainnet_slot_timepoint_calculations() {
202 let calculator = SlotCalculator::mainnet();
203 assert_eq!(calculator.calculate_timepoint_within_slot(1663224179), 0);
205 assert_eq!(calculator.calculate_timepoint_within_slot(1663224180), 1);
206 assert_eq!(calculator.calculate_timepoint_within_slot(1663224190), 11);
207 assert_eq!(calculator.calculate_timepoint_within_slot(1663224191), 0);
208 }
209
210 #[test]
211 fn test_ethereum_slot_window() {
212 let calculator = SlotCalculator::mainnet();
213 assert_eq!(
215 calculator.calculate_slot_window(4700013),
216 (1663224167, 1663224179)
217 );
218 assert_eq!(
219 calculator.calculate_slot_window(4700014),
220 (1663224179, 1663224191)
221 );
222 }
223}