Skip to main content

aptos_sdk/transaction/
types.rs

1//! Transaction types.
2
3use crate::error::AptosResult;
4use crate::transaction::authenticator::TransactionAuthenticator;
5use crate::transaction::payload::TransactionPayload;
6use crate::types::{AccountAddress, ChainId, HashValue};
7use serde::{Deserialize, Serialize};
8
9/// The raw transaction that a client signs.
10///
11/// A `RawTransaction` contains all the details of a transaction before
12/// it is signed, including the sender, payload, gas parameters, and
13/// expiration time.
14#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
15pub struct RawTransaction {
16    /// Sender's address.
17    pub sender: AccountAddress,
18    /// Sequence number of this transaction.
19    pub sequence_number: u64,
20    /// The transaction payload (entry function, script, etc.).
21    pub payload: TransactionPayload,
22    /// Maximum gas units the sender is willing to pay.
23    pub max_gas_amount: u64,
24    /// Price per gas unit in octas.
25    pub gas_unit_price: u64,
26    /// Expiration time in seconds since Unix epoch.
27    pub expiration_timestamp_secs: u64,
28    /// Chain ID to prevent cross-chain replay.
29    pub chain_id: ChainId,
30}
31
32impl RawTransaction {
33    /// Creates a new raw transaction.
34    pub fn new(
35        sender: AccountAddress,
36        sequence_number: u64,
37        payload: TransactionPayload,
38        max_gas_amount: u64,
39        gas_unit_price: u64,
40        expiration_timestamp_secs: u64,
41        chain_id: ChainId,
42    ) -> Self {
43        Self {
44            sender,
45            sequence_number,
46            payload,
47            max_gas_amount,
48            gas_unit_price,
49            expiration_timestamp_secs,
50            chain_id,
51        }
52    }
53
54    /// Generates the signing message for this transaction.
55    ///
56    /// This is the message that should be signed to create a valid
57    /// transaction authenticator.
58    ///
59    /// # Errors
60    ///
61    /// Returns an error if BCS serialization of the transaction fails.
62    pub fn signing_message(&self) -> AptosResult<Vec<u8>> {
63        let prefix = crate::crypto::sha3_256(b"APTOS::RawTransaction");
64        let bcs_bytes = aptos_bcs::to_bytes(self).map_err(crate::error::AptosError::bcs)?;
65
66        let mut message = Vec::with_capacity(prefix.len() + bcs_bytes.len());
67        message.extend_from_slice(&prefix);
68        message.extend_from_slice(&bcs_bytes);
69        Ok(message)
70    }
71
72    /// Serializes this transaction to BCS bytes.
73    ///
74    /// # Errors
75    ///
76    /// Returns an error if BCS serialization fails.
77    pub fn to_bcs(&self) -> AptosResult<Vec<u8>> {
78        aptos_bcs::to_bytes(self).map_err(crate::error::AptosError::bcs)
79    }
80}
81
82/// An orderless transaction using hash-based replay protection.
83///
84/// Unlike standard transactions that use sequence numbers, orderless transactions
85/// use a random nonce and a short expiration window (typically 60 seconds) to
86/// prevent replay attacks. This allows transactions to be submitted in any order.
87///
88/// # Note
89///
90/// Orderless transactions must be configured with a very short expiration time
91/// (recommended: 60 seconds or less) since the replay protection relies on
92/// the chain remembering recently seen transaction hashes.
93#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
94pub struct RawTransactionOrderless {
95    /// Sender's address.
96    pub sender: AccountAddress,
97    /// Random nonce for uniqueness (32 bytes recommended).
98    pub nonce: Vec<u8>,
99    /// The transaction payload (entry function, script, etc.).
100    pub payload: TransactionPayload,
101    /// Maximum gas units the sender is willing to pay.
102    pub max_gas_amount: u64,
103    /// Price per gas unit in octas.
104    pub gas_unit_price: u64,
105    /// Expiration time in seconds since Unix epoch.
106    /// Should be short (e.g., current time + 60 seconds).
107    pub expiration_timestamp_secs: u64,
108    /// Chain ID to prevent cross-chain replay.
109    pub chain_id: ChainId,
110}
111
112impl RawTransactionOrderless {
113    /// Creates a new orderless transaction with a random nonce.
114    pub fn new(
115        sender: AccountAddress,
116        payload: TransactionPayload,
117        max_gas_amount: u64,
118        gas_unit_price: u64,
119        expiration_timestamp_secs: u64,
120        chain_id: ChainId,
121    ) -> Self {
122        // Generate a random 32-byte nonce
123        let mut nonce = vec![0u8; 32];
124        rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut nonce);
125        Self {
126            sender,
127            nonce,
128            payload,
129            max_gas_amount,
130            gas_unit_price,
131            expiration_timestamp_secs,
132            chain_id,
133        }
134    }
135
136    /// Creates a new orderless transaction with a specific nonce.
137    pub fn with_nonce(
138        sender: AccountAddress,
139        nonce: Vec<u8>,
140        payload: TransactionPayload,
141        max_gas_amount: u64,
142        gas_unit_price: u64,
143        expiration_timestamp_secs: u64,
144        chain_id: ChainId,
145    ) -> Self {
146        Self {
147            sender,
148            nonce,
149            payload,
150            max_gas_amount,
151            gas_unit_price,
152            expiration_timestamp_secs,
153            chain_id,
154        }
155    }
156
157    /// Generates the signing message for this orderless transaction.
158    ///
159    /// # Errors
160    ///
161    /// Returns an error if BCS serialization of the transaction fails.
162    pub fn signing_message(&self) -> AptosResult<Vec<u8>> {
163        let prefix = crate::crypto::sha3_256(b"APTOS::RawTransactionOrderless");
164        let bcs_bytes = aptos_bcs::to_bytes(self).map_err(crate::error::AptosError::bcs)?;
165
166        let mut message = Vec::with_capacity(prefix.len() + bcs_bytes.len());
167        message.extend_from_slice(&prefix);
168        message.extend_from_slice(&bcs_bytes);
169        Ok(message)
170    }
171
172    /// Serializes this transaction to BCS bytes.
173    ///
174    /// # Errors
175    ///
176    /// Returns an error if BCS serialization fails.
177    pub fn to_bcs(&self) -> AptosResult<Vec<u8>> {
178        aptos_bcs::to_bytes(self).map_err(crate::error::AptosError::bcs)
179    }
180}
181
182/// A signed orderless transaction ready for submission.
183#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
184pub struct SignedTransactionOrderless {
185    /// The orderless raw transaction.
186    pub raw_txn: RawTransactionOrderless,
187    /// The authenticator (signature(s) and public key(s)).
188    pub authenticator: TransactionAuthenticator,
189}
190
191impl SignedTransactionOrderless {
192    /// Creates a new signed orderless transaction.
193    pub fn new(raw_txn: RawTransactionOrderless, authenticator: TransactionAuthenticator) -> Self {
194        Self {
195            raw_txn,
196            authenticator,
197        }
198    }
199
200    /// Serializes this signed transaction to BCS bytes.
201    ///
202    /// # Errors
203    ///
204    /// Returns an error if BCS serialization fails.
205    pub fn to_bcs(&self) -> AptosResult<Vec<u8>> {
206        aptos_bcs::to_bytes(self).map_err(crate::error::AptosError::bcs)
207    }
208
209    /// Returns the sender address.
210    pub fn sender(&self) -> AccountAddress {
211        self.raw_txn.sender
212    }
213
214    /// Returns the nonce.
215    pub fn nonce(&self) -> &[u8] {
216        &self.raw_txn.nonce
217    }
218
219    /// Computes the transaction hash.
220    ///
221    /// # Errors
222    ///
223    /// Returns an error if BCS serialization of the transaction fails.
224    pub fn hash(&self) -> AptosResult<HashValue> {
225        let bcs_bytes = self.to_bcs()?;
226        let prefix = crate::crypto::sha3_256(b"APTOS::Transaction");
227
228        let mut data = Vec::with_capacity(prefix.len() + 1 + bcs_bytes.len());
229        data.extend_from_slice(&prefix);
230        // Use variant 2 for orderless user transactions (assuming standard is 0, script is 1)
231        data.push(2);
232        data.extend_from_slice(&bcs_bytes);
233
234        Ok(HashValue::new(crate::crypto::sha3_256(&data)))
235    }
236}
237
238/// A signed transaction ready for submission.
239#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
240pub struct SignedTransaction {
241    /// The raw transaction.
242    pub raw_txn: RawTransaction,
243    /// The authenticator (signature(s) and public key(s)).
244    pub authenticator: TransactionAuthenticator,
245}
246
247impl SignedTransaction {
248    /// Creates a new signed transaction.
249    pub fn new(raw_txn: RawTransaction, authenticator: TransactionAuthenticator) -> Self {
250        Self {
251            raw_txn,
252            authenticator,
253        }
254    }
255
256    /// Serializes this signed transaction to BCS bytes.
257    ///
258    /// # Errors
259    ///
260    /// Returns an error if BCS serialization fails.
261    pub fn to_bcs(&self) -> AptosResult<Vec<u8>> {
262        aptos_bcs::to_bytes(self).map_err(crate::error::AptosError::bcs)
263    }
264
265    /// Returns the sender address.
266    pub fn sender(&self) -> AccountAddress {
267        self.raw_txn.sender
268    }
269
270    /// Returns the sequence number.
271    pub fn sequence_number(&self) -> u64 {
272        self.raw_txn.sequence_number
273    }
274
275    /// Computes the transaction hash.
276    ///
277    /// # Errors
278    ///
279    /// Returns an error if BCS serialization of the transaction fails.
280    pub fn hash(&self) -> AptosResult<HashValue> {
281        let bcs_bytes = self.to_bcs()?;
282        let prefix = crate::crypto::sha3_256(b"APTOS::Transaction");
283
284        let mut data = Vec::with_capacity(prefix.len() + 1 + bcs_bytes.len());
285        data.extend_from_slice(&prefix);
286        data.push(0); // User transaction variant
287        data.extend_from_slice(&bcs_bytes);
288
289        Ok(HashValue::new(crate::crypto::sha3_256(&data)))
290    }
291}
292
293/// Information about a submitted/executed transaction.
294#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
295pub struct TransactionInfo {
296    /// The transaction hash.
297    pub hash: HashValue,
298    /// The ledger version this transaction was committed at.
299    #[serde(default)]
300    pub version: Option<u64>,
301    /// Whether the transaction succeeded.
302    #[serde(default)]
303    pub success: Option<bool>,
304    /// The VM status message.
305    #[serde(default)]
306    pub vm_status: Option<String>,
307    /// Gas used by the transaction.
308    #[serde(default)]
309    pub gas_used: Option<u64>,
310}
311
312impl TransactionInfo {
313    /// Returns true if the transaction succeeded.
314    pub fn is_success(&self) -> bool {
315        self.success.unwrap_or(false)
316    }
317}
318
319/// Multi-agent transaction with additional signers.
320#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
321pub struct MultiAgentRawTransaction {
322    /// The raw transaction.
323    pub raw_txn: RawTransaction,
324    /// Secondary signer addresses.
325    pub secondary_signer_addresses: Vec<AccountAddress>,
326}
327
328impl MultiAgentRawTransaction {
329    /// Creates a new multi-agent transaction.
330    pub fn new(raw_txn: RawTransaction, secondary_signer_addresses: Vec<AccountAddress>) -> Self {
331        Self {
332            raw_txn,
333            secondary_signer_addresses,
334        }
335    }
336
337    /// Generates the signing message for multi-agent transactions.
338    ///
339    /// # Errors
340    ///
341    /// Returns an error if BCS serialization fails.
342    pub fn signing_message(&self) -> AptosResult<Vec<u8>> {
343        // Serialize as RawTransactionWithData::MultiAgent variant
344        #[derive(Serialize)]
345        enum RawTransactionWithData<'a> {
346            MultiAgent {
347                raw_txn: &'a RawTransaction,
348                secondary_signer_addresses: &'a Vec<AccountAddress>,
349            },
350        }
351
352        let prefix = crate::crypto::sha3_256(b"APTOS::RawTransactionWithData");
353
354        let data = RawTransactionWithData::MultiAgent {
355            raw_txn: &self.raw_txn,
356            secondary_signer_addresses: &self.secondary_signer_addresses,
357        };
358
359        let bcs_bytes = aptos_bcs::to_bytes(&data).map_err(crate::error::AptosError::bcs)?;
360
361        let mut message = Vec::with_capacity(prefix.len() + bcs_bytes.len());
362        message.extend_from_slice(&prefix);
363        message.extend_from_slice(&bcs_bytes);
364        Ok(message)
365    }
366}
367
368/// Fee payer transaction where a third party pays gas fees.
369#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
370pub struct FeePayerRawTransaction {
371    /// The raw transaction.
372    pub raw_txn: RawTransaction,
373    /// Secondary signer addresses (for multi-agent).
374    pub secondary_signer_addresses: Vec<AccountAddress>,
375    /// The fee payer's address.
376    pub fee_payer_address: AccountAddress,
377}
378
379impl FeePayerRawTransaction {
380    /// Creates a new fee payer transaction.
381    pub fn new(
382        raw_txn: RawTransaction,
383        secondary_signer_addresses: Vec<AccountAddress>,
384        fee_payer_address: AccountAddress,
385    ) -> Self {
386        Self {
387            raw_txn,
388            secondary_signer_addresses,
389            fee_payer_address,
390        }
391    }
392
393    /// Creates a fee payer transaction without secondary signers.
394    pub fn new_simple(raw_txn: RawTransaction, fee_payer_address: AccountAddress) -> Self {
395        Self {
396            raw_txn,
397            secondary_signer_addresses: vec![],
398            fee_payer_address,
399        }
400    }
401
402    /// Generates the signing message for fee payer transactions.
403    ///
404    /// # Errors
405    ///
406    /// Returns an error if BCS serialization fails.
407    pub fn signing_message(&self) -> AptosResult<Vec<u8>> {
408        #[derive(Serialize)]
409        enum RawTransactionWithData<'a> {
410            #[allow(dead_code)]
411            MultiAgent {
412                raw_txn: &'a RawTransaction,
413                secondary_signer_addresses: &'a Vec<AccountAddress>,
414            },
415            MultiAgentWithFeePayer {
416                raw_txn: &'a RawTransaction,
417                secondary_signer_addresses: &'a Vec<AccountAddress>,
418                fee_payer_address: &'a AccountAddress,
419            },
420        }
421        let prefix = crate::crypto::sha3_256(b"APTOS::RawTransactionWithData");
422
423        let data = RawTransactionWithData::MultiAgentWithFeePayer {
424            raw_txn: &self.raw_txn,
425            secondary_signer_addresses: &self.secondary_signer_addresses,
426            fee_payer_address: &self.fee_payer_address,
427        };
428
429        let bcs_bytes = aptos_bcs::to_bytes(&data).map_err(crate::error::AptosError::bcs)?;
430
431        let mut message = Vec::with_capacity(prefix.len() + bcs_bytes.len());
432        message.extend_from_slice(&prefix);
433        message.extend_from_slice(&bcs_bytes);
434        Ok(message)
435    }
436}
437
438#[cfg(test)]
439mod tests {
440    use super::*;
441    use crate::transaction::payload::EntryFunction;
442    use crate::types::MoveModuleId;
443
444    fn create_test_raw_transaction() -> RawTransaction {
445        RawTransaction::new(
446            AccountAddress::ONE,
447            0,
448            TransactionPayload::EntryFunction(EntryFunction {
449                module: MoveModuleId::from_str_strict("0x1::coin").unwrap(),
450                function: "transfer".to_string(),
451                type_args: vec![],
452                args: vec![],
453            }),
454            100_000,
455            100,
456            1_000_000_000,
457            ChainId::testnet(),
458        )
459    }
460
461    #[test]
462    fn test_raw_transaction_signing_message() {
463        let txn = create_test_raw_transaction();
464        let message = txn.signing_message().unwrap();
465        assert!(!message.is_empty());
466        // First 32 bytes should be the hash prefix
467        assert_eq!(message.len(), 32 + txn.to_bcs().unwrap().len());
468    }
469
470    #[test]
471    fn test_raw_transaction_fields() {
472        let txn = create_test_raw_transaction();
473        assert_eq!(txn.sender, AccountAddress::ONE);
474        assert_eq!(txn.sequence_number, 0);
475        assert_eq!(txn.max_gas_amount, 100_000);
476        assert_eq!(txn.gas_unit_price, 100);
477        assert_eq!(txn.expiration_timestamp_secs, 1_000_000_000);
478        assert_eq!(txn.chain_id, ChainId::testnet());
479    }
480
481    #[test]
482    fn test_raw_transaction_bcs_serialization() {
483        let txn = create_test_raw_transaction();
484        let bcs = txn.to_bcs().unwrap();
485        assert!(!bcs.is_empty());
486    }
487
488    #[test]
489    fn test_signed_transaction() {
490        use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
491        let txn = create_test_raw_transaction();
492        // Create a dummy authenticator
493        let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
494            public_key: Ed25519PublicKey([0u8; 32]),
495            signature: Ed25519Signature([0u8; 64]),
496        };
497        let signed = SignedTransaction::new(txn, auth);
498        assert_eq!(signed.sender(), AccountAddress::ONE);
499    }
500
501    #[test]
502    fn test_signed_transaction_bcs() {
503        use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
504        let txn = create_test_raw_transaction();
505        let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
506            public_key: Ed25519PublicKey([0u8; 32]),
507            signature: Ed25519Signature([0u8; 64]),
508        };
509        let signed = SignedTransaction::new(txn, auth);
510        let bcs = signed.to_bcs().unwrap();
511        assert!(!bcs.is_empty());
512    }
513
514    #[test]
515    fn test_authenticator_bcs_format() {
516        // Test that Ed25519 authenticator serializes WITH length prefixes (Aptos BCS format)
517        use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
518        let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
519            public_key: Ed25519PublicKey([0xab; 32]),
520            signature: Ed25519Signature([0xcd; 64]),
521        };
522        let bcs = aptos_bcs::to_bytes(&auth).unwrap();
523
524        // Print for debugging
525        println!("Authenticator BCS bytes: {}", hex::encode(&bcs));
526        println!("First byte (variant index): {}", bcs[0]);
527        println!("Second byte (length prefix): {}", bcs[1]);
528        println!("Third byte (first pubkey byte): {}", bcs[2]);
529
530        // Ed25519 variant should be index 0
531        assert_eq!(bcs[0], 0, "Ed25519 variant index should be 0");
532        // Next byte is length prefix for pubkey (32 = 0x20)
533        assert_eq!(bcs[1], 32, "Pubkey length prefix should be 32");
534        // Next 32 bytes should be pubkey
535        assert_eq!(bcs[2], 0xab, "First pubkey byte should be 0xab");
536        // After pubkey (1 + 1 + 32 = 34), length prefix for signature (64 = 0x40)
537        assert_eq!(bcs[34], 64, "Signature length prefix should be 64");
538        // Signature starts at offset 35
539        assert_eq!(bcs[35], 0xcd, "First signature byte should be 0xcd");
540        // Total: 1 (variant) + 1 (pubkey len) + 32 (pubkey) + 1 (sig len) + 64 (sig) = 99
541        assert_eq!(bcs.len(), 99, "BCS length should be 99");
542    }
543
544    #[test]
545    fn test_transaction_info_deserialization() {
546        let json = r#"{
547            "version": 12345,
548            "hash": "0x0000000000000000000000000000000000000000000000000000000000000001",
549            "gas_used": 100,
550            "success": true,
551            "vm_status": "Executed successfully"
552        }"#;
553        let info: TransactionInfo = serde_json::from_str(json).unwrap();
554        assert_eq!(info.version, Some(12345));
555        assert_eq!(info.gas_used, Some(100));
556        assert_eq!(info.success, Some(true));
557        assert_eq!(info.vm_status, Some("Executed successfully".to_string()));
558    }
559
560    #[test]
561    fn test_fee_payer_raw_transaction_new() {
562        let raw_txn = create_test_raw_transaction();
563        let secondary_addr = AccountAddress::from_hex("0x2").unwrap();
564        let fee_payer_addr = AccountAddress::THREE;
565        let fee_payer = FeePayerRawTransaction::new(raw_txn, vec![secondary_addr], fee_payer_addr);
566        assert_eq!(fee_payer.fee_payer_address, AccountAddress::THREE);
567        assert_eq!(fee_payer.secondary_signer_addresses.len(), 1);
568    }
569
570    #[test]
571    fn test_fee_payer_raw_transaction_new_simple() {
572        let raw_txn = create_test_raw_transaction();
573        let fee_payer = FeePayerRawTransaction::new_simple(raw_txn, AccountAddress::THREE);
574        assert_eq!(fee_payer.fee_payer_address, AccountAddress::THREE);
575        assert!(fee_payer.secondary_signer_addresses.is_empty());
576    }
577
578    #[test]
579    fn test_fee_payer_signing_message() {
580        let raw_txn = create_test_raw_transaction();
581        let fee_payer = FeePayerRawTransaction::new_simple(raw_txn, AccountAddress::THREE);
582        let message = fee_payer.signing_message().unwrap();
583        assert!(!message.is_empty());
584    }
585
586    #[test]
587    fn test_signed_transaction_hash() {
588        use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
589        let txn = create_test_raw_transaction();
590        let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
591            public_key: Ed25519PublicKey([0u8; 32]),
592            signature: Ed25519Signature([0u8; 64]),
593        };
594        let signed = SignedTransaction::new(txn, auth);
595        let hash = signed.hash().unwrap();
596        // Hash should be 32 bytes
597        assert_eq!(hash.as_bytes().len(), 32);
598        // Hash should be deterministic
599        let hash2 = signed.hash().unwrap();
600        assert_eq!(hash, hash2);
601    }
602
603    #[test]
604    fn test_signed_transaction_sequence_number() {
605        use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
606        let txn = create_test_raw_transaction();
607        let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
608            public_key: Ed25519PublicKey([0u8; 32]),
609            signature: Ed25519Signature([0u8; 64]),
610        };
611        let signed = SignedTransaction::new(txn, auth);
612        assert_eq!(signed.sequence_number(), 0);
613    }
614
615    #[test]
616    fn test_transaction_info_is_success() {
617        let info_success = TransactionInfo {
618            hash: HashValue::new([0; 32]),
619            version: Some(1),
620            success: Some(true),
621            vm_status: None,
622            gas_used: Some(100),
623        };
624        assert!(info_success.is_success());
625
626        let info_failed = TransactionInfo {
627            hash: HashValue::new([0; 32]),
628            version: Some(1),
629            success: Some(false),
630            vm_status: Some("Failed".to_string()),
631            gas_used: Some(100),
632        };
633        assert!(!info_failed.is_success());
634
635        let info_unknown = TransactionInfo {
636            hash: HashValue::new([0; 32]),
637            version: None,
638            success: None,
639            vm_status: None,
640            gas_used: None,
641        };
642        assert!(!info_unknown.is_success());
643    }
644
645    fn create_test_orderless_transaction() -> RawTransactionOrderless {
646        RawTransactionOrderless::with_nonce(
647            AccountAddress::ONE,
648            vec![
649                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
650                24, 25, 26, 27, 28, 29, 30, 31, 32,
651            ],
652            TransactionPayload::EntryFunction(EntryFunction {
653                module: MoveModuleId::from_str_strict("0x1::coin").unwrap(),
654                function: "transfer".to_string(),
655                type_args: vec![],
656                args: vec![],
657            }),
658            100_000,
659            100,
660            1_000_000_000,
661            ChainId::testnet(),
662        )
663    }
664
665    #[test]
666    fn test_orderless_transaction_with_nonce() {
667        let nonce = vec![0xab; 32];
668        let txn = RawTransactionOrderless::with_nonce(
669            AccountAddress::ONE,
670            nonce.clone(),
671            TransactionPayload::EntryFunction(EntryFunction {
672                module: MoveModuleId::from_str_strict("0x1::coin").unwrap(),
673                function: "transfer".to_string(),
674                type_args: vec![],
675                args: vec![],
676            }),
677            100_000,
678            100,
679            1_000_000_000,
680            ChainId::testnet(),
681        );
682        assert_eq!(txn.sender, AccountAddress::ONE);
683        assert_eq!(txn.nonce, nonce);
684        assert_eq!(txn.max_gas_amount, 100_000);
685        assert_eq!(txn.gas_unit_price, 100);
686    }
687
688    #[test]
689    fn test_orderless_transaction_new_generates_random_nonce() {
690        let txn1 = RawTransactionOrderless::new(
691            AccountAddress::ONE,
692            TransactionPayload::EntryFunction(EntryFunction {
693                module: MoveModuleId::from_str_strict("0x1::coin").unwrap(),
694                function: "transfer".to_string(),
695                type_args: vec![],
696                args: vec![],
697            }),
698            100_000,
699            100,
700            1_000_000_000,
701            ChainId::testnet(),
702        );
703        let txn2 = RawTransactionOrderless::new(
704            AccountAddress::ONE,
705            TransactionPayload::EntryFunction(EntryFunction {
706                module: MoveModuleId::from_str_strict("0x1::coin").unwrap(),
707                function: "transfer".to_string(),
708                type_args: vec![],
709                args: vec![],
710            }),
711            100_000,
712            100,
713            1_000_000_000,
714            ChainId::testnet(),
715        );
716        // Random nonces should be different
717        assert_ne!(txn1.nonce, txn2.nonce);
718        // Nonce should be 32 bytes
719        assert_eq!(txn1.nonce.len(), 32);
720        assert_eq!(txn2.nonce.len(), 32);
721    }
722
723    #[test]
724    fn test_orderless_transaction_signing_message() {
725        let txn = create_test_orderless_transaction();
726        let message = txn.signing_message().unwrap();
727        assert!(!message.is_empty());
728        // First 32 bytes should be the hash prefix
729        assert_eq!(message.len(), 32 + txn.to_bcs().unwrap().len());
730    }
731
732    #[test]
733    fn test_orderless_transaction_bcs() {
734        let txn = create_test_orderless_transaction();
735        let bcs = txn.to_bcs().unwrap();
736        assert!(!bcs.is_empty());
737    }
738
739    #[test]
740    fn test_signed_orderless_transaction() {
741        use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
742        let txn = create_test_orderless_transaction();
743        let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
744            public_key: Ed25519PublicKey([0u8; 32]),
745            signature: Ed25519Signature([0u8; 64]),
746        };
747        let signed = SignedTransactionOrderless::new(txn, auth);
748        assert_eq!(signed.sender(), AccountAddress::ONE);
749        assert_eq!(signed.nonce().len(), 32);
750    }
751
752    #[test]
753    fn test_signed_orderless_transaction_bcs() {
754        use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
755        let txn = create_test_orderless_transaction();
756        let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
757            public_key: Ed25519PublicKey([0u8; 32]),
758            signature: Ed25519Signature([0u8; 64]),
759        };
760        let signed = SignedTransactionOrderless::new(txn, auth);
761        let bcs = signed.to_bcs().unwrap();
762        assert!(!bcs.is_empty());
763    }
764
765    #[test]
766    fn test_signed_orderless_transaction_hash() {
767        use crate::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
768        let txn = create_test_orderless_transaction();
769        let auth = crate::transaction::TransactionAuthenticator::Ed25519 {
770            public_key: Ed25519PublicKey([0u8; 32]),
771            signature: Ed25519Signature([0u8; 64]),
772        };
773        let signed = SignedTransactionOrderless::new(txn, auth);
774        let hash = signed.hash().unwrap();
775        // Hash should be 32 bytes
776        assert_eq!(hash.as_bytes().len(), 32);
777        // Hash should be deterministic
778        let hash2 = signed.hash().unwrap();
779        assert_eq!(hash, hash2);
780    }
781
782    #[test]
783    fn test_multi_agent_raw_transaction() {
784        let raw_txn = create_test_raw_transaction();
785        let secondary = vec![AccountAddress::from_hex("0x2").unwrap()];
786        let multi_agent = MultiAgentRawTransaction::new(raw_txn, secondary.clone());
787        assert_eq!(multi_agent.secondary_signer_addresses, secondary);
788    }
789
790    #[test]
791    fn test_multi_agent_signing_message() {
792        let raw_txn = create_test_raw_transaction();
793        let secondary = vec![AccountAddress::from_hex("0x2").unwrap()];
794        let multi_agent = MultiAgentRawTransaction::new(raw_txn, secondary);
795        let message = multi_agent.signing_message().unwrap();
796        assert!(!message.is_empty());
797    }
798}