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> {
207 let parts: Vec<Self> = fee_and_amounts
208 .amounts
209 .iter()
210 .rev()
211 .fold((Vec::new(), self.value), |(mut acc, total), &amount| {
212 let count = total / amount;
213 for _ in 0..count {
214 acc.push(Self::from(amount));
215 }
216 (acc, total % amount)
217 })
218 .0;
219
220 let sum: u64 = parts.iter().map(|a| a.value).sum();
221 if sum != self.value {
222 return Err(Error::CannotSplitAmount(self.value, sum));
223 }
224
225 Ok(parts)
226 }
227
228 pub fn split_targeted(
230 &self,
231 target: &SplitTarget,
232 fee_and_amounts: &FeeAndAmounts,
233 ) -> Result<Vec<Self>, Error> {
234 let mut parts = match target {
235 SplitTarget::None => self.split(fee_and_amounts)?,
236 SplitTarget::Value(amount) => {
237 if self.le(amount) {
238 return self.split(fee_and_amounts);
239 }
240
241 let mut parts_total = Amount::ZERO;
242 let mut parts = Vec::new();
243
244 let parts_of_value = amount.split(fee_and_amounts)?;
246
247 while parts_total.lt(self) {
248 for part in parts_of_value.iter().copied() {
249 if (part.checked_add(parts_total).ok_or(Error::AmountOverflow)?).le(self) {
250 parts.push(part);
251 } else {
252 let amount_left =
253 self.checked_sub(parts_total).ok_or(Error::AmountOverflow)?;
254 parts.extend(amount_left.split(fee_and_amounts)?);
255 }
256
257 parts_total = Amount::try_sum(parts.clone().iter().copied())?;
258
259 if parts_total.eq(self) {
260 break;
261 }
262 }
263 }
264
265 parts
266 }
267 SplitTarget::Values(values) => {
268 let values_total: Amount = Amount::try_sum(values.clone())?;
269
270 match self.cmp(&values_total) {
271 Ordering::Equal => values.clone(),
272 Ordering::Less => {
273 return Err(Error::SplitValuesGreater);
274 }
275 Ordering::Greater => {
276 let extra = self
277 .checked_sub(values_total)
278 .ok_or(Error::AmountOverflow)?;
279 let mut extra_amount = extra.split(fee_and_amounts)?;
280 let mut values = values.clone();
281
282 values.append(&mut extra_amount);
283 values
284 }
285 }
286 }
287 };
288
289 parts.sort();
290 Ok(parts)
291 }
292
293 pub fn split_with_fee(&self, fee_and_amounts: &FeeAndAmounts) -> Result<Vec<Self>, Error> {
295 let without_fee_amounts = self.split(fee_and_amounts)?;
296 let total_fee_ppk = fee_and_amounts
297 .fee
298 .checked_mul(without_fee_amounts.len() as u64)
299 .ok_or(Error::AmountOverflow)?;
300 let fee = Amount::from(total_fee_ppk.div_ceil(1000));
301 let new_amount = self.checked_add(fee).ok_or(Error::AmountOverflow)?;
302
303 let split = new_amount.split(fee_and_amounts)?;
304 let split_fee_ppk = (split.len() as u64)
305 .checked_mul(fee_and_amounts.fee)
306 .ok_or(Error::AmountOverflow)?;
307 let split_fee = Amount::from(split_fee_ppk.div_ceil(1000));
308
309 if let Some(net_amount) = new_amount.checked_sub(split_fee) {
310 if net_amount >= *self {
311 return Ok(split);
312 }
313 }
314 self.checked_add(Amount::ONE)
315 .ok_or(Error::AmountOverflow)?
316 .split_with_fee(fee_and_amounts)
317 }
318
319 pub fn checked_add(self, other: Amount<()>) -> Option<Amount<()>> {
321 self.value
322 .checked_add(other.value)
323 .map(|v| Amount { value: v, unit: () })
324 }
325
326 pub fn checked_sub(self, other: Amount<()>) -> Option<Amount<()>> {
328 self.value
329 .checked_sub(other.value)
330 .map(|v| Amount { value: v, unit: () })
331 }
332
333 pub fn checked_mul(self, other: Amount<()>) -> Option<Amount<()>> {
335 self.value
336 .checked_mul(other.value)
337 .map(|v| Amount { value: v, unit: () })
338 }
339
340 pub fn checked_div(self, other: Amount<()>) -> Option<Amount<()>> {
342 self.value
343 .checked_div(other.value)
344 .map(|v| Amount { value: v, unit: () })
345 }
346
347 pub fn saturating_sub(self, other: Self) -> Self {
349 if other > self {
350 Self::ZERO
351 } else {
352 self - other
353 }
354 }
355
356 pub fn try_sum<I>(iter: I) -> Result<Self, Error>
358 where
359 I: IntoIterator<Item = Self>,
360 {
361 iter.into_iter().try_fold(Amount::ZERO, |acc, x| {
362 acc.checked_add(x).ok_or(Error::AmountOverflow)
363 })
364 }
365
366 pub fn convert_unit(
368 &self,
369 current_unit: &CurrencyUnit,
370 target_unit: &CurrencyUnit,
371 ) -> Result<Amount<()>, Error> {
372 Amount::new(self.value, current_unit.clone())
373 .convert_to(target_unit)
374 .map(Into::into)
375 }
376
377 pub fn to_u64(self) -> u64 {
379 self.value
380 }
381
382 pub fn to_i64(self) -> Option<i64> {
384 if self.value <= i64::MAX as u64 {
385 Some(self.value as i64)
386 } else {
387 None
388 }
389 }
390
391 pub fn from_i64(value: i64) -> Option<Self> {
393 if value >= 0 {
394 Some(Amount {
395 value: value as u64,
396 unit: (),
397 })
398 } else {
399 None
400 }
401 }
402}
403
404impl Default for Amount<()> {
405 fn default() -> Self {
406 Amount::ZERO
407 }
408}
409
410impl Default for &Amount<()> {
411 fn default() -> Self {
412 &Amount::ZERO
413 }
414}
415
416impl Amount<CurrencyUnit> {
417 pub fn new(value: u64, unit: CurrencyUnit) -> Self {
429 Self { value, unit }
430 }
431
432 pub fn value(&self) -> u64 {
441 self.value
442 }
443
444 pub fn to_u64(self) -> u64 {
446 self.value
447 }
448
449 pub fn to_i64(self) -> Option<i64> {
451 if self.value <= i64::MAX as u64 {
452 Some(self.value as i64)
453 } else {
454 None
455 }
456 }
457
458 pub fn unit(&self) -> &CurrencyUnit {
467 &self.unit
468 }
469
470 pub fn into_parts(self) -> (u64, CurrencyUnit) {
481 (self.value, self.unit)
482 }
483
484 pub fn checked_add(&self, other: &Self) -> Result<Self, Error> {
501 if self.unit != other.unit {
502 return Err(Error::UnitMismatch(self.unit.clone(), other.unit.clone()));
503 }
504 self.value
505 .checked_add(other.value)
506 .map(|v| Amount::new(v, self.unit.clone()))
507 .ok_or(Error::AmountOverflow)
508 }
509
510 pub fn checked_sub(&self, other: &Self) -> Result<Self, Error> {
523 if self.unit != other.unit {
524 return Err(Error::UnitMismatch(self.unit.clone(), other.unit.clone()));
525 }
526 self.value
527 .checked_sub(other.value)
528 .map(|v| Amount::new(v, self.unit.clone()))
529 .ok_or(Error::AmountOverflow)
530 }
531
532 pub fn convert_to(&self, target_unit: &CurrencyUnit) -> Result<Self, Error> {
543 if &self.unit == target_unit {
544 return Ok(self.clone());
545 }
546
547 let converted_value = match (&self.unit, target_unit) {
548 (CurrencyUnit::Sat, CurrencyUnit::Msat) => self
549 .value
550 .checked_mul(MSAT_IN_SAT)
551 .ok_or(Error::AmountOverflow)?,
552 (CurrencyUnit::Msat, CurrencyUnit::Sat) => self.value / MSAT_IN_SAT,
553 _ => return Err(Error::CannotConvertUnits),
554 };
555
556 Ok(Amount::new(converted_value, target_unit.clone()))
557 }
558
559 pub fn display_with_unit(&self) -> String {
561 format!("{} {}", self.value, self.unit)
562 }
563
564 pub fn to_msat(&self) -> Result<u64, Error> {
569 self.convert_to(&CurrencyUnit::Msat).map(|a| a.value())
570 }
571
572 pub fn to_sat(&self) -> Result<u64, Error> {
577 self.convert_to(&CurrencyUnit::Sat).map(|a| a.value())
578 }
579}
580
581impl<U> fmt::Display for Amount<U> {
582 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583 if let Some(width) = f.width() {
584 write!(f, "{:width$}", self.value, width = width)
585 } else {
586 write!(f, "{}", self.value)
587 }
588 }
589}
590
591impl From<u64> for Amount<()> {
592 fn from(value: u64) -> Self {
593 Amount { value, unit: () }
594 }
595}
596
597impl From<&u64> for Amount<()> {
598 fn from(value: &u64) -> Self {
599 Amount {
600 value: *value,
601 unit: (),
602 }
603 }
604}
605
606impl From<Amount<()>> for u64 {
607 fn from(value: Amount<()>) -> Self {
608 value.value
609 }
610}
611
612impl From<Amount<CurrencyUnit>> for Amount<()> {
613 fn from(value: Amount<CurrencyUnit>) -> Self {
614 Amount {
615 value: value.value,
616 unit: (),
617 }
618 }
619}
620
621impl AsRef<u64> for Amount<()> {
622 fn as_ref(&self) -> &u64 {
623 &self.value
624 }
625}
626
627impl std::ops::Add for Amount<()> {
628 type Output = Amount<()>;
629
630 fn add(self, rhs: Amount<()>) -> Self::Output {
631 self.checked_add(rhs)
632 .expect("Addition overflow: the sum of the amounts exceeds the maximum value")
633 }
634}
635
636impl std::ops::AddAssign for Amount<()> {
637 fn add_assign(&mut self, rhs: Self) {
638 *self = self
639 .checked_add(rhs)
640 .expect("AddAssign overflow: the sum of the amounts exceeds the maximum value");
641 }
642}
643
644impl std::ops::Sub for Amount<()> {
645 type Output = Amount<()>;
646
647 fn sub(self, rhs: Amount<()>) -> Self::Output {
648 self.checked_sub(rhs)
649 .expect("Subtraction underflow: cannot subtract a larger amount from a smaller amount")
650 }
651}
652
653impl std::ops::SubAssign for Amount<()> {
654 fn sub_assign(&mut self, other: Self) {
655 *self = self
656 .checked_sub(other)
657 .expect("SubAssign underflow: cannot subtract a larger amount from a smaller amount");
658 }
659}
660
661impl std::ops::Mul for Amount<()> {
662 type Output = Self;
663
664 fn mul(self, other: Self) -> Self::Output {
665 self.checked_mul(other)
666 .expect("Multiplication overflow: the product of the amounts exceeds the maximum value")
667 }
668}
669
670impl std::ops::Div for Amount<()> {
671 type Output = Self;
672
673 fn div(self, other: Self) -> Self::Output {
674 self.checked_div(other)
675 .expect("Division error: cannot divide by zero or overflow occurred")
676 }
677}
678
679pub fn amount_for_offer(offer: &Offer, unit: &CurrencyUnit) -> Result<Amount, Error> {
681 let offer_amount = offer.amount().ok_or(Error::AmountUndefined)?;
682
683 let (amount, currency) = match offer_amount {
684 lightning::offers::offer::Amount::Bitcoin { amount_msats } => {
685 (amount_msats, CurrencyUnit::Msat)
686 }
687 lightning::offers::offer::Amount::Currency {
688 iso4217_code,
689 amount,
690 } => (
691 amount,
692 CurrencyUnit::from_str(&String::from_utf8(iso4217_code.as_bytes().to_vec())?)
693 .map_err(|_| Error::CannotConvertUnits)?,
694 ),
695 };
696
697 Amount::new(amount, currency)
698 .convert_to(unit)
699 .map(Into::into)
700 .map_err(|_err| Error::CannotConvertUnits)
701}
702
703#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)]
705pub enum SplitTarget {
706 #[default]
708 None,
709 Value(Amount),
711 Values(Vec<Amount>),
713}
714
715pub const MSAT_IN_SAT: u64 = 1000;
717
718#[cfg(test)]
719mod tests {
720 use super::*;
721
722 #[test]
723 fn test_split_amount() {
724 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
725
726 assert_eq!(
727 Amount::from(1).split(&fee_and_amounts).unwrap(),
728 vec![Amount::from(1)]
729 );
730 assert_eq!(
731 Amount::from(2).split(&fee_and_amounts).unwrap(),
732 vec![Amount::from(2)]
733 );
734 assert_eq!(
735 Amount::from(3).split(&fee_and_amounts).unwrap(),
736 vec![Amount::from(2), Amount::from(1)]
737 );
738 let amounts: Vec<Amount> = [8, 2, 1].iter().map(|a| Amount::from(*a)).collect();
739 assert_eq!(Amount::from(11).split(&fee_and_amounts).unwrap(), amounts);
740 let amounts: Vec<Amount> = [128, 64, 32, 16, 8, 4, 2, 1]
741 .iter()
742 .map(|a| Amount::from(*a))
743 .collect();
744 assert_eq!(Amount::from(255).split(&fee_and_amounts).unwrap(), amounts);
745 }
746
747 #[test]
748 fn test_split_target_amount() {
749 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
750 let amount = Amount::from(65);
751
752 let split = amount
753 .split_targeted(&SplitTarget::Value(Amount::from(32)), &fee_and_amounts)
754 .unwrap();
755 assert_eq!(
756 vec![Amount::from(1), Amount::from(32), Amount::from(32)],
757 split
758 );
759
760 let amount = Amount::from(150);
761
762 let split = amount
763 .split_targeted(&SplitTarget::Value(Amount::from(50)), &fee_and_amounts)
764 .unwrap();
765 assert_eq!(
766 vec![
767 Amount::from(2),
768 Amount::from(2),
769 Amount::from(2),
770 Amount::from(16),
771 Amount::from(16),
772 Amount::from(16),
773 Amount::from(32),
774 Amount::from(32),
775 Amount::from(32)
776 ],
777 split
778 );
779
780 let amount = Amount::from(63);
781
782 let split = amount
783 .split_targeted(&SplitTarget::Value(Amount::from(32)), &fee_and_amounts)
784 .unwrap();
785 assert_eq!(
786 vec![
787 Amount::from(1),
788 Amount::from(2),
789 Amount::from(4),
790 Amount::from(8),
791 Amount::from(16),
792 Amount::from(32)
793 ],
794 split
795 );
796 }
797
798 #[test]
799 fn test_split_with_fee() {
800 let fee_and_amounts = (1, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
801 let amount = Amount::from(2);
802
803 let split = amount.split_with_fee(&fee_and_amounts).unwrap();
804 assert_eq!(split, vec![Amount::from(2), Amount::from(1)]);
805
806 let amount = Amount::from(3);
807
808 let split = amount.split_with_fee(&fee_and_amounts).unwrap();
809 assert_eq!(split, vec![Amount::from(4)]);
810
811 let amount = Amount::from(3);
812 let fee_and_amounts = (1000, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
813
814 let split = amount.split_with_fee(&fee_and_amounts).unwrap();
815 assert_eq!(split, vec![Amount::from(4), Amount::from(1)]);
818 }
819
820 #[test]
821 fn test_split_with_fee_reported_issue() {
822 let fee_and_amounts = (100, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
823 let amount = Amount::from(300);
825
826 let split = amount.split_with_fee(&fee_and_amounts).unwrap();
827
828 let total_fee_ppk = (split.len() as u64) * fee_and_amounts.fee;
830 let total_fee = Amount::from(total_fee_ppk.div_ceil(1000));
831
832 let split_total = Amount::try_sum(split.iter().copied()).unwrap();
834 assert!(
835 split_total >= amount.checked_add(total_fee).unwrap(),
836 "Split total {} should be >= amount {} + fee {}",
837 split_total,
838 amount,
839 total_fee
840 );
841 }
842
843 #[test]
844 fn test_split_with_fee_edge_cases() {
845 let test_cases = vec![
847 (Amount::from(1), 100),
848 (Amount::from(10), 100),
849 (Amount::from(50), 100),
850 (Amount::from(100), 100),
851 (Amount::from(200), 100),
852 (Amount::from(300), 100),
853 (Amount::from(500), 100),
854 (Amount::from(600), 100),
855 (Amount::from(1000), 100),
856 (Amount::from(1337), 100),
857 (Amount::from(5000), 100),
858 ];
859
860 for (amount, fee_ppk) in test_cases {
861 let fee_and_amounts =
862 (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
863 let result = amount.split_with_fee(&fee_and_amounts);
864 assert!(
865 result.is_ok(),
866 "split_with_fee failed for amount {} with fee_ppk {}: {:?}",
867 amount,
868 fee_ppk,
869 result.err()
870 );
871
872 let split = result.unwrap();
873
874 let split_total = Amount::try_sum(split.iter().copied()).unwrap();
876 let fee_for_split = (split.len() as u64) * fee_ppk;
877 let total_fee = Amount::from(fee_for_split.div_ceil(1000));
878
879 let net_amount = split_total.checked_sub(total_fee);
881 assert!(
882 net_amount.is_some(),
883 "Net amount calculation failed for amount {} with fee_ppk {}",
884 amount,
885 fee_ppk
886 );
887 assert!(
888 net_amount.unwrap() >= amount,
889 "Net amount {} is less than required {} for amount {} with fee_ppk {}",
890 net_amount.unwrap(),
891 amount,
892 amount,
893 fee_ppk
894 );
895 }
896 }
897
898 #[test]
899 fn test_split_with_fee_high_fees() {
900 let test_cases = vec![
902 (Amount::from(10), 500), (Amount::from(10), 1000), (Amount::from(10), 2000), (Amount::from(100), 500),
906 (Amount::from(100), 1000),
907 (Amount::from(100), 2000),
908 ];
909
910 for (amount, fee_ppk) in test_cases {
911 let fee_and_amounts =
912 (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
913 let result = amount.split_with_fee(&fee_and_amounts);
914 assert!(
915 result.is_ok(),
916 "split_with_fee failed for amount {} with fee_ppk {}: {:?}",
917 amount,
918 fee_ppk,
919 result.err()
920 );
921
922 let split = result.unwrap();
923 let split_total = Amount::try_sum(split.iter().copied()).unwrap();
924
925 assert!(
927 split_total > amount,
928 "Split total {} should be greater than amount {} for fee_ppk {}",
929 split_total,
930 amount,
931 fee_ppk
932 );
933 }
934 }
935
936 #[test]
937 fn test_split_with_fee_recursion_limit() {
938 let amount = Amount::from(1);
941 let fee_ppk = 10000;
942 let fee_and_amounts = (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
943
944 let result = amount.split_with_fee(&fee_and_amounts);
945 assert!(
946 result.is_ok(),
947 "split_with_fee should handle extreme fees without infinite recursion"
948 );
949 }
950
951 #[test]
952 fn test_split_values() {
953 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
954 let amount = Amount::from(10);
955
956 let target = vec![Amount::from(2), Amount::from(4), Amount::from(4)];
957
958 let split_target = SplitTarget::Values(target.clone());
959
960 let values = amount
961 .split_targeted(&split_target, &fee_and_amounts)
962 .unwrap();
963
964 assert_eq!(target, values);
965
966 let target = vec![Amount::from(2), Amount::from(4), Amount::from(4)];
967
968 let split_target = SplitTarget::Values(vec![Amount::from(2), Amount::from(4)]);
969
970 let values = amount
971 .split_targeted(&split_target, &fee_and_amounts)
972 .unwrap();
973
974 assert_eq!(target, values);
975
976 let split_target = SplitTarget::Values(vec![Amount::from(2), Amount::from(10)]);
977
978 let values = amount.split_targeted(&split_target, &fee_and_amounts);
979
980 assert!(values.is_err())
981 }
982
983 #[test]
984 #[should_panic]
985 fn test_amount_addition() {
986 let amount_one: Amount = u64::MAX.into();
987 let amount_two: Amount = 1.into();
988
989 let amounts = vec![amount_one, amount_two];
990
991 let _total: Amount = Amount::try_sum(amounts).unwrap();
992 }
993
994 #[test]
995 fn test_try_amount_addition() {
996 let amount_one: Amount = u64::MAX.into();
997 let amount_two: Amount = 1.into();
998
999 let amounts = vec![amount_one, amount_two];
1000
1001 let total = Amount::try_sum(amounts);
1002
1003 assert!(total.is_err());
1004 let amount_one: Amount = 10000.into();
1005 let amount_two: Amount = 1.into();
1006
1007 let amounts = vec![amount_one, amount_two];
1008 let total = Amount::try_sum(amounts).unwrap();
1009
1010 assert_eq!(total, 10001.into());
1011 }
1012
1013 #[test]
1014 fn test_amount_convert_to() {
1015 let amount = Amount::new(1000, CurrencyUnit::Sat);
1017 let converted = amount.convert_to(&CurrencyUnit::Msat).unwrap();
1018 assert_eq!(converted.value(), 1000000);
1019 assert_eq!(converted.unit(), &CurrencyUnit::Msat);
1020
1021 let amount = Amount::new(1000, CurrencyUnit::Msat);
1023 let converted = amount.convert_to(&CurrencyUnit::Sat).unwrap();
1024 assert_eq!(converted.value(), 1);
1025 assert_eq!(converted.unit(), &CurrencyUnit::Sat);
1026
1027 let amount = Amount::new(1, CurrencyUnit::Usd);
1029 let converted = amount.convert_to(&CurrencyUnit::Usd).unwrap();
1030 assert_eq!(converted.value(), 1);
1031 assert_eq!(converted.unit(), &CurrencyUnit::Usd);
1032
1033 let amount = Amount::new(1, CurrencyUnit::Eur);
1035 let converted = amount.convert_to(&CurrencyUnit::Eur).unwrap();
1036 assert_eq!(converted.value(), 1);
1037 assert_eq!(converted.unit(), &CurrencyUnit::Eur);
1038
1039 let amount = Amount::new(1, CurrencyUnit::Sat);
1041 let converted = amount.convert_to(&CurrencyUnit::Eur);
1042 assert!(converted.is_err());
1043
1044 let amount = Amount::new(500, CurrencyUnit::Sat);
1046 let converted = amount.convert_to(&CurrencyUnit::Sat).unwrap();
1047 assert_eq!(converted.value(), 500);
1048 assert_eq!(converted.unit(), &CurrencyUnit::Sat);
1049
1050 let amount = Amount::new(5000, CurrencyUnit::Msat);
1052 let converted = amount.convert_to(&CurrencyUnit::Msat).unwrap();
1053 assert_eq!(converted.value(), 5000);
1054 assert_eq!(converted.unit(), &CurrencyUnit::Msat);
1055 }
1056
1057 #[test]
1058 fn test_amount_to_msat() {
1059 let sat_amount = Amount::new(2, CurrencyUnit::Sat);
1060 assert_eq!(sat_amount.to_msat().unwrap(), 2000);
1061
1062 let msat_amount = Amount::new(2500, CurrencyUnit::Msat);
1063 assert_eq!(msat_amount.to_msat().unwrap(), 2500);
1064
1065 let usd_amount = Amount::new(1, CurrencyUnit::Usd);
1066 assert!(usd_amount.to_msat().is_err());
1067 }
1068
1069 #[test]
1070 fn test_amount_to_sat() {
1071 let msat_amount = Amount::new(2000, CurrencyUnit::Msat);
1072 assert_eq!(msat_amount.to_sat().unwrap(), 2);
1073
1074 let sat_amount = Amount::new(5, CurrencyUnit::Sat);
1075 assert_eq!(sat_amount.to_sat().unwrap(), 5);
1076
1077 let usd_amount = Amount::new(1, CurrencyUnit::Usd);
1078 assert!(usd_amount.to_sat().is_err());
1079 }
1080
1081 #[test]
1082 fn test_amount_from_typed_to_untyped() {
1083 let typed = Amount::new(1000, CurrencyUnit::Sat);
1085 let untyped: Amount<()> = typed.into();
1086 assert_eq!(u64::from(untyped), 1000);
1087 }
1088
1089 #[test]
1100 fn test_amount_sub_operator() {
1101 let amount1 = Amount::from(100);
1102 let amount2 = Amount::from(30);
1103
1104 let result = amount1.checked_sub(amount2).unwrap();
1105 assert_eq!(result, Amount::from(70));
1106
1107 let amount1 = Amount::from(1000);
1108 let amount2 = Amount::from(1);
1109
1110 let result = amount1.checked_sub(amount2).unwrap();
1111 assert_eq!(result, Amount::from(999));
1112
1113 let amount1 = Amount::from(255);
1114 let amount2 = Amount::from(128);
1115
1116 let result = amount1.checked_sub(amount2).unwrap();
1117 assert_eq!(result, Amount::from(127));
1118 }
1119
1120 #[test]
1130 #[should_panic(expected = "Subtraction underflow")]
1131 fn test_amount_sub_underflow() {
1132 let amount1 = Amount::from(30);
1133 let amount2 = Amount::from(100);
1134
1135 let _result = amount1 - amount2;
1136 }
1137
1138 #[test]
1148 fn test_checked_add_returns_correct_value() {
1149 let amount1 = Amount::from(100);
1150 let amount2 = Amount::from(50);
1151
1152 let result = amount1.checked_add(amount2);
1153 assert_eq!(result, Some(Amount::from(150)));
1154
1155 let amount1 = Amount::from(1);
1156 let amount2 = Amount::from(1);
1157
1158 let result = amount1.checked_add(amount2);
1159 assert_eq!(result, Some(Amount::from(2)));
1160 assert_ne!(result, Some(Amount::ZERO));
1161
1162 let amount1 = Amount::from(1000);
1163 let amount2 = Amount::from(337);
1164
1165 let result = amount1.checked_add(amount2);
1166 assert_eq!(result, Some(Amount::from(1337)));
1167 }
1168
1169 #[test]
1171 fn test_checked_add_overflow() {
1172 let amount1 = Amount::from(u64::MAX);
1173 let amount2 = Amount::from(1);
1174
1175 let result = amount1.checked_add(amount2);
1176 assert!(result.is_none());
1177 }
1178
1179 #[test]
1188 fn test_try_sum_returns_correct_value() {
1189 let amounts = vec![Amount::from(10), Amount::from(20), Amount::from(30)];
1190 let result = Amount::try_sum(amounts).unwrap();
1191 assert_eq!(result, Amount::from(60));
1192 assert_ne!(result, Amount::ZERO);
1193
1194 let amounts = vec![Amount::from(1), Amount::from(1), Amount::from(1)];
1195 let result = Amount::try_sum(amounts).unwrap();
1196 assert_eq!(result, Amount::from(3));
1197
1198 let amounts = vec![Amount::from(100)];
1199 let result = Amount::try_sum(amounts).unwrap();
1200 assert_eq!(result, Amount::from(100));
1201
1202 let empty: Vec<Amount> = vec![];
1203 let result = Amount::try_sum(empty).unwrap();
1204 assert_eq!(result, Amount::ZERO);
1205 }
1206
1207 #[test]
1209 fn test_try_sum_overflow() {
1210 let amounts = vec![Amount::from(u64::MAX), Amount::from(1)];
1211 let result = Amount::try_sum(amounts);
1212 assert!(result.is_err());
1213 }
1214
1215 #[test]
1225 fn test_split_returns_correct_values() {
1226 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
1227
1228 let amount = Amount::from(11);
1229 let result = amount.split(&fee_and_amounts).unwrap();
1230 assert!(!result.is_empty());
1231 assert_eq!(Amount::try_sum(result.iter().copied()).unwrap(), amount);
1232
1233 let amount = Amount::from(255);
1234 let result = amount.split(&fee_and_amounts).unwrap();
1235 assert!(!result.is_empty());
1236 assert_eq!(Amount::try_sum(result.iter().copied()).unwrap(), amount);
1237
1238 let amount = Amount::from(7);
1239 let result = amount.split(&fee_and_amounts).unwrap();
1240 assert_eq!(
1241 result,
1242 vec![Amount::from(4), Amount::from(2), Amount::from(1)]
1243 );
1244 for r in &result {
1245 assert_ne!(*r, Amount::ZERO);
1246 }
1247 }
1248
1249 #[test]
1254 fn test_split_modulo_operation() {
1255 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
1256
1257 let amount = Amount::from(15);
1258 let result = amount.split(&fee_and_amounts).unwrap();
1259
1260 assert_eq!(
1261 result,
1262 vec![
1263 Amount::from(8),
1264 Amount::from(4),
1265 Amount::from(2),
1266 Amount::from(1)
1267 ]
1268 );
1269
1270 let total = Amount::try_sum(result.iter().copied()).unwrap();
1271 assert_eq!(total, amount);
1272 }
1273
1274 #[test]
1277 fn test_split_cannot_represent_amount() {
1278 let fee_and_amounts: FeeAndAmounts = (0, vec![32]).into();
1280
1281 let amount = Amount::from(100);
1284 let result = amount.split(&fee_and_amounts);
1285 assert!(result.is_err());
1286 match result {
1287 Err(Error::CannotSplitAmount(requested, got)) => {
1288 assert_eq!(requested, 100);
1289 assert_eq!(got, 96); }
1291 _ => panic!("Expected CannotSplitAmount error"),
1292 }
1293
1294 let amount = Amount::from(32);
1296 let result = amount.split(&fee_and_amounts);
1297 assert!(result.is_ok());
1298 assert_eq!(result.unwrap(), vec![Amount::from(32)]);
1299
1300 let amount = Amount::from(64);
1302 let result = amount.split(&fee_and_amounts);
1303 assert!(result.is_ok());
1304 assert_eq!(result.unwrap(), vec![Amount::from(32), Amount::from(32)]);
1305
1306 let fee_and_amounts: FeeAndAmounts = (0, vec![32, 64]).into();
1311 let amount = Amount::from(100);
1312 let result = amount.split(&fee_and_amounts);
1313 assert!(result.is_err());
1314 match result {
1315 Err(Error::CannotSplitAmount(requested, got)) => {
1316 assert_eq!(requested, 100);
1317 assert_eq!(got, 96);
1318 }
1319 _ => panic!("Expected CannotSplitAmount error"),
1320 }
1321 }
1322
1323 #[test]
1324 fn test_split_amount_exceeds_keyset_capacity() {
1325 let fee_and_amounts: FeeAndAmounts = (0, vec![32]).into();
1328
1329 let amount = Amount::from(100);
1330 let result = amount.split(&fee_and_amounts);
1331 assert!(result.is_err());
1332 match result {
1333 Err(Error::CannotSplitAmount(requested, got)) => {
1334 assert_eq!(requested, 100);
1335 assert_eq!(got, 96);
1337 }
1338 _ => panic!("Expected CannotSplitAmount error, got {:?}", result),
1339 }
1340
1341 let fee_and_amounts: FeeAndAmounts = (0, vec![4, 16]).into();
1344 let amount = Amount::from(50);
1345 let result = amount.split(&fee_and_amounts);
1346 assert!(result.is_err());
1347 match result {
1348 Err(Error::CannotSplitAmount(requested, got)) => {
1349 assert_eq!(requested, 50);
1350 assert_eq!(got, 48);
1351 }
1352 _ => panic!("Expected CannotSplitAmount error, got {:?}", result),
1353 }
1354 }
1355
1356 #[test]
1364 fn test_from_u64_returns_correct_value() {
1365 let amount = Amount::from(100u64);
1366 assert_eq!(amount, Amount::from(100));
1367 assert_ne!(amount, Amount::ZERO);
1368
1369 let amount = Amount::from(1u64);
1370 assert_eq!(amount, Amount::from(1));
1371 assert_eq!(amount, Amount::ONE);
1372
1373 let amount = Amount::from(1337u64);
1374 assert_eq!(amount.to_u64(), 1337);
1375 }
1376
1377 #[test]
1384 fn test_checked_mul_returns_correct_value() {
1385 let amount1 = Amount::from(10);
1386 let amount2 = Amount::from(5);
1387 let result = amount1.checked_mul(amount2);
1388 assert_eq!(result, Some(Amount::from(50)));
1389 assert_ne!(result, None);
1390 assert_ne!(result, Some(Amount::ZERO));
1391
1392 let amount1 = Amount::from(100);
1393 let amount2 = Amount::from(20);
1394 let result = amount1.checked_mul(amount2);
1395 assert_eq!(result, Some(Amount::from(2000)));
1396 assert_ne!(result, Some(Amount::ZERO));
1397
1398 let amount1 = Amount::from(7);
1399 let amount2 = Amount::from(13);
1400 let result = amount1.checked_mul(amount2);
1401 assert_eq!(result, Some(Amount::from(91)));
1402
1403 let amount1 = Amount::from(100);
1405 let amount2 = Amount::ZERO;
1406 let result = amount1.checked_mul(amount2);
1407 assert_eq!(result, Some(Amount::ZERO));
1408
1409 let amount1 = Amount::from(42);
1411 let amount2 = Amount::ONE;
1412 let result = amount1.checked_mul(amount2);
1413 assert_eq!(result, Some(Amount::from(42)));
1414
1415 let amount1 = Amount::from(u64::MAX);
1417 let amount2 = Amount::from(2);
1418 let result = amount1.checked_mul(amount2);
1419 assert!(result.is_none());
1420 }
1421
1422 #[test]
1429 fn test_checked_div_returns_correct_value() {
1430 let amount1 = Amount::from(100);
1431 let amount2 = Amount::from(5);
1432 let result = amount1.checked_div(amount2);
1433 assert_eq!(result, Some(Amount::from(20)));
1434 assert_ne!(result, None);
1435 assert_ne!(result, Some(Amount::ZERO));
1436
1437 let amount1 = Amount::from(1000);
1438 let amount2 = Amount::from(10);
1439 let result = amount1.checked_div(amount2);
1440 assert_eq!(result, Some(Amount::from(100)));
1441 assert_ne!(result, Some(Amount::ZERO));
1442
1443 let amount1 = Amount::from(91);
1444 let amount2 = Amount::from(7);
1445 let result = amount1.checked_div(amount2);
1446 assert_eq!(result, Some(Amount::from(13)));
1447
1448 let amount1 = Amount::from(42);
1450 let amount2 = Amount::ONE;
1451 let result = amount1.checked_div(amount2);
1452 assert_eq!(result, Some(Amount::from(42)));
1453
1454 let amount1 = Amount::from(10);
1456 let amount2 = Amount::from(3);
1457 let result = amount1.checked_div(amount2);
1458 assert_eq!(result, Some(Amount::from(3)));
1459
1460 let amount1 = Amount::from(100);
1462 let amount2 = Amount::ZERO;
1463 let result = amount1.checked_div(amount2);
1464 assert!(result.is_none());
1465 }
1466
1467 #[test]
1474 fn test_convert_unit_returns_correct_value() {
1475 let amount = Amount::from(1000);
1476 let result = amount
1477 .convert_unit(&CurrencyUnit::Sat, &CurrencyUnit::Msat)
1478 .unwrap();
1479 assert_eq!(result, Amount::from(1_000_000));
1480 assert_ne!(result, Amount::ZERO);
1481
1482 let amount = Amount::from(5000);
1483 let result = amount
1484 .convert_unit(&CurrencyUnit::Msat, &CurrencyUnit::Sat)
1485 .unwrap();
1486 assert_eq!(result, Amount::from(5));
1487 assert_ne!(result, Amount::ZERO);
1488
1489 let amount = Amount::from(123);
1490 let result = amount
1491 .convert_unit(&CurrencyUnit::Sat, &CurrencyUnit::Sat)
1492 .unwrap();
1493 assert_eq!(result, Amount::from(123));
1494
1495 let amount = Amount::from(456);
1496 let result = amount
1497 .convert_unit(&CurrencyUnit::Usd, &CurrencyUnit::Usd)
1498 .unwrap();
1499 assert_eq!(result, Amount::from(456));
1500
1501 let amount = Amount::from(789);
1502 let result = amount
1503 .convert_unit(&CurrencyUnit::Eur, &CurrencyUnit::Eur)
1504 .unwrap();
1505 assert_eq!(result, Amount::from(789));
1506
1507 let amount = Amount::from(100);
1509 let result = amount.convert_unit(&CurrencyUnit::Sat, &CurrencyUnit::Eur);
1510 assert!(result.is_err());
1511 }
1512
1513 #[test]
1523 fn test_amount_to_i64_returns_correct_value() {
1524 let amount = Amount::from(100);
1526 let result = amount.to_i64();
1527 assert_eq!(result, Some(100));
1528 assert!(result.is_some());
1529 assert_ne!(result, Some(0));
1530 assert_ne!(result, Some(1));
1531 assert_ne!(result, Some(-1));
1532
1533 let amount = Amount::from(1000);
1535 let result = amount.to_i64();
1536 assert_eq!(result, Some(1000));
1537 assert_ne!(result, None);
1538 assert_ne!(result, Some(0));
1539 assert_ne!(result, Some(1));
1540 assert_ne!(result, Some(-1));
1541
1542 let amount = Amount::from(2);
1544 let result = amount.to_i64();
1545 assert_eq!(result, Some(2));
1546 assert_ne!(result, Some(1));
1547
1548 let amount = Amount::from(i64::MAX as u64);
1551 let result = amount.to_i64();
1552 assert_eq!(result, Some(i64::MAX));
1553 assert!(result.is_some());
1554
1555 let amount = Amount::from(i64::MAX as u64 + 1);
1558 let result = amount.to_i64();
1559 assert!(result.is_none());
1560
1561 let amount = Amount::from(u64::MAX);
1563 let result = amount.to_i64();
1564 assert!(result.is_none());
1565
1566 let amount = Amount::from(0);
1568 let result = amount.to_i64();
1569 assert_eq!(result, Some(0));
1570
1571 let amount = Amount::from(1);
1573 let result = amount.to_i64();
1574 assert_eq!(result, Some(1));
1575 }
1576
1577 #[test]
1582 fn test_amount_to_i64_boundary() {
1583 let at_max = Amount::from(i64::MAX as u64);
1585 assert!(at_max.to_i64().is_some());
1586 assert_eq!(at_max.to_i64().unwrap(), i64::MAX);
1587
1588 let above_max = Amount::from(i64::MAX as u64 + 1);
1590 assert!(above_max.to_i64().is_none());
1591
1592 let below_max = Amount::from(i64::MAX as u64 - 1);
1594 assert!(below_max.to_i64().is_some());
1595 assert_eq!(below_max.to_i64().unwrap(), i64::MAX - 1);
1596 }
1597
1598 #[test]
1605 fn test_amount_from_i64() {
1606 let result = Amount::from_i64(100);
1608 assert!(result.is_some());
1609 assert_eq!(result.unwrap(), Amount::from(100));
1610 assert_ne!(result, None);
1611 assert_ne!(result, Some(Amount::ZERO));
1612
1613 let result = Amount::from_i64(0);
1616 assert!(result.is_some());
1617 assert_eq!(result.unwrap(), Amount::ZERO);
1618
1619 let result = Amount::from_i64(-1);
1621 assert!(result.is_none());
1622
1623 let result = Amount::from_i64(-100);
1624 assert!(result.is_none());
1625
1626 let result = Amount::from_i64(i64::MAX);
1628 assert!(result.is_some());
1629 assert_eq!(result.unwrap(), Amount::from(i64::MAX as u64));
1630 assert_ne!(result, Some(Amount::ZERO));
1631
1632 let result = Amount::from_i64(1);
1634 assert!(result.is_some());
1635 assert_eq!(result.unwrap(), Amount::ONE);
1636 assert_ne!(result, Some(Amount::ZERO));
1637 }
1638
1639 #[test]
1643 fn test_add_assign() {
1644 let mut amount = Amount::from(100);
1645 amount += Amount::from(50);
1646 assert_eq!(amount, Amount::from(150));
1647 assert_ne!(amount, Amount::from(100)); let mut amount = Amount::from(1);
1650 amount += Amount::from(1);
1651 assert_eq!(amount, Amount::from(2));
1652 assert_ne!(amount, Amount::ONE); let mut amount = Amount::ZERO;
1655 amount += Amount::from(42);
1656 assert_eq!(amount, Amount::from(42));
1657 assert_ne!(amount, Amount::ZERO); }
1659
1660 #[test]
1664 fn test_sub_assign() {
1665 let mut amount = Amount::from(100);
1666 amount -= Amount::from(30);
1667 assert_eq!(amount, Amount::from(70));
1668 assert_ne!(amount, Amount::from(100)); let mut amount = Amount::from(50);
1671 amount -= Amount::from(1);
1672 assert_eq!(amount, Amount::from(49));
1673 assert_ne!(amount, Amount::from(50)); let mut amount = Amount::from(10);
1676 amount -= Amount::from(10);
1677 assert_eq!(amount, Amount::ZERO);
1678 assert_ne!(amount, Amount::from(10)); }
1680
1681 #[test]
1684 fn test_amount_with_currency_unit() {
1685 let amount = Amount::new(1000, CurrencyUnit::Sat);
1686 assert_eq!(amount.value(), 1000);
1687 assert_eq!(amount.unit(), &CurrencyUnit::Sat);
1688 }
1689
1690 #[test]
1691 fn test_amount_new_with_custom_unit() {
1692 let custom_unit = CurrencyUnit::Custom("BTC".to_string());
1693 let amount = Amount::new(50, custom_unit.clone());
1694
1695 assert_eq!(amount.value(), 50);
1696 assert_eq!(amount.unit(), &custom_unit);
1697 }
1698
1699 #[test]
1700 fn test_amount_into_parts() {
1701 let amount = Amount::new(1234, CurrencyUnit::Msat);
1702 let (value, unit) = amount.into_parts();
1703
1704 assert_eq!(value, 1234);
1705 assert_eq!(unit, CurrencyUnit::Msat);
1706 }
1707
1708 #[test]
1709 fn test_amount_with_unit_conversion() {
1710 let untyped: Amount<()> = Amount::from(100);
1711 let typed = untyped.with_unit(CurrencyUnit::Sat);
1712
1713 assert_eq!(typed.value(), 100);
1714 assert_eq!(typed.unit(), &CurrencyUnit::Sat);
1715 }
1716
1717 #[test]
1718 fn test_amount_with_unit_all_variants() {
1719 let untyped = Amount::from(500);
1720
1721 let sat = untyped.with_unit(CurrencyUnit::Sat);
1722 assert_eq!(sat.unit(), &CurrencyUnit::Sat);
1723
1724 let msat = untyped.with_unit(CurrencyUnit::Msat);
1725 assert_eq!(msat.unit(), &CurrencyUnit::Msat);
1726
1727 let usd = untyped.with_unit(CurrencyUnit::Usd);
1728 assert_eq!(usd.unit(), &CurrencyUnit::Usd);
1729
1730 let eur = untyped.with_unit(CurrencyUnit::Eur);
1731 assert_eq!(eur.unit(), &CurrencyUnit::Eur);
1732
1733 let custom = untyped.with_unit(CurrencyUnit::Custom("TEST".into()));
1734 assert_eq!(custom.unit(), &CurrencyUnit::Custom("TEST".into()));
1735 }
1736
1737 #[test]
1738 fn test_typed_amount_is_clone_not_copy() {
1739 let amount = Amount::new(100, CurrencyUnit::Sat);
1740 let cloned = amount.clone();
1741 assert_eq!(cloned.value(), 100);
1743 assert_eq!(cloned.unit(), &CurrencyUnit::Sat);
1744 }
1745
1746 #[test]
1749 fn test_untyped_amount_is_copy() {
1750 let amount: Amount<()> = Amount::from(100);
1752 let copy1 = amount;
1753 let copy2 = amount; assert_eq!(copy1, copy2);
1755 }
1756
1757 #[test]
1758 fn test_amount_serialization_transparent() {
1759 let amount = Amount::from(1234);
1761 let json = serde_json::to_string(&amount).unwrap();
1762 assert_eq!(json, "1234");
1763
1764 let deserialized: Amount<()> = serde_json::from_str(&json).unwrap();
1766 assert_eq!(deserialized, Amount::from(1234));
1767 }
1768
1769 #[test]
1770 fn test_typed_amount_serialization() {
1771 let amount = Amount::new(5678, CurrencyUnit::Sat);
1773 let json = serde_json::to_string(&amount).unwrap();
1774 assert_eq!(json, "5678");
1775
1776 }
1779
1780 #[test]
1781 fn test_protocol_type_pattern() {
1782 let protocol_amount: Amount<()> = Amount::from(1000);
1786 let _copied = protocol_amount; let typed = protocol_amount.with_unit(CurrencyUnit::Sat);
1790 assert_eq!(typed.value(), 1000);
1791
1792 let back_to_protocol = Amount::from(typed.value());
1794 assert_eq!(back_to_protocol, protocol_amount);
1795 }
1796
1797 #[test]
1800 fn test_typed_amount_checked_add() {
1801 let a = Amount::new(100, CurrencyUnit::Sat);
1802 let b = Amount::new(50, CurrencyUnit::Sat);
1803
1804 let sum = a.checked_add(&b).unwrap();
1805 assert_eq!(sum.value(), 150);
1806 assert_eq!(sum.unit(), &CurrencyUnit::Sat);
1807 }
1808
1809 #[test]
1810 fn test_typed_amount_add_unit_mismatch() {
1811 let sat = Amount::new(100, CurrencyUnit::Sat);
1812 let msat = Amount::new(100, CurrencyUnit::Msat);
1813
1814 let result = sat.checked_add(&msat);
1815 assert!(result.is_err());
1816
1817 match result.unwrap_err() {
1818 Error::UnitMismatch(u1, u2) => {
1819 assert_eq!(u1, CurrencyUnit::Sat);
1820 assert_eq!(u2, CurrencyUnit::Msat);
1821 }
1822 _ => panic!("Expected UnitMismatch error"),
1823 }
1824 }
1825
1826 #[test]
1827 fn test_typed_amount_checked_sub() {
1828 let a = Amount::new(100, CurrencyUnit::Sat);
1829 let b = Amount::new(30, CurrencyUnit::Sat);
1830
1831 let diff = a.checked_sub(&b).unwrap();
1832 assert_eq!(diff.value(), 70);
1833 assert_eq!(diff.unit(), &CurrencyUnit::Sat);
1834 }
1835
1836 #[test]
1837 fn test_typed_amount_sub_unit_mismatch() {
1838 let sat = Amount::new(100, CurrencyUnit::Sat);
1839 let usd = Amount::new(30, CurrencyUnit::Usd);
1840
1841 let result = sat.checked_sub(&usd);
1842 assert!(result.is_err());
1843 }
1844
1845 #[test]
1846 fn test_typed_amount_convert_to() {
1847 let sat = Amount::new(1000, CurrencyUnit::Sat);
1849 let msat = sat.convert_to(&CurrencyUnit::Msat).unwrap();
1850 assert_eq!(msat.value(), 1_000_000);
1851 assert_eq!(msat.unit(), &CurrencyUnit::Msat);
1852
1853 let msat = Amount::new(5000, CurrencyUnit::Msat);
1855 let sat = msat.convert_to(&CurrencyUnit::Sat).unwrap();
1856 assert_eq!(sat.value(), 5);
1857 assert_eq!(sat.unit(), &CurrencyUnit::Sat);
1858
1859 let sat = Amount::new(100, CurrencyUnit::Sat);
1861 let same = sat.convert_to(&CurrencyUnit::Sat).unwrap();
1862 assert_eq!(same.value(), 100);
1863 assert_eq!(same.unit(), &CurrencyUnit::Sat);
1864 }
1865
1866 #[test]
1867 fn test_typed_amount_convert_invalid() {
1868 let sat = Amount::new(100, CurrencyUnit::Sat);
1869 let result = sat.convert_to(&CurrencyUnit::Eur);
1870 assert!(result.is_err());
1871
1872 match result.unwrap_err() {
1873 Error::CannotConvertUnits => {}
1874 _ => panic!("Expected CannotConvertUnits error"),
1875 }
1876 }
1877
1878 #[test]
1879 fn test_typed_amount_add_overflow() {
1880 let a = Amount::new(u64::MAX, CurrencyUnit::Sat);
1881 let b = Amount::new(1, CurrencyUnit::Sat);
1882
1883 let result = a.checked_add(&b);
1884 assert!(result.is_err());
1885
1886 match result.unwrap_err() {
1887 Error::AmountOverflow => {}
1888 _ => panic!("Expected AmountOverflow error"),
1889 }
1890 }
1891
1892 #[test]
1893 fn test_typed_amount_sub_underflow() {
1894 let a = Amount::new(50, CurrencyUnit::Sat);
1895 let b = Amount::new(100, CurrencyUnit::Sat);
1896
1897 let result = a.checked_sub(&b);
1898 assert!(result.is_err());
1899
1900 match result.unwrap_err() {
1901 Error::AmountOverflow => {} _ => panic!("Expected AmountOverflow error"),
1903 }
1904 }
1905
1906 #[test]
1910 fn test_typed_amount_equality_same_unit() {
1911 let a = Amount::new(100, CurrencyUnit::Sat);
1912 let b = Amount::new(100, CurrencyUnit::Sat);
1913
1914 assert_eq!(a, b);
1915 assert!(a == b);
1916
1917 let c = Amount::new(50, CurrencyUnit::Sat);
1918 assert_ne!(a, c);
1919 assert!(a != c);
1920 }
1921
1922 #[test]
1924 fn test_typed_amount_equality_different_units() {
1925 let sat = Amount::new(100, CurrencyUnit::Sat);
1926 let msat = Amount::new(100, CurrencyUnit::Msat);
1927
1928 assert_ne!(sat, msat);
1930 assert!(sat != msat);
1931
1932 let usd = Amount::new(100, CurrencyUnit::Usd);
1933 assert_ne!(sat, usd);
1934 assert_ne!(msat, usd);
1935 }
1936
1937 #[test]
1939 fn test_typed_amount_comparison_same_unit() {
1940 let small = Amount::new(50, CurrencyUnit::Sat);
1941 let large = Amount::new(100, CurrencyUnit::Sat);
1942
1943 assert!(large > small);
1945 assert!(small <= large);
1946
1947 assert!(small < large);
1949 assert!(large >= small);
1950
1951 assert!(large >= small);
1953 assert!(large >= Amount::new(100, CurrencyUnit::Sat));
1954
1955 assert!(small <= large);
1957 assert!(small <= Amount::new(50, CurrencyUnit::Sat));
1958
1959 assert_eq!(large.partial_cmp(&small), Some(std::cmp::Ordering::Greater));
1961 assert_eq!(small.partial_cmp(&large), Some(std::cmp::Ordering::Less));
1962 assert_eq!(
1963 small.partial_cmp(&Amount::new(50, CurrencyUnit::Sat)),
1964 Some(std::cmp::Ordering::Equal)
1965 );
1966 }
1967
1968 #[test]
1971 fn test_typed_amount_comparison_different_units_returns_none() {
1972 let sat = Amount::new(100, CurrencyUnit::Sat);
1973 let msat = Amount::new(50, CurrencyUnit::Msat);
1974
1975 assert_eq!(sat.partial_cmp(&msat), None);
1977 assert_eq!(msat.partial_cmp(&sat), None);
1978
1979 let usd = Amount::new(100, CurrencyUnit::Usd);
1981 assert_eq!(sat.partial_cmp(&usd), None);
1982 assert_eq!(usd.partial_cmp(&sat), None);
1983
1984 let eur = Amount::new(100, CurrencyUnit::Eur);
1985 assert_eq!(usd.partial_cmp(&eur), None);
1986
1987 let custom = Amount::new(100, CurrencyUnit::Custom("BTC".into()));
1988 assert_eq!(sat.partial_cmp(&custom), None);
1989 }
1990
1991 #[test]
1994 fn test_typed_amount_comparison_operators_different_units() {
1995 let sat = Amount::new(100, CurrencyUnit::Sat);
1996 let msat = Amount::new(50, CurrencyUnit::Msat);
1997
1998 assert!(sat.partial_cmp(&msat).is_none());
2005 assert!(msat.partial_cmp(&sat).is_none());
2006
2007 let sat100 = Amount::new(100, CurrencyUnit::Sat);
2009 let msat100 = Amount::new(100, CurrencyUnit::Msat);
2010
2011 assert!(sat100.partial_cmp(&msat100).is_none());
2012 }
2013
2014 #[test]
2016 fn test_untyped_amount_has_total_ordering() {
2017 use std::cmp::Ordering;
2018
2019 let a: Amount<()> = Amount::from(50);
2020 let b: Amount<()> = Amount::from(100);
2021 let c: Amount<()> = Amount::from(50);
2022
2023 assert_eq!(a.cmp(&b), Ordering::Less);
2025 assert_eq!(b.cmp(&a), Ordering::Greater);
2026 assert_eq!(a.cmp(&c), Ordering::Equal);
2027
2028 assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
2030 assert_eq!(b.partial_cmp(&a), Some(Ordering::Greater));
2031 assert_eq!(a.partial_cmp(&c), Some(Ordering::Equal));
2032 }
2033
2034 #[test]
2036 fn test_untyped_amount_sorting() {
2037 let mut amounts: Vec<Amount<()>> = vec![
2038 Amount::from(100),
2039 Amount::from(25),
2040 Amount::from(75),
2041 Amount::from(50),
2042 ];
2043
2044 amounts.sort();
2045
2046 assert_eq!(
2047 amounts,
2048 vec![
2049 Amount::from(25),
2050 Amount::from(50),
2051 Amount::from(75),
2052 Amount::from(100),
2053 ]
2054 );
2055 }
2056
2057 #[test]
2058 fn test_amount_currency_unit_to_i64() {
2059 let amount = Amount::new(100, CurrencyUnit::Sat);
2060 assert_eq!(amount.to_i64(), Some(100));
2061
2062 let amount = Amount::new(i64::MAX as u64, CurrencyUnit::Sat);
2063 assert_eq!(amount.to_i64(), Some(i64::MAX));
2064
2065 let amount = Amount::new(i64::MAX as u64 + 1, CurrencyUnit::Sat);
2066 assert_eq!(amount.to_i64(), None);
2067
2068 let amount = Amount::new(0, CurrencyUnit::Sat);
2069 assert_eq!(amount.to_i64(), Some(0));
2070
2071 let amount = Amount::new(1, CurrencyUnit::Sat);
2072 assert_eq!(amount.to_i64(), Some(1));
2073 }
2074
2075 #[test]
2076 fn test_display_with_unit() {
2077 let amount = Amount::new(100, CurrencyUnit::Sat);
2078 assert_eq!(amount.display_with_unit(), "100 sat");
2079
2080 let amount = Amount::new(50, CurrencyUnit::Msat);
2081 assert_eq!(amount.display_with_unit(), "50 msat");
2082
2083 let amount = Amount::new(100, CurrencyUnit::Usd);
2084 assert_eq!(amount.display_with_unit(), "100 usd");
2085
2086 let amount = Amount::new(123, CurrencyUnit::Custom("BTC".to_string()));
2087 assert_eq!(amount.display_with_unit(), "123 btc");
2088 }
2089
2090 #[test]
2091 fn test_amount_add_operator() {
2092 let a = Amount::from(100);
2093 let b = Amount::from(50);
2094 let sum = a + b;
2095 assert_eq!(sum, Amount::from(150));
2096 assert_ne!(sum, Amount::ZERO);
2097 }
2098
2099 #[test]
2110 fn test_saturating_sub_normal_case() {
2111 let amount1 = Amount::from(100);
2113 let amount2 = Amount::from(30);
2114 let result = amount1.saturating_sub(amount2);
2115 assert_eq!(result, Amount::from(70));
2116 assert_ne!(result, Amount::ZERO);
2117
2118 let amount1 = Amount::from(1000);
2120 let amount2 = Amount::from(1);
2121 let result = amount1.saturating_sub(amount2);
2122 assert_eq!(result, Amount::from(999));
2123
2124 let amount1 = Amount::from(2);
2126 let amount2 = Amount::from(1);
2127 let result = amount1.saturating_sub(amount2);
2128 assert_eq!(result, Amount::from(1));
2129 assert_ne!(result, Amount::ZERO);
2130 }
2131
2132 #[test]
2137 fn test_saturating_sub_saturates_at_zero() {
2138 let amount1 = Amount::from(30);
2140 let amount2 = Amount::from(100);
2141 let result = amount1.saturating_sub(amount2);
2142 assert_eq!(result, Amount::ZERO);
2143 assert_ne!(result, Amount::from(30)); let amount1 = Amount::from(5);
2147 let amount2 = Amount::from(10);
2148 let result = amount1.saturating_sub(amount2);
2149 assert_eq!(result, Amount::ZERO);
2150
2151 let amount1 = Amount::ZERO;
2153 let amount2 = Amount::from(1);
2154 let result = amount1.saturating_sub(amount2);
2155 assert_eq!(result, Amount::ZERO);
2156 }
2157
2158 #[test]
2162 fn test_saturating_sub_equal_amounts() {
2163 let amount1 = Amount::from(100);
2165 let amount2 = Amount::from(100);
2166 let result = amount1.saturating_sub(amount2);
2167 assert_eq!(result, Amount::ZERO);
2168
2169 let amount1 = Amount::from(1);
2171 let amount2 = Amount::from(1);
2172 let result = amount1.saturating_sub(amount2);
2173 assert_eq!(result, Amount::ZERO);
2174
2175 let result = Amount::ZERO.saturating_sub(Amount::ZERO);
2177 assert_eq!(result, Amount::ZERO);
2178 }
2179
2180 #[test]
2186 fn test_saturating_sub_edge_case_other_greater_by_one() {
2187 let amount1 = Amount::from(10);
2189 let amount2 = Amount::from(11); let result = amount1.saturating_sub(amount2);
2191 assert_eq!(result, Amount::ZERO);
2193
2194 let amount1 = Amount::from(1);
2196 let amount2 = Amount::from(2);
2197 let result = amount1.saturating_sub(amount2);
2198 assert_eq!(result, Amount::ZERO);
2199
2200 let amount1 = Amount::ZERO;
2202 let amount2 = Amount::from(1);
2203 let result = amount1.saturating_sub(amount2);
2204 assert_eq!(result, Amount::ZERO);
2205 }
2206
2207 #[test]
2220 fn test_split_single_denomination_keyset() {
2221 let fee_and_amounts: FeeAndAmounts = (0, vec![1]).into();
2222
2223 let result = Amount::from(1).split(&fee_and_amounts).unwrap();
2225 assert_eq!(result, vec![Amount::from(1)]);
2226
2227 let result = Amount::from(2).split(&fee_and_amounts).unwrap();
2229 assert_eq!(result, vec![Amount::from(1), Amount::from(1)]);
2230
2231 let result = Amount::from(5).split(&fee_and_amounts).unwrap();
2233 assert_eq!(result, vec![Amount::from(1); 5]);
2234
2235 let result = Amount::from(10).split(&fee_and_amounts).unwrap();
2237 assert_eq!(result, vec![Amount::from(1); 10]);
2238 }
2239
2240 #[test]
2243 fn test_split_denomination_reuse_required() {
2244 let fee_and_amounts: FeeAndAmounts = (0, vec![1, 5]).into();
2245
2246 let result = Amount::from(5).split(&fee_and_amounts).unwrap();
2248 assert_eq!(result, vec![Amount::from(5)]);
2249
2250 let result = Amount::from(10).split(&fee_and_amounts).unwrap();
2252 let total = Amount::try_sum(result.iter().copied()).unwrap();
2253 assert_eq!(total, Amount::from(10));
2254 assert_eq!(result, vec![Amount::from(5), Amount::from(5)]);
2255
2256 let result = Amount::from(12).split(&fee_and_amounts).unwrap();
2258 let total = Amount::try_sum(result.iter().copied()).unwrap();
2259 assert_eq!(total, Amount::from(12));
2260 assert_eq!(
2261 result,
2262 vec![
2263 Amount::from(5),
2264 Amount::from(5),
2265 Amount::from(1),
2266 Amount::from(1),
2267 ]
2268 );
2269 }
2270
2271 #[test]
2274 fn test_split_non_power_of_two_keyset() {
2275 let fee_and_amounts: FeeAndAmounts = (0, vec![1, 3, 5]).into();
2276
2277 let result = Amount::from(6).split(&fee_and_amounts).unwrap();
2279 let total = Amount::try_sum(result.iter().copied()).unwrap();
2280 assert_eq!(total, Amount::from(6));
2281
2282 let result = Amount::from(9).split(&fee_and_amounts).unwrap();
2284 let total = Amount::try_sum(result.iter().copied()).unwrap();
2285 assert_eq!(total, Amount::from(9));
2286
2287 let result = Amount::from(10).split(&fee_and_amounts).unwrap();
2290 let total = Amount::try_sum(result.iter().copied()).unwrap();
2291 assert_eq!(total, Amount::from(10));
2292
2293 let result = Amount::from(15).split(&fee_and_amounts).unwrap();
2295 let total = Amount::try_sum(result.iter().copied()).unwrap();
2296 assert_eq!(total, Amount::from(15));
2297 }
2298
2299 #[test]
2302 fn test_split_sparse_power_of_two_keyset() {
2303 let fee_and_amounts: FeeAndAmounts = (0, vec![1, 8]).into();
2304
2305 let result = Amount::from(3).split(&fee_and_amounts).unwrap();
2307 assert_eq!(result, vec![Amount::from(1); 3]);
2308
2309 let result = Amount::from(9).split(&fee_and_amounts).unwrap();
2311 let total = Amount::try_sum(result.iter().copied()).unwrap();
2312 assert_eq!(total, Amount::from(9));
2313 assert_eq!(result, vec![Amount::from(8), Amount::from(1)]);
2314
2315 let result = Amount::from(17).split(&fee_and_amounts).unwrap();
2317 let total = Amount::try_sum(result.iter().copied()).unwrap();
2318 assert_eq!(total, Amount::from(17));
2319 assert_eq!(
2320 result,
2321 vec![Amount::from(8), Amount::from(8), Amount::from(1)]
2322 );
2323 }
2324
2325 #[test]
2328 fn test_split_partial_power_of_two_keyset() {
2329 let fee_and_amounts: FeeAndAmounts = (0, vec![1, 4, 16]).into();
2330
2331 let result = Amount::from(2).split(&fee_and_amounts).unwrap();
2333 assert_eq!(result, vec![Amount::from(1), Amount::from(1)]);
2334
2335 let result = Amount::from(5).split(&fee_and_amounts).unwrap();
2337 assert_eq!(result, vec![Amount::from(4), Amount::from(1)]);
2338
2339 let result = Amount::from(6).split(&fee_and_amounts).unwrap();
2341 let total = Amount::try_sum(result.iter().copied()).unwrap();
2342 assert_eq!(total, Amount::from(6));
2343 assert_eq!(
2344 result,
2345 vec![Amount::from(4), Amount::from(1), Amount::from(1)]
2346 );
2347
2348 let result = Amount::from(20).split(&fee_and_amounts).unwrap();
2350 assert_eq!(result, vec![Amount::from(16), Amount::from(4)]);
2351
2352 let result = Amount::from(8).split(&fee_and_amounts).unwrap();
2354 assert_eq!(result, vec![Amount::from(4), Amount::from(4)]);
2355 }
2356
2357 #[test]
2360 fn test_split_large_amount_single_denomination() {
2361 let fee_and_amounts: FeeAndAmounts = (0, vec![1]).into();
2362
2363 let result = Amount::from(100).split(&fee_and_amounts).unwrap();
2364 assert_eq!(result.len(), 100);
2365 assert!(result.iter().all(|a| *a == Amount::from(1)));
2366 let total = Amount::try_sum(result.iter().copied()).unwrap();
2367 assert_eq!(total, Amount::from(100));
2368 }
2369
2370 #[test]
2372 fn test_split_zero_amount() {
2373 let fee_and_amounts: FeeAndAmounts = (0, vec![1]).into();
2374
2375 let result = Amount::from(0).split(&fee_and_amounts).unwrap();
2376 assert!(result.is_empty());
2377
2378 let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
2380 let result = Amount::from(0).split(&fee_and_amounts).unwrap();
2381 assert!(result.is_empty());
2382 }
2383
2384 #[test]
2391 fn test_split_targeted_none_single_denomination() {
2392 let fee_and_amounts: FeeAndAmounts = (0, vec![1]).into();
2393
2394 let result = Amount::from(5)
2395 .split_targeted(&SplitTarget::None, &fee_and_amounts)
2396 .unwrap();
2397 assert_eq!(result, vec![Amount::from(1); 5]);
2398
2399 let total = Amount::try_sum(result.iter().copied()).unwrap();
2400 assert_eq!(total, Amount::from(5));
2401 }
2402
2403 #[test]
2406 fn test_split_targeted_value_single_denomination() {
2407 let fee_and_amounts: FeeAndAmounts = (0, vec![1]).into();
2408
2409 let result = Amount::from(5)
2410 .split_targeted(&SplitTarget::Value(Amount::from(1)), &fee_and_amounts)
2411 .unwrap();
2412 assert_eq!(result, vec![Amount::from(1); 5]);
2413
2414 let total = Amount::try_sum(result.iter().copied()).unwrap();
2415 assert_eq!(total, Amount::from(5));
2416 }
2417
2418 #[test]
2421 fn test_split_targeted_values_single_denomination() {
2422 let fee_and_amounts: FeeAndAmounts = (0, vec![1]).into();
2423
2424 let target = SplitTarget::Values(vec![Amount::from(1), Amount::from(1), Amount::from(1)]);
2426 let result = Amount::from(5)
2427 .split_targeted(&target, &fee_and_amounts)
2428 .unwrap();
2429
2430 let total = Amount::try_sum(result.iter().copied()).unwrap();
2431 assert_eq!(total, Amount::from(5));
2432 assert_eq!(result, vec![Amount::from(1); 5]);
2433 }
2434
2435 #[test]
2438 fn test_split_targeted_value_restricted_keyset() {
2439 let fee_and_amounts: FeeAndAmounts = (0, vec![1, 5]).into();
2440
2441 let result = Amount::from(15)
2442 .split_targeted(&SplitTarget::Value(Amount::from(5)), &fee_and_amounts)
2443 .unwrap();
2444
2445 let total = Amount::try_sum(result.iter().copied()).unwrap();
2446 assert_eq!(total, Amount::from(15));
2447 assert_eq!(
2448 result,
2449 vec![Amount::from(5), Amount::from(5), Amount::from(5)]
2450 );
2451 }
2452
2453 #[test]
2460 fn test_split_with_fee_single_denomination() {
2461 let fee_and_amounts: FeeAndAmounts = (100, vec![1]).into();
2462
2463 let amount = Amount::from(5);
2464 let result = amount.split_with_fee(&fee_and_amounts).unwrap();
2465
2466 let total = Amount::try_sum(result.iter().copied()).unwrap();
2467 let total_fee_ppk = (result.len() as u64) * fee_and_amounts.fee;
2468 let total_fee = Amount::from(total_fee_ppk.div_ceil(1000));
2469
2470 assert!(
2472 total >= amount.checked_add(total_fee).unwrap(),
2473 "Split total {} should be >= amount {} + fee {}",
2474 total,
2475 amount,
2476 total_fee
2477 );
2478
2479 assert!(result.iter().all(|a| *a == Amount::from(1)));
2481 }
2482
2483 #[test]
2491 fn test_split_xsr_mint_keyset_regression() {
2492 let fee_and_amounts: FeeAndAmounts = (0, vec![1]).into();
2494
2495 let amount = Amount::from(2);
2497 let result = amount.split(&fee_and_amounts);
2498 assert!(
2499 result.is_ok(),
2500 "split(2) with keyset {{1}} should succeed but got: {:?}",
2501 result.err()
2502 );
2503 let proofs = result.unwrap();
2504 assert_eq!(proofs, vec![Amount::from(1), Amount::from(1)]);
2505
2506 let amount = Amount::from(5);
2508 let result = amount.split(&fee_and_amounts);
2509 assert!(
2510 result.is_ok(),
2511 "split(5) with keyset {{1}} should succeed but got: {:?}",
2512 result.err()
2513 );
2514 let proofs = result.unwrap();
2515 assert_eq!(proofs.len(), 5);
2516 assert_eq!(
2517 Amount::try_sum(proofs.iter().copied()).unwrap(),
2518 Amount::from(5)
2519 );
2520 }
2521}