1use std::num::ParseIntError;
2use std::str::FromStr;
3
4use bitcoin::Denomination;
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8use crate::encoding::{Decodable, Encodable};
9
10pub const SATS_PER_BITCOIN: u64 = 100_000_000;
11
12pub fn msats(msats: u64) -> Amount {
14 Amount::from_msats(msats)
15}
16
17pub fn sats(amount: u64) -> Amount {
19 Amount::from_sats(amount)
20}
21
22#[derive(
25 Clone,
26 Copy,
27 Eq,
28 PartialEq,
29 Ord,
30 PartialOrd,
31 Hash,
32 Deserialize,
33 Serialize,
34 Encodable,
35 Decodable,
36 Default,
37)]
38#[serde(transparent)]
39pub struct Amount {
40 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_units(units: u64) -> Self {
53 Self { msats: units }
54 }
55
56 pub const fn from_sats(sats: u64) -> Self {
58 Self::from_msats(sats * 1000)
59 }
60
61 pub const fn from_bitcoins(bitcoins: u64) -> Self {
63 Self::from_sats(bitcoins * SATS_PER_BITCOIN)
64 }
65
66 pub fn from_str_in(s: &str, denom: Denomination) -> Result<Self, ParseAmountError> {
71 if denom == Denomination::MilliSatoshi {
72 return Ok(Self::from_msats(s.parse()?));
73 }
74 let btc_amt = bitcoin::amount::Amount::from_str_in(s, denom)?;
75 Ok(Self::from(btc_amt))
76 }
77
78 pub fn saturating_sub(self, other: Self) -> Self {
79 Self {
80 msats: self.msats.saturating_sub(other.msats),
81 }
82 }
83
84 pub fn mul_u64(self, other: u64) -> Self {
85 Self {
86 msats: self.msats * other,
87 }
88 }
89
90 pub fn ensure_sats_precision(&self) -> Result<(), AmountConversionError> {
93 if !self.msats.is_multiple_of(1000) {
94 return Err(AmountConversionError::SubSatoshiPrecision { msats: self.msats });
95 }
96 Ok(())
97 }
98
99 pub fn try_into_sats(&self) -> Result<u64, AmountConversionError> {
100 self.ensure_sats_precision()?;
101 Ok(self.msats / 1000)
102 }
103
104 pub const fn sats_round_down(&self) -> u64 {
105 self.msats / 1000
106 }
107
108 pub fn sats_f64(&self) -> f64 {
109 self.msats as f64 / 1000.0
110 }
111
112 pub fn checked_sub(self, other: Self) -> Option<Self> {
113 Some(Self {
114 msats: self.msats.checked_sub(other.msats)?,
115 })
116 }
117
118 pub fn checked_add(self, other: Self) -> Option<Self> {
119 Some(Self {
120 msats: self.msats.checked_add(other.msats)?,
121 })
122 }
123
124 pub fn checked_mul(self, other: u64) -> Option<Self> {
125 Some(Self {
126 msats: self.msats.checked_mul(other)?,
127 })
128 }
129}
130
131impl std::fmt::Display for Amount {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 write!(f, "{} msat", self.msats)
134 }
135}
136
137impl std::fmt::Debug for Amount {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 write!(f, "{}msat", self.msats)
142 }
143}
144
145impl std::ops::Rem for Amount {
146 type Output = Self;
147
148 fn rem(self, rhs: Self) -> Self::Output {
149 Self {
150 msats: self.msats % rhs.msats,
151 }
152 }
153}
154
155impl std::ops::RemAssign for Amount {
156 fn rem_assign(&mut self, rhs: Self) {
157 self.msats %= rhs.msats;
158 }
159}
160
161impl std::ops::Div for Amount {
162 type Output = u64;
163
164 fn div(self, rhs: Self) -> Self::Output {
165 self.msats / rhs.msats
166 }
167}
168
169impl std::ops::SubAssign for Amount {
170 fn sub_assign(&mut self, rhs: Self) {
171 self.msats -= rhs.msats;
172 }
173}
174
175impl std::ops::Mul<u64> for Amount {
176 type Output = Self;
177
178 fn mul(self, rhs: u64) -> Self::Output {
179 Self {
180 msats: self.msats * rhs,
181 }
182 }
183}
184
185impl std::ops::Mul<Amount> for u64 {
186 type Output = Amount;
187
188 fn mul(self, rhs: Amount) -> Self::Output {
189 Amount {
190 msats: self * rhs.msats,
191 }
192 }
193}
194
195impl std::ops::Add for Amount {
196 type Output = Self;
197
198 fn add(self, rhs: Self) -> Self::Output {
199 Self {
200 msats: self.msats + rhs.msats,
201 }
202 }
203}
204
205impl std::ops::Sub for Amount {
206 type Output = Self;
207
208 fn sub(self, rhs: Self) -> Self::Output {
209 Self {
210 msats: self.msats - rhs.msats,
211 }
212 }
213}
214
215impl std::ops::AddAssign for Amount {
216 fn add_assign(&mut self, rhs: Self) {
217 *self = *self + rhs;
218 }
219}
220
221impl std::iter::Sum for Amount {
222 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
223 Self {
224 msats: iter.map(|amt| amt.msats).sum::<u64>(),
225 }
226 }
227}
228
229impl FromStr for Amount {
230 type Err = ParseAmountError;
231
232 fn from_str(s: &str) -> Result<Self, Self::Err> {
233 if let Some(i) = s.find(char::is_alphabetic) {
234 let (amt, denom) = s.split_at(i);
235 Self::from_str_in(amt.trim(), denom.trim().parse()?)
236 } else {
237 Self::from_str_in(s.trim(), Denomination::MilliSatoshi)
239 }
240 }
241}
242
243impl From<bitcoin::Amount> for Amount {
244 fn from(amt: bitcoin::Amount) -> Self {
245 assert!(amt.to_sat() <= 2_100_000_000_000_000);
246 Self {
247 msats: amt.to_sat() * 1000,
248 }
249 }
250}
251
252impl TryFrom<Amount> for bitcoin::Amount {
253 type Error = AmountConversionError;
254
255 fn try_from(value: Amount) -> Result<Self, Self::Error> {
256 value.try_into_sats().map(Self::from_sat)
257 }
258}
259
260#[derive(Debug, Error, Clone, Eq, PartialEq)]
262#[non_exhaustive]
263pub enum AmountConversionError {
264 #[error("Amount {msats} msat is more precise than a satoshi, cannot convert to satoshis")]
267 SubSatoshiPrecision { msats: u64 },
268}
269
270#[derive(Error, Debug)]
271pub enum ParseAmountError {
272 #[error("Error parsing string as integer: {0}")]
273 NotANumber(#[from] ParseIntError),
274 #[error("Error parsing string as a bitcoin amount: {0}")]
275 WrongBitcoinAmount(#[from] bitcoin::amount::ParseAmountError),
276 #[error("Error parsing string as a bitcoin denomination: {0}")]
277 WrongBitcoinDenomination(#[from] bitcoin_units::amount::ParseDenominationError),
278}
279
280#[cfg(test)]
281mod tests;