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 Debug,
27 Clone,
28 Copy,
29 Eq,
30 PartialEq,
31 Ord,
32 PartialOrd,
33 Hash,
34 Deserialize,
35 Serialize,
36 Encodable,
37 Decodable,
38)]
39#[serde(transparent)]
40pub struct Amount {
41 pub msats: u64,
42}
43
44impl Amount {
45 pub const ZERO: Self = Self { msats: 0 };
46
47 pub const fn from_msats(msats: u64) -> Self {
49 Self { msats }
50 }
51
52 pub const fn from_sats(sats: u64) -> Self {
54 Self::from_msats(sats * 1000)
55 }
56
57 pub const fn from_bitcoins(bitcoins: u64) -> Self {
59 Self::from_sats(bitcoins * SATS_PER_BITCOIN)
60 }
61
62 pub fn from_str_in(s: &str, denom: Denomination) -> Result<Self, ParseAmountError> {
67 if denom == Denomination::MilliSatoshi {
68 return Ok(Self::from_msats(s.parse()?));
69 }
70 let btc_amt = bitcoin::amount::Amount::from_str_in(s, denom)?;
71 Ok(Self::from(btc_amt))
72 }
73
74 pub fn saturating_sub(self, other: Self) -> Self {
75 Self {
76 msats: self.msats.saturating_sub(other.msats),
77 }
78 }
79
80 pub fn mul_u64(self, other: u64) -> Self {
81 Self {
82 msats: self.msats * other,
83 }
84 }
85
86 pub fn ensure_sats_precision(&self) -> anyhow::Result<()> {
89 if self.msats % 1000 != 0 {
90 bail!("Amount is using a precision smaller than satoshi, cannot convert to satoshis");
91 }
92 Ok(())
93 }
94
95 pub fn try_into_sats(&self) -> anyhow::Result<u64> {
96 self.ensure_sats_precision()?;
97 Ok(self.msats / 1000)
98 }
99
100 pub const fn sats_round_down(&self) -> u64 {
101 self.msats / 1000
102 }
103
104 pub fn sats_f64(&self) -> f64 {
105 self.msats as f64 / 1000.0
106 }
107
108 pub fn checked_sub(self, other: Self) -> Option<Self> {
109 Some(Self {
110 msats: self.msats.checked_sub(other.msats)?,
111 })
112 }
113
114 pub fn checked_add(self, other: Self) -> Option<Self> {
115 Some(Self {
116 msats: self.msats.checked_add(other.msats)?,
117 })
118 }
119}
120
121impl std::fmt::Display for Amount {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 write!(f, "{} msat", self.msats)
124 }
125}
126
127impl std::ops::Rem for Amount {
128 type Output = Self;
129
130 fn rem(self, rhs: Self) -> Self::Output {
131 Self {
132 msats: self.msats % rhs.msats,
133 }
134 }
135}
136
137impl std::ops::RemAssign for Amount {
138 fn rem_assign(&mut self, rhs: Self) {
139 self.msats %= rhs.msats;
140 }
141}
142
143impl std::ops::Div for Amount {
144 type Output = u64;
145
146 fn div(self, rhs: Self) -> Self::Output {
147 self.msats / rhs.msats
148 }
149}
150
151impl std::ops::SubAssign for Amount {
152 fn sub_assign(&mut self, rhs: Self) {
153 self.msats -= rhs.msats;
154 }
155}
156
157impl std::ops::Mul<u64> for Amount {
158 type Output = Self;
159
160 fn mul(self, rhs: u64) -> Self::Output {
161 Self {
162 msats: self.msats * rhs,
163 }
164 }
165}
166
167impl std::ops::Mul<Amount> for u64 {
168 type Output = Amount;
169
170 fn mul(self, rhs: Amount) -> Self::Output {
171 Amount {
172 msats: self * rhs.msats,
173 }
174 }
175}
176
177impl std::ops::Add for Amount {
178 type Output = Self;
179
180 fn add(self, rhs: Self) -> Self::Output {
181 Self {
182 msats: self.msats + rhs.msats,
183 }
184 }
185}
186
187impl std::ops::AddAssign for Amount {
188 fn add_assign(&mut self, rhs: Self) {
189 *self = *self + rhs;
190 }
191}
192
193impl std::iter::Sum for Amount {
194 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
195 Self {
196 msats: iter.map(|amt| amt.msats).sum::<u64>(),
197 }
198 }
199}
200
201impl std::ops::Sub for Amount {
202 type Output = Self;
203
204 fn sub(self, rhs: Self) -> Self::Output {
205 Self {
206 msats: self.msats - rhs.msats,
207 }
208 }
209}
210
211impl FromStr for Amount {
212 type Err = ParseAmountError;
213
214 fn from_str(s: &str) -> Result<Self, Self::Err> {
215 if let Some(i) = s.find(char::is_alphabetic) {
216 let (amt, denom) = s.split_at(i);
217 Self::from_str_in(amt.trim(), denom.trim().parse()?)
218 } else {
219 Self::from_str_in(s.trim(), Denomination::MilliSatoshi)
221 }
222 }
223}
224
225impl From<bitcoin::Amount> for Amount {
226 fn from(amt: bitcoin::Amount) -> Self {
227 assert!(amt.to_sat() <= 2_100_000_000_000_000);
228 Self {
229 msats: amt.to_sat() * 1000,
230 }
231 }
232}
233
234impl TryFrom<Amount> for bitcoin::Amount {
235 type Error = anyhow::Error;
236
237 fn try_from(value: Amount) -> anyhow::Result<Self> {
238 value.try_into_sats().map(Self::from_sat)
239 }
240}
241
242#[derive(Error, Debug)]
243pub enum ParseAmountError {
244 #[error("Error parsing string as integer: {0}")]
245 NotANumber(#[from] ParseIntError),
246 #[error("Error parsing string as a bitcoin amount: {0}")]
247 WrongBitcoinAmount(#[from] bitcoin::amount::ParseAmountError),
248 #[error("Error parsing string as a bitcoin denomination: {0}")]
249 WrongBitcoinDenomination(#[from] bitcoin_units::amount::ParseDenominationError),
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 #[test]
257 fn amount_multiplication_by_scalar() {
258 assert_eq!(Amount::from_msats(1000) * 123, Amount::from_msats(123_000));
259 }
260
261 #[test]
262 fn scalar_multiplication_by_amount() {
263 assert_eq!(123 * Amount::from_msats(1000), Amount::from_msats(123_000));
264 }
265
266 #[test]
267 fn test_amount_parsing() {
268 assert_eq!(Amount::from_msats(123), Amount::from_str("123").unwrap());
270 assert_eq!(
271 Amount::from_msats(123),
272 Amount::from_str("123msat").unwrap()
273 );
274 assert_eq!(
275 Amount::from_msats(123),
276 Amount::from_str("123 msat").unwrap()
277 );
278 assert_eq!(
279 Amount::from_msats(123),
280 Amount::from_str("123 msats").unwrap()
281 );
282 assert_eq!(Amount::from_sats(123), Amount::from_str("123sat").unwrap());
284 assert_eq!(Amount::from_sats(123), Amount::from_str("123 sat").unwrap());
285 assert_eq!(
286 Amount::from_sats(123),
287 Amount::from_str("123satoshi").unwrap()
288 );
289 assert_eq!(
290 Amount::from_sats(123),
291 Amount::from_str("123satoshis").unwrap()
292 );
293 assert_eq!(
295 Amount::from_bitcoins(123),
296 Amount::from_str("123btc").unwrap()
297 );
298 assert_eq!(
299 Amount::from_sats(12_345_600_000),
300 Amount::from_str("123.456btc").unwrap()
301 );
302 }
303}