amount.rs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  1. //! CDK Amount
  2. //!
  3. //! Is any unit and will be treated as the unit of the wallet
  4. use std::cmp::Ordering;
  5. use std::collections::HashMap;
  6. use std::fmt;
  7. use std::str::FromStr;
  8. use lightning::offers::offer::Offer;
  9. use serde::{Deserialize, Serialize};
  10. use thiserror::Error;
  11. use crate::nuts::CurrencyUnit;
  12. use crate::Id;
  13. /// Amount Error
  14. #[derive(Debug, Error)]
  15. pub enum Error {
  16. /// Split Values must be less then or equal to amount
  17. #[error("Split Values must be less then or equal to amount")]
  18. SplitValuesGreater,
  19. /// Amount overflow
  20. #[error("Amount Overflow")]
  21. AmountOverflow,
  22. /// Cannot convert units
  23. #[error("Cannot convert units")]
  24. CannotConvertUnits,
  25. /// Invalid amount
  26. #[error("Invalid Amount: {0}")]
  27. InvalidAmount(String),
  28. /// Amount undefined
  29. #[error("Amount undefined")]
  30. AmountUndefined,
  31. /// Utf8 parse error
  32. #[error(transparent)]
  33. Utf8ParseError(#[from] std::string::FromUtf8Error),
  34. }
  35. /// Amount can be any unit
  36. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
  37. #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
  38. #[serde(transparent)]
  39. pub struct Amount(u64);
  40. /// Fees and and amount type, it can be casted just as a reference to the inner amounts, or a single
  41. /// u64 which is the fee
  42. #[derive(Debug, Clone)]
  43. pub struct FeeAndAmounts {
  44. fee: u64,
  45. amounts: Vec<u64>,
  46. }
  47. impl From<(u64, Vec<u64>)> for FeeAndAmounts {
  48. fn from(value: (u64, Vec<u64>)) -> Self {
  49. Self {
  50. fee: value.0,
  51. amounts: value.1,
  52. }
  53. }
  54. }
  55. impl FeeAndAmounts {
  56. /// Fees
  57. #[inline(always)]
  58. pub fn fee(&self) -> u64 {
  59. self.fee
  60. }
  61. /// Amounts
  62. #[inline(always)]
  63. pub fn amounts(&self) -> &[u64] {
  64. &self.amounts
  65. }
  66. }
  67. /// Fees and Amounts for each Keyset
  68. pub type KeysetFeeAndAmounts = HashMap<Id, FeeAndAmounts>;
  69. impl FromStr for Amount {
  70. type Err = Error;
  71. fn from_str(s: &str) -> Result<Self, Self::Err> {
  72. let value = s
  73. .parse::<u64>()
  74. .map_err(|_| Error::InvalidAmount(s.to_owned()))?;
  75. Ok(Amount(value))
  76. }
  77. }
  78. impl Amount {
  79. /// Amount zero
  80. pub const ZERO: Amount = Amount(0);
  81. /// Amount one
  82. pub const ONE: Amount = Amount(1);
  83. /// Split into parts that are powers of two
  84. pub fn split(&self, fee_and_amounts: &FeeAndAmounts) -> Vec<Self> {
  85. fee_and_amounts
  86. .amounts
  87. .iter()
  88. .rev()
  89. .fold((Vec::new(), self.0), |(mut acc, total), &amount| {
  90. if total >= amount {
  91. acc.push(Self::from(amount));
  92. }
  93. (acc, total % amount)
  94. })
  95. .0
  96. }
  97. /// Split into parts that are powers of two by target
  98. pub fn split_targeted(
  99. &self,
  100. target: &SplitTarget,
  101. fee_and_amounts: &FeeAndAmounts,
  102. ) -> Result<Vec<Self>, Error> {
  103. let mut parts = match target {
  104. SplitTarget::None => self.split(fee_and_amounts),
  105. SplitTarget::Value(amount) => {
  106. if self.le(amount) {
  107. return Ok(self.split(fee_and_amounts));
  108. }
  109. let mut parts_total = Amount::ZERO;
  110. let mut parts = Vec::new();
  111. // The powers of two that are need to create target value
  112. let parts_of_value = amount.split(fee_and_amounts);
  113. while parts_total.lt(self) {
  114. for part in parts_of_value.iter().copied() {
  115. if (part + parts_total).le(self) {
  116. parts.push(part);
  117. } else {
  118. let amount_left = *self - parts_total;
  119. parts.extend(amount_left.split(fee_and_amounts));
  120. }
  121. parts_total = Amount::try_sum(parts.clone().iter().copied())?;
  122. if parts_total.eq(self) {
  123. break;
  124. }
  125. }
  126. }
  127. parts
  128. }
  129. SplitTarget::Values(values) => {
  130. let values_total: Amount = Amount::try_sum(values.clone().into_iter())?;
  131. match self.cmp(&values_total) {
  132. Ordering::Equal => values.clone(),
  133. Ordering::Less => {
  134. return Err(Error::SplitValuesGreater);
  135. }
  136. Ordering::Greater => {
  137. let extra = *self - values_total;
  138. let mut extra_amount = extra.split(fee_and_amounts);
  139. let mut values = values.clone();
  140. values.append(&mut extra_amount);
  141. values
  142. }
  143. }
  144. }
  145. };
  146. parts.sort();
  147. Ok(parts)
  148. }
  149. /// Splits amount into powers of two while accounting for the swap fee
  150. pub fn split_with_fee(&self, fee_and_amounts: &FeeAndAmounts) -> Result<Vec<Self>, Error> {
  151. let without_fee_amounts = self.split(fee_and_amounts);
  152. let total_fee_ppk = fee_and_amounts
  153. .fee
  154. .checked_mul(without_fee_amounts.len() as u64)
  155. .ok_or(Error::AmountOverflow)?;
  156. let fee = Amount::from(total_fee_ppk.div_ceil(1000));
  157. let new_amount = self.checked_add(fee).ok_or(Error::AmountOverflow)?;
  158. let split = new_amount.split(fee_and_amounts);
  159. let split_fee_ppk = (split.len() as u64)
  160. .checked_mul(fee_and_amounts.fee)
  161. .ok_or(Error::AmountOverflow)?;
  162. let split_fee = Amount::from(split_fee_ppk.div_ceil(1000));
  163. if let Some(net_amount) = new_amount.checked_sub(split_fee) {
  164. if net_amount >= *self {
  165. return Ok(split);
  166. }
  167. }
  168. self.checked_add(Amount::ONE)
  169. .ok_or(Error::AmountOverflow)?
  170. .split_with_fee(fee_and_amounts)
  171. }
  172. /// Checked addition for Amount. Returns None if overflow occurs.
  173. pub fn checked_add(self, other: Amount) -> Option<Amount> {
  174. self.0.checked_add(other.0).map(Amount)
  175. }
  176. /// Checked subtraction for Amount. Returns None if overflow occurs.
  177. pub fn checked_sub(self, other: Amount) -> Option<Amount> {
  178. self.0.checked_sub(other.0).map(Amount)
  179. }
  180. /// Checked multiplication for Amount. Returns None if overflow occurs.
  181. pub fn checked_mul(self, other: Amount) -> Option<Amount> {
  182. self.0.checked_mul(other.0).map(Amount)
  183. }
  184. /// Checked division for Amount. Returns None if overflow occurs.
  185. pub fn checked_div(self, other: Amount) -> Option<Amount> {
  186. self.0.checked_div(other.0).map(Amount)
  187. }
  188. /// Try sum to check for overflow
  189. pub fn try_sum<I>(iter: I) -> Result<Self, Error>
  190. where
  191. I: IntoIterator<Item = Self>,
  192. {
  193. iter.into_iter().try_fold(Amount::ZERO, |acc, x| {
  194. acc.checked_add(x).ok_or(Error::AmountOverflow)
  195. })
  196. }
  197. /// Convert unit
  198. pub fn convert_unit(
  199. &self,
  200. current_unit: &CurrencyUnit,
  201. target_unit: &CurrencyUnit,
  202. ) -> Result<Amount, Error> {
  203. to_unit(self.0, current_unit, target_unit)
  204. }
  205. ///
  206. /// Convert to u64
  207. pub fn to_u64(self) -> u64 {
  208. self.0
  209. }
  210. /// Convert to i64
  211. pub fn to_i64(self) -> Option<i64> {
  212. if self.0 <= i64::MAX as u64 {
  213. Some(self.0 as i64)
  214. } else {
  215. None
  216. }
  217. }
  218. /// Create from i64, returning None if negative
  219. pub fn from_i64(value: i64) -> Option<Self> {
  220. if value >= 0 {
  221. Some(Amount(value as u64))
  222. } else {
  223. None
  224. }
  225. }
  226. }
  227. impl Default for Amount {
  228. fn default() -> Self {
  229. Amount::ZERO
  230. }
  231. }
  232. impl Default for &Amount {
  233. fn default() -> Self {
  234. &Amount::ZERO
  235. }
  236. }
  237. impl fmt::Display for Amount {
  238. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  239. if let Some(width) = f.width() {
  240. write!(f, "{:width$}", self.0, width = width)
  241. } else {
  242. write!(f, "{}", self.0)
  243. }
  244. }
  245. }
  246. impl From<u64> for Amount {
  247. fn from(value: u64) -> Self {
  248. Self(value)
  249. }
  250. }
  251. impl From<&u64> for Amount {
  252. fn from(value: &u64) -> Self {
  253. Self(*value)
  254. }
  255. }
  256. impl From<Amount> for u64 {
  257. fn from(value: Amount) -> Self {
  258. value.0
  259. }
  260. }
  261. impl AsRef<u64> for Amount {
  262. fn as_ref(&self) -> &u64 {
  263. &self.0
  264. }
  265. }
  266. impl std::ops::Add for Amount {
  267. type Output = Amount;
  268. fn add(self, rhs: Amount) -> Self::Output {
  269. self.checked_add(rhs)
  270. .expect("Addition overflow: the sum of the amounts exceeds the maximum value")
  271. }
  272. }
  273. impl std::ops::AddAssign for Amount {
  274. fn add_assign(&mut self, rhs: Self) {
  275. *self = self
  276. .checked_add(rhs)
  277. .expect("AddAssign overflow: the sum of the amounts exceeds the maximum value");
  278. }
  279. }
  280. impl std::ops::Sub for Amount {
  281. type Output = Amount;
  282. fn sub(self, rhs: Amount) -> Self::Output {
  283. self.checked_sub(rhs)
  284. .expect("Subtraction underflow: cannot subtract a larger amount from a smaller amount")
  285. }
  286. }
  287. impl std::ops::SubAssign for Amount {
  288. fn sub_assign(&mut self, other: Self) {
  289. *self = self
  290. .checked_sub(other)
  291. .expect("SubAssign underflow: cannot subtract a larger amount from a smaller amount");
  292. }
  293. }
  294. impl std::ops::Mul for Amount {
  295. type Output = Self;
  296. fn mul(self, other: Self) -> Self::Output {
  297. self.checked_mul(other)
  298. .expect("Multiplication overflow: the product of the amounts exceeds the maximum value")
  299. }
  300. }
  301. impl std::ops::Div for Amount {
  302. type Output = Self;
  303. fn div(self, other: Self) -> Self::Output {
  304. self.checked_div(other)
  305. .expect("Division error: cannot divide by zero or overflow occurred")
  306. }
  307. }
  308. /// Convert offer to amount in unit
  309. pub fn amount_for_offer(offer: &Offer, unit: &CurrencyUnit) -> Result<Amount, Error> {
  310. let offer_amount = offer.amount().ok_or(Error::AmountUndefined)?;
  311. let (amount, currency) = match offer_amount {
  312. lightning::offers::offer::Amount::Bitcoin { amount_msats } => {
  313. (amount_msats, CurrencyUnit::Msat)
  314. }
  315. lightning::offers::offer::Amount::Currency {
  316. iso4217_code,
  317. amount,
  318. } => (
  319. amount,
  320. CurrencyUnit::from_str(&String::from_utf8(iso4217_code.to_vec())?)
  321. .map_err(|_| Error::CannotConvertUnits)?,
  322. ),
  323. };
  324. to_unit(amount, &currency, unit).map_err(|_err| Error::CannotConvertUnits)
  325. }
  326. /// Kinds of targeting that are supported
  327. #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)]
  328. pub enum SplitTarget {
  329. /// Default target; least amount of proofs
  330. #[default]
  331. None,
  332. /// Target amount for wallet to have most proofs that add up to value
  333. Value(Amount),
  334. /// Specific amounts to split into **MUST** equal amount being split
  335. Values(Vec<Amount>),
  336. }
  337. /// Msats in sat
  338. pub const MSAT_IN_SAT: u64 = 1000;
  339. /// Helper function to convert units
  340. pub fn to_unit<T>(
  341. amount: T,
  342. current_unit: &CurrencyUnit,
  343. target_unit: &CurrencyUnit,
  344. ) -> Result<Amount, Error>
  345. where
  346. T: Into<u64>,
  347. {
  348. let amount = amount.into();
  349. match (current_unit, target_unit) {
  350. (CurrencyUnit::Sat, CurrencyUnit::Sat) => Ok(amount.into()),
  351. (CurrencyUnit::Msat, CurrencyUnit::Msat) => Ok(amount.into()),
  352. (CurrencyUnit::Sat, CurrencyUnit::Msat) => amount
  353. .checked_mul(MSAT_IN_SAT)
  354. .map(Amount::from)
  355. .ok_or(Error::AmountOverflow),
  356. (CurrencyUnit::Msat, CurrencyUnit::Sat) => Ok((amount / MSAT_IN_SAT).into()),
  357. (CurrencyUnit::Usd, CurrencyUnit::Usd) => Ok(amount.into()),
  358. (CurrencyUnit::Eur, CurrencyUnit::Eur) => Ok(amount.into()),
  359. _ => Err(Error::CannotConvertUnits),
  360. }
  361. }
  362. #[cfg(test)]
  363. mod tests {
  364. use super::*;
  365. #[test]
  366. fn test_split_amount() {
  367. let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  368. assert_eq!(
  369. Amount::from(1).split(&fee_and_amounts),
  370. vec![Amount::from(1)]
  371. );
  372. assert_eq!(
  373. Amount::from(2).split(&fee_and_amounts),
  374. vec![Amount::from(2)]
  375. );
  376. assert_eq!(
  377. Amount::from(3).split(&fee_and_amounts),
  378. vec![Amount::from(2), Amount::from(1)]
  379. );
  380. let amounts: Vec<Amount> = [8, 2, 1].iter().map(|a| Amount::from(*a)).collect();
  381. assert_eq!(Amount::from(11).split(&fee_and_amounts), amounts);
  382. let amounts: Vec<Amount> = [128, 64, 32, 16, 8, 4, 2, 1]
  383. .iter()
  384. .map(|a| Amount::from(*a))
  385. .collect();
  386. assert_eq!(Amount::from(255).split(&fee_and_amounts), amounts);
  387. }
  388. #[test]
  389. fn test_split_target_amount() {
  390. let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  391. let amount = Amount(65);
  392. let split = amount
  393. .split_targeted(&SplitTarget::Value(Amount(32)), &fee_and_amounts)
  394. .unwrap();
  395. assert_eq!(vec![Amount(1), Amount(32), Amount(32)], split);
  396. let amount = Amount(150);
  397. let split = amount
  398. .split_targeted(&SplitTarget::Value(Amount::from(50)), &fee_and_amounts)
  399. .unwrap();
  400. assert_eq!(
  401. vec![
  402. Amount(2),
  403. Amount(2),
  404. Amount(2),
  405. Amount(16),
  406. Amount(16),
  407. Amount(16),
  408. Amount(32),
  409. Amount(32),
  410. Amount(32)
  411. ],
  412. split
  413. );
  414. let amount = Amount::from(63);
  415. let split = amount
  416. .split_targeted(&SplitTarget::Value(Amount::from(32)), &fee_and_amounts)
  417. .unwrap();
  418. assert_eq!(
  419. vec![
  420. Amount(1),
  421. Amount(2),
  422. Amount(4),
  423. Amount(8),
  424. Amount(16),
  425. Amount(32)
  426. ],
  427. split
  428. );
  429. }
  430. #[test]
  431. fn test_split_with_fee() {
  432. let fee_and_amounts = (1, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  433. let amount = Amount(2);
  434. let split = amount.split_with_fee(&fee_and_amounts).unwrap();
  435. assert_eq!(split, vec![Amount(2), Amount(1)]);
  436. let amount = Amount(3);
  437. let split = amount.split_with_fee(&fee_and_amounts).unwrap();
  438. assert_eq!(split, vec![Amount(4)]);
  439. let amount = Amount(3);
  440. let fee_and_amounts = (1000, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  441. let split = amount.split_with_fee(&fee_and_amounts).unwrap();
  442. // With fee_ppk=1000 (100%), amount 3 requires proofs totaling at least 5
  443. // to cover both the amount (3) and fees (~2 for 2 proofs)
  444. assert_eq!(split, vec![Amount(4), Amount(1)]);
  445. }
  446. #[test]
  447. fn test_split_with_fee_reported_issue() {
  448. let fee_and_amounts = (100, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  449. // Test the reported issue: mint 600, send 300 with fee_ppk=100
  450. let amount = Amount(300);
  451. let split = amount.split_with_fee(&fee_and_amounts).unwrap();
  452. // Calculate the total fee for the split
  453. let total_fee_ppk = (split.len() as u64) * fee_and_amounts.fee;
  454. let total_fee = Amount::from(total_fee_ppk.div_ceil(1000));
  455. // The split should cover the amount plus fees
  456. let split_total = Amount::try_sum(split.iter().copied()).unwrap();
  457. assert!(
  458. split_total >= amount + total_fee,
  459. "Split total {} should be >= amount {} + fee {}",
  460. split_total,
  461. amount,
  462. total_fee
  463. );
  464. }
  465. #[test]
  466. fn test_split_with_fee_edge_cases() {
  467. // Test various amounts with fee_ppk=100
  468. let test_cases = vec![
  469. (Amount(1), 100),
  470. (Amount(10), 100),
  471. (Amount(50), 100),
  472. (Amount(100), 100),
  473. (Amount(200), 100),
  474. (Amount(300), 100),
  475. (Amount(500), 100),
  476. (Amount(600), 100),
  477. (Amount(1000), 100),
  478. (Amount(1337), 100),
  479. (Amount(5000), 100),
  480. ];
  481. for (amount, fee_ppk) in test_cases {
  482. let fee_and_amounts =
  483. (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  484. let result = amount.split_with_fee(&fee_and_amounts);
  485. assert!(
  486. result.is_ok(),
  487. "split_with_fee failed for amount {} with fee_ppk {}: {:?}",
  488. amount,
  489. fee_ppk,
  490. result.err()
  491. );
  492. let split = result.unwrap();
  493. // Verify the split covers the required amount
  494. let split_total = Amount::try_sum(split.iter().copied()).unwrap();
  495. let fee_for_split = (split.len() as u64) * fee_ppk;
  496. let total_fee = Amount::from(fee_for_split.div_ceil(1000));
  497. // The net amount after fees should be at least the original amount
  498. let net_amount = split_total.checked_sub(total_fee);
  499. assert!(
  500. net_amount.is_some(),
  501. "Net amount calculation failed for amount {} with fee_ppk {}",
  502. amount,
  503. fee_ppk
  504. );
  505. assert!(
  506. net_amount.unwrap() >= amount,
  507. "Net amount {} is less than required {} for amount {} with fee_ppk {}",
  508. net_amount.unwrap(),
  509. amount,
  510. amount,
  511. fee_ppk
  512. );
  513. }
  514. }
  515. #[test]
  516. fn test_split_with_fee_high_fees() {
  517. // Test with very high fees
  518. let test_cases = vec![
  519. (Amount(10), 500), // 50% fee
  520. (Amount(10), 1000), // 100% fee
  521. (Amount(10), 2000), // 200% fee
  522. (Amount(100), 500),
  523. (Amount(100), 1000),
  524. (Amount(100), 2000),
  525. ];
  526. for (amount, fee_ppk) in test_cases {
  527. let fee_and_amounts =
  528. (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  529. let result = amount.split_with_fee(&fee_and_amounts);
  530. assert!(
  531. result.is_ok(),
  532. "split_with_fee failed for amount {} with fee_ppk {}: {:?}",
  533. amount,
  534. fee_ppk,
  535. result.err()
  536. );
  537. let split = result.unwrap();
  538. let split_total = Amount::try_sum(split.iter().copied()).unwrap();
  539. // With high fees, we just need to ensure we can cover the amount
  540. assert!(
  541. split_total > amount,
  542. "Split total {} should be greater than amount {} for fee_ppk {}",
  543. split_total,
  544. amount,
  545. fee_ppk
  546. );
  547. }
  548. }
  549. #[test]
  550. fn test_split_with_fee_recursion_limit() {
  551. // Test that the recursion doesn't go infinite
  552. // This tests the edge case where the method keeps adding Amount::ONE
  553. let amount = Amount(1);
  554. let fee_ppk = 10000;
  555. let fee_and_amounts = (fee_ppk, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  556. let result = amount.split_with_fee(&fee_and_amounts);
  557. assert!(
  558. result.is_ok(),
  559. "split_with_fee should handle extreme fees without infinite recursion"
  560. );
  561. }
  562. #[test]
  563. fn test_split_values() {
  564. let fee_and_amounts = (0, (0..32).map(|x| 2u64.pow(x)).collect::<Vec<_>>()).into();
  565. let amount = Amount(10);
  566. let target = vec![Amount(2), Amount(4), Amount(4)];
  567. let split_target = SplitTarget::Values(target.clone());
  568. let values = amount
  569. .split_targeted(&split_target, &fee_and_amounts)
  570. .unwrap();
  571. assert_eq!(target, values);
  572. let target = vec![Amount(2), Amount(4), Amount(4)];
  573. let split_target = SplitTarget::Values(vec![Amount(2), Amount(4)]);
  574. let values = amount
  575. .split_targeted(&split_target, &fee_and_amounts)
  576. .unwrap();
  577. assert_eq!(target, values);
  578. let split_target = SplitTarget::Values(vec![Amount(2), Amount(10)]);
  579. let values = amount.split_targeted(&split_target, &fee_and_amounts);
  580. assert!(values.is_err())
  581. }
  582. #[test]
  583. #[should_panic]
  584. fn test_amount_addition() {
  585. let amount_one: Amount = u64::MAX.into();
  586. let amount_two: Amount = 1.into();
  587. let amounts = vec![amount_one, amount_two];
  588. let _total: Amount = Amount::try_sum(amounts).unwrap();
  589. }
  590. #[test]
  591. fn test_try_amount_addition() {
  592. let amount_one: Amount = u64::MAX.into();
  593. let amount_two: Amount = 1.into();
  594. let amounts = vec![amount_one, amount_two];
  595. let total = Amount::try_sum(amounts);
  596. assert!(total.is_err());
  597. let amount_one: Amount = 10000.into();
  598. let amount_two: Amount = 1.into();
  599. let amounts = vec![amount_one, amount_two];
  600. let total = Amount::try_sum(amounts).unwrap();
  601. assert_eq!(total, 10001.into());
  602. }
  603. #[test]
  604. fn test_amount_to_unit() {
  605. let amount = Amount::from(1000);
  606. let current_unit = CurrencyUnit::Sat;
  607. let target_unit = CurrencyUnit::Msat;
  608. let converted = to_unit(amount, &current_unit, &target_unit).unwrap();
  609. assert_eq!(converted, 1000000.into());
  610. let amount = Amount::from(1000);
  611. let current_unit = CurrencyUnit::Msat;
  612. let target_unit = CurrencyUnit::Sat;
  613. let converted = to_unit(amount, &current_unit, &target_unit).unwrap();
  614. assert_eq!(converted, 1.into());
  615. let amount = Amount::from(1);
  616. let current_unit = CurrencyUnit::Usd;
  617. let target_unit = CurrencyUnit::Usd;
  618. let converted = to_unit(amount, &current_unit, &target_unit).unwrap();
  619. assert_eq!(converted, 1.into());
  620. let amount = Amount::from(1);
  621. let current_unit = CurrencyUnit::Eur;
  622. let target_unit = CurrencyUnit::Eur;
  623. let converted = to_unit(amount, &current_unit, &target_unit).unwrap();
  624. assert_eq!(converted, 1.into());
  625. let amount = Amount::from(1);
  626. let current_unit = CurrencyUnit::Sat;
  627. let target_unit = CurrencyUnit::Eur;
  628. let converted = to_unit(amount, &current_unit, &target_unit);
  629. assert!(converted.is_err());
  630. }
  631. }