cdk_ffi/types/
amount.rs

1//! Amount and currency related types
2
3use cdk::nuts::CurrencyUnit as CdkCurrencyUnit;
4use cdk::Amount as CdkAmount;
5use serde::{Deserialize, Serialize};
6
7use crate::error::FfiError;
8
9/// FFI-compatible Amount type
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, uniffi::Record)]
11#[serde(transparent)]
12pub struct Amount {
13    pub value: u64,
14}
15
16impl Amount {
17    pub fn new(value: u64) -> Self {
18        Self { value }
19    }
20
21    pub fn zero() -> Self {
22        Self { value: 0 }
23    }
24
25    pub fn is_zero(&self) -> bool {
26        self.value == 0
27    }
28
29    pub fn convert_unit(
30        &self,
31        current_unit: CurrencyUnit,
32        target_unit: CurrencyUnit,
33    ) -> Result<Amount, FfiError> {
34        Ok(CdkAmount::from(self.value)
35            .convert_unit(&current_unit.into(), &target_unit.into())
36            .map(Into::into)?)
37    }
38
39    pub fn add(&self, other: Amount) -> Result<Amount, FfiError> {
40        let self_amount = CdkAmount::from(self.value);
41        let other_amount = CdkAmount::from(other.value);
42        self_amount
43            .checked_add(other_amount)
44            .map(Into::into)
45            .ok_or(FfiError::AmountOverflow)
46    }
47
48    pub fn subtract(&self, other: Amount) -> Result<Amount, FfiError> {
49        let self_amount = CdkAmount::from(self.value);
50        let other_amount = CdkAmount::from(other.value);
51        self_amount
52            .checked_sub(other_amount)
53            .map(Into::into)
54            .ok_or(FfiError::AmountOverflow)
55    }
56
57    pub fn multiply(&self, factor: u64) -> Result<Amount, FfiError> {
58        let self_amount = CdkAmount::from(self.value);
59        let factor_amount = CdkAmount::from(factor);
60        self_amount
61            .checked_mul(factor_amount)
62            .map(Into::into)
63            .ok_or(FfiError::AmountOverflow)
64    }
65
66    pub fn divide(&self, divisor: u64) -> Result<Amount, FfiError> {
67        if divisor == 0 {
68            return Err(FfiError::DivisionByZero);
69        }
70        let self_amount = CdkAmount::from(self.value);
71        let divisor_amount = CdkAmount::from(divisor);
72        self_amount
73            .checked_div(divisor_amount)
74            .map(Into::into)
75            .ok_or(FfiError::AmountOverflow)
76    }
77}
78
79impl From<CdkAmount> for Amount {
80    fn from(amount: CdkAmount) -> Self {
81        Self {
82            value: u64::from(amount),
83        }
84    }
85}
86
87impl From<Amount> for CdkAmount {
88    fn from(amount: Amount) -> Self {
89        CdkAmount::from(amount.value)
90    }
91}
92
93/// FFI-compatible Currency Unit
94#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)]
95pub enum CurrencyUnit {
96    Sat,
97    Msat,
98    Usd,
99    Eur,
100    Auth,
101    Custom { unit: String },
102}
103
104impl From<CdkCurrencyUnit> for CurrencyUnit {
105    fn from(unit: CdkCurrencyUnit) -> Self {
106        match unit {
107            CdkCurrencyUnit::Sat => CurrencyUnit::Sat,
108            CdkCurrencyUnit::Msat => CurrencyUnit::Msat,
109            CdkCurrencyUnit::Usd => CurrencyUnit::Usd,
110            CdkCurrencyUnit::Eur => CurrencyUnit::Eur,
111            CdkCurrencyUnit::Auth => CurrencyUnit::Auth,
112            CdkCurrencyUnit::Custom(s) => CurrencyUnit::Custom { unit: s },
113            _ => CurrencyUnit::Sat, // Default for unknown units
114        }
115    }
116}
117
118impl From<CurrencyUnit> for CdkCurrencyUnit {
119    fn from(unit: CurrencyUnit) -> Self {
120        match unit {
121            CurrencyUnit::Sat => CdkCurrencyUnit::Sat,
122            CurrencyUnit::Msat => CdkCurrencyUnit::Msat,
123            CurrencyUnit::Usd => CdkCurrencyUnit::Usd,
124            CurrencyUnit::Eur => CdkCurrencyUnit::Eur,
125            CurrencyUnit::Auth => CdkCurrencyUnit::Auth,
126            CurrencyUnit::Custom { unit } => CdkCurrencyUnit::Custom(unit),
127        }
128    }
129}
130
131/// FFI-compatible SplitTarget
132#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Enum)]
133pub enum SplitTarget {
134    /// Default target; least amount of proofs
135    None,
136    /// Target amount for wallet to have most proofs that add up to value
137    Value { amount: Amount },
138    /// Specific amounts to split into (must equal amount being split)
139    Values { amounts: Vec<Amount> },
140}
141
142impl From<SplitTarget> for cdk::amount::SplitTarget {
143    fn from(target: SplitTarget) -> Self {
144        match target {
145            SplitTarget::None => cdk::amount::SplitTarget::None,
146            SplitTarget::Value { amount } => cdk::amount::SplitTarget::Value(amount.into()),
147            SplitTarget::Values { amounts } => {
148                cdk::amount::SplitTarget::Values(amounts.into_iter().map(Into::into).collect())
149            }
150        }
151    }
152}
153
154impl From<cdk::amount::SplitTarget> for SplitTarget {
155    fn from(target: cdk::amount::SplitTarget) -> Self {
156        match target {
157            cdk::amount::SplitTarget::None => SplitTarget::None,
158            cdk::amount::SplitTarget::Value(amount) => SplitTarget::Value {
159                amount: amount.into(),
160            },
161            cdk::amount::SplitTarget::Values(amounts) => SplitTarget::Values {
162                amounts: amounts.into_iter().map(Into::into).collect(),
163            },
164        }
165    }
166}