stellar_base/operations/
payment.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 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 pub fn source_account(&self) -> &Option<MuxedAccount> {
28 &self.source_account
29 }
30
31 pub fn source_account_mut(&mut self) -> &mut Option<MuxedAccount> {
33 &mut self.source_account
34 }
35
36 pub fn destination(&self) -> &MuxedAccount {
38 &self.destination
39 }
40
41 pub fn destination_mut(&mut self) -> &mut MuxedAccount {
43 &mut self.destination
44 }
45
46 pub fn amount(&self) -> &Stroops {
48 &self.amount
49 }
50
51 pub fn amount_mut(&mut self) -> &mut Stroops {
53 &mut self.amount
54 }
55
56 pub fn asset(&self) -> &Asset {
58 &self.asset
59 }
60
61 pub fn asset_mut(&mut self) -> &mut Asset {
63 &mut self.asset
64 }
65
66 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 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}