Skip to main content

defuse_core/nonce/
expirable.rs

1use defuse_borsh_utils::As;
2use defuse_time::{Timestamp, borsh::TimestampNanoSeconds};
3use near_sdk::borsh::{BorshDeserialize, BorshSerialize};
4
5/// Expirable nonces contain deadline which is 8 bytes of timestamp in nanoseconds
6#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
7#[borsh(crate = "::near_sdk::borsh")]
8pub struct ExpirableNonce<T>
9where
10    T: BorshSerialize + BorshDeserialize,
11{
12    #[borsh(
13        serialize_with = "As::<TimestampNanoSeconds<i64>>::serialize",
14        deserialize_with = "As::<TimestampNanoSeconds<i64>>::deserialize"
15    )]
16    pub deadline: Timestamp,
17    pub nonce: T,
18}
19
20impl<T> ExpirableNonce<T>
21where
22    T: BorshSerialize + BorshDeserialize,
23{
24    pub const fn new(deadline: Timestamp, nonce: T) -> Self {
25        Self { deadline, nonce }
26    }
27
28    #[inline]
29    pub fn has_expired(&self) -> bool {
30        self.deadline < Timestamp::now()
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use std::time::Duration;
37
38    use super::*;
39
40    use defuse_test_utils::random::random_bytes;
41    use rstest::rstest;
42
43    #[rstest]
44    fn expirable_test(random_bytes: Vec<u8>) {
45        let mut u = arbitrary::Unstructured::new(&random_bytes);
46        let nonce: [u8; 24] = u.arbitrary().unwrap();
47
48        let expired = ExpirableNonce::new(Timestamp::now() - Duration::from_hours(24), nonce);
49        assert!(expired.has_expired());
50
51        let not_expired = ExpirableNonce::new(Timestamp::now() + Duration::from_hours(24), nonce);
52        assert!(!not_expired.has_expired());
53    }
54}