templar_common/
time_chunk.rs

1use near_sdk::{env, json_types::U64, near};
2
3#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
4#[near(serializers = [json, borsh])]
5pub enum V0 {
6    BlockHeight { divisor: U64 },
7    EpochHeight { divisor: U64 },
8    BlockTimestampMs { divisor: U64 },
9}
10
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
12#[near(serializers = [json, borsh])]
13pub struct V1 {
14    pub duration_ms: U64,
15}
16
17/// Configure a method of determining the current time chunk.
18#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
19#[near(serializers = [json, borsh])]
20#[serde(tag = "version")]
21pub enum TimeChunkConfiguration {
22    #[serde(untagged)]
23    V0(V0),
24    #[serde(untagged)]
25    V1(V1),
26}
27
28impl TimeChunkConfiguration {
29    pub fn new(duration_ms: u64) -> Self {
30        Self::V1(V1 {
31            duration_ms: U64(duration_ms),
32        })
33    }
34
35    pub fn duration_ms(&self) -> u64 {
36        match self {
37            TimeChunkConfiguration::V0(V0::BlockTimestampMs { divisor }) => divisor.0,
38            TimeChunkConfiguration::V0(_) => {
39                crate::panic_with_message("Unsupported time chunk configuration")
40            }
41            TimeChunkConfiguration::V1(v1) => v1.duration_ms.0,
42        }
43    }
44
45    pub fn now(&self) -> TimeChunk {
46        let block_timestamp_ms = env::block_timestamp_ms();
47        TimeChunk(U64(block_timestamp_ms
48            .checked_div(self.duration_ms())
49            .unwrap_or(block_timestamp_ms)))
50    }
51
52    pub fn previous(&self) -> TimeChunk {
53        let TimeChunk(U64(time)) = self.now();
54        #[allow(clippy::unwrap_used, reason = "Assume now > 0")]
55        TimeChunk(U64(time.checked_sub(1).unwrap()))
56    }
57}
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
60#[near(serializers = [borsh, json])]
61pub struct TimeChunk(pub U64);
62
63#[cfg(test)]
64mod tests {
65    use near_sdk::serde_json;
66    use near_sdk::test_utils;
67
68    use super::*;
69
70    #[test]
71    fn now() {
72        let context = test_utils::VMContextBuilder::new()
73            .block_timestamp((600_000 * 45 + 12345) * 1_000_000 /* ms -> ns */)
74            .build();
75        near_sdk::testing_env!(context.clone());
76
77        let t = TimeChunkConfiguration::new(600_000);
78        assert_eq!(t.duration_ms(), 600_000);
79        let now = t.now();
80        assert_eq!(now, TimeChunk(U64(45)));
81        let prev = t.previous();
82        assert_eq!(prev, TimeChunk(U64(44)));
83    }
84
85    #[test]
86    fn v0_deserialization() {
87        let s = r#"{
88          "BlockTimestampMs": {
89            "divisor": "600000"
90          }
91        }"#;
92
93        let d: TimeChunkConfiguration = serde_json::from_str(s).unwrap();
94
95        assert_eq!(
96            d,
97            TimeChunkConfiguration::V0(V0::BlockTimestampMs {
98                divisor: U64(600_000),
99            }),
100        );
101        assert_eq!(d.duration_ms(), 600_000);
102    }
103
104    #[test]
105    fn v0_serialization() {
106        let v0 = TimeChunkConfiguration::V0(V0::BlockTimestampMs {
107            divisor: U64(600_000),
108        });
109
110        let s = serde_json::to_string(&v0).unwrap();
111
112        assert_eq!(s, r#"{"BlockTimestampMs":{"divisor":"600000"}}"#);
113    }
114
115    #[test]
116    fn v1_deserialization() {
117        let s = r#"{
118          "duration_ms": "600000"
119        }"#;
120
121        let d: TimeChunkConfiguration = serde_json::from_str(s).unwrap();
122
123        assert_eq!(
124            d,
125            TimeChunkConfiguration::V1(V1 {
126                duration_ms: U64(600_000),
127            }),
128        );
129        assert_eq!(d.duration_ms(), 600_000);
130    }
131
132    #[test]
133    fn v1_serialization() {
134        let v0 = TimeChunkConfiguration::V1(V1 {
135            duration_ms: U64(600_000),
136        });
137
138        let s = serde_json::to_string(&v0).unwrap();
139
140        assert_eq!(s, r#"{"duration_ms":"600000"}"#);
141    }
142}