stellar_base/operations/
create_claimable_balance.rs

1use crate::amount::Stroops;
2use crate::asset::Asset;
3use crate::claim::Claimant;
4use crate::crypto::MuxedAccount;
5use crate::error::{Error, Result};
6use crate::operations::Operation;
7use crate::xdr;
8use std::convert::TryInto;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct CreateClaimableBalanceOperation {
12    source_account: Option<MuxedAccount>,
13    asset: Asset,
14    amount: Stroops,
15    claimants: Vec<Claimant>,
16}
17
18#[derive(Debug, Default)]
19pub struct CreateClaimableBalanceOperationBuilder {
20    source_account: Option<MuxedAccount>,
21    asset: Option<Asset>,
22    amount: Option<Stroops>,
23    claimants: Vec<Claimant>,
24}
25
26impl CreateClaimableBalanceOperation {
27    /// Retrieves the operation source account.
28    pub fn source_account(&self) -> &Option<MuxedAccount> {
29        &self.source_account
30    }
31
32    /// Retrieves a reference to the operation source account.
33    pub fn source_account_mut(&mut self) -> &mut Option<MuxedAccount> {
34        &mut self.source_account
35    }
36
37    /// Retrieves a reference to the operation asset.
38    pub fn asset(&self) -> &Asset {
39        &self.asset
40    }
41
42    /// Retrieves a mutable reference to the operation asset.
43    pub fn asset_mut(&mut self) -> &mut Asset {
44        &mut self.asset
45    }
46
47    /// Retrieves a reference to the operation amount.
48    pub fn amount(&self) -> &Stroops {
49        &self.amount
50    }
51
52    /// Retrieves a mutable reference to the operation amount.
53    pub fn amount_mut(&mut self) -> &mut Stroops {
54        &mut self.amount
55    }
56
57    /// Retrieves a reference to the operation claimants.
58    pub fn claimants(&self) -> &Vec<Claimant> {
59        &self.claimants
60    }
61
62    /// Retrieves a mutable reference to the operation claimants.
63    pub fn claimants_mut(&mut self) -> &mut Vec<Claimant> {
64        &mut self.claimants
65    }
66
67    /// Returns tho xdr operation body.
68    pub fn to_xdr_operation_body(&self) -> Result<xdr::OperationBody> {
69        let asset = self.asset.to_xdr()?;
70        let amount = self.amount.to_xdr_int64()?;
71        let claimants_res: Result<Vec<xdr::Claimant>> =
72            self.claimants.iter().map(|a| a.to_xdr()).collect();
73        let claimants = claimants_res?;
74        let inner = xdr::CreateClaimableBalanceOp {
75            asset,
76            amount,
77            claimants: claimants.try_into().map_err(|_| Error::XdrError)?,
78        };
79        Ok(xdr::OperationBody::CreateClaimableBalance(inner))
80    }
81
82    /// Creates from the xdr operation body.
83    pub fn from_xdr_operation_body(
84        source_account: Option<MuxedAccount>,
85        x: &xdr::CreateClaimableBalanceOp,
86    ) -> Result<CreateClaimableBalanceOperation> {
87        let asset = Asset::from_xdr(&x.asset)?;
88        let amount = Stroops::from_xdr_int64(x.amount)?;
89        let claimants_res: Result<Vec<Claimant>> =
90            x.claimants.iter().map(Claimant::from_xdr).collect();
91        let claimants = claimants_res?;
92        Ok(CreateClaimableBalanceOperation {
93            source_account,
94            asset,
95            amount,
96            claimants,
97        })
98    }
99}
100
101impl CreateClaimableBalanceOperationBuilder {
102    pub fn new() -> CreateClaimableBalanceOperationBuilder {
103        Default::default()
104    }
105
106    pub fn with_source_account<S>(mut self, source: S) -> CreateClaimableBalanceOperationBuilder
107    where
108        S: Into<MuxedAccount>,
109    {
110        self.source_account = Some(source.into());
111        self
112    }
113
114    pub fn with_asset(mut self, asset: Asset) -> CreateClaimableBalanceOperationBuilder {
115        self.asset = Some(asset);
116        self
117    }
118
119    pub fn with_amount<A>(mut self, amount: A) -> Result<CreateClaimableBalanceOperationBuilder>
120    where
121        A: TryInto<Stroops>,
122    {
123        self.amount = Some(amount.try_into().map_err(|_| Error::InvalidStroopsAmount)?);
124        Ok(self)
125    }
126
127    pub fn with_claimants(
128        mut self,
129        claimants: Vec<Claimant>,
130    ) -> CreateClaimableBalanceOperationBuilder {
131        self.claimants = claimants;
132        self
133    }
134
135    pub fn add_claimant(mut self, claimant: Claimant) -> CreateClaimableBalanceOperationBuilder {
136        self.claimants.push(claimant);
137        self
138    }
139
140    pub fn build(self) -> Result<Operation> {
141        let asset = self.asset.ok_or_else(|| {
142            Error::InvalidOperation("missing create claimable balance asset".to_string())
143        })?;
144
145        let amount = self.amount.ok_or_else(|| {
146            Error::InvalidOperation("missing create claimable balance amount".to_string())
147        })?;
148
149        if self.claimants.is_empty() {
150            return Err(Error::InvalidOperation(
151                "missing create claimable balance claimants".to_string(),
152            ));
153        }
154
155        Ok(Operation::CreateClaimableBalance(
156            CreateClaimableBalanceOperation {
157                source_account: self.source_account,
158                asset,
159                amount,
160                claimants: self.claimants,
161            },
162        ))
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use crate::amount::Amount;
169    use crate::asset::Asset;
170    use crate::claim::{ClaimPredicate, Claimant};
171
172    use crate::network::Network;
173    use crate::operations::tests::*;
174    use crate::operations::Operation;
175    use crate::transaction::{Transaction, TransactionEnvelope, MIN_BASE_FEE};
176    use crate::xdr::{XDRDeserialize, XDRSerialize};
177    use chrono::Duration;
178    use std::str::FromStr;
179
180    #[test]
181    fn test_create_claimable_balance() {
182        let kp = keypair0();
183        let kp1 = keypair1();
184        let kp2 = keypair2();
185
186        let amount = Amount::from_str("12.0333").unwrap();
187        let asset = Asset::new_credit("ABCD", kp2.public_key()).unwrap();
188
189        let predicate =
190            ClaimPredicate::new_not(ClaimPredicate::new_before_relative_time(Duration::days(7)));
191
192        let claimant = Claimant::new(kp1.public_key(), predicate);
193
194        let op = Operation::new_create_claimable_balance()
195            .with_asset(asset)
196            .with_amount(amount)
197            .unwrap()
198            .add_claimant(claimant)
199            .build()
200            .unwrap();
201
202        let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
203            .add_operation(op)
204            .into_transaction()
205            .unwrap();
206        tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
207        let envelope = tx.to_envelope();
208        let xdr = envelope.xdr_base64().unwrap();
209        let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAAAAAAOAAAAAUFCQ0QAAAAAfhHLNNY19eGrAtSgLD3VpaRm2AjNjxIBWQg9zS4VWZgAAAAABywiyAAAAAEAAAAAAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAwAAAAEAAAAFAAAAAAAJOoAAAAAAAAAAAeoucsUAAABAUA3iWSLubKZc6r4CL2s9WTr/xMS5zuWgzxvm2hBs9use/2ejCagSPlRBeRCe3Ky4R+tKMk8Qpa2LATvgUQS2BQ==";
210        assert_eq!(expected, xdr);
211        let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
212        assert_eq!(envelope, back);
213    }
214
215    #[test]
216    fn test_create_claimable_balance_with_source_account() {
217        let kp = keypair0();
218        let kp1 = keypair1();
219        let kp2 = keypair2();
220
221        let amount = Amount::from_str("12.0333").unwrap();
222        let asset = Asset::new_credit("ABCD", kp2.public_key()).unwrap();
223
224        let predicate =
225            ClaimPredicate::new_not(ClaimPredicate::new_before_relative_time(Duration::days(7)));
226
227        let claimant = Claimant::new(kp1.public_key(), predicate);
228
229        let op = Operation::new_create_claimable_balance()
230            .with_source_account(kp.public_key())
231            .with_asset(asset)
232            .with_amount(amount)
233            .unwrap()
234            .add_claimant(claimant)
235            .build()
236            .unwrap();
237
238        let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
239            .add_operation(op)
240            .into_transaction()
241            .unwrap();
242        tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
243        let envelope = tx.to_envelope();
244        let xdr = envelope.xdr_base64().unwrap();
245        let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAEAAAAA4Nxt4XJcrGZRYrUvrOc1sooiQ+QdEk1suS1wo+oucsUAAAAOAAAAAUFCQ0QAAAAAfhHLNNY19eGrAtSgLD3VpaRm2AjNjxIBWQg9zS4VWZgAAAAABywiyAAAAAEAAAAAAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAwAAAAEAAAAFAAAAAAAJOoAAAAAAAAAAAeoucsUAAABAcaaQuqZMwpwVMS9814lZPhjt43B3xwlGNfeyx2wU2EJSDJ0h0d2a7dxngMzq4/abNVCjBKspCU7XroelAhSNCw==";
246        assert_eq!(expected, xdr);
247        let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
248        assert_eq!(envelope, back);
249    }
250}