stellar_base/operations/
path_payment_strict_receive.rs

1use 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    /// Retrieves the operation source account.
33    pub fn source_account(&self) -> &Option<MuxedAccount> {
34        &self.source_account
35    }
36
37    /// Retrieves a reference to the operation source account.
38    pub fn source_account_mut(&mut self) -> &mut Option<MuxedAccount> {
39        &mut self.source_account
40    }
41
42    /// Retrieves the operation destination.
43    pub fn destination(&self) -> &MuxedAccount {
44        &self.destination
45    }
46
47    /// Retrieves a mutable reference to the operation destination.
48    pub fn destination_mut(&mut self) -> &mut MuxedAccount {
49        &mut self.destination
50    }
51
52    /// Retrieves the operatino send asset.
53    pub fn send_asset(&self) -> &Asset {
54        &self.send_asset
55    }
56
57    /// Retrieves a mutable reference to the operatino send asset.
58    pub fn send_asset_mut(&mut self) -> &mut Asset {
59        &mut self.send_asset
60    }
61
62    /// Retrieves the operation send max.
63    pub fn send_max(&self) -> &Stroops {
64        &self.send_max
65    }
66
67    /// Retrieves a mutable reference to the operation send max.
68    pub fn send_max_mut(&mut self) -> &mut Stroops {
69        &mut self.send_max
70    }
71
72    /// Retrieves the operation destination asset.
73    pub fn destination_asset(&self) -> &Asset {
74        &self.destination_asset
75    }
76
77    /// Retrieves a mutable reference to the operation destination asset.
78    pub fn destination_asset_mut(&mut self) -> &mut Asset {
79        &mut self.destination_asset
80    }
81
82    /// Retrieves the operation destination amount.
83    pub fn destination_amount(&self) -> &Stroops {
84        &self.destination_amount
85    }
86
87    /// Retrieves a mutable reference to the operation destination amount.
88    pub fn destination_amount_mut(&mut self) -> &mut Stroops {
89        &mut self.destination_amount
90    }
91
92    /// Retrieves the operation path.
93    pub fn path(&self) -> &Vec<Asset> {
94        &self.path
95    }
96
97    /// Retrieves a mutable reference to the operation path.
98    pub fn path_mut(&mut self) -> &mut Vec<Asset> {
99        &mut self.path
100    }
101
102    /// Returns the xdr operation body.
103    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    /// Creates from the xdr operation body.
123    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}