data_anchor_client/fees/
lamports.rs

1use std::{fmt::Display, num::TryFromIntError};
2
3use thiserror::Error;
4
5use super::MicroLamports;
6
7/// The smallest fraction of the native Solana token, SOL. 1 lamport = 0.000000001 SOL.
8#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
9pub struct Lamports(pub(crate) u32);
10
11impl Lamports {
12    /// Zero lamports.
13    pub const ZERO: Self = Lamports(0);
14
15    /// Create an instance of `Lamports` from a given value.
16    pub fn new(value: u32) -> Self {
17        Lamports(value)
18    }
19
20    /// Extracts the inner value.
21    pub fn into_inner(self) -> u32 {
22        self.0
23    }
24
25    /// Multiplies the inner value by the given value, returning `None` if the result would overflow.
26    pub fn checked_mul(&self, rhs: u32) -> Option<Self> {
27        self.0.checked_mul(rhs).map(Lamports)
28    }
29
30    /// Divides the inner value by the given value, returning `None` if `rhs` == 0.
31    pub fn checked_div(&self, rhs: u32) -> Option<Self> {
32        self.0.checked_div(rhs).map(Lamports)
33    }
34
35    /// Adds the inner value to the given value, returning `None` if the result would overflow.
36    pub fn checked_add(&self, rhs: Self) -> Option<Self> {
37        self.0.checked_add(rhs.0).map(Lamports)
38    }
39
40    /// Subtracts the inner value from the given value, returning `None` if the result would underflow.
41    pub fn checked_sub(&self, rhs: Self) -> Option<Self> {
42        self.0.checked_sub(rhs.0).map(Lamports)
43    }
44}
45
46impl Display for Lamports {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        write!(f, "{} lamports", self.0)
49    }
50}
51
52impl TryFrom<MicroLamports> for Lamports {
53    type Error = LamportsFromMicroLamportsError;
54
55    fn try_from(value: MicroLamports) -> Result<Self, Self::Error> {
56        Ok(Lamports(value.0.div_ceil(1_000_000).try_into().map_err(
57            |e| LamportsFromMicroLamportsError::Overflow(value.0, e),
58        )?))
59    }
60}
61
62#[derive(Error, Debug, PartialEq, Eq)]
63pub enum LamportsFromMicroLamportsError {
64    #[error(
65        "Microlamports value is too large ({0} / 1 000 000 > 2^32-1), it would overflow ({1})"
66    )]
67    Overflow(u64, #[source] TryFromIntError),
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn micro_lamports_to_lamports_rounds_up() {
76        assert_eq!(Lamports::try_from(MicroLamports(0)), Ok(Lamports(0)));
77        assert_eq!(Lamports::try_from(MicroLamports(500_000)), Ok(Lamports(1)));
78        assert_eq!(
79            Lamports::try_from(MicroLamports(1_000_000)),
80            Ok(Lamports(1))
81        );
82        assert_eq!(
83            Lamports::try_from(MicroLamports(1_000_001)),
84            Ok(Lamports(2))
85        );
86    }
87
88    #[test]
89    fn more_than_max_lamports_errors() {
90        let too_large_value = (u32::MAX as u64 + 1) * 1_000_000;
91        let err = Lamports::try_from(MicroLamports(too_large_value)).unwrap_err();
92        assert_eq!(
93            err.to_string(),
94            "Microlamports value is too large (4294967296000000 / 1 000 000 > 2^32-1), it would overflow (out of range integral type conversion attempted)",
95        );
96    }
97}