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("Invalid Amount: {0}")]
31 InvalidAmount(String),
32 #[error("Amount undefined")]
34 AmountUndefined,
35 #[error(transparent)]
37 Utf8ParseError(#[from] std::string::FromUtf8Error),
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
42#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
43#[serde(transparent)]
44pub struct Amount(u64);
45
46#[derive(Debug, Clone)]
49pub struct FeeAndAmounts {
50 fee: u64,
51 amounts: Vec<u64>,
52}
53
54impl From<(u64, Vec<u64>)> for FeeAndAmounts {
55 fn from(value: (u64, Vec<u64>)) -> Self {
56 Self {
57 fee: value.0,
58 amounts: value.1,
59 }
60 }
61}
62
63impl FeeAndAmounts {
64 #[inline(always)]
66 pub fn fee(&self) -> u64 {
67 self.fee
68 }
69
70 #[inline(always)]
72 pub fn amounts(&self) -> &[u64] {
73 &self.amounts
74 }
75}
76
77pub type KeysetFeeAndAmounts = HashMap<Id, FeeAndAmounts>;
79
80impl FromStr for Amount {
81 type Err = Error;
82
83 fn from_str(s: &str) -> Result<Self, Self::Err> {
84 let value = s
85 .parse::<u64>()
86 .map_err(|_| Error::InvalidAmount(s.to_owned()))?;
87 Ok(Amount(value))
88 }
89}
90
91impl Amount {
92 pub const ZERO: Amount = Amount(0);
94
95 pub const ONE: Amount = Amount(1);
97
98 pub fn split(&self, fee_and_amounts: &FeeAndAmounts) -> Vec<Self> {
100 fee_and_amounts
101 .amounts
102 .iter()
103 .rev()
104 .fold((Vec::new(), self.0), |(mut acc, total), &amount| {
105 if total >= amount {
106 acc.push(Self::from(amount));
107 }
108 (acc, total % amount)
109 })
110 .0
111 }
112
113 pub fn split_targeted(
115 &self,
116 target: &SplitTarget,
117 fee_and_amounts: &FeeAndAmounts,
118 ) -> Result<Vec<Self>, Error> {
119 let mut parts = match target {
120 SplitTarget::None => self.split(fee_and_amounts),
121 SplitTarget::Value(amount) => {
122 if self.le(amount) {
123 return Ok(self.split(fee_and_amounts));
124 }
125
126 let mut parts_total = Amount::ZERO;
127 let mut parts = Vec::new();
128
129 let parts_of_value = amount.split(fee_and_amounts);
131
132 while parts_total.lt(self) {
133 for part in parts_of_value.iter().copied() {
134 if (part + parts_total).le(self) {
135 parts.push(part);
136 } else {
137 let amount_left = *self - parts_total;
138 parts.extend(amount_left.split(fee_and_amounts));
139 }
140
141 parts_total = Amount::try_sum(parts.clone().iter().copied())?;
142
143 if parts_total.eq(self) {
144 break;
145 }
146 }
147 }
148
149 parts
150 }
151 SplitTarget::Values(values) => {
152 let values_total: Amount = Amount::try_sum(values.clone().into_iter())?;
153
154 match self.cmp(&values_total) {
155 Ordering::Equal => values.clone(),
156 Ordering::Less => {
157 return Err(Error::SplitValuesGreater);
158 }
159 Ordering::Greater => {
160 let extra = *self - values_total;
161 let mut extra_amount = extra.split(fee_and_amounts);
162 let mut values = values.clone();
163
164 values.append(&mut extra_amount);
165 values
166 }
167 }
168 }
169 };
170
171 parts.sort();
172 Ok(parts)
173 }
174
175 pub fn split_with_fee(&self, fee_and_amounts: &FeeAndAmounts) -> Result<Vec<Self>, Error> {
177 let without_fee_amounts = self.split(fee_and_amounts);
178 let total_fee_ppk = fee_and_amounts
179 .fee
180 .checked_mul(without_fee_amounts.len() as u64)
181 .ok_or(Error::AmountOverflow)?;
182 let fee = Amount::from(total_fee_ppk.div_ceil(1000));
183 let new_amount = self.checked_add(fee).ok_or(Error::AmountOverflow)?;
184
185 let split = new_amount.split(fee_and_amounts);
186 let split_fee_ppk = (split.len() as u64)
187 .checked_mul(fee_and_amounts.fee)
188 .ok_or(Error::AmountOverflow)?;
189 let split_fee = Amount::from(split_fee_ppk.div_ceil(1000));
190
191 if let Some(net_amount) = new_amount.checked_sub(split_fee) {
192 if net_amount >= *self {
193 return Ok(split);
194 }
195 }
196 self.checked_add(Amount::ONE)
197 .ok_or(Error::AmountOverflow)?
198 .split_with_fee(fee_and_amounts)
199 }
200
201 pub fn checked_add(self, other: Amount) -> Option<Amount> {
203 self.0.checked_add(other.0).map(Amount)
204 }
205
206 pub fn checked_sub(self, other: Amount) -> Option<Amount> {
208 self.0.checked_sub(other.0).map(Amount)
209 }
210
211 pub fn checked_mul(self, other: Amount) -> Option<Amount> {
213 self.0.checked_mul(other.0).map(Amount)
214 }
215
216 pub fn checked_div(self, other: Amount) -> Option<Amount> {
218 self.0.checked_div(other.0).map(Amount)
219 }
220
221 pub fn try_sum<I>(iter: I) -> Result<Self, Error>
223 where
224 I: IntoIterator<Item = Self>,
225 {
226 iter.into_iter().try_fold(Amount::ZERO, |acc, x| {
227 acc.checked_add(x).ok_or(Error::AmountOverflow)
228 })
229 }
230
231 pub fn convert_unit(
233 &self,
234 current_unit: &CurrencyUnit,
235 target_unit: &CurrencyUnit,
236 ) -> Result<Amount, Error> {
237 to_unit(self.0, current_unit, target_unit)
238 }
239 pub fn to_u64(self) -> u64 {
242 self.0
243 }
244
245 pub fn to_i64(self) -> Option<i64> {
247 if self.0 <= i64::MAX as u64 {
248 Some(self.0 as i64)
249 } else {
250 None
251 }
252 }
253
254 pub fn from_i64(value: i64) -> Option<Self> {
256 if value >= 0 {
257 Some(Amount(value as u64))
258 } else {
259 None
260 }
261 }
262}
263
264impl Default for Amount {
265 fn default() -> Self {
266 Amount::ZERO
267 }
268}
269
270impl Default for &Amount {
271 fn default() -> Self {
272 &Amount::ZERO
273 }
274}
275
276impl fmt::Display for Amount {
277 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278 if let Some(width) = f.width() {
279 write!(f, "{:width$}", self.0, width = width)
280 } else {
281 write!(f, "{}", self.0)
282 }
283 }
284}
285
286impl From<u64> for Amount {
287 fn from(value: u64) -> Self {
288 Self(value)
289 }
290}
291
292impl From<&u64> for Amount {
293 fn from(value: &u64) -> Self {
294 Self(*value)
295 }
296}
297
298impl From<Amount> for u64 {
299 fn from(value: Amount) -> Self {
300 value.0
301 }
302}
303
304impl AsRef<u64> for Amount {
305 fn as_ref(&self) -> &u64 {
306 &self.0
307 }
308}
309
310impl std::ops::Add for Amount {
311 type Output = Amount;
312
313 fn add(self, rhs: Amount) -> Self::Output {
314 self.checked_add(rhs)
315 .expect("Addition overflow: the sum of the amounts exceeds the maximum value")
316 }
317}
318
319impl std::ops::AddAssign for Amount {
320 fn add_assign(&mut self, rhs: Self) {
321 *self = self
322 .checked_add(rhs)
323 .expect("AddAssign overflow: the sum of the amounts exceeds the maximum value");
324 }
325}
326
327impl std::ops::Sub for Amount {
328 type Output = Amount;
329
330 fn sub(self, rhs: Amount) -> Self::Output {
331 self.checked_sub(rhs)
332 .expect("Subtraction underflow: cannot subtract a larger amount from a smaller amount")
333 }
334}
335
336impl std::ops::SubAssign for Amount {
337 fn sub_assign(&mut self, other: Self) {
338 *self = self
339 .checked_sub(other)
340 .expect("SubAssign underflow: cannot subtract a larger amount from a smaller amount");
341 }
342}
343
344impl std::ops::Mul for Amount {
345 type Output = Self;
346
347 fn mul(self, other: Self) -> Self::Output {
348 self.checked_mul(other)
349 .expect("Multiplication overflow: the product of the amounts exceeds the maximum value")
350 }
351}
352
353impl std::ops::Div for Amount {
354 type Output = Self;
355
356 fn div(self, other: Self) -> Self::Output {
357 self.checked_div(other)
358 .expect("Division error: cannot divide by zero or overflow occurred")
359 }
360}
361
362pub fn amount_for_offer(offer: &Offer, unit: &CurrencyUnit) -> Result<Amount, Error> {
364 let offer_amount = offer.amount().ok_or(Error::AmountUndefined)?;
365
366 let (amount, currency) = match offer_amount {
367 lightning::offers::offer::Amount::Bitcoin { amount_msats } => {
368 (amount_msats, CurrencyUnit::Msat)
369 }
370 lightning::offers::offer::Amount::Currency {
371 iso4217_code,
372 amount,
373 } => (
374 amount,
375 CurrencyUnit::from_str(&String::from_utf8(iso4217_code.to_vec())?)
376 .map_err(|_| Error::CannotConvertUnits)?,
377 ),
378 };
379
380 to_unit(amount, ¤cy, unit).map_err(|_err| Error::CannotConvertUnits)
381}
382
383#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)]
385pub enum SplitTarget {
386 #[default]
388 None,
389 Value(Amount),
391 Values(Vec<Amount>),
393}
394
395pub const MSAT_IN_SAT: u64 = 1000;
397
398pub fn to_unit<T>(
400 amount: T,
401 current_unit: &CurrencyUnit,
402 target_unit: &CurrencyUnit,
403) -> Result<Amount, Error>
404where
405 T: Into<u64>,
406{
407 let amount = amount.into();
408 match (current_unit, target_unit) {
409 (CurrencyUnit::Sat, CurrencyUnit::Sat) => Ok(amount.into()),
410 (CurrencyUnit::Msat, CurrencyUnit::Msat) => Ok(amount.into()),
411 (CurrencyUnit::Sat, CurrencyUnit::Msat) => amount
412 .checked_mul(MSAT_IN_SAT)
413 .map(Amount::from)
414 .ok_or(Error::AmountOverflow),
415 (CurrencyUnit::Msat, CurrencyUnit::Sat) => Ok((amount / MSAT_IN_SAT).into()),
416 (CurrencyUnit::Usd, CurrencyUnit::Usd) => Ok(amount.into()),
417 (CurrencyUnit::Eur, CurrencyUnit::Eur) => Ok(amount.into()),
418 _ => Err(Error::CannotConvertUnits),
419 }
420}
421
422#[cfg(test)]
423mod tests {
424 use super::*;
425
426 #[test]
427 fn test_split_amount() {
428 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
429
430 assert_eq!(
431 Amount::from(1).split(&fee_and_amounts),
432 vec![Amount::from(1)]
433 );
434 assert_eq!(
435 Amount::from(2).split(&fee_and_amounts),
436 vec![Amount::from(2)]
437 );
438 assert_eq!(
439 Amount::from(3).split(&fee_and_amounts),
440 vec![Amount::from(2), Amount::from(1)]
441 );
442 let amounts: Vec<Amount> = [8, 2, 1].iter().map(|a| Amount::from(*a)).collect();
443 assert_eq!(Amount::from(11).split(&fee_and_amounts), amounts);
444 let amounts: Vec<Amount> = [128, 64, 32, 16, 8, 4, 2, 1]
445 .iter()
446 .map(|a| Amount::from(*a))
447 .collect();
448 assert_eq!(Amount::from(255).split(&fee_and_amounts), amounts);
449 }
450
451 #[test]
452 fn test_split_target_amount() {
453 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
454 let amount = Amount(65);
455
456 let split = amount
457 .split_targeted(&SplitTarget::Value(Amount(32)), &fee_and_amounts)
458 .unwrap();
459 assert_eq!(vec![Amount(1), Amount(32), Amount(32)], split);
460
461 let amount = Amount(150);
462
463 let split = amount
464 .split_targeted(&SplitTarget::Value(Amount::from(50)), &fee_and_amounts)
465 .unwrap();
466 assert_eq!(
467 vec![
468 Amount(2),
469 Amount(2),
470 Amount(2),
471 Amount(16),
472 Amount(16),
473 Amount(16),
474 Amount(32),
475 Amount(32),
476 Amount(32)
477 ],
478 split
479 );
480
481 let amount = Amount::from(63);
482
483 let split = amount
484 .split_targeted(&SplitTarget::Value(Amount::from(32)), &fee_and_amounts)
485 .unwrap();
486 assert_eq!(
487 vec![
488 Amount(1),
489 Amount(2),
490 Amount(4),
491 Amount(8),
492 Amount(16),
493 Amount(32)
494 ],
495 split
496 );
497 }
498
499 #[test]
500 fn test_split_with_fee() {
501 let fee_and_amounts = (1, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
502 let amount = Amount(2);
503
504 let split = amount.split_with_fee(&fee_and_amounts).unwrap();
505 assert_eq!(split, vec![Amount(2), Amount(1)]);
506
507 let amount = Amount(3);
508
509 let split = amount.split_with_fee(&fee_and_amounts).unwrap();
510 assert_eq!(split, vec![Amount(4)]);
511
512 let amount = Amount(3);
513 let fee_and_amounts = (1000, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
514
515 let split = amount.split_with_fee(&fee_and_amounts).unwrap();
516 assert_eq!(split, vec![Amount(4), Amount(1)]);
519 }
520
521 #[test]
522 fn test_split_with_fee_reported_issue() {
523 let fee_and_amounts = (100, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
524 let amount = Amount(300);
526
527 let split = amount.split_with_fee(&fee_and_amounts).unwrap();
528
529 let total_fee_ppk = (split.len() as u64) * fee_and_amounts.fee;
531 let total_fee = Amount::from(total_fee_ppk.div_ceil(1000));
532
533 let split_total = Amount::try_sum(split.iter().copied()).unwrap();
535 assert!(
536 split_total >= amount + total_fee,
537 "Split total {} should be >= amount {} + fee {}",
538 split_total,
539 amount,
540 total_fee
541 );
542 }
543
544 #[test]
545 fn test_split_with_fee_edge_cases() {
546 let test_cases = vec![
548 (Amount(1), 100),
549 (Amount(10), 100),
550 (Amount(50), 100),
551 (Amount(100), 100),
552 (Amount(200), 100),
553 (Amount(300), 100),
554 (Amount(500), 100),
555 (Amount(600), 100),
556 (Amount(1000), 100),
557 (Amount(1337), 100),
558 (Amount(5000), 100),
559 ];
560
561 for (amount, fee_ppk) in test_cases {
562 let fee_and_amounts =
563 (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
564 let result = amount.split_with_fee(&fee_and_amounts);
565 assert!(
566 result.is_ok(),
567 "split_with_fee failed for amount {} with fee_ppk {}: {:?}",
568 amount,
569 fee_ppk,
570 result.err()
571 );
572
573 let split = result.unwrap();
574
575 let split_total = Amount::try_sum(split.iter().copied()).unwrap();
577 let fee_for_split = (split.len() as u64) * fee_ppk;
578 let total_fee = Amount::from(fee_for_split.div_ceil(1000));
579
580 let net_amount = split_total.checked_sub(total_fee);
582 assert!(
583 net_amount.is_some(),
584 "Net amount calculation failed for amount {} with fee_ppk {}",
585 amount,
586 fee_ppk
587 );
588 assert!(
589 net_amount.unwrap() >= amount,
590 "Net amount {} is less than required {} for amount {} with fee_ppk {}",
591 net_amount.unwrap(),
592 amount,
593 amount,
594 fee_ppk
595 );
596 }
597 }
598
599 #[test]
600 fn test_split_with_fee_high_fees() {
601 let test_cases = vec![
603 (Amount(10), 500), (Amount(10), 1000), (Amount(10), 2000), (Amount(100), 500),
607 (Amount(100), 1000),
608 (Amount(100), 2000),
609 ];
610
611 for (amount, fee_ppk) in test_cases {
612 let fee_and_amounts =
613 (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
614 let result = amount.split_with_fee(&fee_and_amounts);
615 assert!(
616 result.is_ok(),
617 "split_with_fee failed for amount {} with fee_ppk {}: {:?}",
618 amount,
619 fee_ppk,
620 result.err()
621 );
622
623 let split = result.unwrap();
624 let split_total = Amount::try_sum(split.iter().copied()).unwrap();
625
626 assert!(
628 split_total > amount,
629 "Split total {} should be greater than amount {} for fee_ppk {}",
630 split_total,
631 amount,
632 fee_ppk
633 );
634 }
635 }
636
637 #[test]
638 fn test_split_with_fee_recursion_limit() {
639 let amount = Amount(1);
642 let fee_ppk = 10000;
643 let fee_and_amounts = (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
644
645 let result = amount.split_with_fee(&fee_and_amounts);
646 assert!(
647 result.is_ok(),
648 "split_with_fee should handle extreme fees without infinite recursion"
649 );
650 }
651
652 #[test]
653 fn test_split_values() {
654 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
655 let amount = Amount(10);
656
657 let target = vec![Amount(2), Amount(4), Amount(4)];
658
659 let split_target = SplitTarget::Values(target.clone());
660
661 let values = amount
662 .split_targeted(&split_target, &fee_and_amounts)
663 .unwrap();
664
665 assert_eq!(target, values);
666
667 let target = vec![Amount(2), Amount(4), Amount(4)];
668
669 let split_target = SplitTarget::Values(vec![Amount(2), Amount(4)]);
670
671 let values = amount
672 .split_targeted(&split_target, &fee_and_amounts)
673 .unwrap();
674
675 assert_eq!(target, values);
676
677 let split_target = SplitTarget::Values(vec![Amount(2), Amount(10)]);
678
679 let values = amount.split_targeted(&split_target, &fee_and_amounts);
680
681 assert!(values.is_err())
682 }
683
684 #[test]
685 #[should_panic]
686 fn test_amount_addition() {
687 let amount_one: Amount = u64::MAX.into();
688 let amount_two: Amount = 1.into();
689
690 let amounts = vec![amount_one, amount_two];
691
692 let _total: Amount = Amount::try_sum(amounts).unwrap();
693 }
694
695 #[test]
696 fn test_try_amount_addition() {
697 let amount_one: Amount = u64::MAX.into();
698 let amount_two: Amount = 1.into();
699
700 let amounts = vec![amount_one, amount_two];
701
702 let total = Amount::try_sum(amounts);
703
704 assert!(total.is_err());
705 let amount_one: Amount = 10000.into();
706 let amount_two: Amount = 1.into();
707
708 let amounts = vec![amount_one, amount_two];
709 let total = Amount::try_sum(amounts).unwrap();
710
711 assert_eq!(total, 10001.into());
712 }
713
714 #[test]
715 fn test_amount_to_unit() {
716 let amount = Amount::from(1000);
717 let current_unit = CurrencyUnit::Sat;
718 let target_unit = CurrencyUnit::Msat;
719
720 let converted = to_unit(amount, ¤t_unit, &target_unit).unwrap();
721
722 assert_eq!(converted, 1000000.into());
723
724 let amount = Amount::from(1000);
725 let current_unit = CurrencyUnit::Msat;
726 let target_unit = CurrencyUnit::Sat;
727
728 let converted = to_unit(amount, ¤t_unit, &target_unit).unwrap();
729
730 assert_eq!(converted, 1.into());
731
732 let amount = Amount::from(1);
733 let current_unit = CurrencyUnit::Usd;
734 let target_unit = CurrencyUnit::Usd;
735
736 let converted = to_unit(amount, ¤t_unit, &target_unit).unwrap();
737
738 assert_eq!(converted, 1.into());
739
740 let amount = Amount::from(1);
741 let current_unit = CurrencyUnit::Eur;
742 let target_unit = CurrencyUnit::Eur;
743
744 let converted = to_unit(amount, ¤t_unit, &target_unit).unwrap();
745
746 assert_eq!(converted, 1.into());
747
748 let amount = Amount::from(1);
749 let current_unit = CurrencyUnit::Sat;
750 let target_unit = CurrencyUnit::Eur;
751
752 let converted = to_unit(amount, ¤t_unit, &target_unit);
753
754 assert!(converted.is_err());
755 }
756
757 #[test]
768 fn test_amount_sub_operator() {
769 let amount1 = Amount::from(100);
770 let amount2 = Amount::from(30);
771
772 let result = amount1 - amount2;
773 assert_eq!(result, Amount::from(70));
774
775 let amount1 = Amount::from(1000);
776 let amount2 = Amount::from(1);
777
778 let result = amount1 - amount2;
779 assert_eq!(result, Amount::from(999));
780
781 let amount1 = Amount::from(255);
782 let amount2 = Amount::from(128);
783
784 let result = amount1 - amount2;
785 assert_eq!(result, Amount::from(127));
786 }
787
788 #[test]
798 #[should_panic(expected = "Subtraction underflow")]
799 fn test_amount_sub_underflow() {
800 let amount1 = Amount::from(30);
801 let amount2 = Amount::from(100);
802
803 let _result = amount1 - amount2;
804 }
805
806 #[test]
816 fn test_checked_add_returns_correct_value() {
817 let amount1 = Amount::from(100);
818 let amount2 = Amount::from(50);
819
820 let result = amount1.checked_add(amount2);
821 assert_eq!(result, Some(Amount::from(150)));
822
823 let amount1 = Amount::from(1);
824 let amount2 = Amount::from(1);
825
826 let result = amount1.checked_add(amount2);
827 assert_eq!(result, Some(Amount::from(2)));
828 assert_ne!(result, Some(Amount::ZERO));
829
830 let amount1 = Amount::from(1000);
831 let amount2 = Amount::from(337);
832
833 let result = amount1.checked_add(amount2);
834 assert_eq!(result, Some(Amount::from(1337)));
835 }
836
837 #[test]
839 fn test_checked_add_overflow() {
840 let amount1 = Amount::from(u64::MAX);
841 let amount2 = Amount::from(1);
842
843 let result = amount1.checked_add(amount2);
844 assert!(result.is_none());
845 }
846
847 #[test]
856 fn test_try_sum_returns_correct_value() {
857 let amounts = vec![Amount::from(10), Amount::from(20), Amount::from(30)];
858 let result = Amount::try_sum(amounts).unwrap();
859 assert_eq!(result, Amount::from(60));
860 assert_ne!(result, Amount::ZERO);
861
862 let amounts = vec![Amount::from(1), Amount::from(1), Amount::from(1)];
863 let result = Amount::try_sum(amounts).unwrap();
864 assert_eq!(result, Amount::from(3));
865
866 let amounts = vec![Amount::from(100)];
867 let result = Amount::try_sum(amounts).unwrap();
868 assert_eq!(result, Amount::from(100));
869
870 let empty: Vec<Amount> = vec![];
871 let result = Amount::try_sum(empty).unwrap();
872 assert_eq!(result, Amount::ZERO);
873 }
874
875 #[test]
877 fn test_try_sum_overflow() {
878 let amounts = vec![Amount::from(u64::MAX), Amount::from(1)];
879 let result = Amount::try_sum(amounts);
880 assert!(result.is_err());
881 }
882
883 #[test]
893 fn test_split_returns_correct_values() {
894 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
895
896 let amount = Amount::from(11);
897 let result = amount.split(&fee_and_amounts);
898 assert!(!result.is_empty());
899 assert_eq!(Amount::try_sum(result.iter().copied()).unwrap(), amount);
900
901 let amount = Amount::from(255);
902 let result = amount.split(&fee_and_amounts);
903 assert!(!result.is_empty());
904 assert_eq!(Amount::try_sum(result.iter().copied()).unwrap(), amount);
905
906 let amount = Amount::from(7);
907 let result = amount.split(&fee_and_amounts);
908 assert_eq!(
909 result,
910 vec![Amount::from(4), Amount::from(2), Amount::from(1)]
911 );
912 for r in &result {
913 assert_ne!(*r, Amount::ZERO);
914 }
915 }
916
917 #[test]
925 fn test_split_modulo_operation() {
926 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
927
928 let amount = Amount::from(15);
929 let result = amount.split(&fee_and_amounts);
930
931 assert_eq!(
932 result,
933 vec![
934 Amount::from(8),
935 Amount::from(4),
936 Amount::from(2),
937 Amount::from(1)
938 ]
939 );
940
941 let total = Amount::try_sum(result.iter().copied()).unwrap();
942 assert_eq!(total, amount);
943 }
944
945 #[test]
953 fn test_from_u64_returns_correct_value() {
954 let amount = Amount::from(100u64);
955 assert_eq!(amount, Amount(100));
956 assert_ne!(amount, Amount::ZERO);
957
958 let amount = Amount::from(1u64);
959 assert_eq!(amount, Amount(1));
960 assert_eq!(amount, Amount::ONE);
961
962 let amount = Amount::from(1337u64);
963 assert_eq!(amount.to_u64(), 1337);
964 }
965
966 #[test]
973 fn test_checked_mul_returns_correct_value() {
974 let amount1 = Amount::from(10);
975 let amount2 = Amount::from(5);
976 let result = amount1.checked_mul(amount2);
977 assert_eq!(result, Some(Amount::from(50)));
978 assert_ne!(result, None);
979 assert_ne!(result, Some(Amount::ZERO));
980
981 let amount1 = Amount::from(100);
982 let amount2 = Amount::from(20);
983 let result = amount1.checked_mul(amount2);
984 assert_eq!(result, Some(Amount::from(2000)));
985 assert_ne!(result, Some(Amount::ZERO));
986
987 let amount1 = Amount::from(7);
988 let amount2 = Amount::from(13);
989 let result = amount1.checked_mul(amount2);
990 assert_eq!(result, Some(Amount::from(91)));
991
992 let amount1 = Amount::from(100);
994 let amount2 = Amount::ZERO;
995 let result = amount1.checked_mul(amount2);
996 assert_eq!(result, Some(Amount::ZERO));
997
998 let amount1 = Amount::from(42);
1000 let amount2 = Amount::ONE;
1001 let result = amount1.checked_mul(amount2);
1002 assert_eq!(result, Some(Amount::from(42)));
1003
1004 let amount1 = Amount::from(u64::MAX);
1006 let amount2 = Amount::from(2);
1007 let result = amount1.checked_mul(amount2);
1008 assert!(result.is_none());
1009 }
1010
1011 #[test]
1018 fn test_checked_div_returns_correct_value() {
1019 let amount1 = Amount::from(100);
1020 let amount2 = Amount::from(5);
1021 let result = amount1.checked_div(amount2);
1022 assert_eq!(result, Some(Amount::from(20)));
1023 assert_ne!(result, None);
1024 assert_ne!(result, Some(Amount::ZERO));
1025
1026 let amount1 = Amount::from(1000);
1027 let amount2 = Amount::from(10);
1028 let result = amount1.checked_div(amount2);
1029 assert_eq!(result, Some(Amount::from(100)));
1030 assert_ne!(result, Some(Amount::ZERO));
1031
1032 let amount1 = Amount::from(91);
1033 let amount2 = Amount::from(7);
1034 let result = amount1.checked_div(amount2);
1035 assert_eq!(result, Some(Amount::from(13)));
1036
1037 let amount1 = Amount::from(42);
1039 let amount2 = Amount::ONE;
1040 let result = amount1.checked_div(amount2);
1041 assert_eq!(result, Some(Amount::from(42)));
1042
1043 let amount1 = Amount::from(10);
1045 let amount2 = Amount::from(3);
1046 let result = amount1.checked_div(amount2);
1047 assert_eq!(result, Some(Amount::from(3)));
1048
1049 let amount1 = Amount::from(100);
1051 let amount2 = Amount::ZERO;
1052 let result = amount1.checked_div(amount2);
1053 assert!(result.is_none());
1054 }
1055
1056 #[test]
1063 fn test_convert_unit_returns_correct_value() {
1064 let amount = Amount::from(1000);
1065 let result = amount
1066 .convert_unit(&CurrencyUnit::Sat, &CurrencyUnit::Msat)
1067 .unwrap();
1068 assert_eq!(result, Amount::from(1_000_000));
1069 assert_ne!(result, Amount::ZERO);
1070
1071 let amount = Amount::from(5000);
1072 let result = amount
1073 .convert_unit(&CurrencyUnit::Msat, &CurrencyUnit::Sat)
1074 .unwrap();
1075 assert_eq!(result, Amount::from(5));
1076 assert_ne!(result, Amount::ZERO);
1077
1078 let amount = Amount::from(123);
1079 let result = amount
1080 .convert_unit(&CurrencyUnit::Sat, &CurrencyUnit::Sat)
1081 .unwrap();
1082 assert_eq!(result, Amount::from(123));
1083
1084 let amount = Amount::from(456);
1085 let result = amount
1086 .convert_unit(&CurrencyUnit::Usd, &CurrencyUnit::Usd)
1087 .unwrap();
1088 assert_eq!(result, Amount::from(456));
1089
1090 let amount = Amount::from(789);
1091 let result = amount
1092 .convert_unit(&CurrencyUnit::Eur, &CurrencyUnit::Eur)
1093 .unwrap();
1094 assert_eq!(result, Amount::from(789));
1095
1096 let amount = Amount::from(100);
1098 let result = amount.convert_unit(&CurrencyUnit::Sat, &CurrencyUnit::Eur);
1099 assert!(result.is_err());
1100 }
1101}