1use std::num::ParseIntError;
2use std::str::FromStr;
3
4use anyhow::bail;
5use bitcoin::Denomination;
6use serde::{Deserialize, Serialize};
7use thiserror::Error;
8
9use crate::encoding::{Decodable, Encodable};
10
11pub const SATS_PER_BITCOIN: u64 = 100_000_000;
12
13pub fn msats(msats: u64) -> Amount {
15 Amount::from_msats(msats)
16}
17
18pub fn sats(amount: u64) -> Amount {
20 Amount::from_sats(amount)
21}
22
23#[derive(
26 Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, Encodable, Decodable,
27)]
28#[serde(transparent)]
29pub struct Amount {
30 pub msats: u64,
31}
32
33impl Amount {
34 pub const ZERO: Self = Self { msats: 0 };
35
36 pub const fn from_msats(msats: u64) -> Self {
38 Self { msats }
39 }
40
41 pub const fn from_sats(sats: u64) -> Self {
43 Self::from_msats(sats * 1000)
44 }
45
46 pub const fn from_bitcoins(bitcoins: u64) -> Self {
48 Self::from_sats(bitcoins * SATS_PER_BITCOIN)
49 }
50
51 pub fn from_str_in(s: &str, denom: Denomination) -> Result<Self, ParseAmountError> {
56 if denom == Denomination::MilliSatoshi {
57 return Ok(Self::from_msats(s.parse()?));
58 }
59 let btc_amt = bitcoin::amount::Amount::from_str_in(s, denom)?;
60 Ok(Self::from(btc_amt))
61 }
62
63 pub fn saturating_sub(self, other: Self) -> Self {
64 Self {
65 msats: self.msats.saturating_sub(other.msats),
66 }
67 }
68
69 pub fn mul_u64(self, other: u64) -> Self {
70 Self {
71 msats: self.msats * other,
72 }
73 }
74
75 pub fn ensure_sats_precision(&self) -> anyhow::Result<()> {
78 if self.msats % 1000 != 0 {
79 bail!("Amount is using a precision smaller than satoshi, cannot convert to satoshis");
80 }
81 Ok(())
82 }
83
84 pub fn try_into_sats(&self) -> anyhow::Result<u64> {
85 self.ensure_sats_precision()?;
86 Ok(self.msats / 1000)
87 }
88
89 pub const fn sats_round_down(&self) -> u64 {
90 self.msats / 1000
91 }
92
93 pub fn sats_f64(&self) -> f64 {
94 self.msats as f64 / 1000.0
95 }
96
97 pub fn checked_sub(self, other: Self) -> Option<Self> {
98 Some(Self {
99 msats: self.msats.checked_sub(other.msats)?,
100 })
101 }
102
103 pub fn checked_add(self, other: Self) -> Option<Self> {
104 Some(Self {
105 msats: self.msats.checked_add(other.msats)?,
106 })
107 }
108}
109
110impl std::fmt::Display for Amount {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 write!(f, "{} msat", self.msats)
113 }
114}
115
116impl std::fmt::Debug for Amount {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 write!(f, "{}msat", self.msats)
121 }
122}
123
124impl std::ops::Rem for Amount {
125 type Output = Self;
126
127 fn rem(self, rhs: Self) -> Self::Output {
128 Self {
129 msats: self.msats % rhs.msats,
130 }
131 }
132}
133
134impl std::ops::RemAssign for Amount {
135 fn rem_assign(&mut self, rhs: Self) {
136 self.msats %= rhs.msats;
137 }
138}
139
140impl std::ops::Div for Amount {
141 type Output = u64;
142
143 fn div(self, rhs: Self) -> Self::Output {
144 self.msats / rhs.msats
145 }
146}
147
148impl std::ops::SubAssign for Amount {
149 fn sub_assign(&mut self, rhs: Self) {
150 self.msats -= rhs.msats;
151 }
152}
153
154impl std::ops::Mul<u64> for Amount {
155 type Output = Self;
156
157 fn mul(self, rhs: u64) -> Self::Output {
158 Self {
159 msats: self.msats * rhs,
160 }
161 }
162}
163
164impl std::ops::Mul<Amount> for u64 {
165 type Output = Amount;
166
167 fn mul(self, rhs: Amount) -> Self::Output {
168 Amount {
169 msats: self * rhs.msats,
170 }
171 }
172}
173
174impl std::ops::Add for Amount {
175 type Output = Self;
176
177 fn add(self, rhs: Self) -> Self::Output {
178 Self {
179 msats: self.msats + rhs.msats,
180 }
181 }
182}
183
184impl std::ops::AddAssign for Amount {
185 fn add_assign(&mut self, rhs: Self) {
186 *self = *self + rhs;
187 }
188}
189
190impl std::iter::Sum for Amount {
191 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
192 Self {
193 msats: iter.map(|amt| amt.msats).sum::<u64>(),
194 }
195 }
196}
197
198impl FromStr for Amount {
199 type Err = ParseAmountError;
200
201 fn from_str(s: &str) -> Result<Self, Self::Err> {
202 if let Some(i) = s.find(char::is_alphabetic) {
203 let (amt, denom) = s.split_at(i);
204 Self::from_str_in(amt.trim(), denom.trim().parse()?)
205 } else {
206 Self::from_str_in(s.trim(), Denomination::MilliSatoshi)
208 }
209 }
210}
211
212impl From<bitcoin::Amount> for Amount {
213 fn from(amt: bitcoin::Amount) -> Self {
214 assert!(amt.to_sat() <= 2_100_000_000_000_000);
215 Self {
216 msats: amt.to_sat() * 1000,
217 }
218 }
219}
220
221impl TryFrom<Amount> for bitcoin::Amount {
222 type Error = anyhow::Error;
223
224 fn try_from(value: Amount) -> anyhow::Result<Self> {
225 value.try_into_sats().map(Self::from_sat)
226 }
227}
228
229#[derive(Error, Debug)]
230pub enum ParseAmountError {
231 #[error("Error parsing string as integer: {0}")]
232 NotANumber(#[from] ParseIntError),
233 #[error("Error parsing string as a bitcoin amount: {0}")]
234 WrongBitcoinAmount(#[from] bitcoin::amount::ParseAmountError),
235 #[error("Error parsing string as a bitcoin denomination: {0}")]
236 WrongBitcoinDenomination(#[from] bitcoin_units::amount::ParseDenominationError),
237}
238
239#[cfg(test)]
240mod tests;