|
@@ -59,32 +59,43 @@ impl Amount {
|
|
|
/// Amount one
|
|
|
pub const ONE: Amount = Amount(1);
|
|
|
|
|
|
- /// Split into parts that are powers of two
|
|
|
- pub fn split(&self) -> Vec<Self> {
|
|
|
- let sats = self.0;
|
|
|
- (0_u64..64)
|
|
|
- .rev()
|
|
|
- .filter_map(|bit| {
|
|
|
- let part = 1 << bit;
|
|
|
- ((sats & part) == part).then_some(Self::from(part))
|
|
|
- })
|
|
|
- .collect()
|
|
|
+ /// Split into parts that are in the `supported_amounts`
|
|
|
+ pub fn split(&self, supported_amounts: &[u64]) -> Result<Vec<Self>, Error> {
|
|
|
+ let (amounts, left_over) =
|
|
|
+ supported_amounts
|
|
|
+ .iter()
|
|
|
+ .fold((Vec::new(), self.0), |(mut acc, mut total), &amount| {
|
|
|
+ while total >= amount {
|
|
|
+ acc.push(Self::from(amount));
|
|
|
+ total = total.checked_sub(amount).unwrap_or_default();
|
|
|
+ }
|
|
|
+ (acc, total)
|
|
|
+ });
|
|
|
+
|
|
|
+ if left_over != 0 {
|
|
|
+ return Err(Error::SplitValuesGreater);
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(amounts)
|
|
|
}
|
|
|
|
|
|
/// Split into parts that are powers of two by target
|
|
|
- pub fn split_targeted(&self, target: &SplitTarget) -> Result<Vec<Self>, Error> {
|
|
|
+ pub fn split_targeted(
|
|
|
+ &self,
|
|
|
+ target: &SplitTarget,
|
|
|
+ supported_amounts: &[u64],
|
|
|
+ ) -> Result<Vec<Self>, Error> {
|
|
|
let mut parts = match target {
|
|
|
- SplitTarget::None => self.split(),
|
|
|
+ SplitTarget::None => self.split(supported_amounts)?,
|
|
|
SplitTarget::Value(amount) => {
|
|
|
if self.le(amount) {
|
|
|
- return Ok(self.split());
|
|
|
+ return self.split(supported_amounts);
|
|
|
}
|
|
|
|
|
|
let mut parts_total = Amount::ZERO;
|
|
|
let mut parts = Vec::new();
|
|
|
|
|
|
- // The powers of two that are need to create target value
|
|
|
- let parts_of_value = amount.split();
|
|
|
+ let parts_of_value = amount.split(supported_amounts)?;
|
|
|
|
|
|
while parts_total.lt(self) {
|
|
|
for part in parts_of_value.iter().copied() {
|
|
@@ -92,7 +103,7 @@ impl Amount {
|
|
|
parts.push(part);
|
|
|
} else {
|
|
|
let amount_left = *self - parts_total;
|
|
|
- parts.extend(amount_left.split());
|
|
|
+ parts.extend(amount_left.split(supported_amounts)?);
|
|
|
}
|
|
|
|
|
|
parts_total = Amount::try_sum(parts.clone().iter().copied())?;
|
|
@@ -115,7 +126,7 @@ impl Amount {
|
|
|
}
|
|
|
Ordering::Greater => {
|
|
|
let extra = *self - values_total;
|
|
|
- let mut extra_amount = extra.split();
|
|
|
+ let mut extra_amount = extra.split(supported_amounts)?;
|
|
|
let mut values = values.clone();
|
|
|
|
|
|
values.append(&mut extra_amount);
|
|
@@ -130,13 +141,17 @@ impl Amount {
|
|
|
}
|
|
|
|
|
|
/// Splits amount into powers of two while accounting for the swap fee
|
|
|
- pub fn split_with_fee(&self, fee_ppk: u64) -> Result<Vec<Self>, Error> {
|
|
|
- let without_fee_amounts = self.split();
|
|
|
+ pub fn split_with_fee(
|
|
|
+ &self,
|
|
|
+ fee_ppk: u64,
|
|
|
+ supported_amounts: &[u64],
|
|
|
+ ) -> Result<Vec<Self>, Error> {
|
|
|
+ let without_fee_amounts = self.split(supported_amounts)?;
|
|
|
let fee_ppk = fee_ppk * without_fee_amounts.len() as u64;
|
|
|
let fee = Amount::from(fee_ppk.div_ceil(1000));
|
|
|
let new_amount = self.checked_add(fee).ok_or(Error::AmountOverflow)?;
|
|
|
|
|
|
- let split = new_amount.split();
|
|
|
+ let split = new_amount.split(supported_amounts)?;
|
|
|
let split_fee_ppk = split.len() as u64 * fee_ppk;
|
|
|
let split_fee = Amount::from(split_fee_ppk.div_ceil(1000));
|
|
|
|
|
@@ -147,7 +162,7 @@ impl Amount {
|
|
|
}
|
|
|
self.checked_add(Amount::ONE)
|
|
|
.ok_or(Error::AmountOverflow)?
|
|
|
- .split_with_fee(fee_ppk)
|
|
|
+ .split_with_fee(fee_ppk, supported_amounts)
|
|
|
}
|
|
|
|
|
|
/// Checked addition for Amount. Returns None if overflow occurs.
|
|
@@ -361,19 +376,30 @@ mod tests {
|
|
|
|
|
|
#[test]
|
|
|
fn test_split_amount() {
|
|
|
- assert_eq!(Amount::from(1).split(), vec![Amount::from(1)]);
|
|
|
- assert_eq!(Amount::from(2).split(), vec![Amount::from(2)]);
|
|
|
assert_eq!(
|
|
|
- Amount::from(3).split(),
|
|
|
+ Amount::from(1).split(&[3, 2, 1]).unwrap(),
|
|
|
+ vec![Amount::from(1)]
|
|
|
+ );
|
|
|
+ assert_eq!(
|
|
|
+ Amount::from(2).split(&[3, 2, 1]).unwrap(),
|
|
|
+ vec![Amount::from(2)]
|
|
|
+ );
|
|
|
+ assert_eq!(
|
|
|
+ Amount::from(3).split(&[2, 1]).unwrap(),
|
|
|
vec![Amount::from(2), Amount::from(1)]
|
|
|
);
|
|
|
let amounts: Vec<Amount> = [8, 2, 1].iter().map(|a| Amount::from(*a)).collect();
|
|
|
- assert_eq!(Amount::from(11).split(), amounts);
|
|
|
+ assert_eq!(Amount::from(11).split(&[20, 8, 2, 1]).unwrap(), amounts);
|
|
|
let amounts: Vec<Amount> = [128, 64, 32, 16, 8, 4, 2, 1]
|
|
|
.iter()
|
|
|
.map(|a| Amount::from(*a))
|
|
|
.collect();
|
|
|
- assert_eq!(Amount::from(255).split(), amounts);
|
|
|
+ assert_eq!(
|
|
|
+ Amount::from(255)
|
|
|
+ .split(&[500, 128, 64, 32, 16, 8, 4, 2, 1])
|
|
|
+ .unwrap(),
|
|
|
+ amounts
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
@@ -381,14 +407,14 @@ mod tests {
|
|
|
let amount = Amount(65);
|
|
|
|
|
|
let split = amount
|
|
|
- .split_targeted(&SplitTarget::Value(Amount(32)))
|
|
|
+ .split_targeted(&SplitTarget::Value(Amount(32)), &[64, 32, 4, 2, 1])
|
|
|
.unwrap();
|
|
|
assert_eq!(vec![Amount(1), Amount(32), Amount(32)], split);
|
|
|
|
|
|
let amount = Amount(150);
|
|
|
|
|
|
let split = amount
|
|
|
- .split_targeted(&SplitTarget::Value(Amount::from(50)))
|
|
|
+ .split_targeted(&SplitTarget::Value(Amount::from(50)), &[50])
|
|
|
.unwrap();
|
|
|
assert_eq!(
|
|
|
vec![
|
|
@@ -408,7 +434,10 @@ mod tests {
|
|
|
let amount = Amount::from(63);
|
|
|
|
|
|
let split = amount
|
|
|
- .split_targeted(&SplitTarget::Value(Amount::from(32)))
|
|
|
+ .split_targeted(
|
|
|
+ &SplitTarget::Value(Amount::from(32)),
|
|
|
+ &[128, 64, 32, 16, 8, 4, 2, 1],
|
|
|
+ )
|
|
|
.unwrap();
|
|
|
assert_eq!(
|
|
|
vec![
|