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 if btc_amt > bitcoin::Amount::MAX_MONEY {
78 return Err(ParseAmountError::TooLarge);
79 }
80 Ok(Self::from(btc_amt))
81 }
82
83 pub fn saturating_sub(self, other: Self) -> Self {
84 Self {
85 msats: self.msats.saturating_sub(other.msats),
86 }
87 }
88
89 pub fn mul_u64(self, other: u64) -> Self {
90 Self {
91 msats: self.msats * other,
92 }
93 }
94
95 pub fn ensure_sats_precision(&self) -> Result<(), AmountConversionError> {
98 if !self.msats.is_multiple_of(1000) {
99 return Err(AmountConversionError::SubSatoshiPrecision { msats: self.msats });
100 }
101 Ok(())
102 }
103
104 pub fn try_into_sats(&self) -> Result<u64, AmountConversionError> {
105 self.ensure_sats_precision()?;
106 Ok(self.msats / 1000)
107 }
108
109 pub const fn sats_round_down(&self) -> u64 {
110 self.msats / 1000
111 }
112
113 pub fn sats_f64(&self) -> f64 {
114 self.msats as f64 / 1000.0
115 }
116
117 pub fn checked_sub(self, other: Self) -> Option<Self> {
118 Some(Self {
119 msats: self.msats.checked_sub(other.msats)?,
120 })
121 }
122
123 pub fn checked_add(self, other: Self) -> Option<Self> {
124 Some(Self {
125 msats: self.msats.checked_add(other.msats)?,
126 })
127 }
128
129 pub fn checked_mul(self, other: u64) -> Option<Self> {
130 Some(Self {
131 msats: self.msats.checked_mul(other)?,
132 })
133 }
134}
135
136impl std::fmt::Display for Amount {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 write!(f, "{} msat", self.msats)
139 }
140}
141
142impl std::fmt::Debug for Amount {
143 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144 write!(f, "{}msat", self.msats)
147 }
148}
149
150impl std::ops::Rem for Amount {
151 type Output = Self;
152
153 fn rem(self, rhs: Self) -> Self::Output {
154 Self {
155 msats: self.msats % rhs.msats,
156 }
157 }
158}
159
160impl std::ops::RemAssign for Amount {
161 fn rem_assign(&mut self, rhs: Self) {
162 self.msats %= rhs.msats;
163 }
164}
165
166impl std::ops::Div for Amount {
167 type Output = u64;
168
169 fn div(self, rhs: Self) -> Self::Output {
170 self.msats / rhs.msats
171 }
172}
173
174impl std::ops::SubAssign for Amount {
175 fn sub_assign(&mut self, rhs: Self) {
176 self.msats -= rhs.msats;
177 }
178}
179
180impl std::ops::Mul<u64> for Amount {
181 type Output = Self;
182
183 fn mul(self, rhs: u64) -> Self::Output {
184 Self {
185 msats: self.msats * rhs,
186 }
187 }
188}
189
190impl std::ops::Mul<Amount> for u64 {
191 type Output = Amount;
192
193 fn mul(self, rhs: Amount) -> Self::Output {
194 Amount {
195 msats: self * rhs.msats,
196 }
197 }
198}
199
200impl std::ops::Add for Amount {
201 type Output = Self;
202
203 fn add(self, rhs: Self) -> Self::Output {
204 Self {
205 msats: self.msats + rhs.msats,
206 }
207 }
208}
209
210impl std::ops::Sub for Amount {
211 type Output = Self;
212
213 fn sub(self, rhs: Self) -> Self::Output {
214 Self {
215 msats: self.msats - rhs.msats,
216 }
217 }
218}
219
220impl std::ops::AddAssign for Amount {
221 fn add_assign(&mut self, rhs: Self) {
222 *self = *self + rhs;
223 }
224}
225
226impl std::iter::Sum for Amount {
227 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
228 Self {
229 msats: iter.map(|amt| amt.msats).sum::<u64>(),
230 }
231 }
232}
233
234impl FromStr for Amount {
235 type Err = ParseAmountError;
236
237 fn from_str(s: &str) -> Result<Self, Self::Err> {
238 if let Some(i) = s.find(char::is_alphabetic) {
239 let (amt, denom) = s.split_at(i);
240 Self::from_str_in(
241 amt.trim(),
242 denom
243 .trim()
244 .parse()
245 .map_err(ParseAmountError::WrongBitcoinDenomination)?,
246 )
247 } else {
248 Self::from_str_in(s.trim(), Denomination::MilliSatoshi)
250 }
251 }
252}
253
254impl From<bitcoin::Amount> for Amount {
255 fn from(amt: bitcoin::Amount) -> Self {
256 assert!(amt.to_sat() <= 2_100_000_000_000_000);
257 Self {
258 msats: amt.to_sat() * 1000,
259 }
260 }
261}
262
263impl TryFrom<Amount> for bitcoin::Amount {
264 type Error = AmountConversionError;
265
266 fn try_from(value: Amount) -> Result<Self, Self::Error> {
267 value.try_into_sats().map(Self::from_sat)
268 }
269}
270
271#[derive(Debug, Error, Clone, Eq, PartialEq)]
273#[non_exhaustive]
274pub enum AmountConversionError {
275 #[error("Amount {msats} msat is more precise than a satoshi, cannot convert to satoshis")]
278 SubSatoshiPrecision { msats: u64 },
279}
280
281#[derive(Error, Debug)]
282pub enum ParseAmountError {
283 #[error("Error parsing string as integer: {0}")]
284 NotANumber(#[from] ParseIntError),
285 #[error("Error parsing string as a bitcoin amount: {0}")]
286 WrongBitcoinAmount(#[from] bitcoin::amount::ParseAmountError),
287 #[error("Error parsing string as a bitcoin denomination: {0}")]
288 WrongBitcoinDenomination(#[source] <Denomination as FromStr>::Err),
289 #[error("Amount exceeds the maximum bitcoin supply of 21,000,000 BTC")]
290 TooLarge,
291}
292
293#[cfg(test)]
294mod tests;