levana_perpswap_cosmos/
time.rs

1//! Types to represent timestamps and durations.
2use crate::error::{ErrorDomain, ErrorId, PerpError};
3use crate::prelude::*;
4use anyhow::Result;
5#[cfg(feature = "chrono")]
6use chrono::{DateTime, TimeZone, Utc};
7use cosmwasm_std::{Decimal256, Timestamp as CWTimestamp};
8use cw_storage_plus::{KeyDeserialize, Prefixer, PrimaryKey};
9use schemars::JsonSchema;
10use serde::de::Visitor;
11use serde::{Deserialize, Serialize};
12use std::fmt::{Display, Formatter};
13use std::ops::{Add, Div, Mul, Sub};
14
15/// Essentially a newtype wrapper for [Timestamp] providing additional impls.
16///
17/// Internal representation in nanoseconds since the epoch. We keep a [u64]
18/// directly (instead of a [Timestamp] or [cosmwasm_std::Uint64]) to make it
19/// easier to derive some impls. The result is that we need to explicitly
20/// implement [Serialize] and [Deserialize] to keep the stringy representation.
21#[derive(Debug, Clone, Default, Copy, Eq, PartialEq, Ord, PartialOrd, JsonSchema, Hash)]
22#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
23pub struct Timestamp(#[schemars(with = "String")] u64);
24
25impl Display for Timestamp {
26    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
27        let whole = self.0 / 1_000_000_000;
28        let fractional = self.0 % 1_000_000_000;
29        write!(f, "{}.{:09}", whole, fractional)
30    }
31}
32
33impl Serialize for Timestamp {
34    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
35    where
36        S: serde::Serializer,
37    {
38        serializer.serialize_str(&self.0.to_string())
39    }
40}
41
42impl<'de> Deserialize<'de> for Timestamp {
43    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
44    where
45        D: serde::Deserializer<'de>,
46    {
47        deserializer.deserialize_str(NanoVisitor)
48    }
49}
50
51struct NanoVisitor;
52
53impl<'de> Visitor<'de> for NanoVisitor {
54    type Value = Timestamp;
55
56    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
57        formatter.write_str("nanoseconds since epoch, string-encoded")
58    }
59
60    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
61    where
62        E: serde::de::Error,
63    {
64        match v.parse::<u64>() {
65            Ok(v) => Ok(Timestamp(v)),
66            Err(e) => Err(E::custom(format!("invalid Nano '{v}' - {e}"))),
67        }
68    }
69}
70
71impl Timestamp {
72    /// Construct a new value from the given number of nanoseconds since the
73    /// epoch
74    pub fn from_nanos(nanos: u64) -> Self {
75        Timestamp(nanos)
76    }
77
78    /// Construct a new value from the given number of seconds since the
79    /// epoch
80    pub fn from_seconds(seconds: u64) -> Self {
81        Timestamp(seconds * 1_000_000_000)
82    }
83
84    /// Construct a new value from the given number of millisecond since the epoch.
85    pub fn from_millis(millis: u64) -> Self {
86        Timestamp(millis * 1_000_000)
87    }
88
89    /// Add the given number of seconds to the given timestamp
90    pub fn plus_seconds(self, secs: u64) -> Self {
91        self + Duration::from_seconds(secs)
92    }
93
94    /// Subtract two timestamps to get the duration between them.
95    ///
96    /// Will fail if the right hand side is greater than the left hand side.
97    pub fn checked_sub(self, rhs: Self, desc: &str) -> Result<Duration> {
98        #[derive(serde::Serialize)]
99        struct Data {
100            lhs: Timestamp,
101            rhs: Timestamp,
102            desc: String,
103        }
104        let data = Data {
105            lhs: self,
106            rhs,
107            desc: desc.to_owned(),
108        };
109        match self.0.checked_sub(rhs.0) {
110            Some(x) => Ok(Duration(x)),
111            None => Err(anyhow!(PerpError {
112                id: ErrorId::TimestampSubtractUnderflow,
113                domain: ErrorDomain::Default,
114                description: format!(
115                    "Invalid timestamp subtraction during. Action: {desc}. Values: {} - {}",
116                    data.lhs, data.rhs
117                ),
118                data: Some(data),
119            })),
120        }
121    }
122
123    #[cfg(feature = "chrono")]
124    /// Convert into a chrono [`DateTime<Utc>`]
125    pub fn try_into_chrono_datetime(self) -> Result<DateTime<Utc>> {
126        let secs = self.0 / 1_000_000_000;
127        let nanos = self.0 % 1_000_000_000;
128
129        Utc.timestamp_opt(secs.try_into()?, nanos.try_into()?)
130            .single()
131            .with_context(|| format!("Could not convert {self} into DateTime<Utc>"))
132    }
133}
134
135// Lossless conversions. In the future, we may want to focus on using this type
136// throughout the codebase instead of Timestamp, in which case removing these
137// impls and instead having explicit helper functions may help identify stray
138// conversions still occurring.
139impl From<Timestamp> for CWTimestamp {
140    fn from(Timestamp(nanos): Timestamp) -> Self {
141        CWTimestamp::from_nanos(nanos)
142    }
143}
144
145impl From<CWTimestamp> for Timestamp {
146    fn from(timestamp: CWTimestamp) -> Self {
147        Timestamp(timestamp.nanos())
148    }
149}
150
151impl<'a> PrimaryKey<'a> for Timestamp {
152    type Prefix = ();
153    type SubPrefix = ();
154    type Suffix = Timestamp;
155    type SuperSuffix = Timestamp;
156
157    fn key(&self) -> Vec<cw_storage_plus::Key> {
158        self.0.key()
159    }
160}
161
162impl KeyDeserialize for Timestamp {
163    type Output = Timestamp;
164
165    const KEY_ELEMS: u16 = 1;
166
167    fn from_vec(value: Vec<u8>) -> cosmwasm_std::StdResult<Self::Output> {
168        u64::from_vec(value).map(Timestamp)
169    }
170}
171
172impl<'a> Prefixer<'a> for Timestamp {
173    fn prefix(&self) -> Vec<cw_storage_plus::Key> {
174        self.0.prefix()
175    }
176}
177
178/// A duration of time measured in nanoseconds
179#[derive(
180    Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema,
181)]
182pub struct Duration(u64);
183
184impl Duration {
185    /// Construct a [Duration] from a given number of nanoseconds.
186    pub(crate) fn from_nanos(nanos: u64) -> Self {
187        Duration(nanos)
188    }
189
190    /// Returns the underlying nanos value as a u64
191    pub fn as_nanos(&self) -> u64 {
192        self.0
193    }
194
195    /// Convert to milliseconds and represent as a [Number].
196    ///
197    /// This is intended for performing calculations. Remember that this is a lossy conversion!
198    pub fn as_ms_number_lossy(&self) -> Number {
199        Number::from(self.0 / 1_000_000)
200    }
201
202    /// Convert to milliseconds and represent as a [Decimal256].
203    ///
204    /// This is intended for performing calculations. Remember that this is a lossy conversion!
205    pub fn as_ms_decimal_lossy(&self) -> Decimal256 {
206        Decimal256::from_atomics(self.0, 6)
207            .expect("as_ms_decimal_lossy failed, but range won't allow that to happen")
208    }
209
210    /// Convert a number of seconds into a [Duration].
211    pub const fn from_seconds(seconds: u64) -> Self {
212        Duration(seconds * 1_000_000_000)
213    }
214}
215
216// Arithmetic operators. Consider removing these impls in favor of checked
217// versions in the future to avoid panicking. Leaving for now to ease
218// conversion.
219
220impl Add<Duration> for Timestamp {
221    type Output = Timestamp;
222
223    fn add(self, rhs: Duration) -> Self::Output {
224        Timestamp(self.0 + rhs.0)
225    }
226}
227
228impl Sub<Duration> for Duration {
229    type Output = Duration;
230
231    fn sub(self, rhs: Duration) -> Self::Output {
232        Duration(self.0 - rhs.0)
233    }
234}
235
236impl Add<Duration> for Duration {
237    type Output = Duration;
238
239    fn add(self, rhs: Duration) -> Self::Output {
240        Duration(self.0 + rhs.0)
241    }
242}
243
244impl Mul<u64> for Duration {
245    type Output = Duration;
246
247    fn mul(self, rhs: u64) -> Self::Output {
248        Duration(self.0 * rhs)
249    }
250}
251
252impl Div<u64> for Duration {
253    type Output = Duration;
254
255    fn div(self, rhs: u64) -> Self::Output {
256        Duration(self.0 / rhs)
257    }
258}
259
260// Used for questions like "how many epochs do I need to fill up the given duration?"
261impl Div<Duration> for Duration {
262    type Output = u64;
263
264    fn div(self, rhs: Self) -> Self::Output {
265        self.0 / rhs.0
266    }
267}
268
269impl FromStr for Timestamp {
270    type Err = PerpError;
271
272    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
273        let err = |msg: &str| -> PerpError {
274            PerpError::new(
275                ErrorId::Conversion,
276                ErrorDomain::Default,
277                format!("error converting {} to Timestamp, {}", s, msg),
278            )
279        };
280
281        let (seconds, nanos) = s
282            .split_once('.')
283            .ok_or_else(|| err("missing decimal point"))?;
284        let seconds = seconds.parse().map_err(|_| err("unable to parse second"))?;
285        let nanos = nanos.parse().map_err(|_| err("unable to parse nanos"))?;
286
287        let timestamp = Timestamp::from_seconds(seconds) + Duration::from_nanos(nanos);
288
289        Ok(timestamp)
290    }
291}