stellar_base/operations/
payment.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 PaymentOperation {
11    source_account: Option<MuxedAccount>,
12    destination: MuxedAccount,
13    amount: Stroops,
14    asset: Asset,
15}
16
17#[derive(Debug, Default)]
18pub struct PaymentOperationBuilder {
19    source_account: Option<MuxedAccount>,
20    destination: Option<MuxedAccount>,
21    amount: Option<Stroops>,
22    asset: Option<Asset>,
23}
24
25impl PaymentOperation {
26    /// Retrieves the operation source account.
27    pub fn source_account(&self) -> &Option<MuxedAccount> {
28        &self.source_account
29    }
30
31    /// Retrieves a reference to the operation source account.
32    pub fn source_account_mut(&mut self) -> &mut Option<MuxedAccount> {
33        &mut self.source_account
34    }
35
36    /// Retrieves the operation destination.
37    pub fn destination(&self) -> &MuxedAccount {
38        &self.destination
39    }
40
41    /// Retrieves a mutable reference to the operation destination.
42    pub fn destination_mut(&mut self) -> &mut MuxedAccount {
43        &mut self.destination
44    }
45
46    /// Retrieves the operation amount.
47    pub fn amount(&self) -> &Stroops {
48        &self.amount
49    }
50
51    /// Retrieves a mutable reference to the operation amount.
52    pub fn amount_mut(&mut self) -> &mut Stroops {
53        &mut self.amount
54    }
55
56    /// Retrieves the operation asset.
57    pub fn asset(&self) -> &Asset {
58        &self.asset
59    }
60
61    /// Retrieves a mutable reference to the operation asset.
62    pub fn asset_mut(&mut self) -> &mut Asset {
63        &mut self.asset
64    }
65
66    /// Returns the xdr operation body.
67    pub fn to_xdr_operation_body(&self) -> Result<xdr::OperationBody> {
68        let destination = self.destination.to_xdr()?;
69        let amount = self.amount.to_xdr_int64()?;
70        let asset = self.asset.to_xdr()?;
71        let inner = xdr::PaymentOp {
72            destination,
73            amount,
74            asset,
75        };
76        Ok(xdr::OperationBody::Payment(inner))
77    }
78
79    /// Creates from the xdr operation body.
80    pub fn from_xdr_operation_body(
81        source_account: Option<MuxedAccount>,
82        x: &xdr::PaymentOp,
83    ) -> Result<PaymentOperation> {
84        let destination = MuxedAccount::from_xdr(&x.destination)?;
85        let asset = Asset::from_xdr(&x.asset)?;
86        let amount = Stroops::from_xdr_int64(x.amount)?;
87        Ok(PaymentOperation {
88            source_account,
89            destination,
90            asset,
91            amount,
92        })
93    }
94}
95
96impl PaymentOperationBuilder {
97    pub fn new() -> PaymentOperationBuilder {
98        Default::default()
99    }
100
101    pub fn with_source_account<S>(mut self, source: S) -> PaymentOperationBuilder
102    where
103        S: Into<MuxedAccount>,
104    {
105        self.source_account = Some(source.into());
106        self
107    }
108
109    pub fn with_destination<A>(mut self, destination: A) -> PaymentOperationBuilder
110    where
111        A: Into<MuxedAccount>,
112    {
113        self.destination = Some(destination.into());
114        self
115    }
116
117    pub fn with_amount<B>(mut self, amount: B) -> Result<PaymentOperationBuilder>
118    where
119        B: TryInto<Stroops>,
120    {
121        self.amount = Some(amount.try_into().map_err(|_| Error::InvalidStroopsAmount)?);
122        Ok(self)
123    }
124
125    pub fn with_asset(mut self, asset: Asset) -> PaymentOperationBuilder {
126        self.asset = Some(asset);
127        self
128    }
129
130    pub fn build(self) -> Result<Operation> {
131        let destination = self
132            .destination
133            .ok_or_else(|| Error::InvalidOperation("missing payment destination".to_string()))?;
134
135        let amount = self
136            .amount
137            .ok_or_else(|| Error::InvalidOperation("missing payment amount".to_string()))?;
138
139        let asset = self
140            .asset
141            .ok_or_else(|| Error::InvalidOperation("missing payment asset".to_string()))?;
142
143        Ok(Operation::Payment(PaymentOperation {
144            source_account: self.source_account,
145            destination,
146            amount,
147            asset,
148        }))
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use crate::amount::Amount;
155    use crate::asset::Asset;
156
157    use crate::network::Network;
158    use crate::operations::tests::*;
159    use crate::operations::Operation;
160    use crate::transaction::{Transaction, TransactionEnvelope, MIN_BASE_FEE};
161    use crate::xdr::{XDRDeserialize, XDRSerialize};
162    use std::str::FromStr;
163
164    #[test]
165    fn test_payment() {
166        let kp = keypair0();
167        let kp1 = keypair1();
168        let dest = kp1.public_key();
169        let amount = Amount::from_str("12.301").unwrap();
170
171        let op = Operation::new_payment()
172            .with_destination(dest)
173            .with_amount(amount)
174            .unwrap()
175            .with_asset(Asset::new_native())
176            .build()
177            .unwrap();
178        let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
179            .add_operation(op)
180            .into_transaction()
181            .unwrap();
182        tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
183        let envelope = tx.to_envelope();
184        let xdr = envelope.xdr_base64().unwrap();
185        let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAAAAAABAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAAAAAAAHVPvQAAAAAAAAAAHqLnLFAAAAQFOPIvnhDoRtPKJl7mJGPD69z2riRwZCJJcLRD+QaJ1Wg+yMiDHLiheBZv/BodiTqEvFHFxcmSxo7pjyzoc7mQ8=";
186        assert_eq!(expected, xdr);
187        let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
188        assert_eq!(envelope, back);
189    }
190
191    #[test]
192    fn test_payment_with_source_account() {
193        let kp = keypair0();
194        let kp1 = keypair1();
195        let kp2 = keypair2();
196        let dest = kp1.public_key();
197        let amount = Amount::from_str("12.301").unwrap();
198
199        let op = Operation::new_payment()
200            .with_source_account(kp2.public_key())
201            .with_destination(dest)
202            .with_amount(amount)
203            .unwrap()
204            .with_asset(Asset::new_native())
205            .build()
206            .unwrap();
207        let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
208            .add_operation(op)
209            .into_transaction()
210            .unwrap();
211        tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
212        let envelope = tx.to_envelope();
213        let xdr = envelope.xdr_base64().unwrap();
214        let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAEAAAAAfhHLNNY19eGrAtSgLD3VpaRm2AjNjxIBWQg9zS4VWZgAAAABAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAAAAAAAHVPvQAAAAAAAAAAHqLnLFAAAAQE6H/Nqe7xqcdepF+y5PxXJaGn6aG1xzKXDqWQajo3lsPec0puxpaFvOKs2shgjKap4BgzREPyIs9st4IKq9kAo=";
215        assert_eq!(expected, xdr);
216        let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
217        assert_eq!(envelope, back);
218    }
219}