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