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