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