stellar_base/operations/
create_account.rs

1use crate::amount::Stroops;
2use crate::crypto::{MuxedAccount, PublicKey};
3use crate::error::{Error, Result};
4use crate::operations::Operation;
5use crate::xdr;
6use std::convert::TryInto;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct CreateAccountOperation {
10    source_account: Option<MuxedAccount>,
11    destination: PublicKey,
12    starting_balance: Stroops,
13}
14
15#[derive(Debug, Default)]
16pub struct CreateAccountOperationBuilder {
17    source_account: Option<MuxedAccount>,
18    destination: Option<PublicKey>,
19    starting_balance: Option<Stroops>,
20}
21
22impl CreateAccountOperation {
23    /// Retrieves the operation source account.
24    pub fn source_account(&self) -> &Option<MuxedAccount> {
25        &self.source_account
26    }
27
28    /// Retrieves a reference to the operation source account.
29    pub fn source_account_mut(&mut self) -> &mut Option<MuxedAccount> {
30        &mut self.source_account
31    }
32
33    /// Retrieves the operation destination.
34    pub fn destination(&self) -> &PublicKey {
35        &self.destination
36    }
37
38    /// Retrieves a mutable reference to the operation destination.
39    pub fn destination_mut(&mut self) -> &mut PublicKey {
40        &mut self.destination
41    }
42
43    /// Retrieves the operation starting balance.
44    pub fn starting_balance(&self) -> &Stroops {
45        &self.starting_balance
46    }
47
48    /// Retrieves a mutable reference to the operation starting balance.
49    pub fn starting_balance_mut(&mut self) -> &mut Stroops {
50        &mut self.starting_balance
51    }
52
53    /// Returns the operation xdr body.
54    pub fn to_xdr_operation_body(&self) -> Result<xdr::OperationBody> {
55        let destination = self.destination.to_xdr_account_id()?;
56        let starting_balance = self.starting_balance.to_xdr_int64()?;
57        let inner = xdr::CreateAccountOp {
58            destination,
59            starting_balance,
60        };
61        Ok(xdr::OperationBody::CreateAccount(inner))
62    }
63
64    /// Creates from the xdr operation body.
65    pub fn from_xdr_operation_body(
66        source_account: Option<MuxedAccount>,
67        x: &xdr::CreateAccountOp,
68    ) -> Result<CreateAccountOperation> {
69        let destination = PublicKey::from_xdr_account_id(&x.destination)?;
70        let starting_balance = Stroops::from_xdr_int64(x.starting_balance)?;
71        Ok(CreateAccountOperation {
72            source_account,
73            destination,
74            starting_balance,
75        })
76    }
77}
78
79impl CreateAccountOperationBuilder {
80    pub fn new() -> CreateAccountOperationBuilder {
81        Default::default()
82    }
83
84    pub fn with_source_account<S>(mut self, source: S) -> CreateAccountOperationBuilder
85    where
86        S: Into<MuxedAccount>,
87    {
88        self.source_account = Some(source.into());
89        self
90    }
91
92    pub fn with_destination(mut self, destination: PublicKey) -> CreateAccountOperationBuilder {
93        self.destination = Some(destination);
94        self
95    }
96
97    pub fn with_starting_balance<B: TryInto<Stroops>>(
98        mut self,
99        starting_balance: B,
100    ) -> Result<CreateAccountOperationBuilder> {
101        self.starting_balance = Some(
102            starting_balance
103                .try_into()
104                .map_err(|_| Error::InvalidStroopsAmount)?,
105        );
106        Ok(self)
107    }
108
109    pub fn build(self) -> Result<Operation> {
110        let destination = self.destination.ok_or_else(|| {
111            Error::InvalidOperation("missing create account destination".to_string())
112        })?;
113
114        let starting_balance = self.starting_balance.ok_or_else(|| {
115            Error::InvalidOperation("missing create account starting balance".to_string())
116        })?;
117
118        Ok(Operation::CreateAccount(CreateAccountOperation {
119            source_account: self.source_account,
120            destination,
121            starting_balance,
122        }))
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use crate::amount::Amount;
129    use crate::network::Network;
130    use crate::operations::tests::*;
131    use crate::operations::*;
132    use crate::transaction::{Transaction, TransactionEnvelope, MIN_BASE_FEE};
133    use crate::xdr::{XDRDeserialize, XDRSerialize};
134    use std::str::FromStr;
135
136    #[test]
137    fn test_create_account() {
138        let kp = keypair0();
139        let kp1 = keypair1();
140        let dest = kp1.public_key();
141        let starting_balance = Amount::from_str("12.30").unwrap();
142
143        let op = Operation::new_create_account()
144            .with_destination(dest)
145            .with_starting_balance(starting_balance)
146            .unwrap()
147            .build()
148            .unwrap();
149
150        let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
151            .add_operation(op)
152            .into_transaction()
153            .unwrap();
154        tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
155
156        let envelope = tx.to_envelope();
157        let xdr = envelope.xdr_base64().unwrap();
158        let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAAdU1MAAAAAAAAAAAeoucsUAAABA0LiVS5BXQiPx/ZkMiJ55RngpeurtEgOrqbzAy99ZGnLUh68uiBejtKJdJPlw4XmVP/kojrA6nLI00zXhUiI7AQ==";
159        assert_eq!(expected, xdr);
160        let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
161        assert_eq!(envelope, back);
162    }
163
164    #[test]
165    fn test_create_account_with_source() {
166        let kp = keypair0();
167        let kp1 = keypair1();
168        let kp2 = keypair2();
169        let dest = kp1.public_key();
170        let starting_balance = "456.9878".parse::<Amount>().unwrap();
171
172        let op = Operation::new_create_account()
173            .with_source_account(kp2.public_key())
174            .with_destination(dest)
175            .with_starting_balance(starting_balance)
176            .unwrap()
177            .build()
178            .unwrap();
179        let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
180            .add_operation(op)
181            .into_transaction()
182            .unwrap();
183        tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
184        let envelope = tx.to_envelope();
185        let xdr = envelope.xdr_base64().unwrap();
186        let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAEAAAAAfhHLNNY19eGrAtSgLD3VpaRm2AjNjxIBWQg9zS4VWZgAAAAAAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAARBizfAAAAAAAAAAAeoucsUAAABACIZzOxwBATuDx2738HBsh0VA0oYQT1mTfrlOROtQeBb8h4AkOJeQYn3VEkij0ZnDnrZAmRP/rR7WD714PQioCA==";
187        assert_eq!(expected, xdr);
188        let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
189        assert_eq!(envelope, back);
190    }
191}