1use crate::account::Account;
4use crate::crypto::{AnyPublicKey, AnySignature, MultiKeyPublicKey, MultiKeySignature};
5use crate::error::{AptosError, AptosResult};
6use crate::transaction::authenticator::{AccountAuthenticator, TransactionAuthenticator};
7use crate::transaction::payload::TransactionPayload;
8use crate::transaction::types::{
9 FeePayerRawTransaction, MultiAgentRawTransaction, RawTransaction, SignedTransaction,
10};
11use crate::types::{AccountAddress, ChainId};
12use std::time::{SystemTime, UNIX_EPOCH};
13
14pub const DEFAULT_MAX_GAS_AMOUNT: u64 = 2_000_000;
16pub const DEFAULT_GAS_UNIT_PRICE: u64 = 100;
18pub const DEFAULT_EXPIRATION_SECONDS: u64 = 600; #[derive(Debug, Clone)]
43pub struct TransactionBuilder {
44 sender: Option<AccountAddress>,
45 sequence_number: Option<u64>,
46 payload: Option<TransactionPayload>,
47 max_gas_amount: u64,
48 gas_unit_price: u64,
49 expiration_timestamp_secs: Option<u64>,
50 chain_id: Option<ChainId>,
51}
52
53impl Default for TransactionBuilder {
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59impl TransactionBuilder {
60 #[must_use]
62 pub fn new() -> Self {
63 Self {
64 sender: None,
65 sequence_number: None,
66 payload: None,
67 max_gas_amount: DEFAULT_MAX_GAS_AMOUNT,
68 gas_unit_price: DEFAULT_GAS_UNIT_PRICE,
69 expiration_timestamp_secs: None,
70 chain_id: None,
71 }
72 }
73
74 #[must_use]
76 pub fn sender(mut self, sender: AccountAddress) -> Self {
77 self.sender = Some(sender);
78 self
79 }
80
81 #[must_use]
83 pub fn sequence_number(mut self, sequence_number: u64) -> Self {
84 self.sequence_number = Some(sequence_number);
85 self
86 }
87
88 #[must_use]
90 pub fn payload(mut self, payload: TransactionPayload) -> Self {
91 self.payload = Some(payload);
92 self
93 }
94
95 #[must_use]
97 pub fn max_gas_amount(mut self, max_gas_amount: u64) -> Self {
98 self.max_gas_amount = max_gas_amount;
99 self
100 }
101
102 #[must_use]
104 pub fn gas_unit_price(mut self, gas_unit_price: u64) -> Self {
105 self.gas_unit_price = gas_unit_price;
106 self
107 }
108
109 #[must_use]
111 pub fn expiration_timestamp_secs(mut self, expiration_timestamp_secs: u64) -> Self {
112 self.expiration_timestamp_secs = Some(expiration_timestamp_secs);
113 self
114 }
115
116 #[must_use]
120 pub fn expiration_from_now(mut self, seconds: u64) -> Self {
121 let now = SystemTime::now()
122 .duration_since(UNIX_EPOCH)
123 .unwrap_or_default()
124 .as_secs();
125 self.expiration_timestamp_secs = Some(now.saturating_add(seconds));
126 self
127 }
128
129 #[must_use]
131 pub fn chain_id(mut self, chain_id: ChainId) -> Self {
132 self.chain_id = Some(chain_id);
133 self
134 }
135
136 pub fn build(self) -> AptosResult<RawTransaction> {
146 let sender = self
147 .sender
148 .ok_or_else(|| AptosError::transaction("sender is required"))?;
149 let sequence_number = self
150 .sequence_number
151 .ok_or_else(|| AptosError::transaction("sequence_number is required"))?;
152 let payload = self
153 .payload
154 .ok_or_else(|| AptosError::transaction("payload is required"))?;
155 let chain_id = self
156 .chain_id
157 .ok_or_else(|| AptosError::transaction("chain_id is required"))?;
158
159 let expiration_timestamp_secs = self.expiration_timestamp_secs.unwrap_or_else(|| {
160 SystemTime::now()
161 .duration_since(UNIX_EPOCH)
162 .unwrap_or_default()
163 .as_secs()
164 .saturating_add(DEFAULT_EXPIRATION_SECONDS)
165 });
166
167 Ok(RawTransaction::new(
168 sender,
169 sequence_number,
170 payload,
171 self.max_gas_amount,
172 self.gas_unit_price,
173 expiration_timestamp_secs,
174 chain_id,
175 ))
176 }
177
178 #[cfg(feature = "ed25519")]
184 pub fn build_and_sign<A: Account>(self, account: &A) -> AptosResult<SignedTransaction> {
185 let sender = self.sender.unwrap_or_else(|| account.address());
186 let raw_txn = Self {
187 sender: Some(sender),
188 ..self
189 }
190 .build()?;
191
192 sign_transaction(&raw_txn, account)
193 }
194}
195
196pub fn sign_transaction<A: Account>(
202 raw_txn: &RawTransaction,
203 account: &A,
204) -> AptosResult<SignedTransaction> {
205 let signing_message = raw_txn.signing_message()?;
206 let signature = account.sign(&signing_message)?;
207 let public_key = account.public_key_bytes();
208
209 let authenticator =
210 make_transaction_authenticator(account.signature_scheme(), public_key, signature)?;
211
212 Ok(SignedTransaction::new(raw_txn.clone(), authenticator))
213}
214
215fn make_transaction_authenticator(
221 scheme: u8,
222 public_key: Vec<u8>,
223 signature: Vec<u8>,
224) -> AptosResult<TransactionAuthenticator> {
225 match scheme {
226 crate::crypto::ED25519_SCHEME => {
227 Ok(TransactionAuthenticator::ed25519(public_key, signature))
228 }
229 crate::crypto::MULTI_ED25519_SCHEME => Ok(TransactionAuthenticator::multi_ed25519(
230 public_key, signature,
231 )),
232 crate::crypto::MULTI_KEY_SCHEME => {
233 let _ = MultiKeyPublicKey::from_bytes(&public_key)?;
234 let _ = MultiKeySignature::from_bytes(&signature)?;
235 Ok(TransactionAuthenticator::single_sender(
236 AccountAuthenticator::multi_key(public_key, signature),
237 ))
238 }
239 crate::crypto::SINGLE_KEY_SCHEME => {
240 let _ = AnyPublicKey::from_bcs_bytes(&public_key)?;
241 let _ = AnySignature::from_bcs_bytes(&signature)?;
242 Ok(TransactionAuthenticator::single_sender(
243 AccountAuthenticator::single_key(public_key, signature),
244 ))
245 }
246 #[cfg(feature = "keyless")]
247 crate::crypto::KEYLESS_SCHEME => {
248 Ok(TransactionAuthenticator::single_sender(
252 AccountAuthenticator::keyless(public_key, signature),
253 ))
254 }
255 _ => Err(AptosError::InvalidSignature(format!(
256 "unknown signature scheme: {scheme}"
257 ))),
258 }
259}
260
261fn make_account_authenticator(
267 scheme: u8,
268 public_key: Vec<u8>,
269 signature: Vec<u8>,
270) -> AptosResult<AccountAuthenticator> {
271 match scheme {
272 crate::crypto::ED25519_SCHEME => Ok(AccountAuthenticator::ed25519(public_key, signature)),
273 crate::crypto::MULTI_ED25519_SCHEME => Ok(AccountAuthenticator::MultiEd25519 {
274 public_key,
275 signature,
276 }),
277 crate::crypto::SINGLE_KEY_SCHEME => {
278 let _ = AnyPublicKey::from_bcs_bytes(&public_key)?;
279 let _ = AnySignature::from_bcs_bytes(&signature)?;
280 Ok(AccountAuthenticator::single_key(public_key, signature))
281 }
282 crate::crypto::MULTI_KEY_SCHEME => {
283 let _ = MultiKeyPublicKey::from_bytes(&public_key)?;
284 let _ = MultiKeySignature::from_bytes(&signature)?;
285 Ok(AccountAuthenticator::multi_key(public_key, signature))
286 }
287 #[cfg(feature = "keyless")]
288 crate::crypto::KEYLESS_SCHEME => Ok(AccountAuthenticator::keyless(public_key, signature)),
289 _ => Err(AptosError::InvalidSignature(format!(
290 "unknown signature scheme: {scheme}"
291 ))),
292 }
293}
294
295pub fn sign_multi_agent_transaction<A: Account>(
301 multi_agent: &MultiAgentRawTransaction,
302 sender: &A,
303 secondary_signers: &[&dyn Account],
304) -> AptosResult<SignedTransaction> {
305 let signing_message = multi_agent.signing_message()?;
306
307 let sender_signature = sender.sign(&signing_message)?;
309 let sender_public_key = sender.public_key_bytes();
310 let sender_auth = make_account_authenticator(
311 sender.signature_scheme(),
312 sender_public_key,
313 sender_signature,
314 )?;
315
316 let mut secondary_auths = Vec::with_capacity(secondary_signers.len());
318 for signer in secondary_signers {
319 let signature = signer.sign(&signing_message)?;
320 let public_key = signer.public_key_bytes();
321 secondary_auths.push(make_account_authenticator(
322 signer.signature_scheme(),
323 public_key,
324 signature,
325 )?);
326 }
327
328 let authenticator = TransactionAuthenticator::multi_agent(
329 sender_auth,
330 multi_agent.secondary_signer_addresses.clone(),
331 secondary_auths,
332 );
333
334 Ok(SignedTransaction::new(
335 multi_agent.raw_txn.clone(),
336 authenticator,
337 ))
338}
339
340pub fn sign_fee_payer_transaction<A: Account>(
346 fee_payer_txn: &FeePayerRawTransaction,
347 sender: &A,
348 secondary_signers: &[&dyn Account],
349 fee_payer: &dyn Account,
350) -> AptosResult<SignedTransaction> {
351 let signing_message = fee_payer_txn.signing_message()?;
352
353 let sender_signature = sender.sign(&signing_message)?;
355 let sender_public_key = sender.public_key_bytes();
356 let sender_auth = make_account_authenticator(
357 sender.signature_scheme(),
358 sender_public_key,
359 sender_signature,
360 )?;
361
362 let mut secondary_auths = Vec::with_capacity(secondary_signers.len());
364 for signer in secondary_signers {
365 let signature = signer.sign(&signing_message)?;
366 let public_key = signer.public_key_bytes();
367 secondary_auths.push(make_account_authenticator(
368 signer.signature_scheme(),
369 public_key,
370 signature,
371 )?);
372 }
373
374 let fee_payer_signature = fee_payer.sign(&signing_message)?;
376 let fee_payer_public_key = fee_payer.public_key_bytes();
377 let fee_payer_auth = make_account_authenticator(
378 fee_payer.signature_scheme(),
379 fee_payer_public_key,
380 fee_payer_signature,
381 )?;
382
383 let authenticator = TransactionAuthenticator::fee_payer(
384 sender_auth,
385 fee_payer_txn.secondary_signer_addresses.clone(),
386 secondary_auths,
387 fee_payer_txn.fee_payer_address,
388 fee_payer_auth,
389 );
390
391 Ok(SignedTransaction::new(
392 fee_payer_txn.raw_txn.clone(),
393 authenticator,
394 ))
395}
396
397pub fn build_simulation_signed_multi_agent(
411 multi_agent: &MultiAgentRawTransaction,
412) -> SignedTransaction {
413 let sender_auth = AccountAuthenticator::no_account_authenticator();
414 let secondary_auths: Vec<AccountAuthenticator> = multi_agent
415 .secondary_signer_addresses
416 .iter()
417 .map(|_| AccountAuthenticator::no_account_authenticator())
418 .collect();
419 let authenticator = TransactionAuthenticator::multi_agent(
420 sender_auth,
421 multi_agent.secondary_signer_addresses.clone(),
422 secondary_auths,
423 );
424 SignedTransaction::new(multi_agent.raw_txn.clone(), authenticator)
425}
426
427pub fn build_simulation_signed_fee_payer(
442 fee_payer_txn: &FeePayerRawTransaction,
443) -> SignedTransaction {
444 let sender_auth = AccountAuthenticator::no_account_authenticator();
445 let secondary_auths: Vec<AccountAuthenticator> = fee_payer_txn
446 .secondary_signer_addresses
447 .iter()
448 .map(|_| AccountAuthenticator::no_account_authenticator())
449 .collect();
450 let fee_payer_auth = AccountAuthenticator::no_account_authenticator();
451 let authenticator = TransactionAuthenticator::fee_payer(
452 sender_auth,
453 fee_payer_txn.secondary_signer_addresses.clone(),
454 secondary_auths,
455 fee_payer_txn.fee_payer_address,
456 fee_payer_auth,
457 );
458 SignedTransaction::new(fee_payer_txn.raw_txn.clone(), authenticator)
459}
460
461#[cfg(test)]
462mod tests {
463 use super::*;
464 use crate::transaction::payload::EntryFunction;
465
466 #[test]
467 fn test_builder_missing_fields() {
468 let result = TransactionBuilder::new().build();
469 assert!(result.is_err());
470 }
471
472 #[test]
473 fn test_builder_complete() {
474 let recipient = AccountAddress::from_hex("0x123").unwrap();
475 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
476
477 let txn = TransactionBuilder::new()
478 .sender(AccountAddress::ONE)
479 .sequence_number(0)
480 .payload(payload.into())
481 .chain_id(ChainId::testnet())
482 .build()
483 .unwrap();
484
485 assert_eq!(txn.sender, AccountAddress::ONE);
486 assert_eq!(txn.sequence_number, 0);
487 assert_eq!(txn.max_gas_amount, DEFAULT_MAX_GAS_AMOUNT);
488 assert_eq!(txn.gas_unit_price, DEFAULT_GAS_UNIT_PRICE);
489 }
490
491 #[test]
492 fn test_builder_custom_gas() {
493 let recipient = AccountAddress::from_hex("0x123").unwrap();
494 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
495
496 let txn = TransactionBuilder::new()
497 .sender(AccountAddress::ONE)
498 .sequence_number(0)
499 .payload(payload.into())
500 .max_gas_amount(500_000)
501 .gas_unit_price(200)
502 .chain_id(ChainId::testnet())
503 .build()
504 .unwrap();
505
506 assert_eq!(txn.max_gas_amount, 500_000);
507 assert_eq!(txn.gas_unit_price, 200);
508 }
509
510 #[test]
511 fn test_builder_missing_sender() {
512 let recipient = AccountAddress::from_hex("0x123").unwrap();
513 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
514
515 let result = TransactionBuilder::new()
516 .sequence_number(0)
517 .payload(payload.into())
518 .chain_id(ChainId::testnet())
519 .build();
520
521 assert!(result.is_err());
522 }
523
524 #[test]
525 fn test_builder_missing_payload() {
526 let result = TransactionBuilder::new()
527 .sender(AccountAddress::ONE)
528 .sequence_number(0)
529 .chain_id(ChainId::testnet())
530 .build();
531
532 assert!(result.is_err());
533 }
534
535 #[test]
536 fn test_builder_missing_chain_id() {
537 let recipient = AccountAddress::from_hex("0x123").unwrap();
538 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
539
540 let result = TransactionBuilder::new()
541 .sender(AccountAddress::ONE)
542 .sequence_number(0)
543 .payload(payload.into())
544 .build();
545
546 assert!(result.is_err());
547 }
548
549 #[test]
550 fn test_builder_custom_expiration() {
551 let recipient = AccountAddress::from_hex("0x123").unwrap();
552 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
553 let custom_expiration = 9_999_999_999;
554
555 let txn = TransactionBuilder::new()
556 .sender(AccountAddress::ONE)
557 .sequence_number(0)
558 .payload(payload.into())
559 .expiration_timestamp_secs(custom_expiration)
560 .chain_id(ChainId::testnet())
561 .build()
562 .unwrap();
563
564 assert_eq!(txn.expiration_timestamp_secs, custom_expiration);
565 }
566
567 #[test]
568 fn test_default_expiration() {
569 let now = std::time::SystemTime::now()
571 .duration_since(std::time::UNIX_EPOCH)
572 .unwrap()
573 .as_secs();
574
575 let recipient = AccountAddress::from_hex("0x123").unwrap();
576 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
577
578 let txn = TransactionBuilder::new()
579 .sender(AccountAddress::ONE)
580 .sequence_number(0)
581 .payload(payload.into())
582 .chain_id(ChainId::testnet())
583 .build()
584 .unwrap();
585
586 let expected_min = now + DEFAULT_EXPIRATION_SECONDS - 5;
588 let expected_max = now + DEFAULT_EXPIRATION_SECONDS + 5;
589 assert!(txn.expiration_timestamp_secs >= expected_min);
590 assert!(txn.expiration_timestamp_secs <= expected_max);
591 }
592
593 #[cfg(feature = "ed25519")]
594 #[test]
595 fn test_sign_transaction() {
596 use crate::account::Ed25519Account;
597
598 let account = Ed25519Account::generate();
599 let recipient = AccountAddress::from_hex("0x123").unwrap();
600 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
601
602 let txn = TransactionBuilder::new()
603 .sender(account.address())
604 .sequence_number(0)
605 .payload(payload.into())
606 .chain_id(ChainId::testnet())
607 .build()
608 .unwrap();
609
610 let signed = sign_transaction(&txn, &account).unwrap();
611 assert_eq!(signed.sender(), account.address());
612 signed.verify_signature().unwrap();
613 }
614
615 #[cfg(feature = "ed25519")]
616 #[test]
617 fn test_sign_transaction_with_ed25519_single_key_account() {
618 use crate::account::Ed25519SingleKeyAccount;
619 use crate::transaction::authenticator::{AccountAuthenticator, TransactionAuthenticator};
620
621 let account = Ed25519SingleKeyAccount::generate();
622 let recipient = AccountAddress::from_hex("0x123").unwrap();
623 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
624
625 let txn = TransactionBuilder::new()
626 .sender(account.address())
627 .sequence_number(0)
628 .payload(payload.into())
629 .chain_id(ChainId::testnet())
630 .build()
631 .unwrap();
632
633 let signed = sign_transaction(&txn, &account).unwrap();
634 match &signed.authenticator {
635 TransactionAuthenticator::SingleSender { sender } => {
636 assert!(matches!(sender, AccountAuthenticator::SingleKey { .. }));
637 }
638 _ => panic!("expected SingleSender authenticator"),
639 }
640 signed.verify_signature().unwrap();
641 }
642
643 #[cfg(all(feature = "ed25519", feature = "secp256k1"))]
644 #[test]
645 fn test_sign_transaction_with_mixed_multi_key_account() {
646 use crate::account::{AnyPrivateKey, MultiKeyAccount};
647 use crate::crypto::{Ed25519PrivateKey, Secp256k1PrivateKey};
648
649 let account = MultiKeyAccount::new(
650 vec![
651 AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()),
652 AnyPrivateKey::secp256k1(Secp256k1PrivateKey::generate()),
653 AnyPrivateKey::ed25519(Ed25519PrivateKey::generate()),
654 ],
655 2,
656 )
657 .unwrap();
658 let recipient = AccountAddress::from_hex("0x123").unwrap();
659 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
660
661 let txn = TransactionBuilder::new()
662 .sender(account.address())
663 .sequence_number(0)
664 .payload(payload.into())
665 .chain_id(ChainId::testnet())
666 .build()
667 .unwrap();
668
669 let signed = sign_transaction(&txn, &account).unwrap();
670 assert_eq!(signed.sender(), account.address());
671 signed.verify_signature().unwrap();
672 }
673
674 #[cfg(feature = "ed25519")]
675 #[test]
676 fn test_sign_multi_agent_transaction() {
677 use crate::account::{Account, Ed25519Account};
678
679 let sender = Ed25519Account::generate();
680 let secondary = Ed25519Account::generate();
681 let recipient = AccountAddress::from_hex("0x123").unwrap();
682 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
683
684 let raw_txn = TransactionBuilder::new()
685 .sender(sender.address())
686 .sequence_number(0)
687 .payload(payload.into())
688 .chain_id(ChainId::testnet())
689 .build()
690 .unwrap();
691
692 let multi_agent = MultiAgentRawTransaction {
693 raw_txn,
694 secondary_signer_addresses: vec![secondary.address()],
695 };
696
697 let secondary_signers: Vec<&dyn Account> = vec![&secondary];
698 let signed =
699 sign_multi_agent_transaction(&multi_agent, &sender, &secondary_signers).unwrap();
700 assert_eq!(signed.sender(), sender.address());
701 signed.verify_signature().unwrap();
702 }
703
704 #[cfg(feature = "ed25519")]
705 #[test]
706 fn test_sign_multi_agent_transaction_with_single_key_accounts() {
707 use crate::account::{Account, Ed25519SingleKeyAccount};
708 use crate::transaction::authenticator::{AccountAuthenticator, TransactionAuthenticator};
709
710 let sender = Ed25519SingleKeyAccount::generate();
711 let secondary = Ed25519SingleKeyAccount::generate();
712 let recipient = AccountAddress::from_hex("0x123").unwrap();
713 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
714
715 let raw_txn = TransactionBuilder::new()
716 .sender(sender.address())
717 .sequence_number(0)
718 .payload(payload.into())
719 .chain_id(ChainId::testnet())
720 .build()
721 .unwrap();
722
723 let multi_agent = MultiAgentRawTransaction {
724 raw_txn,
725 secondary_signer_addresses: vec![secondary.address()],
726 };
727
728 let secondary_signers: Vec<&dyn Account> = vec![&secondary];
729 let signed =
730 sign_multi_agent_transaction(&multi_agent, &sender, &secondary_signers).unwrap();
731 match &signed.authenticator {
732 TransactionAuthenticator::MultiAgent {
733 sender,
734 secondary_signers,
735 ..
736 } => {
737 assert!(matches!(sender, AccountAuthenticator::SingleKey { .. }));
738 assert!(matches!(
739 secondary_signers[0],
740 AccountAuthenticator::SingleKey { .. }
741 ));
742 }
743 _ => panic!("expected MultiAgent authenticator"),
744 }
745 signed.verify_signature().unwrap();
746 }
747
748 #[cfg(feature = "ed25519")]
749 #[test]
750 fn test_multi_agent_verify_signature_rejects_secondary_signer_length_mismatch() {
751 use crate::account::{Account, Ed25519Account};
752 use crate::transaction::authenticator::{AccountAuthenticator, TransactionAuthenticator};
753
754 let sender = Ed25519Account::generate();
755 let secondary = Ed25519Account::generate();
756 let recipient = AccountAddress::from_hex("0x123").unwrap();
757 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
758
759 let raw_txn = TransactionBuilder::new()
760 .sender(sender.address())
761 .sequence_number(0)
762 .payload(payload.into())
763 .chain_id(ChainId::testnet())
764 .build()
765 .unwrap();
766
767 let multi_agent = MultiAgentRawTransaction {
768 raw_txn,
769 secondary_signer_addresses: vec![secondary.address()],
770 };
771
772 let secondary_signers: Vec<&dyn Account> = vec![&secondary];
773 let signed =
774 sign_multi_agent_transaction(&multi_agent, &sender, &secondary_signers).unwrap();
775 signed.verify_signature().unwrap();
776
777 let mut missing_secondary_signer = signed.clone();
778 if let TransactionAuthenticator::MultiAgent {
779 secondary_signers, ..
780 } = &mut missing_secondary_signer.authenticator
781 {
782 secondary_signers.clear();
783 } else {
784 panic!("expected MultiAgent authenticator");
785 }
786 assert!(missing_secondary_signer.verify_signature().is_err());
787
788 let mut extra_secondary_signer = signed;
789 if let TransactionAuthenticator::MultiAgent {
790 secondary_signers, ..
791 } = &mut extra_secondary_signer.authenticator
792 {
793 secondary_signers.push(AccountAuthenticator::NoAccountAuthenticator);
794 } else {
795 panic!("expected MultiAgent authenticator");
796 }
797 assert!(extra_secondary_signer.verify_signature().is_err());
798 }
799
800 #[cfg(feature = "ed25519")]
801 #[test]
802 fn test_sign_fee_payer_transaction() {
803 use crate::account::Ed25519Account;
804
805 let sender = Ed25519Account::generate();
806 let fee_payer = Ed25519Account::generate();
807 let recipient = AccountAddress::from_hex("0x123").unwrap();
808 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
809
810 let raw_txn = TransactionBuilder::new()
811 .sender(sender.address())
812 .sequence_number(0)
813 .payload(payload.into())
814 .chain_id(ChainId::testnet())
815 .build()
816 .unwrap();
817
818 let fee_payer_txn = FeePayerRawTransaction {
819 raw_txn,
820 secondary_signer_addresses: vec![],
821 fee_payer_address: fee_payer.address(),
822 };
823
824 let signed = sign_fee_payer_transaction(&fee_payer_txn, &sender, &[], &fee_payer).unwrap();
825 assert_eq!(signed.sender(), sender.address());
826 signed.verify_signature().unwrap();
827 }
828
829 #[cfg(feature = "ed25519")]
830 #[test]
831 fn test_sign_fee_payer_transaction_with_multi_key_accounts() {
832 use crate::account::{AnyPrivateKey, MultiKeyAccount};
833 use crate::crypto::Ed25519PrivateKey;
834 use crate::transaction::authenticator::{AccountAuthenticator, TransactionAuthenticator};
835
836 let sender = MultiKeyAccount::new(
837 vec![AnyPrivateKey::ed25519(Ed25519PrivateKey::generate())],
838 1,
839 )
840 .unwrap();
841 let fee_payer = MultiKeyAccount::new(
842 vec![AnyPrivateKey::ed25519(Ed25519PrivateKey::generate())],
843 1,
844 )
845 .unwrap();
846 let recipient = AccountAddress::from_hex("0x123").unwrap();
847 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
848
849 let raw_txn = TransactionBuilder::new()
850 .sender(sender.address())
851 .sequence_number(0)
852 .payload(payload.into())
853 .chain_id(ChainId::testnet())
854 .build()
855 .unwrap();
856
857 let fee_payer_txn = FeePayerRawTransaction::new_simple(raw_txn, fee_payer.address());
858 let signed = sign_fee_payer_transaction(&fee_payer_txn, &sender, &[], &fee_payer).unwrap();
859 match &signed.authenticator {
860 TransactionAuthenticator::FeePayer {
861 sender,
862 fee_payer_signer,
863 ..
864 } => {
865 assert!(matches!(sender, AccountAuthenticator::MultiKey { .. }));
866 assert!(matches!(
867 fee_payer_signer,
868 AccountAuthenticator::MultiKey { .. }
869 ));
870 }
871 _ => panic!("expected FeePayer authenticator"),
872 }
873 signed.verify_signature().unwrap();
874 }
875
876 #[cfg(feature = "ed25519")]
877 #[test]
878 fn test_fee_payer_verify_signature_rejects_secondary_signer_length_mismatch() {
879 use crate::account::{Account, Ed25519Account};
880 use crate::transaction::authenticator::{AccountAuthenticator, TransactionAuthenticator};
881
882 let sender = Ed25519Account::generate();
883 let secondary = Ed25519Account::generate();
884 let fee_payer = Ed25519Account::generate();
885 let recipient = AccountAddress::from_hex("0x123").unwrap();
886 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
887
888 let raw_txn = TransactionBuilder::new()
889 .sender(sender.address())
890 .sequence_number(0)
891 .payload(payload.into())
892 .chain_id(ChainId::testnet())
893 .build()
894 .unwrap();
895
896 let fee_payer_txn = FeePayerRawTransaction {
897 raw_txn,
898 secondary_signer_addresses: vec![secondary.address()],
899 fee_payer_address: fee_payer.address(),
900 };
901
902 let secondary_signers: Vec<&dyn Account> = vec![&secondary];
903 let signed =
904 sign_fee_payer_transaction(&fee_payer_txn, &sender, &secondary_signers, &fee_payer)
905 .unwrap();
906 signed.verify_signature().unwrap();
907
908 let mut missing_secondary_signer = signed.clone();
909 if let TransactionAuthenticator::FeePayer {
910 secondary_signers, ..
911 } = &mut missing_secondary_signer.authenticator
912 {
913 secondary_signers.clear();
914 } else {
915 panic!("expected FeePayer authenticator");
916 }
917 assert!(missing_secondary_signer.verify_signature().is_err());
918
919 let mut extra_secondary_signer = signed;
920 if let TransactionAuthenticator::FeePayer {
921 secondary_signers, ..
922 } = &mut extra_secondary_signer.authenticator
923 {
924 secondary_signers.push(AccountAuthenticator::NoAccountAuthenticator);
925 } else {
926 panic!("expected FeePayer authenticator");
927 }
928 assert!(extra_secondary_signer.verify_signature().is_err());
929 }
930
931 #[test]
932 fn test_build_simulation_signed_multi_agent() {
933 use crate::transaction::authenticator::{AccountAuthenticator, TransactionAuthenticator};
934
935 let recipient = AccountAddress::from_hex("0x123").unwrap();
936 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
937 let raw_txn = TransactionBuilder::new()
938 .sender(AccountAddress::ONE)
939 .sequence_number(0)
940 .payload(payload.into())
941 .chain_id(ChainId::testnet())
942 .build()
943 .unwrap();
944
945 let secondary_addr = AccountAddress::from_hex("0x456").unwrap();
946 let multi_agent = MultiAgentRawTransaction::new(raw_txn.clone(), vec![secondary_addr]);
947
948 let signed = build_simulation_signed_multi_agent(&multi_agent);
949 assert_eq!(signed.sender(), AccountAddress::ONE);
950 assert!(signed.to_bcs().is_ok());
951
952 match &signed.authenticator {
953 TransactionAuthenticator::MultiAgent {
954 sender,
955 secondary_signer_addresses,
956 secondary_signers,
957 } => {
958 assert!(matches!(
959 sender,
960 AccountAuthenticator::NoAccountAuthenticator
961 ));
962 assert_eq!(secondary_signer_addresses.len(), 1);
963 assert_eq!(secondary_signer_addresses[0], secondary_addr);
964 assert_eq!(secondary_signers.len(), 1);
965 assert!(matches!(
966 &secondary_signers[0],
967 AccountAuthenticator::NoAccountAuthenticator
968 ));
969 }
970 _ => panic!("expected MultiAgent authenticator"),
971 }
972 }
973
974 #[test]
975 fn test_build_simulation_signed_fee_payer() {
976 use crate::transaction::authenticator::{AccountAuthenticator, TransactionAuthenticator};
977
978 let recipient = AccountAddress::from_hex("0x123").unwrap();
979 let payload = EntryFunction::apt_transfer(recipient, 1000).unwrap();
980 let raw_txn = TransactionBuilder::new()
981 .sender(AccountAddress::ONE)
982 .sequence_number(0)
983 .payload(payload.into())
984 .chain_id(ChainId::testnet())
985 .build()
986 .unwrap();
987
988 let fee_payer_addr = AccountAddress::from_hex("0x789").unwrap();
989 let fee_payer_txn = FeePayerRawTransaction::new_simple(raw_txn.clone(), fee_payer_addr);
990
991 let signed = build_simulation_signed_fee_payer(&fee_payer_txn);
992 assert_eq!(signed.sender(), AccountAddress::ONE);
993 assert!(signed.to_bcs().is_ok());
994
995 match &signed.authenticator {
996 TransactionAuthenticator::FeePayer {
997 sender,
998 fee_payer_address,
999 fee_payer_signer,
1000 ..
1001 } => {
1002 assert!(matches!(
1003 sender,
1004 AccountAuthenticator::NoAccountAuthenticator
1005 ));
1006 assert_eq!(*fee_payer_address, fee_payer_addr);
1007 assert!(matches!(
1008 fee_payer_signer,
1009 AccountAuthenticator::NoAccountAuthenticator
1010 ));
1011 }
1012 _ => panic!("expected FeePayer authenticator"),
1013 }
1014 }
1015
1016 #[test]
1017 fn test_default_impl() {
1018 let builder = TransactionBuilder::default();
1019 assert!(builder.sender.is_none());
1021 assert!(builder.sequence_number.is_none());
1022 assert!(builder.payload.is_none());
1023 assert_eq!(builder.max_gas_amount, DEFAULT_MAX_GAS_AMOUNT);
1024 assert_eq!(builder.gas_unit_price, DEFAULT_GAS_UNIT_PRICE);
1025 }
1026}