1use std::io::{Read, Write};
3
4use crate::amount::Stroops;
5use crate::crypto::{
6 hash, DecoratedSignature, Ed25519Signer, Ed25519Verifier, KeyPair, MuxedAccount,
7};
8use crate::error::{Error, Result};
9use crate::memo::Memo;
10use crate::network::Network;
11use crate::operations::Operation;
12use crate::time_bounds::TimeBounds;
13use crate::{xdr, PublicKey};
14use ed25519::Signature;
15
16pub const MIN_BASE_FEE: Stroops = Stroops(100);
18
19#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct Transaction {
22 source_account: MuxedAccount,
23 fee: Stroops,
24 sequence: i64,
25 time_bounds: Option<TimeBounds>,
26 memo: Memo,
27 operations: Vec<Operation>,
28 signatures: Vec<DecoratedSignature>,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct FeeBumpTransaction {
34 fee_source: MuxedAccount,
35 fee: Stroops,
36 inner_tx: Transaction,
37 signatures: Vec<DecoratedSignature>,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
42pub enum TransactionEnvelope {
43 Transaction(Transaction),
45 FeeBumpTransaction(FeeBumpTransaction),
47}
48
49pub struct TransactionBuilder {
51 base_fee: Stroops,
52 tx: Result<Transaction>,
53}
54
55impl Transaction {
56 pub fn builder<S: Into<MuxedAccount>>(
60 source_account: S,
61 sequence: i64,
62 fee: Stroops,
63 ) -> TransactionBuilder {
64 TransactionBuilder::new(source_account, sequence, fee)
65 }
66
67 pub fn source_account(&self) -> &MuxedAccount {
69 &self.source_account
70 }
71
72 pub fn source_account_mut(&mut self) -> &mut MuxedAccount {
74 &mut self.source_account
75 }
76
77 pub fn fee(&self) -> &Stroops {
79 &self.fee
80 }
81
82 pub fn fee_mut(&mut self) -> &mut Stroops {
84 &mut self.fee
85 }
86
87 pub fn sequence(&self) -> &i64 {
89 &self.sequence
90 }
91
92 pub fn sequence_mut(&mut self) -> &mut i64 {
94 &mut self.sequence
95 }
96
97 pub fn time_bounds(&self) -> &Option<TimeBounds> {
99 &self.time_bounds
100 }
101
102 pub fn time_bounds_mut(&mut self) -> &mut Option<TimeBounds> {
104 &mut self.time_bounds
105 }
106
107 pub fn memo(&self) -> &Memo {
109 &self.memo
110 }
111
112 pub fn memo_mut(&mut self) -> &mut Memo {
114 &mut self.memo
115 }
116
117 pub fn operations(&self) -> &Vec<Operation> {
119 &self.operations
120 }
121
122 pub fn operations_mut(&mut self) -> &mut Vec<Operation> {
124 &mut self.operations
125 }
126
127 pub fn signatures(&self) -> &Vec<DecoratedSignature> {
129 &self.signatures
130 }
131
132 pub fn signatures_mut(&mut self) -> &mut Vec<DecoratedSignature> {
134 &mut self.signatures
135 }
136
137 pub fn to_envelope(&self) -> TransactionEnvelope {
139 self.clone().into_envelope()
140 }
141
142 pub fn into_envelope(self) -> TransactionEnvelope {
146 TransactionEnvelope::Transaction(self)
147 }
148
149 pub fn sign_hashx(&mut self, preimage: &[u8]) -> Result<()> {
153 let signature = self.decorated_signature_from_preimage(preimage)?;
154 self.signatures.push(signature);
155 Ok(())
156 }
157
158 pub fn sign<S, V>(&mut self, key: &KeyPair<S, V>, network: &Network) -> Result<()>
160 where
161 S: Ed25519Signer<Signature>,
162 V: Ed25519Verifier<Signature> + AsRef<[u8]>,
163 {
164 let signature = self.decorated_signature(key, network)?;
165 self.signatures.push(signature);
166 Ok(())
167 }
168
169 pub fn decorated_signature_from_preimage(&self, preimage: &[u8]) -> Result<DecoratedSignature> {
171 DecoratedSignature::new_from_preimage(preimage)
172 }
173
174 pub fn decorated_signature<S, V>(
176 &self,
177 key: &KeyPair<S, V>,
178 network: &Network,
179 ) -> Result<DecoratedSignature>
180 where
181 S: Ed25519Signer<Signature>,
182 V: Ed25519Verifier<Signature> + AsRef<[u8]>,
183 {
184 let tx_hash = self.hash(network)?;
185 Ok(key.sign_decorated(&tx_hash))
186 }
187
188 pub fn hash(&self, network: &Network) -> Result<Vec<u8>> {
190 let signature_data = self.signature_data(network)?;
191 Ok(hash(&signature_data))
192 }
193
194 pub fn signature_data(&self, network: &Network) -> Result<Vec<u8>> {
196 let mut base = Vec::new();
197 let tx_signature_payload = self.to_xdr_transaction_signature_payload(network)?;
198 xdr::XDRSerialize::write_xdr(&tx_signature_payload, &mut base)?;
199 Ok(base)
200 }
201
202 pub fn to_xdr(&self) -> Result<xdr::Transaction> {
204 let source_account = self.source_account.to_xdr()?;
205 let fee = self.fee.to_xdr_uint32()?;
206 let seq_num = xdr::SequenceNumber(self.sequence);
207 let cond = match &self.time_bounds {
208 None => xdr::Preconditions::None,
209 Some(tb) => xdr::Preconditions::Time(tb.to_xdr()?),
210 };
211 let memo = self.memo.to_xdr()?;
212 let mut operations = Vec::new();
213 for operation in self.operations() {
214 let xdr_operation = operation.to_xdr()?;
215 operations.push(xdr_operation);
216 }
217 let ext = xdr::TransactionExt::V0;
218 Ok(xdr::Transaction {
219 source_account,
220 fee,
221 seq_num,
222 cond,
223 memo,
224 operations: operations.try_into().map_err(|_| Error::XdrError)?,
225 ext,
226 })
227 }
228
229 pub fn to_xdr_envelope(&self) -> Result<xdr::TransactionV1Envelope> {
231 let tx = self.to_xdr()?;
232 let signatures = signatures_to_xdr(self.signatures())?;
233 Ok(xdr::TransactionV1Envelope { tx, signatures })
234 }
235
236 pub fn to_xdr_transaction_signature_payload(
238 &self,
239 network: &Network,
240 ) -> Result<xdr::TransactionSignaturePayload> {
241 let network_id = network
242 .network_id()
243 .try_into()
244 .map_err(|_| Error::XdrError)?;
245 let inner = self.to_xdr()?;
246 let tagged_transaction = xdr::TransactionSignaturePayloadTaggedTransaction::Tx(inner);
247 Ok(xdr::TransactionSignaturePayload {
248 network_id,
249 tagged_transaction,
250 })
251 }
252
253 pub fn from_xdr_v0(x: &xdr::TransactionV0) -> Result<Transaction> {
255 let source_account =
256 MuxedAccount::Ed25519(PublicKey::from_slice(&x.source_account_ed25519.0)?);
257 let fee = Stroops::from_xdr_uint32(x.fee)?;
258 let sequence = x.seq_num.0;
259 let time_bounds = match &x.time_bounds {
260 None => None,
261 Some(tb) => Some(TimeBounds::from_xdr(tb)?),
262 };
263 let memo = Memo::from_xdr(&x.memo)?;
264 let mut operations = Vec::new();
265 for operation in x.operations.as_slice() {
266 let xdr_operation = Operation::from_xdr(operation)?;
267 operations.push(xdr_operation);
268 }
269 Ok(Transaction {
270 source_account,
271 fee,
272 sequence,
273 time_bounds,
274 memo,
275 operations,
276 signatures: Vec::new(),
277 })
278 }
279
280 pub fn from_xdr(x: &xdr::Transaction) -> Result<Transaction> {
282 let source_account = MuxedAccount::from_xdr(&x.source_account)?;
283 let fee = Stroops::from_xdr_uint32(x.fee)?;
284 let sequence = x.seq_num.0;
285 let time_bounds = match &x.cond {
286 xdr::Preconditions::None => None,
287 xdr::Preconditions::Time(tb) => Some(TimeBounds::from_xdr(tb)?),
288 xdr::Preconditions::V2(_) => return Err(Error::UnsupportedFeature),
289 };
290 let memo = Memo::from_xdr(&x.memo)?;
291 let mut operations = Vec::new();
292 for operation in x.operations.as_slice() {
293 let xdr_operation = Operation::from_xdr(operation)?;
294 operations.push(xdr_operation);
295 }
296 match &x.ext {
297 xdr::TransactionExt::V0 => {}
298 xdr::TransactionExt::V1(_) => return Err(Error::UnsupportedFeature),
299 }
300 Ok(Transaction {
301 source_account,
302 fee,
303 sequence,
304 time_bounds,
305 memo,
306 operations,
307 signatures: Vec::new(),
308 })
309 }
310
311 pub fn from_xdr_v0_envelope(x: &xdr::TransactionV0Envelope) -> Result<Transaction> {
313 let mut tx = Self::from_xdr_v0(&x.tx)?;
314 let signatures = signatures_from_xdr(&x.signatures)?;
315 tx.signatures = signatures;
316 Ok(tx)
317 }
318
319 pub fn from_xdr_envelope(x: &xdr::TransactionV1Envelope) -> Result<Transaction> {
321 let mut tx = Self::from_xdr(&x.tx)?;
322 let signatures = signatures_from_xdr(&x.signatures)?;
323 tx.signatures = signatures;
324 Ok(tx)
325 }
326}
327
328impl FeeBumpTransaction {
329 pub fn new(
331 fee_source: MuxedAccount,
332 fee: Stroops,
333 inner_tx: Transaction,
334 ) -> FeeBumpTransaction {
335 FeeBumpTransaction {
336 fee_source,
337 fee,
338 inner_tx,
339 signatures: Vec::new(),
340 }
341 }
342
343 pub fn fee_source(&self) -> &MuxedAccount {
345 &self.fee_source
346 }
347
348 pub fn fee_source_mut(&mut self) -> &mut MuxedAccount {
350 &mut self.fee_source
351 }
352
353 pub fn fee(&self) -> &Stroops {
355 &self.fee
356 }
357
358 pub fn fee_mut(&mut self) -> &mut Stroops {
360 &mut self.fee
361 }
362
363 pub fn inner_transaction(&self) -> &Transaction {
365 &self.inner_tx
366 }
367
368 pub fn inner_transaction_mut(&mut self) -> &mut Transaction {
370 &mut self.inner_tx
371 }
372
373 pub fn signatures(&self) -> &Vec<DecoratedSignature> {
375 &self.signatures
376 }
377
378 pub fn signatures_mut(&mut self) -> &mut Vec<DecoratedSignature> {
380 &mut self.signatures
381 }
382
383 pub fn into_envelope(self) -> TransactionEnvelope {
387 TransactionEnvelope::FeeBumpTransaction(self)
388 }
389
390 pub fn to_envelope(&self) -> TransactionEnvelope {
392 self.clone().into_envelope()
393 }
394
395 pub fn sign_hashx(&mut self, preimage: &[u8]) -> Result<()> {
399 let signature = self.decorated_signature_from_preimage(preimage)?;
400 self.signatures.push(signature);
401 Ok(())
402 }
403
404 pub fn sign<S, V>(&mut self, key: &KeyPair<S, V>, network: &Network) -> Result<()>
406 where
407 S: Ed25519Signer<Signature>,
408 V: Ed25519Verifier<Signature> + AsRef<[u8]>,
409 {
410 let signature = self.decorated_signature(key, network)?;
411 self.signatures.push(signature);
412 Ok(())
413 }
414
415 pub fn decorated_signature_from_preimage(&self, preimage: &[u8]) -> Result<DecoratedSignature> {
417 DecoratedSignature::new_from_preimage(preimage)
418 }
419
420 pub fn decorated_signature<S, V>(
422 &self,
423 key: &KeyPair<S, V>,
424 network: &Network,
425 ) -> Result<DecoratedSignature>
426 where
427 S: Ed25519Signer<Signature>,
428 V: Ed25519Verifier<Signature> + AsRef<[u8]>,
429 {
430 let tx_hash = self.hash(network)?;
431 Ok(key.sign_decorated(&tx_hash))
432 }
433
434 pub fn hash(&self, network: &Network) -> Result<Vec<u8>> {
436 let signature_data = self.signature_data(network)?;
437 Ok(hash(&signature_data))
438 }
439
440 pub fn signature_data(&self, network: &Network) -> Result<Vec<u8>> {
442 let mut base = Vec::new();
443 let tx_signature_payload = self.to_xdr_transaction_signature_payload(network)?;
444 xdr::XDRSerialize::write_xdr(&tx_signature_payload, &mut base)?;
445 Ok(base)
446 }
447
448 pub fn to_xdr(&self) -> Result<xdr::FeeBumpTransaction> {
450 let fee_source = self.fee_source.to_xdr()?;
451 let fee = self.fee.to_xdr_int64()?;
452 let tx_envelope = self.inner_tx.to_xdr_envelope()?;
453 let inner_tx = xdr::FeeBumpTransactionInnerTx::Tx(tx_envelope);
454 let ext = xdr::FeeBumpTransactionExt::V0;
455 Ok(xdr::FeeBumpTransaction {
456 fee_source,
457 fee,
458 inner_tx,
459 ext,
460 })
461 }
462
463 pub fn to_xdr_envelope(&self) -> Result<xdr::FeeBumpTransactionEnvelope> {
465 let tx = self.to_xdr()?;
466 let signatures = signatures_to_xdr(self.signatures())?;
467 Ok(xdr::FeeBumpTransactionEnvelope { tx, signatures })
468 }
469
470 pub fn from_xdr(x: &xdr::FeeBumpTransaction) -> Result<FeeBumpTransaction> {
472 let fee_source = MuxedAccount::from_xdr(&x.fee_source)?;
473 let fee = Stroops::new(x.fee);
474 let inner_tx = match &x.inner_tx {
475 xdr::FeeBumpTransactionInnerTx::Tx(inner_tx) => {
476 Transaction::from_xdr_envelope(inner_tx)?
477 }
478 };
479 Ok(FeeBumpTransaction {
480 fee_source,
481 fee,
482 inner_tx,
483 signatures: Vec::new(),
484 })
485 }
486
487 pub fn from_xdr_envelope(x: &xdr::FeeBumpTransactionEnvelope) -> Result<FeeBumpTransaction> {
489 let mut tx = FeeBumpTransaction::from_xdr(&x.tx)?;
490 let signatures = signatures_from_xdr(&x.signatures)?;
491 tx.signatures = signatures;
492 Ok(tx)
493 }
494
495 pub fn to_xdr_transaction_signature_payload(
497 &self,
498 network: &Network,
499 ) -> Result<xdr::TransactionSignaturePayload> {
500 let network_id = network
501 .network_id()
502 .try_into()
503 .map_err(|_| Error::XdrError)?;
504 let inner = self.to_xdr()?;
505 let tagged_transaction =
506 xdr::TransactionSignaturePayloadTaggedTransaction::TxFeeBump(inner);
507 Ok(xdr::TransactionSignaturePayload {
508 network_id,
509 tagged_transaction,
510 })
511 }
512}
513
514impl TransactionEnvelope {
515 pub fn as_transaction(&self) -> Option<&Transaction> {
517 match *self {
518 TransactionEnvelope::Transaction(ref tx) => Some(tx),
519 _ => None,
520 }
521 }
522
523 pub fn as_transaction_mut(&mut self) -> Option<&mut Transaction> {
525 match *self {
526 TransactionEnvelope::Transaction(ref mut tx) => Some(tx),
527 _ => None,
528 }
529 }
530
531 pub fn is_transaction(&self) -> bool {
533 self.as_transaction().is_some()
534 }
535
536 pub fn as_fee_bump_transaction(&self) -> Option<&FeeBumpTransaction> {
538 match *self {
539 TransactionEnvelope::FeeBumpTransaction(ref tx) => Some(tx),
540 _ => None,
541 }
542 }
543
544 pub fn as_fee_bump_transaction_mut(&mut self) -> Option<&mut FeeBumpTransaction> {
546 match *self {
547 TransactionEnvelope::FeeBumpTransaction(ref mut tx) => Some(tx),
548 _ => None,
549 }
550 }
551
552 pub fn is_fee_bump_transaction(&self) -> bool {
554 self.as_fee_bump_transaction().is_some()
555 }
556
557 pub fn sign_hashx(&mut self, preimage: &[u8]) -> Result<()> {
561 match self {
562 TransactionEnvelope::Transaction(tx) => tx.sign_hashx(preimage),
563 TransactionEnvelope::FeeBumpTransaction(tx) => tx.sign_hashx(preimage),
564 }
565 }
566
567 pub fn sign<S, V>(&mut self, key: &KeyPair<S, V>, network: &Network) -> Result<()>
569 where
570 S: Ed25519Signer<Signature>,
571 V: Ed25519Verifier<Signature> + AsRef<[u8]>,
572 {
573 match self {
574 TransactionEnvelope::Transaction(tx) => tx.sign(key, network),
575 TransactionEnvelope::FeeBumpTransaction(tx) => tx.sign(key, network),
576 }
577 }
578
579 pub fn decorated_signature_from_preimage(&self, preimage: &[u8]) -> Result<DecoratedSignature> {
581 match self {
582 TransactionEnvelope::Transaction(tx) => tx.decorated_signature_from_preimage(preimage),
583 TransactionEnvelope::FeeBumpTransaction(tx) => {
584 tx.decorated_signature_from_preimage(preimage)
585 }
586 }
587 }
588
589 pub fn decorated_signature<S, V>(
591 &self,
592 key: &KeyPair<S, V>,
593 network: &Network,
594 ) -> Result<DecoratedSignature>
595 where
596 S: Ed25519Signer<Signature>,
597 V: Ed25519Verifier<Signature> + AsRef<[u8]>,
598 {
599 match self {
600 TransactionEnvelope::Transaction(tx) => tx.decorated_signature(key, network),
601 TransactionEnvelope::FeeBumpTransaction(tx) => tx.decorated_signature(key, network),
602 }
603 }
604
605 pub fn hash(&self, network: &Network) -> Result<Vec<u8>> {
607 match self {
608 TransactionEnvelope::Transaction(tx) => tx.hash(network),
609 TransactionEnvelope::FeeBumpTransaction(tx) => tx.hash(network),
610 }
611 }
612
613 pub fn signature_data(&self, network: &Network) -> Result<Vec<u8>> {
615 match self {
616 TransactionEnvelope::Transaction(tx) => tx.signature_data(network),
617 TransactionEnvelope::FeeBumpTransaction(tx) => tx.signature_data(network),
618 }
619 }
620
621 pub fn to_xdr(&self) -> Result<xdr::TransactionEnvelope> {
623 match self {
624 TransactionEnvelope::Transaction(tx) => {
625 let inner = tx.to_xdr_envelope()?;
626 Ok(xdr::TransactionEnvelope::Tx(inner))
627 }
628 TransactionEnvelope::FeeBumpTransaction(tx) => {
629 let inner = tx.to_xdr_envelope()?;
630 Ok(xdr::TransactionEnvelope::TxFeeBump(inner))
631 }
632 }
633 }
634
635 pub fn from_xdr(x: &xdr::TransactionEnvelope) -> Result<TransactionEnvelope> {
637 match x {
638 xdr::TransactionEnvelope::Tx(inner) => {
639 let tx = Transaction::from_xdr_envelope(inner)?;
640 Ok(TransactionEnvelope::Transaction(tx))
641 }
642 xdr::TransactionEnvelope::TxV0(inner) => {
643 let tx = Transaction::from_xdr_v0_envelope(inner)?;
644 Ok(TransactionEnvelope::Transaction(tx))
645 }
646 xdr::TransactionEnvelope::TxFeeBump(inner) => {
647 let tx = FeeBumpTransaction::from_xdr_envelope(inner)?;
648 Ok(TransactionEnvelope::FeeBumpTransaction(tx))
649 }
650 }
651 }
652
653 pub fn to_xdr_transaction_signature_payload(
655 &self,
656 network: &Network,
657 ) -> Result<xdr::TransactionSignaturePayload> {
658 match self {
659 TransactionEnvelope::Transaction(tx) => {
660 tx.to_xdr_transaction_signature_payload(network)
661 }
662 TransactionEnvelope::FeeBumpTransaction(tx) => {
663 tx.to_xdr_transaction_signature_payload(network)
664 }
665 }
666 }
667}
668
669impl TransactionBuilder {
670 pub fn new<S: Into<MuxedAccount>>(
671 source_account: S,
672 sequence: i64,
673 base_fee: Stroops,
674 ) -> TransactionBuilder {
675 let tx = Transaction {
676 source_account: source_account.into(),
677 sequence,
678 fee: Stroops::new(0),
679 time_bounds: None,
680 memo: Memo::new_none(),
681 operations: Vec::new(),
682 signatures: Vec::new(),
683 };
684 let tx = if base_fee < MIN_BASE_FEE {
685 Err(Error::TransactionFeeTooLow)
686 } else {
687 Ok(tx)
688 };
689 TransactionBuilder { tx, base_fee }
690 }
691
692 pub fn with_time_bounds(mut self, time_bounds: TimeBounds) -> TransactionBuilder {
693 if let Ok(ref mut tx) = self.tx {
694 *tx.time_bounds_mut() = Some(time_bounds);
695 }
696 self
697 }
698
699 pub fn with_memo(mut self, memo: Memo) -> TransactionBuilder {
700 if let Ok(ref mut tx) = self.tx {
701 *tx.memo_mut() = memo;
702 }
703 self
704 }
705
706 pub fn add_operation(mut self, operation: Operation) -> TransactionBuilder {
707 let mut error = None;
708 if let Ok(ref mut tx) = self.tx {
709 let operations = tx.operations_mut();
710 if operations.len() > xdr::MAX_OPS_PER_TX as usize {
711 error = Some(Err(Error::TooManyOperations));
712 } else {
713 operations.push(operation);
714 }
715 }
716 if let Some(error) = error {
717 self.tx = error;
718 }
719 self
720 }
721
722 pub fn into_transaction(mut self) -> Result<Transaction> {
723 let mut error = None;
724 if let Ok(ref mut tx) = self.tx {
725 if tx.operations().is_empty() {
726 error = Some(Err(Error::MissingOperations));
727 }
728 let fee = self
729 .base_fee
730 .checked_mul(&Stroops::new(tx.operations.len() as i64))
731 .ok_or(Error::TransactionFeeOverflow)?;
732 *tx.fee_mut() = fee;
733 }
734
735 if let Some(error) = error {
736 self.tx = error;
737 }
738 self.tx
739 }
740}
741
742impl xdr::WriteXdr for TransactionEnvelope {
743 fn write_xdr<W: Write>(&self, w: &mut xdr::Limited<W>) -> xdr::Result<()> {
744 let xdr_tx = self.to_xdr().map_err(|_| xdr::Error::Invalid)?;
745 xdr_tx.write_xdr(w)
746 }
747}
748
749impl xdr::ReadXdr for TransactionEnvelope {
750 fn read_xdr<R: Read>(r: &mut xdr::Limited<R>) -> xdr::Result<Self> {
751 let xdr_result = xdr::TransactionEnvelope::read_xdr(r)?;
752 Self::from_xdr(&xdr_result).map_err(|_| xdr::Error::Invalid)
753 }
754}
755
756fn signatures_to_xdr(
757 signatures: &[DecoratedSignature],
758) -> Result<xdr::VecM<xdr::DecoratedSignature, 20>> {
759 let mut xdr_signatures = Vec::new();
760 for signature in signatures {
761 let xdr_signature = signature.to_xdr()?;
762 xdr_signatures.push(xdr_signature);
763 }
764 xdr_signatures.try_into().map_err(|_| Error::XdrError)
765}
766
767fn signatures_from_xdr(
768 xdr_signatures: &[xdr::DecoratedSignature],
769) -> Result<Vec<DecoratedSignature>> {
770 let mut signatures = Vec::new();
771 for xdr_signature in xdr_signatures {
772 let signature = DecoratedSignature::from_xdr(xdr_signature)?;
773 signatures.push(signature);
774 }
775 Ok(signatures)
776}
777
778#[cfg(test)]
779mod tests {
780 use super::Transaction;
781 use crate::amount::Stroops;
782 use crate::crypto::DalekKeyPair;
783 use crate::memo::Memo;
784 use crate::operations::Operation;
785 use crate::time_bounds::TimeBounds;
786
787 #[test]
788 fn test_transaction_builder() {
789 let kp = DalekKeyPair::random().unwrap();
790 let tx = Transaction::builder(kp.public_key(), 123, Stroops::new(100))
791 .with_memo(Memo::new_id(987))
792 .with_time_bounds(TimeBounds::always_valid())
793 .add_operation(Operation::new_inflation().build())
794 .into_transaction()
795 .unwrap();
796 assert_eq!(123, *tx.sequence());
797 assert_eq!(&Stroops::new(100), tx.fee());
798 assert!(tx.memo().is_id());
799 assert!(tx.time_bounds().is_some());
800 assert_eq!(1, tx.operations().len());
801 }
802}