1use std::cmp::Ordering;
6use std::collections::HashMap;
7use std::fmt;
8use std::str::FromStr;
9
10use lightning::offers::offer::Offer;
11use serde::{Deserialize, Serialize};
12use thiserror::Error;
13
14use crate::nuts::CurrencyUnit;
15use crate::Id;
16
17#[derive(Debug, Error)]
19pub enum Error {
20 #[error("Split Values must be less then or equal to amount")]
22 SplitValuesGreater,
23 #[error("Amount Overflow")]
25 AmountOverflow,
26 #[error("Cannot convert units")]
28 CannotConvertUnits,
29 #[error("Unit mismatch: cannot operate on {0} and {1}")]
31 UnitMismatch(CurrencyUnit, CurrencyUnit),
32 #[error("Invalid Amount: {0}")]
34 InvalidAmount(String),
35 #[error("Amount undefined")]
37 AmountUndefined,
38 #[error(transparent)]
40 Utf8ParseError(#[from] std::string::FromUtf8Error),
41 #[error("Cannot represent amount {0} with available denominations (got {1})")]
43 CannotSplitAmount(u64, u64),
44}
45
46#[derive(Debug, Hash, PartialEq, Eq)]
51#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
52pub struct Amount<U = ()> {
53 value: u64,
54 unit: U,
55}
56
57#[derive(Debug, Clone)]
60pub struct FeeAndAmounts {
61 fee: u64,
62 amounts: Vec<u64>,
63}
64
65impl From<(u64, Vec<u64>)> for FeeAndAmounts {
66 fn from(value: (u64, Vec<u64>)) -> Self {
67 Self {
68 fee: value.0,
69 amounts: value.1,
70 }
71 }
72}
73
74impl FeeAndAmounts {
75 #[inline(always)]
77 pub fn fee(&self) -> u64 {
78 self.fee
79 }
80
81 #[inline(always)]
83 pub fn amounts(&self) -> &[u64] {
84 &self.amounts
85 }
86}
87
88pub type KeysetFeeAndAmounts = HashMap<Id, FeeAndAmounts>;
90
91impl Copy for Amount<()> {}
93
94impl Clone for Amount<()> {
95 fn clone(&self) -> Self {
96 *self
97 }
98}
99
100impl Clone for Amount<CurrencyUnit> {
102 fn clone(&self) -> Self {
103 Self {
104 value: self.value,
105 unit: self.unit.clone(),
106 }
107 }
108}
109
110impl PartialOrd for Amount<()> {
112 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
113 Some(self.cmp(other))
114 }
115}
116
117impl Ord for Amount<()> {
119 fn cmp(&self, other: &Self) -> Ordering {
120 self.value.cmp(&other.value)
121 }
122}
123
124impl PartialOrd for Amount<CurrencyUnit> {
126 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
127 if self.unit != other.unit {
128 None
130 } else {
131 Some(self.value.cmp(&other.value))
132 }
133 }
134}
135
136impl<U> Serialize for Amount<U> {
141 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
142 where
143 S: serde::Serializer,
144 {
145 self.value.serialize(serializer)
146 }
147}
148
149impl<'de> Deserialize<'de> for Amount<()> {
150 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
151 where
152 D: serde::Deserializer<'de>,
153 {
154 let value = u64::deserialize(deserializer)?;
155 Ok(Amount { value, unit: () })
156 }
157}
158
159impl FromStr for Amount<()> {
160 type Err = Error;
161
162 fn from_str(s: &str) -> Result<Self, Self::Err> {
163 let value = s
164 .parse::<u64>()
165 .map_err(|_| Error::InvalidAmount(s.to_owned()))?;
166 Ok(Amount { value, unit: () })
167 }
168}
169
170impl Amount<()> {
171 pub const ZERO: Amount<()> = Amount { value: 0, unit: () };
173
174 pub const ONE: Amount<()> = Amount { value: 1, unit: () };
176
177 pub fn with_unit(self, unit: CurrencyUnit) -> Amount<CurrencyUnit> {
192 Amount {
193 value: self.value,
194 unit,
195 }
196 }
197
198 pub fn split(&self, fee_and_amounts: &FeeAndAmounts) -> Result<Vec<Self>, Error> {
203 let parts: Vec<Self> = fee_and_amounts
204 .amounts
205 .iter()
206 .rev()
207 .fold((Vec::new(), self.value), |(mut acc, total), &amount| {
208 if total >= amount {
209 acc.push(Self::from(amount));
210 }
211 (acc, total % amount)
212 })
213 .0;
214
215 let sum: u64 = parts.iter().map(|a| a.value).sum();
216 if sum != self.value {
217 return Err(Error::CannotSplitAmount(self.value, sum));
218 }
219
220 Ok(parts)
221 }
222
223 pub fn split_targeted(
225 &self,
226 target: &SplitTarget,
227 fee_and_amounts: &FeeAndAmounts,
228 ) -> Result<Vec<Self>, Error> {
229 let mut parts = match target {
230 SplitTarget::None => self.split(fee_and_amounts)?,
231 SplitTarget::Value(amount) => {
232 if self.le(amount) {
233 return self.split(fee_and_amounts);
234 }
235
236 let mut parts_total = Amount::ZERO;
237 let mut parts = Vec::new();
238
239 let parts_of_value = amount.split(fee_and_amounts)?;
241
242 while parts_total.lt(self) {
243 for part in parts_of_value.iter().copied() {
244 if (part.checked_add(parts_total).ok_or(Error::AmountOverflow)?).le(self) {
245 parts.push(part);
246 } else {
247 let amount_left =
248 self.checked_sub(parts_total).ok_or(Error::AmountOverflow)?;
249 parts.extend(amount_left.split(fee_and_amounts)?);
250 }
251
252 parts_total = Amount::try_sum(parts.clone().iter().copied())?;
253
254 if parts_total.eq(self) {
255 break;
256 }
257 }
258 }
259
260 parts
261 }
262 SplitTarget::Values(values) => {
263 let values_total: Amount = Amount::try_sum(values.clone().into_iter())?;
264
265 match self.cmp(&values_total) {
266 Ordering::Equal => values.clone(),
267 Ordering::Less => {
268 return Err(Error::SplitValuesGreater);
269 }
270 Ordering::Greater => {
271 let extra = self
272 .checked_sub(values_total)
273 .ok_or(Error::AmountOverflow)?;
274 let mut extra_amount = extra.split(fee_and_amounts)?;
275 let mut values = values.clone();
276
277 values.append(&mut extra_amount);
278 values
279 }
280 }
281 }
282 };
283
284 parts.sort();
285 Ok(parts)
286 }
287
288 pub fn split_with_fee(&self, fee_and_amounts: &FeeAndAmounts) -> Result<Vec<Self>, Error> {
290 let without_fee_amounts = self.split(fee_and_amounts)?;
291 let total_fee_ppk = fee_and_amounts
292 .fee
293 .checked_mul(without_fee_amounts.len() as u64)
294 .ok_or(Error::AmountOverflow)?;
295 let fee = Amount::from(total_fee_ppk.div_ceil(1000));
296 let new_amount = self.checked_add(fee).ok_or(Error::AmountOverflow)?;
297
298 let split = new_amount.split(fee_and_amounts)?;
299 let split_fee_ppk = (split.len() as u64)
300 .checked_mul(fee_and_amounts.fee)
301 .ok_or(Error::AmountOverflow)?;
302 let split_fee = Amount::from(split_fee_ppk.div_ceil(1000));
303
304 if let Some(net_amount) = new_amount.checked_sub(split_fee) {
305 if net_amount >= *self {
306 return Ok(split);
307 }
308 }
309 self.checked_add(Amount::ONE)
310 .ok_or(Error::AmountOverflow)?
311 .split_with_fee(fee_and_amounts)
312 }
313
314 pub fn checked_add(self, other: Amount<()>) -> Option<Amount<()>> {
316 self.value
317 .checked_add(other.value)
318 .map(|v| Amount { value: v, unit: () })
319 }
320
321 pub fn checked_sub(self, other: Amount<()>) -> Option<Amount<()>> {
323 self.value
324 .checked_sub(other.value)
325 .map(|v| Amount { value: v, unit: () })
326 }
327
328 pub fn checked_mul(self, other: Amount<()>) -> Option<Amount<()>> {
330 self.value
331 .checked_mul(other.value)
332 .map(|v| Amount { value: v, unit: () })
333 }
334
335 pub fn checked_div(self, other: Amount<()>) -> Option<Amount<()>> {
337 self.value
338 .checked_div(other.value)
339 .map(|v| Amount { value: v, unit: () })
340 }
341
342 pub fn saturating_sub(self, other: Self) -> Self {
344 if other > self {
345 Self::ZERO
346 } else {
347 self - other
348 }
349 }
350
351 pub fn try_sum<I>(iter: I) -> Result<Self, Error>
353 where
354 I: IntoIterator<Item = Self>,
355 {
356 iter.into_iter().try_fold(Amount::ZERO, |acc, x| {
357 acc.checked_add(x).ok_or(Error::AmountOverflow)
358 })
359 }
360
361 pub fn convert_unit(
363 &self,
364 current_unit: &CurrencyUnit,
365 target_unit: &CurrencyUnit,
366 ) -> Result<Amount<()>, Error> {
367 Amount::new(self.value, current_unit.clone())
368 .convert_to(target_unit)
369 .map(Into::into)
370 }
371
372 pub fn to_u64(self) -> u64 {
374 self.value
375 }
376
377 pub fn to_i64(self) -> Option<i64> {
379 if self.value <= i64::MAX as u64 {
380 Some(self.value as i64)
381 } else {
382 None
383 }
384 }
385
386 pub fn from_i64(value: i64) -> Option<Self> {
388 if value >= 0 {
389 Some(Amount {
390 value: value as u64,
391 unit: (),
392 })
393 } else {
394 None
395 }
396 }
397}
398
399impl Default for Amount<()> {
400 fn default() -> Self {
401 Amount::ZERO
402 }
403}
404
405impl Default for &Amount<()> {
406 fn default() -> Self {
407 &Amount::ZERO
408 }
409}
410
411impl Amount<CurrencyUnit> {
412 pub fn new(value: u64, unit: CurrencyUnit) -> Self {
424 Self { value, unit }
425 }
426
427 pub fn value(&self) -> u64 {
436 self.value
437 }
438
439 pub fn to_u64(self) -> u64 {
441 self.value
442 }
443
444 pub fn to_i64(self) -> Option<i64> {
446 if self.value <= i64::MAX as u64 {
447 Some(self.value as i64)
448 } else {
449 None
450 }
451 }
452
453 pub fn unit(&self) -> &CurrencyUnit {
462 &self.unit
463 }
464
465 pub fn into_parts(self) -> (u64, CurrencyUnit) {
476 (self.value, self.unit)
477 }
478
479 pub fn checked_add(&self, other: &Self) -> Result<Self, Error> {
496 if self.unit != other.unit {
497 return Err(Error::UnitMismatch(self.unit.clone(), other.unit.clone()));
498 }
499 self.value
500 .checked_add(other.value)
501 .map(|v| Amount::new(v, self.unit.clone()))
502 .ok_or(Error::AmountOverflow)
503 }
504
505 pub fn checked_sub(&self, other: &Self) -> Result<Self, Error> {
518 if self.unit != other.unit {
519 return Err(Error::UnitMismatch(self.unit.clone(), other.unit.clone()));
520 }
521 self.value
522 .checked_sub(other.value)
523 .map(|v| Amount::new(v, self.unit.clone()))
524 .ok_or(Error::AmountOverflow)
525 }
526
527 pub fn convert_to(&self, target_unit: &CurrencyUnit) -> Result<Self, Error> {
538 if &self.unit == target_unit {
539 return Ok(self.clone());
540 }
541
542 let converted_value = match (&self.unit, target_unit) {
543 (CurrencyUnit::Sat, CurrencyUnit::Msat) => self
544 .value
545 .checked_mul(MSAT_IN_SAT)
546 .ok_or(Error::AmountOverflow)?,
547 (CurrencyUnit::Msat, CurrencyUnit::Sat) => self.value / MSAT_IN_SAT,
548 _ => return Err(Error::CannotConvertUnits),
549 };
550
551 Ok(Amount::new(converted_value, target_unit.clone()))
552 }
553
554 pub fn display_with_unit(&self) -> String {
556 format!("{} {}", self.value, self.unit)
557 }
558}
559
560impl<U> fmt::Display for Amount<U> {
561 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
562 if let Some(width) = f.width() {
563 write!(f, "{:width$}", self.value, width = width)
564 } else {
565 write!(f, "{}", self.value)
566 }
567 }
568}
569
570impl From<u64> for Amount<()> {
571 fn from(value: u64) -> Self {
572 Amount { value, unit: () }
573 }
574}
575
576impl From<&u64> for Amount<()> {
577 fn from(value: &u64) -> Self {
578 Amount {
579 value: *value,
580 unit: (),
581 }
582 }
583}
584
585impl From<Amount<()>> for u64 {
586 fn from(value: Amount<()>) -> Self {
587 value.value
588 }
589}
590
591impl From<Amount<CurrencyUnit>> for Amount<()> {
592 fn from(value: Amount<CurrencyUnit>) -> Self {
593 Amount {
594 value: value.value,
595 unit: (),
596 }
597 }
598}
599
600impl AsRef<u64> for Amount<()> {
601 fn as_ref(&self) -> &u64 {
602 &self.value
603 }
604}
605
606impl std::ops::Add for Amount<()> {
607 type Output = Amount<()>;
608
609 fn add(self, rhs: Amount<()>) -> Self::Output {
610 self.checked_add(rhs)
611 .expect("Addition overflow: the sum of the amounts exceeds the maximum value")
612 }
613}
614
615impl std::ops::AddAssign for Amount<()> {
616 fn add_assign(&mut self, rhs: Self) {
617 *self = self
618 .checked_add(rhs)
619 .expect("AddAssign overflow: the sum of the amounts exceeds the maximum value");
620 }
621}
622
623impl std::ops::Sub for Amount<()> {
624 type Output = Amount<()>;
625
626 fn sub(self, rhs: Amount<()>) -> Self::Output {
627 self.checked_sub(rhs)
628 .expect("Subtraction underflow: cannot subtract a larger amount from a smaller amount")
629 }
630}
631
632impl std::ops::SubAssign for Amount<()> {
633 fn sub_assign(&mut self, other: Self) {
634 *self = self
635 .checked_sub(other)
636 .expect("SubAssign underflow: cannot subtract a larger amount from a smaller amount");
637 }
638}
639
640impl std::ops::Mul for Amount<()> {
641 type Output = Self;
642
643 fn mul(self, other: Self) -> Self::Output {
644 self.checked_mul(other)
645 .expect("Multiplication overflow: the product of the amounts exceeds the maximum value")
646 }
647}
648
649impl std::ops::Div for Amount<()> {
650 type Output = Self;
651
652 fn div(self, other: Self) -> Self::Output {
653 self.checked_div(other)
654 .expect("Division error: cannot divide by zero or overflow occurred")
655 }
656}
657
658pub fn amount_for_offer(offer: &Offer, unit: &CurrencyUnit) -> Result<Amount, Error> {
660 let offer_amount = offer.amount().ok_or(Error::AmountUndefined)?;
661
662 let (amount, currency) = match offer_amount {
663 lightning::offers::offer::Amount::Bitcoin { amount_msats } => {
664 (amount_msats, CurrencyUnit::Msat)
665 }
666 lightning::offers::offer::Amount::Currency {
667 iso4217_code,
668 amount,
669 } => (
670 amount,
671 CurrencyUnit::from_str(&String::from_utf8(iso4217_code.as_bytes().to_vec())?)
672 .map_err(|_| Error::CannotConvertUnits)?,
673 ),
674 };
675
676 Amount::new(amount, currency)
677 .convert_to(unit)
678 .map(Into::into)
679 .map_err(|_err| Error::CannotConvertUnits)
680}
681
682#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)]
684pub enum SplitTarget {
685 #[default]
687 None,
688 Value(Amount),
690 Values(Vec<Amount>),
692}
693
694pub const MSAT_IN_SAT: u64 = 1000;
696
697#[cfg(test)]
698mod tests {
699 use super::*;
700
701 #[test]
702 fn test_split_amount() {
703 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
704
705 assert_eq!(
706 Amount::from(1).split(&fee_and_amounts).unwrap(),
707 vec![Amount::from(1)]
708 );
709 assert_eq!(
710 Amount::from(2).split(&fee_and_amounts).unwrap(),
711 vec![Amount::from(2)]
712 );
713 assert_eq!(
714 Amount::from(3).split(&fee_and_amounts).unwrap(),
715 vec![Amount::from(2), Amount::from(1)]
716 );
717 let amounts: Vec<Amount> = [8, 2, 1].iter().map(|a| Amount::from(*a)).collect();
718 assert_eq!(Amount::from(11).split(&fee_and_amounts).unwrap(), amounts);
719 let amounts: Vec<Amount> = [128, 64, 32, 16, 8, 4, 2, 1]
720 .iter()
721 .map(|a| Amount::from(*a))
722 .collect();
723 assert_eq!(Amount::from(255).split(&fee_and_amounts).unwrap(), amounts);
724 }
725
726 #[test]
727 fn test_split_target_amount() {
728 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
729 let amount = Amount::from(65);
730
731 let split = amount
732 .split_targeted(&SplitTarget::Value(Amount::from(32)), &fee_and_amounts)
733 .unwrap();
734 assert_eq!(
735 vec![Amount::from(1), Amount::from(32), Amount::from(32)],
736 split
737 );
738
739 let amount = Amount::from(150);
740
741 let split = amount
742 .split_targeted(&SplitTarget::Value(Amount::from(50)), &fee_and_amounts)
743 .unwrap();
744 assert_eq!(
745 vec![
746 Amount::from(2),
747 Amount::from(2),
748 Amount::from(2),
749 Amount::from(16),
750 Amount::from(16),
751 Amount::from(16),
752 Amount::from(32),
753 Amount::from(32),
754 Amount::from(32)
755 ],
756 split
757 );
758
759 let amount = Amount::from(63);
760
761 let split = amount
762 .split_targeted(&SplitTarget::Value(Amount::from(32)), &fee_and_amounts)
763 .unwrap();
764 assert_eq!(
765 vec![
766 Amount::from(1),
767 Amount::from(2),
768 Amount::from(4),
769 Amount::from(8),
770 Amount::from(16),
771 Amount::from(32)
772 ],
773 split
774 );
775 }
776
777 #[test]
778 fn test_split_with_fee() {
779 let fee_and_amounts = (1, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
780 let amount = Amount::from(2);
781
782 let split = amount.split_with_fee(&fee_and_amounts).unwrap();
783 assert_eq!(split, vec![Amount::from(2), Amount::from(1)]);
784
785 let amount = Amount::from(3);
786
787 let split = amount.split_with_fee(&fee_and_amounts).unwrap();
788 assert_eq!(split, vec![Amount::from(4)]);
789
790 let amount = Amount::from(3);
791 let fee_and_amounts = (1000, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
792
793 let split = amount.split_with_fee(&fee_and_amounts).unwrap();
794 assert_eq!(split, vec![Amount::from(4), Amount::from(1)]);
797 }
798
799 #[test]
800 fn test_split_with_fee_reported_issue() {
801 let fee_and_amounts = (100, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
802 let amount = Amount::from(300);
804
805 let split = amount.split_with_fee(&fee_and_amounts).unwrap();
806
807 let total_fee_ppk = (split.len() as u64) * fee_and_amounts.fee;
809 let total_fee = Amount::from(total_fee_ppk.div_ceil(1000));
810
811 let split_total = Amount::try_sum(split.iter().copied()).unwrap();
813 assert!(
814 split_total >= amount.checked_add(total_fee).unwrap(),
815 "Split total {} should be >= amount {} + fee {}",
816 split_total,
817 amount,
818 total_fee
819 );
820 }
821
822 #[test]
823 fn test_split_with_fee_edge_cases() {
824 let test_cases = vec![
826 (Amount::from(1), 100),
827 (Amount::from(10), 100),
828 (Amount::from(50), 100),
829 (Amount::from(100), 100),
830 (Amount::from(200), 100),
831 (Amount::from(300), 100),
832 (Amount::from(500), 100),
833 (Amount::from(600), 100),
834 (Amount::from(1000), 100),
835 (Amount::from(1337), 100),
836 (Amount::from(5000), 100),
837 ];
838
839 for (amount, fee_ppk) in test_cases {
840 let fee_and_amounts =
841 (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
842 let result = amount.split_with_fee(&fee_and_amounts);
843 assert!(
844 result.is_ok(),
845 "split_with_fee failed for amount {} with fee_ppk {}: {:?}",
846 amount,
847 fee_ppk,
848 result.err()
849 );
850
851 let split = result.unwrap();
852
853 let split_total = Amount::try_sum(split.iter().copied()).unwrap();
855 let fee_for_split = (split.len() as u64) * fee_ppk;
856 let total_fee = Amount::from(fee_for_split.div_ceil(1000));
857
858 let net_amount = split_total.checked_sub(total_fee);
860 assert!(
861 net_amount.is_some(),
862 "Net amount calculation failed for amount {} with fee_ppk {}",
863 amount,
864 fee_ppk
865 );
866 assert!(
867 net_amount.unwrap() >= amount,
868 "Net amount {} is less than required {} for amount {} with fee_ppk {}",
869 net_amount.unwrap(),
870 amount,
871 amount,
872 fee_ppk
873 );
874 }
875 }
876
877 #[test]
878 fn test_split_with_fee_high_fees() {
879 let test_cases = vec![
881 (Amount::from(10), 500), (Amount::from(10), 1000), (Amount::from(10), 2000), (Amount::from(100), 500),
885 (Amount::from(100), 1000),
886 (Amount::from(100), 2000),
887 ];
888
889 for (amount, fee_ppk) in test_cases {
890 let fee_and_amounts =
891 (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
892 let result = amount.split_with_fee(&fee_and_amounts);
893 assert!(
894 result.is_ok(),
895 "split_with_fee failed for amount {} with fee_ppk {}: {:?}",
896 amount,
897 fee_ppk,
898 result.err()
899 );
900
901 let split = result.unwrap();
902 let split_total = Amount::try_sum(split.iter().copied()).unwrap();
903
904 assert!(
906 split_total > amount,
907 "Split total {} should be greater than amount {} for fee_ppk {}",
908 split_total,
909 amount,
910 fee_ppk
911 );
912 }
913 }
914
915 #[test]
916 fn test_split_with_fee_recursion_limit() {
917 let amount = Amount::from(1);
920 let fee_ppk = 10000;
921 let fee_and_amounts = (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
922
923 let result = amount.split_with_fee(&fee_and_amounts);
924 assert!(
925 result.is_ok(),
926 "split_with_fee should handle extreme fees without infinite recursion"
927 );
928 }
929
930 #[test]
931 fn test_split_values() {
932 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
933 let amount = Amount::from(10);
934
935 let target = vec![Amount::from(2), Amount::from(4), Amount::from(4)];
936
937 let split_target = SplitTarget::Values(target.clone());
938
939 let values = amount
940 .split_targeted(&split_target, &fee_and_amounts)
941 .unwrap();
942
943 assert_eq!(target, values);
944
945 let target = vec![Amount::from(2), Amount::from(4), Amount::from(4)];
946
947 let split_target = SplitTarget::Values(vec![Amount::from(2), Amount::from(4)]);
948
949 let values = amount
950 .split_targeted(&split_target, &fee_and_amounts)
951 .unwrap();
952
953 assert_eq!(target, values);
954
955 let split_target = SplitTarget::Values(vec![Amount::from(2), Amount::from(10)]);
956
957 let values = amount.split_targeted(&split_target, &fee_and_amounts);
958
959 assert!(values.is_err())
960 }
961
962 #[test]
963 #[should_panic]
964 fn test_amount_addition() {
965 let amount_one: Amount = u64::MAX.into();
966 let amount_two: Amount = 1.into();
967
968 let amounts = vec![amount_one, amount_two];
969
970 let _total: Amount = Amount::try_sum(amounts).unwrap();
971 }
972
973 #[test]
974 fn test_try_amount_addition() {
975 let amount_one: Amount = u64::MAX.into();
976 let amount_two: Amount = 1.into();
977
978 let amounts = vec![amount_one, amount_two];
979
980 let total = Amount::try_sum(amounts);
981
982 assert!(total.is_err());
983 let amount_one: Amount = 10000.into();
984 let amount_two: Amount = 1.into();
985
986 let amounts = vec![amount_one, amount_two];
987 let total = Amount::try_sum(amounts).unwrap();
988
989 assert_eq!(total, 10001.into());
990 }
991
992 #[test]
993 fn test_amount_convert_to() {
994 let amount = Amount::new(1000, CurrencyUnit::Sat);
996 let converted = amount.convert_to(&CurrencyUnit::Msat).unwrap();
997 assert_eq!(converted.value(), 1000000);
998 assert_eq!(converted.unit(), &CurrencyUnit::Msat);
999
1000 let amount = Amount::new(1000, CurrencyUnit::Msat);
1002 let converted = amount.convert_to(&CurrencyUnit::Sat).unwrap();
1003 assert_eq!(converted.value(), 1);
1004 assert_eq!(converted.unit(), &CurrencyUnit::Sat);
1005
1006 let amount = Amount::new(1, CurrencyUnit::Usd);
1008 let converted = amount.convert_to(&CurrencyUnit::Usd).unwrap();
1009 assert_eq!(converted.value(), 1);
1010 assert_eq!(converted.unit(), &CurrencyUnit::Usd);
1011
1012 let amount = Amount::new(1, CurrencyUnit::Eur);
1014 let converted = amount.convert_to(&CurrencyUnit::Eur).unwrap();
1015 assert_eq!(converted.value(), 1);
1016 assert_eq!(converted.unit(), &CurrencyUnit::Eur);
1017
1018 let amount = Amount::new(1, CurrencyUnit::Sat);
1020 let converted = amount.convert_to(&CurrencyUnit::Eur);
1021 assert!(converted.is_err());
1022
1023 let amount = Amount::new(500, CurrencyUnit::Sat);
1025 let converted = amount.convert_to(&CurrencyUnit::Sat).unwrap();
1026 assert_eq!(converted.value(), 500);
1027 assert_eq!(converted.unit(), &CurrencyUnit::Sat);
1028
1029 let amount = Amount::new(5000, CurrencyUnit::Msat);
1031 let converted = amount.convert_to(&CurrencyUnit::Msat).unwrap();
1032 assert_eq!(converted.value(), 5000);
1033 assert_eq!(converted.unit(), &CurrencyUnit::Msat);
1034 }
1035
1036 #[test]
1037 fn test_amount_from_typed_to_untyped() {
1038 let typed = Amount::new(1000, CurrencyUnit::Sat);
1040 let untyped: Amount<()> = typed.into();
1041 assert_eq!(u64::from(untyped), 1000);
1042 }
1043
1044 #[test]
1055 fn test_amount_sub_operator() {
1056 let amount1 = Amount::from(100);
1057 let amount2 = Amount::from(30);
1058
1059 let result = amount1.checked_sub(amount2).unwrap();
1060 assert_eq!(result, Amount::from(70));
1061
1062 let amount1 = Amount::from(1000);
1063 let amount2 = Amount::from(1);
1064
1065 let result = amount1.checked_sub(amount2).unwrap();
1066 assert_eq!(result, Amount::from(999));
1067
1068 let amount1 = Amount::from(255);
1069 let amount2 = Amount::from(128);
1070
1071 let result = amount1.checked_sub(amount2).unwrap();
1072 assert_eq!(result, Amount::from(127));
1073 }
1074
1075 #[test]
1085 #[should_panic(expected = "Subtraction underflow")]
1086 fn test_amount_sub_underflow() {
1087 let amount1 = Amount::from(30);
1088 let amount2 = Amount::from(100);
1089
1090 let _result = amount1 - amount2;
1091 }
1092
1093 #[test]
1103 fn test_checked_add_returns_correct_value() {
1104 let amount1 = Amount::from(100);
1105 let amount2 = Amount::from(50);
1106
1107 let result = amount1.checked_add(amount2);
1108 assert_eq!(result, Some(Amount::from(150)));
1109
1110 let amount1 = Amount::from(1);
1111 let amount2 = Amount::from(1);
1112
1113 let result = amount1.checked_add(amount2);
1114 assert_eq!(result, Some(Amount::from(2)));
1115 assert_ne!(result, Some(Amount::ZERO));
1116
1117 let amount1 = Amount::from(1000);
1118 let amount2 = Amount::from(337);
1119
1120 let result = amount1.checked_add(amount2);
1121 assert_eq!(result, Some(Amount::from(1337)));
1122 }
1123
1124 #[test]
1126 fn test_checked_add_overflow() {
1127 let amount1 = Amount::from(u64::MAX);
1128 let amount2 = Amount::from(1);
1129
1130 let result = amount1.checked_add(amount2);
1131 assert!(result.is_none());
1132 }
1133
1134 #[test]
1143 fn test_try_sum_returns_correct_value() {
1144 let amounts = vec![Amount::from(10), Amount::from(20), Amount::from(30)];
1145 let result = Amount::try_sum(amounts).unwrap();
1146 assert_eq!(result, Amount::from(60));
1147 assert_ne!(result, Amount::ZERO);
1148
1149 let amounts = vec![Amount::from(1), Amount::from(1), Amount::from(1)];
1150 let result = Amount::try_sum(amounts).unwrap();
1151 assert_eq!(result, Amount::from(3));
1152
1153 let amounts = vec![Amount::from(100)];
1154 let result = Amount::try_sum(amounts).unwrap();
1155 assert_eq!(result, Amount::from(100));
1156
1157 let empty: Vec<Amount> = vec![];
1158 let result = Amount::try_sum(empty).unwrap();
1159 assert_eq!(result, Amount::ZERO);
1160 }
1161
1162 #[test]
1164 fn test_try_sum_overflow() {
1165 let amounts = vec![Amount::from(u64::MAX), Amount::from(1)];
1166 let result = Amount::try_sum(amounts);
1167 assert!(result.is_err());
1168 }
1169
1170 #[test]
1180 fn test_split_returns_correct_values() {
1181 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
1182
1183 let amount = Amount::from(11);
1184 let result = amount.split(&fee_and_amounts).unwrap();
1185 assert!(!result.is_empty());
1186 assert_eq!(Amount::try_sum(result.iter().copied()).unwrap(), amount);
1187
1188 let amount = Amount::from(255);
1189 let result = amount.split(&fee_and_amounts).unwrap();
1190 assert!(!result.is_empty());
1191 assert_eq!(Amount::try_sum(result.iter().copied()).unwrap(), amount);
1192
1193 let amount = Amount::from(7);
1194 let result = amount.split(&fee_and_amounts).unwrap();
1195 assert_eq!(
1196 result,
1197 vec![Amount::from(4), Amount::from(2), Amount::from(1)]
1198 );
1199 for r in &result {
1200 assert_ne!(*r, Amount::ZERO);
1201 }
1202 }
1203
1204 #[test]
1212 fn test_split_modulo_operation() {
1213 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
1214
1215 let amount = Amount::from(15);
1216 let result = amount.split(&fee_and_amounts).unwrap();
1217
1218 assert_eq!(
1219 result,
1220 vec![
1221 Amount::from(8),
1222 Amount::from(4),
1223 Amount::from(2),
1224 Amount::from(1)
1225 ]
1226 );
1227
1228 let total = Amount::try_sum(result.iter().copied()).unwrap();
1229 assert_eq!(total, amount);
1230 }
1231
1232 #[test]
1235 fn test_split_cannot_represent_amount() {
1236 let fee_and_amounts: FeeAndAmounts = (0, vec![32]).into();
1238
1239 let amount = Amount::from(100);
1241 let result = amount.split(&fee_and_amounts);
1242 assert!(result.is_err());
1243 match result {
1244 Err(Error::CannotSplitAmount(requested, got)) => {
1245 assert_eq!(requested, 100);
1246 assert_eq!(got, 32); }
1248 _ => panic!("Expected CannotSplitAmount error"),
1249 }
1250
1251 let amount = Amount::from(32);
1253 let result = amount.split(&fee_and_amounts);
1254 assert!(result.is_ok());
1255 assert_eq!(result.unwrap(), vec![Amount::from(32)]);
1256
1257 let fee_and_amounts: FeeAndAmounts = (0, vec![32, 64]).into();
1262 let amount = Amount::from(100);
1263 let result = amount.split(&fee_and_amounts);
1264 assert!(result.is_err());
1265 match result {
1266 Err(Error::CannotSplitAmount(requested, got)) => {
1267 assert_eq!(requested, 100);
1268 assert_eq!(got, 96);
1269 }
1270 _ => panic!("Expected CannotSplitAmount error"),
1271 }
1272 }
1273
1274 #[test]
1275 fn test_split_amount_exceeds_keyset_capacity() {
1276 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
1278
1279 let amount = Amount::from(2u64.pow(63));
1281 let result = amount.split(&fee_and_amounts);
1282
1283 assert!(result.is_err());
1284 match result {
1285 Err(Error::CannotSplitAmount(requested, got)) => {
1286 assert_eq!(requested, 2u64.pow(63));
1287 assert_eq!(got, 2u64.pow(31));
1290 }
1291 _ => panic!("Expected CannotSplitAmount error, got {:?}", result),
1292 }
1293 }
1294
1295 #[test]
1303 fn test_from_u64_returns_correct_value() {
1304 let amount = Amount::from(100u64);
1305 assert_eq!(amount, Amount::from(100));
1306 assert_ne!(amount, Amount::ZERO);
1307
1308 let amount = Amount::from(1u64);
1309 assert_eq!(amount, Amount::from(1));
1310 assert_eq!(amount, Amount::ONE);
1311
1312 let amount = Amount::from(1337u64);
1313 assert_eq!(amount.to_u64(), 1337);
1314 }
1315
1316 #[test]
1323 fn test_checked_mul_returns_correct_value() {
1324 let amount1 = Amount::from(10);
1325 let amount2 = Amount::from(5);
1326 let result = amount1.checked_mul(amount2);
1327 assert_eq!(result, Some(Amount::from(50)));
1328 assert_ne!(result, None);
1329 assert_ne!(result, Some(Amount::ZERO));
1330
1331 let amount1 = Amount::from(100);
1332 let amount2 = Amount::from(20);
1333 let result = amount1.checked_mul(amount2);
1334 assert_eq!(result, Some(Amount::from(2000)));
1335 assert_ne!(result, Some(Amount::ZERO));
1336
1337 let amount1 = Amount::from(7);
1338 let amount2 = Amount::from(13);
1339 let result = amount1.checked_mul(amount2);
1340 assert_eq!(result, Some(Amount::from(91)));
1341
1342 let amount1 = Amount::from(100);
1344 let amount2 = Amount::ZERO;
1345 let result = amount1.checked_mul(amount2);
1346 assert_eq!(result, Some(Amount::ZERO));
1347
1348 let amount1 = Amount::from(42);
1350 let amount2 = Amount::ONE;
1351 let result = amount1.checked_mul(amount2);
1352 assert_eq!(result, Some(Amount::from(42)));
1353
1354 let amount1 = Amount::from(u64::MAX);
1356 let amount2 = Amount::from(2);
1357 let result = amount1.checked_mul(amount2);
1358 assert!(result.is_none());
1359 }
1360
1361 #[test]
1368 fn test_checked_div_returns_correct_value() {
1369 let amount1 = Amount::from(100);
1370 let amount2 = Amount::from(5);
1371 let result = amount1.checked_div(amount2);
1372 assert_eq!(result, Some(Amount::from(20)));
1373 assert_ne!(result, None);
1374 assert_ne!(result, Some(Amount::ZERO));
1375
1376 let amount1 = Amount::from(1000);
1377 let amount2 = Amount::from(10);
1378 let result = amount1.checked_div(amount2);
1379 assert_eq!(result, Some(Amount::from(100)));
1380 assert_ne!(result, Some(Amount::ZERO));
1381
1382 let amount1 = Amount::from(91);
1383 let amount2 = Amount::from(7);
1384 let result = amount1.checked_div(amount2);
1385 assert_eq!(result, Some(Amount::from(13)));
1386
1387 let amount1 = Amount::from(42);
1389 let amount2 = Amount::ONE;
1390 let result = amount1.checked_div(amount2);
1391 assert_eq!(result, Some(Amount::from(42)));
1392
1393 let amount1 = Amount::from(10);
1395 let amount2 = Amount::from(3);
1396 let result = amount1.checked_div(amount2);
1397 assert_eq!(result, Some(Amount::from(3)));
1398
1399 let amount1 = Amount::from(100);
1401 let amount2 = Amount::ZERO;
1402 let result = amount1.checked_div(amount2);
1403 assert!(result.is_none());
1404 }
1405
1406 #[test]
1413 fn test_convert_unit_returns_correct_value() {
1414 let amount = Amount::from(1000);
1415 let result = amount
1416 .convert_unit(&CurrencyUnit::Sat, &CurrencyUnit::Msat)
1417 .unwrap();
1418 assert_eq!(result, Amount::from(1_000_000));
1419 assert_ne!(result, Amount::ZERO);
1420
1421 let amount = Amount::from(5000);
1422 let result = amount
1423 .convert_unit(&CurrencyUnit::Msat, &CurrencyUnit::Sat)
1424 .unwrap();
1425 assert_eq!(result, Amount::from(5));
1426 assert_ne!(result, Amount::ZERO);
1427
1428 let amount = Amount::from(123);
1429 let result = amount
1430 .convert_unit(&CurrencyUnit::Sat, &CurrencyUnit::Sat)
1431 .unwrap();
1432 assert_eq!(result, Amount::from(123));
1433
1434 let amount = Amount::from(456);
1435 let result = amount
1436 .convert_unit(&CurrencyUnit::Usd, &CurrencyUnit::Usd)
1437 .unwrap();
1438 assert_eq!(result, Amount::from(456));
1439
1440 let amount = Amount::from(789);
1441 let result = amount
1442 .convert_unit(&CurrencyUnit::Eur, &CurrencyUnit::Eur)
1443 .unwrap();
1444 assert_eq!(result, Amount::from(789));
1445
1446 let amount = Amount::from(100);
1448 let result = amount.convert_unit(&CurrencyUnit::Sat, &CurrencyUnit::Eur);
1449 assert!(result.is_err());
1450 }
1451
1452 #[test]
1461 fn test_amount_to_i64_returns_correct_value() {
1462 let amount = Amount::from(100);
1464 let result = amount.to_i64();
1465 assert_eq!(result, Some(100));
1466 assert!(result.is_some());
1467 assert_ne!(result, Some(0));
1468 assert_ne!(result, Some(1));
1469 assert_ne!(result, Some(-1));
1470
1471 let amount = Amount::from(1000);
1473 let result = amount.to_i64();
1474 assert_eq!(result, Some(1000));
1475 assert_ne!(result, None);
1476 assert_ne!(result, Some(0));
1477 assert_ne!(result, Some(1));
1478 assert_ne!(result, Some(-1));
1479
1480 let amount = Amount::from(2);
1482 let result = amount.to_i64();
1483 assert_eq!(result, Some(2));
1484 assert_ne!(result, Some(1));
1485
1486 let amount = Amount::from(i64::MAX as u64);
1489 let result = amount.to_i64();
1490 assert_eq!(result, Some(i64::MAX));
1491 assert!(result.is_some());
1492
1493 let amount = Amount::from(i64::MAX as u64 + 1);
1496 let result = amount.to_i64();
1497 assert!(result.is_none());
1498
1499 let amount = Amount::from(u64::MAX);
1501 let result = amount.to_i64();
1502 assert!(result.is_none());
1503
1504 let amount = Amount::from(0);
1506 let result = amount.to_i64();
1507 assert_eq!(result, Some(0));
1508
1509 let amount = Amount::from(1);
1511 let result = amount.to_i64();
1512 assert_eq!(result, Some(1));
1513 }
1514
1515 #[test]
1520 fn test_amount_to_i64_boundary() {
1521 let at_max = Amount::from(i64::MAX as u64);
1523 assert!(at_max.to_i64().is_some());
1524 assert_eq!(at_max.to_i64().unwrap(), i64::MAX);
1525
1526 let above_max = Amount::from(i64::MAX as u64 + 1);
1528 assert!(above_max.to_i64().is_none());
1529
1530 let below_max = Amount::from(i64::MAX as u64 - 1);
1532 assert!(below_max.to_i64().is_some());
1533 assert_eq!(below_max.to_i64().unwrap(), i64::MAX - 1);
1534 }
1535
1536 #[test]
1543 fn test_amount_from_i64() {
1544 let result = Amount::from_i64(100);
1546 assert!(result.is_some());
1547 assert_eq!(result.unwrap(), Amount::from(100));
1548 assert_ne!(result, None);
1549 assert_ne!(result, Some(Amount::ZERO));
1550
1551 let result = Amount::from_i64(0);
1554 assert!(result.is_some());
1555 assert_eq!(result.unwrap(), Amount::ZERO);
1556
1557 let result = Amount::from_i64(-1);
1559 assert!(result.is_none());
1560
1561 let result = Amount::from_i64(-100);
1562 assert!(result.is_none());
1563
1564 let result = Amount::from_i64(i64::MAX);
1566 assert!(result.is_some());
1567 assert_eq!(result.unwrap(), Amount::from(i64::MAX as u64));
1568 assert_ne!(result, Some(Amount::ZERO));
1569
1570 let result = Amount::from_i64(1);
1572 assert!(result.is_some());
1573 assert_eq!(result.unwrap(), Amount::ONE);
1574 assert_ne!(result, Some(Amount::ZERO));
1575 }
1576
1577 #[test]
1581 fn test_add_assign() {
1582 let mut amount = Amount::from(100);
1583 amount += Amount::from(50);
1584 assert_eq!(amount, Amount::from(150));
1585 assert_ne!(amount, Amount::from(100)); let mut amount = Amount::from(1);
1588 amount += Amount::from(1);
1589 assert_eq!(amount, Amount::from(2));
1590 assert_ne!(amount, Amount::ONE); let mut amount = Amount::ZERO;
1593 amount += Amount::from(42);
1594 assert_eq!(amount, Amount::from(42));
1595 assert_ne!(amount, Amount::ZERO); }
1597
1598 #[test]
1602 fn test_sub_assign() {
1603 let mut amount = Amount::from(100);
1604 amount -= Amount::from(30);
1605 assert_eq!(amount, Amount::from(70));
1606 assert_ne!(amount, Amount::from(100)); let mut amount = Amount::from(50);
1609 amount -= Amount::from(1);
1610 assert_eq!(amount, Amount::from(49));
1611 assert_ne!(amount, Amount::from(50)); let mut amount = Amount::from(10);
1614 amount -= Amount::from(10);
1615 assert_eq!(amount, Amount::ZERO);
1616 assert_ne!(amount, Amount::from(10)); }
1618
1619 #[test]
1622 fn test_amount_with_currency_unit() {
1623 let amount = Amount::new(1000, CurrencyUnit::Sat);
1624 assert_eq!(amount.value(), 1000);
1625 assert_eq!(amount.unit(), &CurrencyUnit::Sat);
1626 }
1627
1628 #[test]
1629 fn test_amount_new_with_custom_unit() {
1630 let custom_unit = CurrencyUnit::Custom("BTC".to_string());
1631 let amount = Amount::new(50, custom_unit.clone());
1632
1633 assert_eq!(amount.value(), 50);
1634 assert_eq!(amount.unit(), &custom_unit);
1635 }
1636
1637 #[test]
1638 fn test_amount_into_parts() {
1639 let amount = Amount::new(1234, CurrencyUnit::Msat);
1640 let (value, unit) = amount.into_parts();
1641
1642 assert_eq!(value, 1234);
1643 assert_eq!(unit, CurrencyUnit::Msat);
1644 }
1645
1646 #[test]
1647 fn test_amount_with_unit_conversion() {
1648 let untyped: Amount<()> = Amount::from(100);
1649 let typed = untyped.with_unit(CurrencyUnit::Sat);
1650
1651 assert_eq!(typed.value(), 100);
1652 assert_eq!(typed.unit(), &CurrencyUnit::Sat);
1653 }
1654
1655 #[test]
1656 fn test_amount_with_unit_all_variants() {
1657 let untyped = Amount::from(500);
1658
1659 let sat = untyped.with_unit(CurrencyUnit::Sat);
1660 assert_eq!(sat.unit(), &CurrencyUnit::Sat);
1661
1662 let msat = untyped.with_unit(CurrencyUnit::Msat);
1663 assert_eq!(msat.unit(), &CurrencyUnit::Msat);
1664
1665 let usd = untyped.with_unit(CurrencyUnit::Usd);
1666 assert_eq!(usd.unit(), &CurrencyUnit::Usd);
1667
1668 let eur = untyped.with_unit(CurrencyUnit::Eur);
1669 assert_eq!(eur.unit(), &CurrencyUnit::Eur);
1670
1671 let custom = untyped.with_unit(CurrencyUnit::Custom("TEST".into()));
1672 assert_eq!(custom.unit(), &CurrencyUnit::Custom("TEST".into()));
1673 }
1674
1675 #[test]
1676 fn test_typed_amount_is_clone_not_copy() {
1677 let amount = Amount::new(100, CurrencyUnit::Sat);
1678 let cloned = amount.clone();
1679 assert_eq!(cloned.value(), 100);
1681 assert_eq!(cloned.unit(), &CurrencyUnit::Sat);
1682 }
1683
1684 #[test]
1687 fn test_untyped_amount_is_copy() {
1688 let amount: Amount<()> = Amount::from(100);
1690 let copy1 = amount;
1691 let copy2 = amount; assert_eq!(copy1, copy2);
1693 }
1694
1695 #[test]
1696 fn test_amount_serialization_transparent() {
1697 let amount = Amount::from(1234);
1699 let json = serde_json::to_string(&amount).unwrap();
1700 assert_eq!(json, "1234");
1701
1702 let deserialized: Amount<()> = serde_json::from_str(&json).unwrap();
1704 assert_eq!(deserialized, Amount::from(1234));
1705 }
1706
1707 #[test]
1708 fn test_typed_amount_serialization() {
1709 let amount = Amount::new(5678, CurrencyUnit::Sat);
1711 let json = serde_json::to_string(&amount).unwrap();
1712 assert_eq!(json, "5678");
1713
1714 }
1717
1718 #[test]
1719 fn test_protocol_type_pattern() {
1720 let protocol_amount: Amount<()> = Amount::from(1000);
1724 let _copied = protocol_amount; let typed = protocol_amount.with_unit(CurrencyUnit::Sat);
1728 assert_eq!(typed.value(), 1000);
1729
1730 let back_to_protocol = Amount::from(typed.value());
1732 assert_eq!(back_to_protocol, protocol_amount);
1733 }
1734
1735 #[test]
1738 fn test_typed_amount_checked_add() {
1739 let a = Amount::new(100, CurrencyUnit::Sat);
1740 let b = Amount::new(50, CurrencyUnit::Sat);
1741
1742 let sum = a.checked_add(&b).unwrap();
1743 assert_eq!(sum.value(), 150);
1744 assert_eq!(sum.unit(), &CurrencyUnit::Sat);
1745 }
1746
1747 #[test]
1748 fn test_typed_amount_add_unit_mismatch() {
1749 let sat = Amount::new(100, CurrencyUnit::Sat);
1750 let msat = Amount::new(100, CurrencyUnit::Msat);
1751
1752 let result = sat.checked_add(&msat);
1753 assert!(result.is_err());
1754
1755 match result.unwrap_err() {
1756 Error::UnitMismatch(u1, u2) => {
1757 assert_eq!(u1, CurrencyUnit::Sat);
1758 assert_eq!(u2, CurrencyUnit::Msat);
1759 }
1760 _ => panic!("Expected UnitMismatch error"),
1761 }
1762 }
1763
1764 #[test]
1765 fn test_typed_amount_checked_sub() {
1766 let a = Amount::new(100, CurrencyUnit::Sat);
1767 let b = Amount::new(30, CurrencyUnit::Sat);
1768
1769 let diff = a.checked_sub(&b).unwrap();
1770 assert_eq!(diff.value(), 70);
1771 assert_eq!(diff.unit(), &CurrencyUnit::Sat);
1772 }
1773
1774 #[test]
1775 fn test_typed_amount_sub_unit_mismatch() {
1776 let sat = Amount::new(100, CurrencyUnit::Sat);
1777 let usd = Amount::new(30, CurrencyUnit::Usd);
1778
1779 let result = sat.checked_sub(&usd);
1780 assert!(result.is_err());
1781 }
1782
1783 #[test]
1784 fn test_typed_amount_convert_to() {
1785 let sat = Amount::new(1000, CurrencyUnit::Sat);
1787 let msat = sat.convert_to(&CurrencyUnit::Msat).unwrap();
1788 assert_eq!(msat.value(), 1_000_000);
1789 assert_eq!(msat.unit(), &CurrencyUnit::Msat);
1790
1791 let msat = Amount::new(5000, CurrencyUnit::Msat);
1793 let sat = msat.convert_to(&CurrencyUnit::Sat).unwrap();
1794 assert_eq!(sat.value(), 5);
1795 assert_eq!(sat.unit(), &CurrencyUnit::Sat);
1796
1797 let sat = Amount::new(100, CurrencyUnit::Sat);
1799 let same = sat.convert_to(&CurrencyUnit::Sat).unwrap();
1800 assert_eq!(same.value(), 100);
1801 assert_eq!(same.unit(), &CurrencyUnit::Sat);
1802 }
1803
1804 #[test]
1805 fn test_typed_amount_convert_invalid() {
1806 let sat = Amount::new(100, CurrencyUnit::Sat);
1807 let result = sat.convert_to(&CurrencyUnit::Eur);
1808 assert!(result.is_err());
1809
1810 match result.unwrap_err() {
1811 Error::CannotConvertUnits => {}
1812 _ => panic!("Expected CannotConvertUnits error"),
1813 }
1814 }
1815
1816 #[test]
1817 fn test_typed_amount_add_overflow() {
1818 let a = Amount::new(u64::MAX, CurrencyUnit::Sat);
1819 let b = Amount::new(1, CurrencyUnit::Sat);
1820
1821 let result = a.checked_add(&b);
1822 assert!(result.is_err());
1823
1824 match result.unwrap_err() {
1825 Error::AmountOverflow => {}
1826 _ => panic!("Expected AmountOverflow error"),
1827 }
1828 }
1829
1830 #[test]
1831 fn test_typed_amount_sub_underflow() {
1832 let a = Amount::new(50, CurrencyUnit::Sat);
1833 let b = Amount::new(100, CurrencyUnit::Sat);
1834
1835 let result = a.checked_sub(&b);
1836 assert!(result.is_err());
1837
1838 match result.unwrap_err() {
1839 Error::AmountOverflow => {} _ => panic!("Expected AmountOverflow error"),
1841 }
1842 }
1843
1844 #[test]
1848 fn test_typed_amount_equality_same_unit() {
1849 let a = Amount::new(100, CurrencyUnit::Sat);
1850 let b = Amount::new(100, CurrencyUnit::Sat);
1851
1852 assert_eq!(a, b);
1853 assert!(a == b);
1854
1855 let c = Amount::new(50, CurrencyUnit::Sat);
1856 assert_ne!(a, c);
1857 assert!(a != c);
1858 }
1859
1860 #[test]
1862 fn test_typed_amount_equality_different_units() {
1863 let sat = Amount::new(100, CurrencyUnit::Sat);
1864 let msat = Amount::new(100, CurrencyUnit::Msat);
1865
1866 assert_ne!(sat, msat);
1868 assert!(sat != msat);
1869
1870 let usd = Amount::new(100, CurrencyUnit::Usd);
1871 assert_ne!(sat, usd);
1872 assert_ne!(msat, usd);
1873 }
1874
1875 #[test]
1877 fn test_typed_amount_comparison_same_unit() {
1878 let small = Amount::new(50, CurrencyUnit::Sat);
1879 let large = Amount::new(100, CurrencyUnit::Sat);
1880
1881 assert!(large > small);
1883 assert!(!(small > large));
1884
1885 assert!(small < large);
1887 assert!(!(large < small));
1888
1889 assert!(large >= small);
1891 assert!(large >= Amount::new(100, CurrencyUnit::Sat));
1892
1893 assert!(small <= large);
1895 assert!(small <= Amount::new(50, CurrencyUnit::Sat));
1896
1897 assert_eq!(large.partial_cmp(&small), Some(std::cmp::Ordering::Greater));
1899 assert_eq!(small.partial_cmp(&large), Some(std::cmp::Ordering::Less));
1900 assert_eq!(
1901 small.partial_cmp(&Amount::new(50, CurrencyUnit::Sat)),
1902 Some(std::cmp::Ordering::Equal)
1903 );
1904 }
1905
1906 #[test]
1909 fn test_typed_amount_comparison_different_units_returns_none() {
1910 let sat = Amount::new(100, CurrencyUnit::Sat);
1911 let msat = Amount::new(50, CurrencyUnit::Msat);
1912
1913 assert_eq!(sat.partial_cmp(&msat), None);
1915 assert_eq!(msat.partial_cmp(&sat), None);
1916
1917 let usd = Amount::new(100, CurrencyUnit::Usd);
1919 assert_eq!(sat.partial_cmp(&usd), None);
1920 assert_eq!(usd.partial_cmp(&sat), None);
1921
1922 let eur = Amount::new(100, CurrencyUnit::Eur);
1923 assert_eq!(usd.partial_cmp(&eur), None);
1924
1925 let custom = Amount::new(100, CurrencyUnit::Custom("BTC".into()));
1926 assert_eq!(sat.partial_cmp(&custom), None);
1927 }
1928
1929 #[test]
1932 fn test_typed_amount_comparison_operators_different_units() {
1933 let sat = Amount::new(100, CurrencyUnit::Sat);
1934 let msat = Amount::new(50, CurrencyUnit::Msat);
1935
1936 assert!(!(sat > msat));
1943 assert!(!(sat < msat));
1944 assert!(!(sat >= msat));
1945 assert!(!(sat <= msat));
1946
1947 assert!(!(msat > sat));
1948 assert!(!(msat < sat));
1949 assert!(!(msat >= sat));
1950 assert!(!(msat <= sat));
1951
1952 let sat100 = Amount::new(100, CurrencyUnit::Sat);
1954 let msat100 = Amount::new(100, CurrencyUnit::Msat);
1955
1956 assert!(!(sat100 > msat100));
1957 assert!(!(sat100 < msat100));
1958 assert!(!(sat100 >= msat100));
1959 assert!(!(sat100 <= msat100));
1960 }
1961
1962 #[test]
1964 fn test_untyped_amount_has_total_ordering() {
1965 use std::cmp::Ordering;
1966
1967 let a: Amount<()> = Amount::from(50);
1968 let b: Amount<()> = Amount::from(100);
1969 let c: Amount<()> = Amount::from(50);
1970
1971 assert_eq!(a.cmp(&b), Ordering::Less);
1973 assert_eq!(b.cmp(&a), Ordering::Greater);
1974 assert_eq!(a.cmp(&c), Ordering::Equal);
1975
1976 assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
1978 assert_eq!(b.partial_cmp(&a), Some(Ordering::Greater));
1979 assert_eq!(a.partial_cmp(&c), Some(Ordering::Equal));
1980 }
1981
1982 #[test]
1984 fn test_untyped_amount_sorting() {
1985 let mut amounts: Vec<Amount<()>> = vec![
1986 Amount::from(100),
1987 Amount::from(25),
1988 Amount::from(75),
1989 Amount::from(50),
1990 ];
1991
1992 amounts.sort();
1993
1994 assert_eq!(
1995 amounts,
1996 vec![
1997 Amount::from(25),
1998 Amount::from(50),
1999 Amount::from(75),
2000 Amount::from(100),
2001 ]
2002 );
2003 }
2004
2005 #[test]
2006 fn test_amount_currency_unit_to_i64() {
2007 let amount = Amount::new(100, CurrencyUnit::Sat);
2008 assert_eq!(amount.to_i64(), Some(100));
2009
2010 let amount = Amount::new(i64::MAX as u64, CurrencyUnit::Sat);
2011 assert_eq!(amount.to_i64(), Some(i64::MAX));
2012
2013 let amount = Amount::new(i64::MAX as u64 + 1, CurrencyUnit::Sat);
2014 assert_eq!(amount.to_i64(), None);
2015
2016 let amount = Amount::new(0, CurrencyUnit::Sat);
2017 assert_eq!(amount.to_i64(), Some(0));
2018
2019 let amount = Amount::new(1, CurrencyUnit::Sat);
2020 assert_eq!(amount.to_i64(), Some(1));
2021 }
2022
2023 #[test]
2024 fn test_display_with_unit() {
2025 let amount = Amount::new(100, CurrencyUnit::Sat);
2026 assert_eq!(amount.display_with_unit(), "100 sat");
2027
2028 let amount = Amount::new(50, CurrencyUnit::Msat);
2029 assert_eq!(amount.display_with_unit(), "50 msat");
2030
2031 let amount = Amount::new(100, CurrencyUnit::Usd);
2032 assert_eq!(amount.display_with_unit(), "100 usd");
2033
2034 let amount = Amount::new(123, CurrencyUnit::Custom("BTC".to_string()));
2035 assert_eq!(amount.display_with_unit(), "123 btc");
2036 }
2037
2038 #[test]
2039 fn test_amount_add_operator() {
2040 let a = Amount::from(100);
2041 let b = Amount::from(50);
2042 let sum = a + b;
2043 assert_eq!(sum, Amount::from(150));
2044 assert_ne!(sum, Amount::ZERO);
2045 }
2046
2047 #[test]
2058 fn test_saturating_sub_normal_case() {
2059 let amount1 = Amount::from(100);
2061 let amount2 = Amount::from(30);
2062 let result = amount1.saturating_sub(amount2);
2063 assert_eq!(result, Amount::from(70));
2064 assert_ne!(result, Amount::ZERO);
2065
2066 let amount1 = Amount::from(1000);
2068 let amount2 = Amount::from(1);
2069 let result = amount1.saturating_sub(amount2);
2070 assert_eq!(result, Amount::from(999));
2071
2072 let amount1 = Amount::from(2);
2074 let amount2 = Amount::from(1);
2075 let result = amount1.saturating_sub(amount2);
2076 assert_eq!(result, Amount::from(1));
2077 assert_ne!(result, Amount::ZERO);
2078 }
2079
2080 #[test]
2085 fn test_saturating_sub_saturates_at_zero() {
2086 let amount1 = Amount::from(30);
2088 let amount2 = Amount::from(100);
2089 let result = amount1.saturating_sub(amount2);
2090 assert_eq!(result, Amount::ZERO);
2091 assert_ne!(result, Amount::from(30)); let amount1 = Amount::from(5);
2095 let amount2 = Amount::from(10);
2096 let result = amount1.saturating_sub(amount2);
2097 assert_eq!(result, Amount::ZERO);
2098
2099 let amount1 = Amount::ZERO;
2101 let amount2 = Amount::from(1);
2102 let result = amount1.saturating_sub(amount2);
2103 assert_eq!(result, Amount::ZERO);
2104 }
2105
2106 #[test]
2110 fn test_saturating_sub_equal_amounts() {
2111 let amount1 = Amount::from(100);
2113 let amount2 = Amount::from(100);
2114 let result = amount1.saturating_sub(amount2);
2115 assert_eq!(result, Amount::ZERO);
2116
2117 let amount1 = Amount::from(1);
2119 let amount2 = Amount::from(1);
2120 let result = amount1.saturating_sub(amount2);
2121 assert_eq!(result, Amount::ZERO);
2122
2123 let result = Amount::ZERO.saturating_sub(Amount::ZERO);
2125 assert_eq!(result, Amount::ZERO);
2126 }
2127}