bitcoin_units/amount/unsigned.rs
1// SPDX-License-Identifier: CC0-1.0
2
3//! An unsigned bitcoin amount.
4
5#[cfg(feature = "alloc")]
6use alloc::string::{String, ToString};
7use core::str::FromStr;
8use core::{default, fmt};
9
10#[cfg(feature = "arbitrary")]
11use arbitrary::{Arbitrary, Unstructured};
12use internals::const_casts;
13use NumOpResult as R;
14
15#[cfg(feature = "encoding")]
16use super::error::AmountDecoderError;
17use super::error::{ParseAmountErrorInner, ParseErrorInner};
18use super::{
19 parse_signed_to_satoshi, split_amount_and_denomination, Denomination, Display, DisplayStyle,
20 OutOfRangeError, ParseAmountError, ParseError, SignedAmount,
21};
22use crate::result::{MathOp, NumOpError as E, NumOpResult};
23use crate::{parse_int, FeeRate, Weight};
24
25mod encapsulate {
26 use super::OutOfRangeError;
27
28 /// An amount.
29 ///
30 /// The [`Amount`] type can be used to express Bitcoin amounts that support arithmetic and
31 /// conversion to various denominations. The [`Amount`] type does not implement [`serde`] traits
32 /// but we do provide modules for serializing as satoshis or bitcoin.
33 ///
34 /// **Warning!**
35 ///
36 /// This type implements several arithmetic operations from [`core::ops`].
37 /// To prevent errors due to an overflow when using these operations,
38 /// it is advised to instead use the checked arithmetic methods whose names
39 /// start with `checked_`. The operations from [`core::ops`] that [`Amount`]
40 /// implements will panic when an overflow occurs.
41 ///
42 /// # Examples
43 ///
44 /// ```
45 /// # #[cfg(feature = "serde")] {
46 /// use serde::{Serialize, Deserialize};
47 /// use bitcoin_units::Amount;
48 ///
49 /// #[derive(Serialize, Deserialize)]
50 /// struct Foo {
51 /// // If you are using `rust-bitcoin` then `bitcoin::amount::serde::as_sat` also works.
52 /// #[serde(with = "bitcoin_units::amount::serde::as_sat")] // Also `serde::as_btc`.
53 /// amount: Amount,
54 /// }
55 /// # }
56 /// ```
57 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
58 pub struct Amount(u64);
59
60 impl Amount {
61 /// The maximum value of an amount.
62 pub const MAX: Self = Self(21_000_000 * 100_000_000);
63 /// The minimum value of an amount.
64 pub const MIN: Self = Self(0);
65
66 /// Gets the number of satoshis in this [`Amount`].
67 ///
68 /// # Examples
69 ///
70 /// ```
71 /// # use bitcoin_units::Amount;
72 /// assert_eq!(Amount::ONE_BTC.to_sat(), 100_000_000);
73 /// ```
74 #[inline]
75 pub const fn to_sat(self) -> u64 { self.0 }
76
77 /// Constructs a new [`Amount`] from the given number of satoshis.
78 ///
79 /// # Errors
80 ///
81 /// If `satoshi` is outside of valid range (greater than [`Self::MAX_MONEY`]).
82 ///
83 /// # Examples
84 ///
85 /// ```
86 /// # use bitcoin_units::{amount, Amount};
87 /// # let sat = 100_000;
88 /// let amount = Amount::from_sat(sat)?;
89 /// assert_eq!(amount.to_sat(), sat);
90 /// # Ok::<_, amount::OutOfRangeError>(())
91 /// ```
92 #[inline]
93 pub const fn from_sat(satoshi: u64) -> Result<Self, OutOfRangeError> {
94 if satoshi > Self::MAX_MONEY.to_sat() {
95 Err(OutOfRangeError { is_signed: false, is_greater_than_max: true })
96 } else {
97 Ok(Self(satoshi))
98 }
99 }
100 }
101}
102#[doc(inline)]
103pub use encapsulate::Amount;
104
105impl Amount {
106 /// The zero amount.
107 pub const ZERO: Self = Self::from_sat_u32(0);
108 /// Exactly one satoshi.
109 pub const ONE_SAT: Self = Self::from_sat_u32(1);
110 /// Exactly one bitcoin.
111 pub const ONE_BTC: Self = Self::from_btc_u16(1);
112 /// Exactly fifty bitcoin.
113 pub const FIFTY_BTC: Self = Self::from_btc_u16(50);
114 /// The maximum value allowed as an amount. Useful for sanity checking.
115 pub const MAX_MONEY: Self = Self::MAX;
116 /// The number of bytes that an amount contributes to the size of a transaction.
117 pub const SIZE: usize = 8; // Serialized length of a u64.
118
119 /// Constructs a new [`Amount`] with satoshi precision and the given number of satoshis.
120 ///
121 /// Accepts an `u32` which is guaranteed to be in range for the type, but which can only
122 /// represent roughly 0 to 42.95 BTC.
123 #[inline]
124 #[allow(clippy::missing_panics_doc)]
125 pub const fn from_sat_u32(satoshi: u32) -> Self {
126 let sats = const_casts::u32_to_u64(satoshi);
127 match Self::from_sat(sats) {
128 Ok(amount) => amount,
129 Err(_) =>
130 panic!("unreachable - u32 input [0 to 4,294,967,295 satoshis] is within range"),
131 }
132 }
133
134 /// Converts from a value expressing a decimal number of bitcoin to an [`Amount`].
135 ///
136 /// # Errors
137 ///
138 /// If the amount is too precise, negative, or greater than 21,000,000.
139 ///
140 /// Please be aware of the risk of using floating-point numbers.
141 ///
142 /// # Examples
143 ///
144 /// ```
145 /// # use bitcoin_units::{amount, Amount};
146 /// let amount = Amount::from_btc(0.01)?;
147 /// assert_eq!(amount.to_sat(), 1_000_000);
148 /// # Ok::<_, amount::ParseAmountError>(())
149 /// ```
150 #[inline]
151 #[cfg(feature = "alloc")]
152 pub fn from_btc(btc: f64) -> Result<Self, ParseAmountError> {
153 Self::from_float_in(btc, Denomination::Bitcoin)
154 }
155
156 /// Converts from a value expressing a whole number of bitcoin to an [`Amount`].
157 #[inline]
158 #[allow(clippy::missing_panics_doc)]
159 pub fn from_int_btc<T: Into<u16>>(whole_bitcoin: T) -> Self {
160 Self::from_btc_u16(whole_bitcoin.into())
161 }
162
163 /// Converts from a value expressing a whole number of bitcoin to an [`Amount`]
164 /// in const context.
165 #[inline]
166 #[allow(clippy::missing_panics_doc)]
167 pub const fn from_btc_u16(whole_bitcoin: u16) -> Self {
168 let btc = const_casts::u16_to_u64(whole_bitcoin);
169 let sats = btc * 100_000_000;
170
171 match Self::from_sat(sats) {
172 Ok(amount) => amount,
173 Err(_) => panic!("unreachable - 65,535 BTC is within range"),
174 }
175 }
176
177 /// Parses a decimal string as a value in the given [`Denomination`].
178 ///
179 /// Note: This only parses the value string. If you want to parse a string
180 /// containing the value with denomination, use [`FromStr`].
181 ///
182 /// # Errors
183 ///
184 /// If the amount is too precise, negative, or greater than 21,000,000.
185 #[inline]
186 pub fn from_str_in(s: &str, denom: Denomination) -> Result<Self, ParseAmountError> {
187 let (is_neg, amount) =
188 parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(false))?;
189 if is_neg {
190 return Err(ParseAmountError(ParseAmountErrorInner::OutOfRange(
191 OutOfRangeError::negative(),
192 )));
193 }
194 Self::try_from(amount).map_err(|e| ParseAmountError(ParseAmountErrorInner::OutOfRange(e)))
195 }
196
197 /// Parses amounts with denomination suffix as produced by [`Self::to_string_with_denomination`]
198 /// or with [`fmt::Display`].
199 ///
200 /// If you want to parse only the amount without the denomination, use [`Self::from_str_in`].
201 ///
202 /// # Errors
203 ///
204 /// If the amount is too big, too precise or negative.
205 ///
206 /// # Examples
207 ///
208 /// ```
209 /// # use bitcoin_units::{amount, Amount};
210 /// let amount = Amount::from_str_with_denomination("0.1 BTC")?;
211 /// assert_eq!(amount, Amount::from_sat_u32(10_000_000));
212 /// # Ok::<_, amount::ParseError>(())
213 /// ```
214 #[inline]
215 pub fn from_str_with_denomination(s: &str) -> Result<Self, ParseError> {
216 let (amt, denom) = split_amount_and_denomination(s)?;
217 Self::from_str_in(amt, denom).map_err(|e| ParseError(ParseErrorInner::Amount(e)))
218 }
219
220 /// Expresses this [`Amount`] as a floating-point value in the given [`Denomination`].
221 ///
222 /// Please be aware of the risk of using floating-point numbers.
223 ///
224 /// # Examples
225 ///
226 /// ```
227 /// # use bitcoin_units::amount::{self, Amount, Denomination};
228 /// let amount = Amount::from_sat(100_000)?;
229 /// assert_eq!(amount.to_float_in(Denomination::Bitcoin), 0.001);
230 /// # Ok::<_, amount::OutOfRangeError>(())
231 /// ```
232 #[inline]
233 #[cfg(feature = "alloc")]
234 #[allow(clippy::missing_panics_doc)]
235 pub fn to_float_in(self, denom: Denomination) -> f64 {
236 self.to_string_in(denom).parse::<f64>().unwrap()
237 }
238
239 /// Expresses this [`Amount`] as a floating-point value in Bitcoin.
240 ///
241 /// Please be aware of the risk of using floating-point numbers.
242 ///
243 /// # Examples
244 ///
245 /// ```
246 /// # use bitcoin_units::amount::{self, Amount, Denomination};
247 /// let amount = Amount::from_sat(100_000)?;
248 /// assert_eq!(amount.to_btc(), amount.to_float_in(Denomination::Bitcoin));
249 /// # Ok::<_, amount::OutOfRangeError>(())
250 /// ```
251 #[inline]
252 #[cfg(feature = "alloc")]
253 pub fn to_btc(self) -> f64 { self.to_float_in(Denomination::Bitcoin) }
254
255 /// Converts this [`Amount`] in floating-point notation in the given [`Denomination`].
256 ///
257 /// # Errors
258 ///
259 /// If the amount is too big, too precise or negative.
260 ///
261 /// Please be aware of the risk of using floating-point numbers.
262 #[inline]
263 #[cfg(feature = "alloc")]
264 pub fn from_float_in(value: f64, denom: Denomination) -> Result<Self, ParseAmountError> {
265 if value < 0.0 {
266 return Err(ParseAmountError(ParseAmountErrorInner::OutOfRange(
267 OutOfRangeError::negative(),
268 )));
269 }
270 // This is inefficient, but the safest way to deal with this. The parsing logic is safe.
271 // Any performance-critical application should not be dealing with floats.
272 Self::from_str_in(&value.to_string(), denom)
273 }
274
275 /// Constructs a new `Amount` from a prefixed hex string.
276 ///
277 /// # Errors
278 ///
279 /// If the input string is not a valid hex representation of an amount in sats or it does not
280 /// include the `0x` prefix.
281 #[inline]
282 pub fn from_sat_hex(s: &str) -> Result<Self, ParseAmountError> {
283 let amount = parse_int::hex_u64_prefixed(s)
284 .map_err(|e| ParseAmountError(ParseAmountErrorInner::PrefixedHex(e)))?;
285 Self::from_sat(amount).map_err(|e| ParseAmountError(ParseAmountErrorInner::OutOfRange(e)))
286 }
287
288 /// Constructs a new `Amount` from an unprefixed hex string.
289 ///
290 /// # Errors
291 ///
292 /// If the input string is not a valid hex representation of an amount in sats or if it
293 /// includes the `0x` prefix.
294 #[inline]
295 pub fn from_sat_unprefixed_hex(s: &str) -> Result<Self, ParseAmountError> {
296 let amount = parse_int::hex_u64_unprefixed(s)
297 .map_err(|e| ParseAmountError(ParseAmountErrorInner::UnprefixedHex(e)))?;
298 Self::from_sat(amount).map_err(|e| ParseAmountError(ParseAmountErrorInner::OutOfRange(e)))
299 }
300
301 /// Constructs a new object that implements [`fmt::Display`] in the given [`Denomination`].
302 ///
303 /// This function is useful if you do not wish to allocate. See also [`Self::to_string_in`].
304 ///
305 /// # Examples
306 ///
307 /// ```
308 /// # use bitcoin_units::amount::{self, Amount, Denomination};
309 /// # use std::fmt::Write;
310 /// let amount = Amount::from_sat(10_000_000)?;
311 /// let mut output = String::new();
312 /// let _ = write!(&mut output, "{}", amount.display_in(Denomination::Bitcoin));
313 /// assert_eq!(output, "0.1");
314 /// # Ok::<_, amount::OutOfRangeError>(())
315 /// ```
316 #[inline]
317 #[must_use]
318 pub fn display_in(self, denomination: Denomination) -> Display {
319 Display {
320 sats_abs: self.to_sat(),
321 is_negative: false,
322 style: DisplayStyle::FixedDenomination { denomination, show_denomination: false },
323 }
324 }
325
326 /// Constructs a new object that implements [`fmt::Display`] dynamically selecting
327 /// [`Denomination`].
328 ///
329 /// This will use BTC for values greater than or equal to 1 BTC and satoshis otherwise. To
330 /// avoid confusion the denomination is always shown.
331 #[inline]
332 #[must_use]
333 pub fn display_dynamic(self) -> Display {
334 Display {
335 sats_abs: self.to_sat(),
336 is_negative: false,
337 style: DisplayStyle::DynamicDenomination,
338 }
339 }
340
341 /// Returns a formatted string representing this [`Amount`] in the given [`Denomination`].
342 ///
343 /// Returned string does not include the denomination.
344 ///
345 /// # Examples
346 ///
347 /// ```
348 /// # use bitcoin_units::amount::{self, Amount, Denomination};
349 /// let amount = Amount::from_sat(10_000_000)?;
350 /// assert_eq!(amount.to_string_in(Denomination::Bitcoin), "0.1");
351 /// # Ok::<_, amount::OutOfRangeError>(())
352 /// ```
353 #[inline]
354 #[cfg(feature = "alloc")]
355 pub fn to_string_in(self, denom: Denomination) -> String { self.display_in(denom).to_string() }
356
357 /// Returns a formatted string representing this [`Amount`] in the given [`Denomination`],
358 /// suffixed with the abbreviation for the denomination.
359 ///
360 /// # Examples
361 ///
362 /// ```
363 /// # use bitcoin_units::amount::{self, Amount, Denomination};
364 /// let amount = Amount::from_sat(10_000_000)?;
365 /// assert_eq!(amount.to_string_with_denomination(Denomination::Bitcoin), "0.1 BTC");
366 /// # Ok::<_, amount::OutOfRangeError>(())
367 /// ```
368 #[inline]
369 #[cfg(feature = "alloc")]
370 pub fn to_string_with_denomination(self, denom: Denomination) -> String {
371 self.display_in(denom).show_denomination().to_string()
372 }
373
374 /// Checked addition.
375 ///
376 /// Returns [`None`] if the sum is larger than [`Amount::MAX`].
377 #[inline]
378 #[must_use]
379 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
380 // No `map()` in const context.
381 // Unchecked add ok, adding two values less than `MAX_MONEY` cannot overflow an `i64`.
382 match Self::from_sat(self.to_sat() + rhs.to_sat()) {
383 Ok(amount) => Some(amount),
384 Err(_) => None,
385 }
386 }
387
388 /// Checked subtraction.
389 ///
390 /// Returns [`None`] if overflow occurred.
391 #[inline]
392 #[must_use]
393 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
394 // No `map()` in const context.
395 match self.to_sat().checked_sub(rhs.to_sat()) {
396 Some(res) => match Self::from_sat(res) {
397 Ok(amount) => Some(amount),
398 Err(_) => None, // Unreachable because of checked_sub above.
399 },
400 None => None,
401 }
402 }
403
404 /// Checked multiplication.
405 ///
406 /// Returns [`None`] if the product is larger than [`Amount::MAX`].
407 #[inline]
408 #[must_use]
409 pub const fn checked_mul(self, rhs: u64) -> Option<Self> {
410 // No `map()` in const context.
411 match self.to_sat().checked_mul(rhs) {
412 Some(res) => match Self::from_sat(res) {
413 Ok(amount) => Some(amount),
414 Err(_) => None,
415 },
416 None => None,
417 }
418 }
419
420 /// Checked integer division.
421 ///
422 /// Be aware that integer division loses the remainder if no exact division can be made.
423 ///
424 /// Returns [`None`] if overflow occurred.
425 #[inline]
426 #[must_use]
427 pub const fn checked_div(self, rhs: u64) -> Option<Self> {
428 // No `map()` in const context.
429 match self.to_sat().checked_div(rhs) {
430 Some(res) => match Self::from_sat(res) {
431 Ok(amount) => Some(amount),
432 Err(_) => None, // Unreachable because of checked_div above.
433 },
434 None => None,
435 }
436 }
437
438 /// Checked remainder.
439 ///
440 /// Returns [`None`] if overflow occurred.
441 #[inline]
442 #[must_use]
443 pub const fn checked_rem(self, rhs: u64) -> Option<Self> {
444 // No `map()` in const context.
445 match self.to_sat().checked_rem(rhs) {
446 Some(res) => match Self::from_sat(res) {
447 Ok(amount) => Some(amount),
448 Err(_) => None, // Unreachable because of checked_rem above.
449 },
450 None => None,
451 }
452 }
453
454 /// Converts to a signed amount.
455 #[inline]
456 #[rustfmt::skip] // Moves code comments to the wrong line.
457 #[allow(clippy::missing_panics_doc)]
458 pub fn to_signed(self) -> SignedAmount {
459 SignedAmount::from_sat(self.to_sat() as i64) // Cast ok, signed amount and amount share positive range.
460 .expect("range of Amount is within range of SignedAmount")
461 }
462
463 /// Infallibly subtracts one `Amount` from another returning a [`SignedAmount`].
464 ///
465 /// Since `SignedAmount::MIN` is equivalent to `-Amount::MAX` subtraction of two signed amounts
466 /// can never overflow a `SignedAmount`.
467 #[inline]
468 #[must_use]
469 pub fn signed_sub(self, rhs: Self) -> SignedAmount {
470 (self.to_signed() - rhs.to_signed())
471 .expect("difference of two amounts is always within SignedAmount range")
472 }
473
474 /// Checked weight floor division.
475 ///
476 /// Be aware that integer division loses the remainder if no exact division
477 /// can be made. See also [`Self::div_by_weight_ceil`].
478 pub const fn div_by_weight_floor(self, weight: Weight) -> NumOpResult<FeeRate> {
479 let wu = weight.to_wu();
480
481 // Mul by 1,000 because we use per/kwu.
482 if let Some(sats) = self.to_sat().checked_mul(1_000) {
483 match sats.checked_div(wu) {
484 Some(fee_rate) =>
485 if let Ok(amount) = Self::from_sat(fee_rate) {
486 return FeeRate::from_per_kwu(amount);
487 },
488 None => return R::Error(E::while_doing(MathOp::Div)),
489 }
490 }
491 // Use `MathOp::Mul` because `Div` implies div by zero.
492 R::Error(E::while_doing(MathOp::Mul))
493 }
494
495 /// Checked weight ceiling division.
496 ///
497 /// Be aware that integer division loses the remainder if no exact division
498 /// can be made. This method rounds up ensuring the transaction fee rate is
499 /// sufficient. See also [`Self::div_by_weight_floor`].
500 ///
501 /// # Examples
502 ///
503 /// ```
504 /// # use bitcoin_units::{amount, Amount, FeeRate, Weight};
505 /// let amount = Amount::from_sat(10)?;
506 /// let weight = Weight::from_wu(300);
507 /// let fee_rate = amount.div_by_weight_ceil(weight).expect("valid fee rate");
508 /// assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(34));
509 /// # Ok::<_, amount::OutOfRangeError>(())
510 /// ```
511 pub const fn div_by_weight_ceil(self, weight: Weight) -> NumOpResult<FeeRate> {
512 let wu = weight.to_wu();
513 if wu == 0 {
514 return R::Error(E::while_doing(MathOp::Div));
515 }
516
517 // Mul by 1,000 because we use per/kwu.
518 if let Some(sats) = self.to_sat().checked_mul(1_000) {
519 // No need to use checked arithmetic because wu is non-zero.
520 let fee_rate = sats.div_ceil(wu);
521 if let Ok(amount) = Self::from_sat(fee_rate) {
522 return FeeRate::from_per_kwu(amount);
523 }
524 }
525 // Use `MathOp::Mul` because `Div` implies div by zero.
526 R::Error(E::while_doing(MathOp::Mul))
527 }
528
529 /// Checked fee rate floor division.
530 ///
531 /// Computes the maximum weight that would result in a fee less than or equal to this amount
532 /// at the given `fee_rate`. Uses floor division to ensure the resulting weight doesn't cause
533 /// the fee to exceed the amount.
534 pub const fn div_by_fee_rate_floor(self, fee_rate: FeeRate) -> NumOpResult<Weight> {
535 debug_assert!(Self::MAX.to_sat().checked_mul(1_000).is_some());
536 let msats = self.to_sat() * 1_000;
537 match msats.checked_div(fee_rate.to_sat_per_kwu_ceil()) {
538 Some(wu) => R::Valid(Weight::from_wu(wu)),
539 None => R::Error(E::while_doing(MathOp::Div)),
540 }
541 }
542
543 /// Checked fee rate ceiling division.
544 ///
545 /// Computes the minimum weight that would result in a fee greater than or equal to this amount
546 /// at the given `fee_rate`. Uses ceiling division to ensure the resulting weight is sufficient.
547 pub const fn div_by_fee_rate_ceil(self, fee_rate: FeeRate) -> NumOpResult<Weight> {
548 // Use ceil because result is used as the divisor.
549 let rate = fee_rate.to_sat_per_kwu_ceil();
550 // Early return so we do not have to use checked arithmetic below.
551 if rate == 0 {
552 return R::Error(E::while_doing(MathOp::Div));
553 }
554
555 debug_assert!(Self::MAX.to_sat().checked_mul(1_000).is_some());
556 let msats = self.to_sat() * 1_000;
557 NumOpResult::Valid(Weight::from_wu(msats.div_ceil(rate)))
558 }
559}
560
561crate::internal_macros::impl_fmt_traits_for_u32_wrapper!(Amount, to_sat);
562
563impl default::Default for Amount {
564 #[inline]
565 fn default() -> Self { Self::ZERO }
566}
567
568impl fmt::Debug for Amount {
569 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
570 write!(f, "Amount({} SAT)", self.to_sat())
571 }
572}
573
574// No one should depend on a binding contract for Display for this type.
575// Just using Bitcoin denominated string.
576impl fmt::Display for Amount {
577 #[inline]
578 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
579 fmt::Display::fmt(&self.display_in(Denomination::Bitcoin).show_denomination(), f)
580 }
581}
582
583impl FromStr for Amount {
584 type Err = ParseError;
585
586 /// Parses a string slice where the slice includes a denomination.
587 ///
588 /// If the returned value would be zero or negative zero, then no denomination is required.
589 fn from_str(s: &str) -> Result<Self, Self::Err> {
590 let result = Self::from_str_with_denomination(s);
591
592 match result {
593 Err(ParseError(ParseErrorInner::MissingDenomination(_))) => {
594 let d = Self::from_str_in(s, Denomination::Satoshi);
595
596 if d == Ok(Self::ZERO) {
597 Ok(Self::ZERO)
598 } else {
599 result
600 }
601 }
602 _ => result,
603 }
604 }
605}
606
607impl TryFrom<SignedAmount> for Amount {
608 type Error = OutOfRangeError;
609
610 #[inline]
611 fn try_from(value: SignedAmount) -> Result<Self, Self::Error> { value.to_unsigned() }
612}
613
614#[cfg(feature = "encoding")]
615impl encoding::Encode for Amount {
616 type Encoder<'e> = AmountEncoder<'e>;
617
618 #[inline]
619 fn encoder(&self) -> Self::Encoder<'_> {
620 AmountEncoder::new(encoding::ArrayEncoder::without_length_prefix(
621 self.to_sat().to_le_bytes(),
622 ))
623 }
624}
625
626#[cfg(feature = "encoding")]
627impl encoding::Decode for Amount {
628 type Decoder = AmountDecoder;
629}
630
631#[cfg(feature = "encoding")]
632encoding::encoder_newtype_exact! {
633 /// The encoder for the [`Amount`] type.
634 #[derive(Debug, Clone)]
635 pub struct AmountEncoder<'e>(encoding::ArrayEncoder<8>);
636}
637
638#[cfg(feature = "encoding")]
639crate::decoder_newtype! {
640 /// The decoder for the [`Amount`] type.
641 #[derive(Debug, Clone)]
642 pub struct AmountDecoder(encoding::ArrayDecoder<8>);
643
644 /// Constructs a new [`Amount`] decoder.
645 pub const fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
646
647 fn map_push_bytes_err(e: encoding::UnexpectedEofError) -> AmountDecoderError {
648 AmountDecoderError::eof(e)
649 }
650
651 fn end(result: Result<[u8; 8], encoding::UnexpectedEofError>) -> Result<Amount, AmountDecoderError> {
652 let value = result.map_err(AmountDecoderError::eof)?;
653 let a = u64::from_le_bytes(value);
654 Amount::from_sat(a).map_err(AmountDecoderError::out_of_range)
655 }
656}
657
658#[cfg(feature = "arbitrary")]
659impl<'a> Arbitrary<'a> for Amount {
660 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
661 let sats = u.int_in_range(Self::MIN.to_sat()..=Self::MAX.to_sat())?;
662 Ok(Self::from_sat(sats).expect("range is valid"))
663 }
664}