stellar_base/operations/
manage_buy_offer.rs

1use crate::amount::{Price, 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 ManageBuyOfferOperation {
11    source_account: Option<MuxedAccount>,
12    selling: Asset,
13    buying: Asset,
14    buy_amount: Stroops,
15    price: Price,
16    offer_id: Option<i64>,
17}
18
19#[derive(Debug, Default)]
20pub struct ManageBuyOfferOperationBuilder {
21    source_account: Option<MuxedAccount>,
22    selling: Option<Asset>,
23    buying: Option<Asset>,
24    buy_amount: Option<Stroops>,
25    price: Option<Price>,
26    offer_id: Option<i64>,
27}
28
29impl ManageBuyOfferOperation {
30    /// Retrieves the operation source account.
31    pub fn source_account(&self) -> &Option<MuxedAccount> {
32        &self.source_account
33    }
34
35    /// Retrieves a reference to the operation source account.
36    pub fn source_account_mut(&mut self) -> &mut Option<MuxedAccount> {
37        &mut self.source_account
38    }
39
40    /// Retrieves the operation seling asset.
41    pub fn selling(&self) -> &Asset {
42        &self.selling
43    }
44
45    /// Retrieves a mutable reference to the operation seling asset.
46    pub fn selling_mut(&mut self) -> &mut Asset {
47        &mut self.selling
48    }
49
50    /// Retrieves the operation buying asset.
51    pub fn buying(&self) -> &Asset {
52        &self.buying
53    }
54
55    /// Retrieves a mutable reference to the operation buying asset.
56    pub fn buying_mut(&mut self) -> &mut Asset {
57        &mut self.buying
58    }
59
60    /// Retrieves the operation buy amount.
61    pub fn buy_amount(&self) -> &Stroops {
62        &self.buy_amount
63    }
64
65    /// Retrieves a mutable reference to the operation buy amount.
66    pub fn buy_amount_mut(&mut self) -> &mut Stroops {
67        &mut self.buy_amount
68    }
69
70    /// Retrieves the operation price.
71    pub fn price(&self) -> &Price {
72        &self.price
73    }
74
75    /// Retrieves a mutable reference to the operation price.
76    pub fn price_mut(&mut self) -> &mut Price {
77        &mut self.price
78    }
79
80    /// Retrieves the operation offer id.
81    pub fn offer_id(&self) -> &Option<i64> {
82        &self.offer_id
83    }
84
85    /// Retrieves a mutable reference the operation offer id.
86    pub fn offer_id_mut(&mut self) -> &mut Option<i64> {
87        &mut self.offer_id
88    }
89
90    /// Returns the xdr operation body.
91    pub fn to_xdr_operation_body(&self) -> Result<xdr::OperationBody> {
92        let selling = self.selling.to_xdr()?;
93        let buying = self.buying.to_xdr()?;
94        let buy_amount = self.buy_amount.to_xdr_int64()?;
95        let price = self.price.to_xdr()?;
96        let offer_id = match &self.offer_id {
97            None => 0,
98            Some(id) => *id,
99        };
100        let inner = xdr::ManageBuyOfferOp {
101            selling,
102            buying,
103            buy_amount,
104            price,
105            offer_id,
106        };
107        Ok(xdr::OperationBody::ManageBuyOffer(inner))
108    }
109
110    /// Creates from xdr operation body.
111    pub fn from_xdr_operation_body(
112        source_account: Option<MuxedAccount>,
113        x: &xdr::ManageBuyOfferOp,
114    ) -> Result<ManageBuyOfferOperation> {
115        let selling = Asset::from_xdr(&x.selling)?;
116        let buying = Asset::from_xdr(&x.buying)?;
117        let buy_amount = Stroops::from_xdr_int64(x.buy_amount)?;
118        let price = Price::from_xdr(&x.price)?;
119        // Don't check if it's positive because the library user
120        // has no control over the xdr.
121        let offer_id = match &x.offer_id {
122            0 => None,
123            n => Some(*n),
124        };
125        Ok(ManageBuyOfferOperation {
126            source_account,
127            selling,
128            buying,
129            buy_amount,
130            price,
131            offer_id,
132        })
133    }
134}
135
136impl ManageBuyOfferOperationBuilder {
137    pub fn new() -> ManageBuyOfferOperationBuilder {
138        Default::default()
139    }
140
141    pub fn with_source_account<S>(mut self, source: S) -> ManageBuyOfferOperationBuilder
142    where
143        S: Into<MuxedAccount>,
144    {
145        self.source_account = Some(source.into());
146        self
147    }
148
149    pub fn with_selling_asset(mut self, asset: Asset) -> ManageBuyOfferOperationBuilder {
150        self.selling = Some(asset);
151        self
152    }
153
154    pub fn with_buying_asset(mut self, asset: Asset) -> ManageBuyOfferOperationBuilder {
155        self.buying = Some(asset);
156        self
157    }
158
159    pub fn with_buy_amount<A>(mut self, amount: A) -> Result<ManageBuyOfferOperationBuilder>
160    where
161        A: TryInto<Stroops, Error = Error>,
162    {
163        self.buy_amount = Some(amount.try_into()?);
164        Ok(self)
165    }
166
167    pub fn with_price(mut self, price: Price) -> ManageBuyOfferOperationBuilder {
168        self.price = Some(price);
169        self
170    }
171
172    pub fn with_offer_id(mut self, offer_id: Option<i64>) -> ManageBuyOfferOperationBuilder {
173        self.offer_id = offer_id;
174        self
175    }
176
177    pub fn build(self) -> Result<Operation> {
178        let selling = self.selling.ok_or_else(|| {
179            Error::InvalidOperation("missing manage buy offer selling asset".to_string())
180        })?;
181
182        let buying = self.buying.ok_or_else(|| {
183            Error::InvalidOperation("missing manage buy offer buying asset".to_string())
184        })?;
185
186        let buy_amount = self.buy_amount.ok_or_else(|| {
187            Error::InvalidOperation("missing manage buy offer amount".to_string())
188        })?;
189
190        let price = self
191            .price
192            .ok_or_else(|| Error::InvalidOperation("missing manage buy offer price".to_string()))?;
193
194        if let Some(offer_id) = self.offer_id {
195            if offer_id <= 0 {
196                return Err(Error::InvalidOperation(
197                    "manage buy offer offer_id must be positive".to_string(),
198                ));
199            }
200        }
201
202        Ok(Operation::ManageBuyOffer(ManageBuyOfferOperation {
203            source_account: self.source_account,
204            selling,
205            buying,
206            buy_amount,
207            price,
208            offer_id: self.offer_id,
209        }))
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    use crate::amount::{Amount, Price};
216    use crate::asset::Asset;
217
218    use crate::network::Network;
219    use crate::operations::tests::*;
220    use crate::operations::Operation;
221    use crate::transaction::{Transaction, TransactionEnvelope, MIN_BASE_FEE};
222    use crate::xdr::{XDRDeserialize, XDRSerialize};
223    use std::str::FromStr;
224
225    #[test]
226    fn test_manage_buy_offer() {
227        let kp = keypair0();
228        let kp1 = keypair1();
229
230        let amount = Amount::from_str("100.0").unwrap();
231        let buying = Asset::new_credit("ABCDEFGH", kp1.public_key()).unwrap();
232        let price = Price::from_str("12.35").unwrap();
233
234        let op = Operation::new_manage_buy_offer()
235            .with_selling_asset(Asset::new_native())
236            .with_buying_asset(buying)
237            .with_buy_amount(amount)
238            .unwrap()
239            .with_price(price)
240            .build()
241            .unwrap();
242        let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
243            .add_operation(op)
244            .into_transaction()
245            .unwrap();
246        tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
247        let envelope = tx.to_envelope();
248        let xdr = envelope.xdr_base64().unwrap();
249        let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAAAAAAMAAAAAAAAAAJBQkNERUZHSAAAAAAAAAAAJcrx2g/Hbs/ohF5CVFG7B5JJSJR+OqDKzDGK7dKHZH4AAAAAO5rKAAAAAPcAAAAUAAAAAAAAAAAAAAAAAAAAAeoucsUAAABAwJNdoP6KJlGw9S5BglgiBsWQPPKuPZQVqsFr0b9x3xSXUn+HOagzv210kzga37vSEFGIQ3GyoAgWLbxbYVcKDg==";
250        assert_eq!(expected, xdr);
251        let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
252        assert_eq!(envelope, back);
253    }
254
255    #[test]
256    fn test_manage_buy_offer_with_offer_id() {
257        let kp = keypair0();
258        let kp1 = keypair1();
259
260        let amount = Amount::from_str("100.0").unwrap();
261        let buying = Asset::new_credit("AB", kp1.public_key()).unwrap();
262        let price = Price::from_str("12.35").unwrap();
263
264        let op = Operation::new_manage_buy_offer()
265            .with_selling_asset(Asset::new_native())
266            .with_buying_asset(buying)
267            .with_buy_amount(amount)
268            .unwrap()
269            .with_price(price)
270            .with_offer_id(Some(888))
271            .build()
272            .unwrap();
273        let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
274            .add_operation(op)
275            .into_transaction()
276            .unwrap();
277        tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
278        let envelope = tx.to_envelope();
279        let xdr = envelope.xdr_base64().unwrap();
280        let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAAAAAAMAAAAAAAAAAFBQgAAAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAADuaygAAAAD3AAAAFAAAAAAAAAN4AAAAAAAAAAHqLnLFAAAAQJiREkdqaD2QzbsQWcuaUdr5mhJmbatEzAEqChBjtlUQ44C7nFbashDHyTN/Q6YkYOGr2xwL7yWIK9SCJKfeSQU=";
281        assert_eq!(expected, xdr);
282        let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
283        assert_eq!(envelope, back);
284    }
285
286    #[test]
287    fn test_manage_buy_offer_with_source_account() {
288        let kp = keypair0();
289        let kp1 = keypair1();
290        let kp2 = keypair2();
291
292        let amount = Amount::from_str("100.0").unwrap();
293        let buying = Asset::new_credit("AB", kp1.public_key()).unwrap();
294        let price = Price::from_str("12.35").unwrap();
295
296        let op = Operation::new_manage_buy_offer()
297            .with_source_account(kp2.public_key())
298            .with_selling_asset(Asset::new_native())
299            .with_buying_asset(buying)
300            .with_buy_amount(amount)
301            .unwrap()
302            .with_price(price)
303            .build()
304            .unwrap();
305        let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
306            .add_operation(op)
307            .into_transaction()
308            .unwrap();
309        tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
310        let envelope = tx.to_envelope();
311        let xdr = envelope.xdr_base64().unwrap();
312        let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAEAAAAAfhHLNNY19eGrAtSgLD3VpaRm2AjNjxIBWQg9zS4VWZgAAAAMAAAAAAAAAAFBQgAAAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAADuaygAAAAD3AAAAFAAAAAAAAAAAAAAAAAAAAAHqLnLFAAAAQLZdtLQcZZgoBIbkkHsnZ0Afhm5M+szpVmwulAYpde9aZx8FpN8ZRRHxW0qoLgr9Y1K7W/8jOyuEItqMd+PDewA=";
313        assert_eq!(expected, xdr);
314        let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
315        assert_eq!(envelope, back);
316    }
317}