stellar_base/operations/
path_payment_strict_receive.rs1use crate::amount::Stroops;
2use crate::asset::Asset;
3use crate::crypto::MuxedAccount;
4use crate::error::{Error, Result};
5use crate::operations::Operation;
6use crate::xdr;
7use std::convert::TryInto;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct PathPaymentStrictReceiveOperation {
11 source_account: Option<MuxedAccount>,
12 destination: MuxedAccount,
13 send_asset: Asset,
14 send_max: Stroops,
15 destination_asset: Asset,
16 destination_amount: Stroops,
17 path: Vec<Asset>,
18}
19
20#[derive(Debug, Default)]
21pub struct PathPaymentStrictReceiveOperationBuilder {
22 source_account: Option<MuxedAccount>,
23 destination: Option<MuxedAccount>,
24 send_asset: Option<Asset>,
25 send_max: Option<Stroops>,
26 destination_asset: Option<Asset>,
27 destination_amount: Option<Stroops>,
28 path: Vec<Asset>,
29}
30
31impl PathPaymentStrictReceiveOperation {
32 pub fn source_account(&self) -> &Option<MuxedAccount> {
34 &self.source_account
35 }
36
37 pub fn source_account_mut(&mut self) -> &mut Option<MuxedAccount> {
39 &mut self.source_account
40 }
41
42 pub fn destination(&self) -> &MuxedAccount {
44 &self.destination
45 }
46
47 pub fn destination_mut(&mut self) -> &mut MuxedAccount {
49 &mut self.destination
50 }
51
52 pub fn send_asset(&self) -> &Asset {
54 &self.send_asset
55 }
56
57 pub fn send_asset_mut(&mut self) -> &mut Asset {
59 &mut self.send_asset
60 }
61
62 pub fn send_max(&self) -> &Stroops {
64 &self.send_max
65 }
66
67 pub fn send_max_mut(&mut self) -> &mut Stroops {
69 &mut self.send_max
70 }
71
72 pub fn destination_asset(&self) -> &Asset {
74 &self.destination_asset
75 }
76
77 pub fn destination_asset_mut(&mut self) -> &mut Asset {
79 &mut self.destination_asset
80 }
81
82 pub fn destination_amount(&self) -> &Stroops {
84 &self.destination_amount
85 }
86
87 pub fn destination_amount_mut(&mut self) -> &mut Stroops {
89 &mut self.destination_amount
90 }
91
92 pub fn path(&self) -> &Vec<Asset> {
94 &self.path
95 }
96
97 pub fn path_mut(&mut self) -> &mut Vec<Asset> {
99 &mut self.path
100 }
101
102 pub fn to_xdr_operation_body(&self) -> Result<xdr::OperationBody> {
104 let destination = self.destination.to_xdr()?;
105 let send_asset = self.send_asset.to_xdr()?;
106 let send_max = self.send_max.to_xdr_int64()?;
107 let dest_asset = self.destination_asset.to_xdr()?;
108 let dest_amount = self.destination_amount.to_xdr_int64()?;
109 let path_res: Result<Vec<xdr::Asset>> = self.path.iter().map(|a| a.to_xdr()).collect();
110 let path = path_res?;
111 let inner = xdr::PathPaymentStrictReceiveOp {
112 destination,
113 send_asset,
114 send_max,
115 dest_asset,
116 dest_amount,
117 path: path.try_into().map_err(|_| Error::XdrError)?,
118 };
119 Ok(xdr::OperationBody::PathPaymentStrictReceive(inner))
120 }
121
122 pub fn from_xdr_operation_body(
124 source_account: Option<MuxedAccount>,
125 x: &xdr::PathPaymentStrictReceiveOp,
126 ) -> Result<PathPaymentStrictReceiveOperation> {
127 let destination = MuxedAccount::from_xdr(&x.destination)?;
128 let send_asset = Asset::from_xdr(&x.send_asset)?;
129 let send_max = Stroops::from_xdr_int64(x.send_max)?;
130 let destination_asset = Asset::from_xdr(&x.dest_asset)?;
131 let destination_amount = Stroops::from_xdr_int64(x.dest_amount)?;
132 let path_res: Result<Vec<Asset>> = x.path.iter().map(Asset::from_xdr).collect();
133 let path = path_res?;
134 Ok(PathPaymentStrictReceiveOperation {
135 source_account,
136 destination,
137 send_asset,
138 send_max,
139 destination_asset,
140 destination_amount,
141 path,
142 })
143 }
144}
145
146impl PathPaymentStrictReceiveOperationBuilder {
147 pub fn new() -> PathPaymentStrictReceiveOperationBuilder {
148 Default::default()
149 }
150
151 pub fn with_source_account<S>(mut self, source: S) -> PathPaymentStrictReceiveOperationBuilder
152 where
153 S: Into<MuxedAccount>,
154 {
155 self.source_account = Some(source.into());
156 self
157 }
158
159 pub fn with_destination<A>(mut self, destination: A) -> PathPaymentStrictReceiveOperationBuilder
160 where
161 A: Into<MuxedAccount>,
162 {
163 self.destination = Some(destination.into());
164 self
165 }
166
167 pub fn with_send_asset(
168 mut self,
169 send_asset: Asset,
170 ) -> PathPaymentStrictReceiveOperationBuilder {
171 self.send_asset = Some(send_asset);
172 self
173 }
174
175 pub fn with_send_max<A>(
176 mut self,
177 send_max: A,
178 ) -> Result<PathPaymentStrictReceiveOperationBuilder>
179 where
180 A: TryInto<Stroops>,
181 {
182 self.send_max = Some(
183 send_max
184 .try_into()
185 .map_err(|_| Error::InvalidStroopsAmount)?,
186 );
187 Ok(self)
188 }
189
190 pub fn with_destination_asset(
191 mut self,
192 dest_asset: Asset,
193 ) -> PathPaymentStrictReceiveOperationBuilder {
194 self.destination_asset = Some(dest_asset);
195 self
196 }
197
198 pub fn with_destination_amount<A>(
199 mut self,
200 dest_amount: A,
201 ) -> Result<PathPaymentStrictReceiveOperationBuilder>
202 where
203 A: TryInto<Stroops>,
204 {
205 self.destination_amount = Some(
206 dest_amount
207 .try_into()
208 .map_err(|_| Error::InvalidStroopsAmount)?,
209 );
210 Ok(self)
211 }
212
213 pub fn add_asset(mut self, asset: Asset) -> PathPaymentStrictReceiveOperationBuilder {
214 self.path.push(asset);
215 self
216 }
217
218 pub fn build(self) -> Result<Operation> {
219 let destination = self
220 .destination
221 .ok_or_else(|| Error::InvalidOperation("missing payment destination".to_string()))?;
222
223 let send_asset = self.send_asset.ok_or_else(|| {
224 Error::InvalidOperation("missing path payment strict send send asset".to_string())
225 })?;
226
227 let send_max = self.send_max.ok_or_else(|| {
228 Error::InvalidOperation("missing path payment strict send send max".to_string())
229 })?;
230
231 let destination_asset = self.destination_asset.ok_or_else(|| {
232 Error::InvalidOperation(
233 "missing path payment strict send destination asset".to_string(),
234 )
235 })?;
236
237 let destination_amount = self.destination_amount.ok_or_else(|| {
238 Error::InvalidOperation(
239 "missing path payment strict send destination amount".to_string(),
240 )
241 })?;
242
243 if self.path.len() > 5 {
244 return Err(Error::InvalidOperation(
245 "path payment strict send path too long".to_string(),
246 ));
247 }
248
249 Ok(Operation::PathPaymentStrictReceive(
250 PathPaymentStrictReceiveOperation {
251 source_account: self.source_account,
252 destination,
253 send_asset,
254 send_max,
255 destination_asset,
256 destination_amount,
257 path: self.path,
258 },
259 ))
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use crate::amount::Amount;
266 use crate::asset::Asset;
267
268 use crate::network::Network;
269 use crate::operations::tests::*;
270 use crate::operations::Operation;
271 use crate::transaction::{Transaction, TransactionEnvelope, MIN_BASE_FEE};
272 use crate::xdr::{XDRDeserialize, XDRSerialize};
273 use std::str::FromStr;
274
275 #[test]
276 fn test_path_payment_strict_receive() {
277 let kp = keypair0();
278 let kp1 = keypair1();
279 let kp2 = keypair2();
280 let dest = kp1.public_key();
281
282 let dest_amount = Amount::from_str("12.301").unwrap();
283 let send_max = Amount::from_str("0.333").unwrap();
284
285 let abcd = Asset::new_credit("ABCD", kp2.public_key()).unwrap();
286 let dest_asset = Asset::new_credit("DESTASSET", kp2.public_key()).unwrap();
287
288 let op = Operation::new_path_payment_strict_receive()
289 .with_destination(dest)
290 .with_send_asset(Asset::new_native())
291 .with_send_max(send_max)
292 .unwrap()
293 .with_destination_asset(dest_asset)
294 .with_destination_amount(dest_amount)
295 .unwrap()
296 .add_asset(abcd)
297 .build()
298 .unwrap();
299 let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
300 .add_operation(op)
301 .into_transaction()
302 .unwrap();
303 tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
304 let envelope = tx.to_envelope();
305 let xdr = envelope.xdr_base64().unwrap();
306 let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAAAMs/QAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAkRFU1RBU1NFVAAAAAAAAAB+Ecs01jX14asC1KAsPdWlpGbYCM2PEgFZCD3NLhVZmAAAAAAHVPvQAAAAAQAAAAFBQkNEAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAHqLnLFAAAAQLZISKYSR3RXr9Hvxw1tr9P1B4fst/sDuQMGapBvSpLYU6DpDSOFM/vVEuB94HXWI79fSJmfyEl+gR6Zh+o0Yw4=";
307 assert_eq!(expected, xdr);
308 let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
309 assert_eq!(envelope, back);
310 }
311
312 #[test]
313 fn test_path_payment_strict_receive_with_source_account() {
314 let kp = keypair0();
315 let kp1 = keypair1();
316 let kp2 = keypair2();
317 let dest = kp1.public_key();
318
319 let dest_amount = Amount::from_str("12.301").unwrap();
320 let send_max = Amount::from_str("0.333").unwrap();
321
322 let abcd = Asset::new_credit("ABCD", kp2.public_key()).unwrap();
323 let dest_asset = Asset::new_credit("DESTASSET", kp2.public_key()).unwrap();
324
325 let op = Operation::new_path_payment_strict_receive()
326 .with_source_account(kp1.public_key())
327 .with_destination(dest)
328 .with_send_asset(Asset::new_native())
329 .with_send_max(send_max)
330 .unwrap()
331 .with_destination_asset(dest_asset)
332 .with_destination_amount(dest_amount)
333 .unwrap()
334 .add_asset(abcd)
335 .build()
336 .unwrap();
337 let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
338 .add_operation(op)
339 .into_transaction()
340 .unwrap();
341 tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
342 let envelope = tx.to_envelope();
343 let xdr = envelope.xdr_base64().unwrap();
344 let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAACAAAAAAAAAAAAMs/QAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAkRFU1RBU1NFVAAAAAAAAAB+Ecs01jX14asC1KAsPdWlpGbYCM2PEgFZCD3NLhVZmAAAAAAHVPvQAAAAAQAAAAFBQkNEAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAHqLnLFAAAAQJgKu/fRcqT/SwSy1ejitxV6hGH/CZZtv+Qoe1usuSK2kN0UYz6YQOy0aqMwP1iJrIV5DbiDRymKEdEZAo9a5Q4=";
345 assert_eq!(expected, xdr);
346 let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
347 assert_eq!(envelope, back);
348 }
349}