stellar_base/operations/
clawback.rs1use 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 pub fn source_account(&self) -> &Option<MuxedAccount> {
26 &self.source_account
27 }
28
29 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 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 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}