stellar_base/operations/
clawback.rs

1use crate::amount::Stroops;
2use crate::crypto::MuxedAccount;
3use crate::error::{Error, Result};
4use crate::{xdr, Asset, Operation};
5use std::convert::TryInto;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct ClawbackOperation {
9    source_account: Option<MuxedAccount>,
10    asset: Asset,
11    from: MuxedAccount,
12    amount: Stroops,
13}
14
15#[derive(Debug, Default)]
16pub struct ClawbackOperationBuilder {
17    source_account: Option<MuxedAccount>,
18    asset: Option<Asset>,
19    from: Option<MuxedAccount>,
20    amount: Option<Stroops>,
21}
22
23impl ClawbackOperation {
24    /// Retrieves the operation source account.
25    pub fn source_account(&self) -> &Option<MuxedAccount> {
26        &self.source_account
27    }
28
29    /// Retrieves a reference to the operation source account.
30    pub fn source_account_mut(&mut self) -> &mut Option<MuxedAccount> {
31        &mut self.source_account
32    }
33
34    pub fn asset(&self) -> &Asset {
35        &self.asset
36    }
37
38    pub fn asset_mut(&mut self) -> &mut Asset {
39        &mut self.asset
40    }
41
42    pub fn from(&self) -> &MuxedAccount {
43        &self.from
44    }
45
46    pub fn from_mut(&mut self) -> &mut MuxedAccount {
47        &mut self.from
48    }
49
50    pub fn amount(&self) -> &Stroops {
51        &self.amount
52    }
53
54    pub fn amount_mut(&mut self) -> &mut Stroops {
55        &mut self.amount
56    }
57
58    /// Returns tho xdr operation body.
59    pub fn to_xdr_operation_body(&self) -> Result<xdr::OperationBody> {
60        let inner = xdr::ClawbackOp {
61            asset: self.asset.to_xdr()?,
62            from: self.from.to_xdr()?,
63            amount: self.amount.to_xdr_int64()?,
64        };
65        Ok(xdr::OperationBody::Clawback(inner))
66    }
67
68    /// Creates from the xdr operation body.
69    pub fn from_xdr_operation_body(
70        source_account: Option<MuxedAccount>,
71        x: &xdr::ClawbackOp,
72    ) -> Result<Self> {
73        Ok(Self {
74            source_account,
75            asset: Asset::from_xdr(&x.asset)?,
76            from: MuxedAccount::from_xdr(&x.from)?,
77            amount: Stroops::from_xdr_int64(x.amount)?,
78        })
79    }
80}
81
82impl ClawbackOperationBuilder {
83    pub fn new() -> Self {
84        Default::default()
85    }
86
87    pub fn with_source_account<S>(mut self, source: S) -> Self
88    where
89        S: Into<MuxedAccount>,
90    {
91        self.source_account = Some(source.into());
92        self
93    }
94
95    pub fn with_asset(mut self, asset: Asset) -> Self {
96        self.asset = Some(asset);
97        self
98    }
99
100    pub fn with_from<S>(mut self, from: S) -> Self
101    where
102        S: Into<MuxedAccount>,
103    {
104        self.from = Some(from.into());
105        self
106    }
107
108    pub fn with_amount<S>(mut self, amount: S) -> Result<Self>
109    where
110        S: TryInto<Stroops>,
111    {
112        self.amount = Some(amount.try_into().map_err(|_| Error::InvalidStroopsAmount)?);
113        Ok(self)
114    }
115
116    pub fn build(self) -> Result<Operation> {
117        let asset = self
118            .asset
119            .ok_or_else(|| Error::InvalidOperation("missing clawback asset".to_string()))?;
120
121        let from = self
122            .from
123            .ok_or_else(|| Error::InvalidOperation("missing clawback from account".to_string()))?;
124
125        let amount = self
126            .amount
127            .ok_or_else(|| Error::InvalidOperation("missing clawback amount".to_string()))?;
128
129        Ok(Operation::Clawback(ClawbackOperation {
130            source_account: self.source_account,
131            asset,
132            from,
133            amount,
134        }))
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use crate::amount::Amount;
141    use crate::operations::tests::*;
142    use crate::xdr::{XDRDeserialize, XDRSerialize};
143    use crate::{Asset, Operation, PublicKey};
144    use std::str::FromStr;
145
146    fn asset0() -> Asset {
147        let issuer =
148            PublicKey::from_account_id("GBO7YJU3JMAGYVXDYA3IYZ2GZS2JKPRZIHMUY5MYKKJPJUBYWAGDWXQG")
149                .unwrap();
150        Asset::new_credit("TEST", issuer).unwrap()
151    }
152
153    #[test]
154    fn test_clawback() {
155        let from = keypair1().public_key();
156        let asset = asset0();
157        let amount = Amount::from_str("17.6301").unwrap();
158
159        let clawback_op = Operation::new_clawback()
160            .with_from(from)
161            .with_asset(asset)
162            .with_amount(amount)
163            .unwrap()
164            .build()
165            .unwrap();
166
167        let encoded = clawback_op.xdr_base64().unwrap();
168        let expected = "AAAAAAAAABMAAAABVEVTVAAAAABd/CabSwBsVuPANoxnRsy0lT45QdlMdZhSkvTQOLAMOwAAAAAlyvHaD8duz+iEXkJUUbsHkklIlH46oMrMMYrt0odkfgAAAAAKgiPI";
169        assert_eq!(expected, encoded);
170        let back = Operation::from_xdr_base64(&encoded).unwrap();
171        assert_eq!(clawback_op, back);
172    }
173
174    #[test]
175    fn test_clawback_with_source_account() {
176        let source_account = keypair0().public_key();
177        let from = keypair1().public_key();
178        let asset = asset0();
179        let amount = Amount::from_str("17.6301").unwrap();
180
181        let clawback_op = Operation::new_clawback()
182            .with_source_account(source_account)
183            .with_from(from)
184            .with_asset(asset)
185            .with_amount(amount)
186            .unwrap()
187            .build()
188            .unwrap();
189
190        let encoded = clawback_op.xdr_base64().unwrap();
191        let expected = "AAAAAQAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAABMAAAABVEVTVAAAAABd/CabSwBsVuPANoxnRsy0lT45QdlMdZhSkvTQOLAMOwAAAAAlyvHaD8duz+iEXkJUUbsHkklIlH46oMrMMYrt0odkfgAAAAAKgiPI";
192        assert_eq!(expected, encoded);
193        let back = Operation::from_xdr_base64(&encoded).unwrap();
194        assert_eq!(clawback_op, back);
195    }
196}