stellar_base/operations/
create_account.rs1use 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 pub fn source_account(&self) -> &Option<MuxedAccount> {
25 &self.source_account
26 }
27
28 pub fn source_account_mut(&mut self) -> &mut Option<MuxedAccount> {
30 &mut self.source_account
31 }
32
33 pub fn destination(&self) -> &PublicKey {
35 &self.destination
36 }
37
38 pub fn destination_mut(&mut self) -> &mut PublicKey {
40 &mut self.destination
41 }
42
43 pub fn starting_balance(&self) -> &Stroops {
45 &self.starting_balance
46 }
47
48 pub fn starting_balance_mut(&mut self) -> &mut Stroops {
50 &mut self.starting_balance
51 }
52
53 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 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}