Skip to main content

df_hyperliquid_core/utils/
nonce.rs

1/// One account's nonces: milliseconds, strictly increasing. The venue keeps
2/// the highest nonces it has seen per signer and refuses a repeat, so each
3/// account holds its own clock. The time is passed in, since the browser and
4/// Workers have no std clock.
5#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
6pub struct NonceClock {
7    last: u64,
8}
9
10impl NonceClock {
11    #[must_use]
12    pub const fn new() -> Self {
13        NonceClock { last: 0 }
14    }
15
16    /// A clock that hands out nothing at or below `last`.
17    #[must_use]
18    pub const fn after(last: u64) -> Self {
19        NonceClock { last }
20    }
21
22    /// `max(now_ms, last + 1)`, remembered as the new last.
23    pub fn next(&mut self, now_ms: u64) -> u64 {
24        let nonce = now_ms.max(self.last.saturating_add(1));
25        self.last = nonce;
26        nonce
27    }
28
29    #[must_use]
30    pub const fn last(&self) -> u64 {
31        self.last
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn a_nonce_follows_the_clock_and_never_repeats() {
41        let mut clock = NonceClock::new();
42        assert_eq!(clock.next(10), 10);
43        assert_eq!(clock.next(10), 11);
44        assert_eq!(clock.next(10), 12);
45        assert_eq!(clock.next(5), 13, "a clock that steps back does not take the nonce with it");
46        assert_eq!(clock.next(100), 100);
47        assert_eq!(clock.last(), 100);
48    }
49
50    #[test]
51    fn two_accounts_keep_their_own_counts() {
52        let mut one = NonceClock::new();
53        let mut two = NonceClock::after(1_790_000_000_000);
54        assert_eq!(one.next(1_790_000_000_000), 1_790_000_000_000);
55        assert_eq!(two.next(1_790_000_000_000), 1_790_000_000_001);
56        assert_eq!(one.last(), 1_790_000_000_000);
57    }
58}