templar_common/
time_chunk.rs1use 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#[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 )
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}