stellar_base/operations/
path_payment_strict_send.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 PathPaymentStrictSendOperation {
11 source_account: Option<MuxedAccount>,
12 destination: MuxedAccount,
13 send_asset: Asset,
14 send_amount: Stroops,
15 destination_asset: Asset,
16 destination_min: Stroops,
17 path: Vec<Asset>,
18}
19
20#[derive(Debug, Default)]
21pub struct PathPaymentStrictSendOperationBuilder {
22 source_account: Option<MuxedAccount>,
23 destination: Option<MuxedAccount>,
24 send_asset: Option<Asset>,
25 send_amount: Option<Stroops>,
26 destination_asset: Option<Asset>,
27 destination_min: Option<Stroops>,
28 path: Vec<Asset>,
29}
30
31impl PathPaymentStrictSendOperation {
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_amount(&self) -> &Stroops {
64 &self.send_amount
65 }
66
67 pub fn send_amount_mut(&mut self) -> &mut Stroops {
69 &mut self.send_amount
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_min(&self) -> &Stroops {
84 &self.destination_min
85 }
86
87 pub fn destination_min_mut(&mut self) -> &mut Stroops {
89 &mut self.destination_min
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_amount = self.send_amount.to_xdr_int64()?;
107 let dest_asset = self.destination_asset.to_xdr()?;
108 let dest_min = self.destination_min.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::PathPaymentStrictSendOp {
112 destination,
113 send_asset,
114 send_amount,
115 dest_asset,
116 dest_min,
117 path: path.try_into().map_err(|_| Error::XdrError)?,
118 };
119 Ok(xdr::OperationBody::PathPaymentStrictSend(inner))
120 }
121
122 pub fn from_xdr_operation_body(
124 source_account: Option<MuxedAccount>,
125 x: &xdr::PathPaymentStrictSendOp,
126 ) -> Result<PathPaymentStrictSendOperation> {
127 let destination = MuxedAccount::from_xdr(&x.destination)?;
128 let send_asset = Asset::from_xdr(&x.send_asset)?;
129 let send_amount = Stroops::from_xdr_int64(x.send_amount)?;
130 let destination_asset = Asset::from_xdr(&x.dest_asset)?;
131 let destination_min = Stroops::from_xdr_int64(x.dest_min)?;
132 let path_res: Result<Vec<Asset>> = x.path.iter().map(Asset::from_xdr).collect();
133 let path = path_res?;
134 Ok(PathPaymentStrictSendOperation {
135 source_account,
136 destination,
137 send_asset,
138 send_amount,
139 destination_asset,
140 destination_min,
141 path,
142 })
143 }
144}
145
146impl PathPaymentStrictSendOperationBuilder {
147 pub fn new() -> PathPaymentStrictSendOperationBuilder {
148 Default::default()
149 }
150
151 pub fn with_source_account<S>(mut self, source: S) -> PathPaymentStrictSendOperationBuilder
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) -> PathPaymentStrictSendOperationBuilder
160 where
161 A: Into<MuxedAccount>,
162 {
163 self.destination = Some(destination.into());
164 self
165 }
166
167 pub fn with_send_asset(mut self, send_asset: Asset) -> PathPaymentStrictSendOperationBuilder {
168 self.send_asset = Some(send_asset);
169 self
170 }
171
172 pub fn with_send_amount<A>(
173 mut self,
174 send_amount: A,
175 ) -> Result<PathPaymentStrictSendOperationBuilder>
176 where
177 A: TryInto<Stroops>,
178 {
179 self.send_amount = Some(
180 send_amount
181 .try_into()
182 .map_err(|_| Error::InvalidStroopsAmount)?,
183 );
184 Ok(self)
185 }
186
187 pub fn with_destination_asset(
188 mut self,
189 dest_asset: Asset,
190 ) -> PathPaymentStrictSendOperationBuilder {
191 self.destination_asset = Some(dest_asset);
192 self
193 }
194
195 pub fn with_destination_min<A>(
196 mut self,
197 dest_min: A,
198 ) -> Result<PathPaymentStrictSendOperationBuilder>
199 where
200 A: TryInto<Stroops>,
201 {
202 self.destination_min = Some(
203 dest_min
204 .try_into()
205 .map_err(|_| Error::InvalidStroopsAmount)?,
206 );
207 Ok(self)
208 }
209
210 pub fn add_asset(mut self, asset: Asset) -> PathPaymentStrictSendOperationBuilder {
211 self.path.push(asset);
212 self
213 }
214
215 pub fn build(self) -> Result<Operation> {
216 let destination = self
217 .destination
218 .ok_or_else(|| Error::InvalidOperation("missing payment destination".to_string()))?;
219
220 let send_asset = self.send_asset.ok_or_else(|| {
221 Error::InvalidOperation("missing path payment strict send send asset".to_string())
222 })?;
223
224 let send_amount = self.send_amount.ok_or_else(|| {
225 Error::InvalidOperation("missing path payment strict send send amount".to_string())
226 })?;
227
228 let destination_asset = self.destination_asset.ok_or_else(|| {
229 Error::InvalidOperation(
230 "missing path payment strict send destination asset".to_string(),
231 )
232 })?;
233
234 let destination_min = self.destination_min.ok_or_else(|| {
235 Error::InvalidOperation("missing path payment strict send destination min".to_string())
236 })?;
237
238 if self.path.len() > 5 {
239 return Err(Error::InvalidOperation(
240 "path payment strict send path too long".to_string(),
241 ));
242 }
243
244 Ok(Operation::PathPaymentStrictSend(
245 PathPaymentStrictSendOperation {
246 source_account: self.source_account,
247 destination,
248 send_asset,
249 send_amount,
250 destination_asset,
251 destination_min,
252 path: self.path,
253 },
254 ))
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use crate::amount::Amount;
261 use crate::asset::Asset;
262
263 use crate::network::Network;
264 use crate::operations::tests::*;
265 use crate::operations::Operation;
266 use crate::transaction::{Transaction, TransactionEnvelope, MIN_BASE_FEE};
267 use crate::xdr::{XDRDeserialize, XDRSerialize};
268 use std::str::FromStr;
269
270 #[test]
271 fn test_path_payment_strict_send() {
272 let kp = keypair0();
273 let kp1 = keypair1();
274 let kp2 = keypair2();
275 let dest = kp1.public_key();
276
277 let dest_amount = Amount::from_str("12.301").unwrap();
278 let send_amount = Amount::from_str("0.333").unwrap();
279
280 let abcd = Asset::new_credit("ABCD", kp2.public_key()).unwrap();
281 let dest_asset = Asset::new_credit("DESTASSET", kp2.public_key()).unwrap();
282
283 let op = Operation::new_path_payment_strict_send()
284 .with_destination(dest)
285 .with_send_asset(Asset::new_native())
286 .with_send_amount(send_amount)
287 .unwrap()
288 .with_destination_asset(dest_asset)
289 .with_destination_min(dest_amount)
290 .unwrap()
291 .add_asset(abcd)
292 .build()
293 .unwrap();
294 let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
295 .add_operation(op)
296 .into_transaction()
297 .unwrap();
298 tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
299 let envelope = tx.to_envelope();
300 let xdr = envelope.xdr_base64().unwrap();
301 let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAAAAAANAAAAAAAAAAAAMs/QAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAkRFU1RBU1NFVAAAAAAAAAB+Ecs01jX14asC1KAsPdWlpGbYCM2PEgFZCD3NLhVZmAAAAAAHVPvQAAAAAQAAAAFBQkNEAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAHqLnLFAAAAQKDDuyBJaD3+y98EloB5VJi1wYamH+poOoaOhxGGFcH4ZhFI04TRAY3Ahggs3bMV7pcOmw120oZ4P4vA0aFjWgk=";
302 assert_eq!(expected, xdr);
303 let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
304 assert_eq!(envelope, back);
305 }
306
307 #[test]
308 fn test_path_payment_strict_send_with_source_account() {
309 let kp = keypair0();
310 let kp1 = keypair1();
311 let kp2 = keypair2();
312 let dest = kp1.public_key();
313
314 let dest_amount = Amount::from_str("12.301").unwrap();
315 let send_amount = Amount::from_str("0.333").unwrap();
316
317 let abcd = Asset::new_credit("ABCD", kp2.public_key()).unwrap();
318 let dest_asset = Asset::new_credit("DESTASSET", kp2.public_key()).unwrap();
319
320 let op = Operation::new_path_payment_strict_send()
321 .with_source_account(kp1.public_key())
322 .with_destination(dest)
323 .with_send_asset(Asset::new_native())
324 .with_send_amount(send_amount)
325 .unwrap()
326 .with_destination_asset(dest_asset)
327 .with_destination_min(dest_amount)
328 .unwrap()
329 .add_asset(abcd)
330 .build()
331 .unwrap();
332 let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
333 .add_operation(op)
334 .into_transaction()
335 .unwrap();
336 tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
337 let envelope = tx.to_envelope();
338 let xdr = envelope.xdr_base64().unwrap();
339 let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAEAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAANAAAAAAAAAAAAMs/QAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAkRFU1RBU1NFVAAAAAAAAAB+Ecs01jX14asC1KAsPdWlpGbYCM2PEgFZCD3NLhVZmAAAAAAHVPvQAAAAAQAAAAFBQkNEAAAAAH4RyzTWNfXhqwLUoCw91aWkZtgIzY8SAVkIPc0uFVmYAAAAAAAAAAHqLnLFAAAAQI4En43OnB/OEQ9ZAjymT8dGwnHVah2gqkq1AQuwJ89e7kVvwWPl/axspv25B0x9NnEdNZd+KKhoZfmA4B5EWwg=";
340 assert_eq!(expected, xdr);
341 let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
342 assert_eq!(envelope, back);
343 }
344}