Skip to main content

aptos_sdk/transaction/
input.rs

1//! Type-safe entry function payload builders.
2//!
3//! This module provides ergonomic builders for constructing entry function
4//! payloads with automatic BCS encoding of arguments.
5//!
6//! # Overview
7//!
8//! `InputEntryFunctionData` provides a builder pattern that:
9//! - Accepts Rust types directly (no manual BCS encoding)
10//! - Validates function IDs at construction
11//! - Supports all Move types
12//!
13//! # Example
14//!
15//! ```rust,ignore
16//! use aptos_sdk::transaction::InputEntryFunctionData;
17//!
18//! // Simple transfer
19//! let payload = InputEntryFunctionData::new("0x1::aptos_account::transfer")
20//!     .arg(recipient_address)
21//!     .arg(1_000_000u64)
22//!     .build()?;
23//!
24//! // Generic function with type args
25//! let payload = InputEntryFunctionData::new("0x1::coin::transfer")
26//!     .type_arg("0x1::aptos_coin::AptosCoin")
27//!     .arg(recipient_address)
28//!     .arg(amount)
29//!     .build()?;
30//! ```
31
32use crate::error::{AptosError, AptosResult};
33use crate::transaction::{EntryFunction, TransactionPayload};
34use crate::types::{AccountAddress, EntryFunctionId, MoveModuleId, TypeTag};
35use serde::Serialize;
36
37/// A type-safe builder for entry function payloads.
38///
39/// This builder provides an ergonomic way to construct entry function calls
40/// with automatic BCS encoding of arguments.
41///
42/// # Example
43///
44/// ```rust,ignore
45/// use aptos_sdk::transaction::InputEntryFunctionData;
46/// use aptos_sdk::types::AccountAddress;
47///
48/// let payload = InputEntryFunctionData::new("0x1::aptos_account::transfer")
49///     .arg(AccountAddress::from_hex("0x123").unwrap())
50///     .arg(1_000_000u64)  // 0.01 APT in octas
51///     .build()?;
52/// ```
53#[allow(dead_code)] // Public API struct - fields used via builder pattern
54#[derive(Debug, Clone)]
55pub struct InputEntryFunctionData {
56    module: MoveModuleId,
57    function: String,
58    type_args: Vec<TypeTag>,
59    args: Vec<Vec<u8>>,
60}
61
62impl InputEntryFunctionData {
63    /// Creates a new entry function data builder.
64    ///
65    /// # Arguments
66    ///
67    /// * `function_id` - The full function identifier (e.g., "`0x1::coin::transfer`")
68    ///
69    /// # Example
70    ///
71    /// ```rust,ignore
72    /// let builder = InputEntryFunctionData::new("0x1::aptos_account::transfer");
73    /// ```
74    #[allow(clippy::new_ret_no_self)] // Returns builder pattern intentionally
75    pub fn new(function_id: &str) -> InputEntryFunctionDataBuilder {
76        InputEntryFunctionDataBuilder::new(function_id)
77    }
78
79    /// Creates a builder from module and function name.
80    ///
81    /// # Arguments
82    ///
83    /// * `module` - The module ID
84    /// * `function` - The function name
85    pub fn from_parts(
86        module: MoveModuleId,
87        function: impl Into<String>,
88    ) -> InputEntryFunctionDataBuilder {
89        InputEntryFunctionDataBuilder {
90            module: Ok(module),
91            function: function.into(),
92            type_args: Vec::new(),
93            args: Vec::new(),
94            errors: Vec::new(),
95        }
96    }
97
98    /// Builds an APT transfer payload.
99    ///
100    /// # Arguments
101    ///
102    /// * `recipient` - The recipient address
103    /// * `amount` - Amount in octas (1 APT = 10^8 octas)
104    ///
105    /// # Example
106    ///
107    /// ```rust,ignore
108    /// let payload = InputEntryFunctionData::transfer_apt(recipient, 1_000_000)?;
109    /// ```
110    ///
111    /// # Errors
112    ///
113    /// Returns an error if the function ID is invalid or if BCS encoding of arguments fails.
114    pub fn transfer_apt(recipient: AccountAddress, amount: u64) -> AptosResult<TransactionPayload> {
115        InputEntryFunctionData::new("0x1::aptos_account::transfer")
116            .arg(recipient)
117            .arg(amount)
118            .build()
119    }
120
121    /// Builds a coin transfer payload for any coin type.
122    ///
123    /// # Arguments
124    ///
125    /// * `coin_type` - The coin type (e.g., "`0x1::aptos_coin::AptosCoin`")
126    /// * `recipient` - The recipient address
127    /// * `amount` - Amount in the coin's smallest unit
128    ///
129    /// # Errors
130    ///
131    /// Returns an error if the function ID is invalid, the coin type is invalid, or if BCS encoding of arguments fails.
132    pub fn transfer_coin(
133        coin_type: &str,
134        recipient: AccountAddress,
135        amount: u64,
136    ) -> AptosResult<TransactionPayload> {
137        InputEntryFunctionData::new("0x1::coin::transfer")
138            .type_arg(coin_type)
139            .arg(recipient)
140            .arg(amount)
141            .build()
142    }
143
144    /// Builds an account creation payload.
145    ///
146    /// # Arguments
147    ///
148    /// * `auth_key` - The authentication key (32 bytes)
149    ///
150    /// # Errors
151    ///
152    /// Returns an error if the function ID is invalid or if BCS encoding of arguments fails.
153    pub fn create_account(auth_key: AccountAddress) -> AptosResult<TransactionPayload> {
154        InputEntryFunctionData::new("0x1::aptos_account::create_account")
155            .arg(auth_key)
156            .build()
157    }
158
159    /// Builds a payload to rotate an account's authentication key.
160    ///
161    /// # Arguments
162    ///
163    /// * Various rotation parameters
164    ///
165    /// # Errors
166    ///
167    /// Returns an error if the function ID is invalid or if BCS encoding of arguments fails.
168    pub fn rotate_authentication_key(
169        from_scheme: u8,
170        from_public_key_bytes: Vec<u8>,
171        to_scheme: u8,
172        to_public_key_bytes: Vec<u8>,
173        cap_rotate_key: Vec<u8>,
174        cap_update_table: Vec<u8>,
175    ) -> AptosResult<TransactionPayload> {
176        InputEntryFunctionData::new("0x1::account::rotate_authentication_key")
177            .arg(from_scheme)
178            .arg(from_public_key_bytes)
179            .arg(to_scheme)
180            .arg(to_public_key_bytes)
181            .arg(cap_rotate_key)
182            .arg(cap_update_table)
183            .build()
184    }
185
186    /// Builds a payload to register a coin store.
187    ///
188    /// # Arguments
189    ///
190    /// * `coin_type` - The coin type to register
191    ///
192    /// # Errors
193    ///
194    /// Returns an error if the function ID is invalid, the coin type is invalid, or if building the payload fails.
195    pub fn register_coin(coin_type: &str) -> AptosResult<TransactionPayload> {
196        InputEntryFunctionData::new("0x1::managed_coin::register")
197            .type_arg(coin_type)
198            .build()
199    }
200
201    /// Builds a payload to publish a module.
202    ///
203    /// # Arguments
204    ///
205    /// * `metadata_serialized` - Serialized module metadata
206    /// * `code` - Vector of module bytecode
207    ///
208    /// # Errors
209    ///
210    /// Returns an error if the function ID is invalid or if BCS encoding of arguments fails.
211    pub fn publish_package(
212        metadata_serialized: Vec<u8>,
213        code: Vec<Vec<u8>>,
214    ) -> AptosResult<TransactionPayload> {
215        InputEntryFunctionData::new("0x1::code::publish_package_txn")
216            .arg(metadata_serialized)
217            .arg(code)
218            .build()
219    }
220}
221
222/// Builder for `InputEntryFunctionData`.
223#[derive(Debug, Clone)]
224pub struct InputEntryFunctionDataBuilder {
225    module: Result<MoveModuleId, String>,
226    function: String,
227    type_args: Vec<TypeTag>,
228    args: Vec<Vec<u8>>,
229    errors: Vec<String>,
230}
231
232impl InputEntryFunctionDataBuilder {
233    /// Creates a new builder from a function ID string.
234    #[must_use]
235    fn new(function_id: &str) -> Self {
236        match EntryFunctionId::from_str_strict(function_id) {
237            Ok(func_id) => Self {
238                module: Ok(func_id.module),
239                function: func_id.name.as_str().to_string(),
240                type_args: Vec::new(),
241                args: Vec::new(),
242                errors: Vec::new(),
243            },
244            Err(e) => Self {
245                module: Err(format!("Invalid function ID '{function_id}': {e}")),
246                function: String::new(),
247                type_args: Vec::new(),
248                args: Vec::new(),
249                errors: Vec::new(),
250            },
251        }
252    }
253
254    /// Adds a type argument.
255    ///
256    /// # Arguments
257    ///
258    /// * `type_arg` - A type tag string (e.g., "`0x1::aptos_coin::AptosCoin`")
259    ///
260    /// # Example
261    ///
262    /// ```rust,ignore
263    /// let builder = InputEntryFunctionData::new("0x1::coin::transfer")
264    ///     .type_arg("0x1::aptos_coin::AptosCoin");
265    /// ```
266    #[must_use]
267    pub fn type_arg(mut self, type_arg: &str) -> Self {
268        match TypeTag::from_str_strict(type_arg) {
269            Ok(tag) => self.type_args.push(tag),
270            Err(e) => self
271                .errors
272                .push(format!("Invalid type argument '{type_arg}': {e}")),
273        }
274        self
275    }
276
277    /// Adds a type argument from a `TypeTag`.
278    #[must_use]
279    pub fn type_arg_typed(mut self, type_arg: TypeTag) -> Self {
280        self.type_args.push(type_arg);
281        self
282    }
283
284    /// Adds multiple type arguments.
285    #[must_use]
286    pub fn type_args(mut self, type_args: impl IntoIterator<Item = &'static str>) -> Self {
287        for type_arg in type_args {
288            self = self.type_arg(type_arg);
289        }
290        self
291    }
292
293    /// Adds multiple typed type arguments.
294    #[must_use]
295    pub fn type_args_typed(mut self, type_args: impl IntoIterator<Item = TypeTag>) -> Self {
296        self.type_args.extend(type_args);
297        self
298    }
299
300    /// Adds a BCS-encodable argument.
301    ///
302    /// Accepts any type that implements `Serialize` (BCS encoding).
303    ///
304    /// # Supported Types
305    ///
306    /// - Integers: `u8`, `u16`, `u32`, `u64`, `u128`
307    /// - Boolean: `bool`
308    /// - Strings: `String`, `&str`
309    /// - Addresses: `AccountAddress`
310    /// - Vectors: `Vec<T>` where T is serializable
311    /// - Bytes: `Vec<u8>`, `&[u8]`
312    ///
313    /// # Example
314    ///
315    /// ```rust,ignore
316    /// let builder = InputEntryFunctionData::new("0x1::my_module::my_function")
317    ///     .arg(42u64)
318    ///     .arg(true)
319    ///     .arg(AccountAddress::ONE)
320    ///     .arg("hello".to_string());
321    /// ```
322    #[must_use]
323    pub fn arg<T: Serialize>(mut self, value: T) -> Self {
324        match aptos_bcs::to_bytes(&value) {
325            Ok(bytes) => self.args.push(bytes),
326            Err(e) => self
327                .errors
328                .push(format!("Failed to serialize argument: {e}")),
329        }
330        self
331    }
332
333    /// Adds a raw BCS-encoded argument.
334    ///
335    /// Use this when you have pre-encoded bytes.
336    #[must_use]
337    pub fn arg_raw(mut self, bytes: Vec<u8>) -> Self {
338        self.args.push(bytes);
339        self
340    }
341
342    /// Adds multiple BCS-encodable arguments.
343    #[must_use]
344    pub fn args<T: Serialize>(mut self, values: impl IntoIterator<Item = T>) -> Self {
345        for value in values {
346            self = self.arg(value);
347        }
348        self
349    }
350
351    /// Builds the transaction payload.
352    ///
353    /// # Returns
354    ///
355    /// The constructed `TransactionPayload`, or an error if any
356    /// validation or serialization failed.
357    ///
358    /// # Errors
359    ///
360    /// Returns an error if the function ID is invalid, any type argument is invalid, or if any argument serialization failed.
361    pub fn build(self) -> AptosResult<TransactionPayload> {
362        // Check for module parsing error
363        let module = self.module.map_err(AptosError::Transaction)?;
364
365        // Check for any accumulated errors
366        if !self.errors.is_empty() {
367            return Err(AptosError::Transaction(self.errors.join("; ")));
368        }
369
370        Ok(TransactionPayload::EntryFunction(EntryFunction {
371            module,
372            function: self.function,
373            type_args: self.type_args,
374            args: self.args,
375        }))
376    }
377
378    /// Builds just the entry function (without wrapping in `TransactionPayload`).
379    ///
380    /// # Errors
381    ///
382    /// Returns an error if the function ID is invalid, any type argument is invalid, or if any argument serialization failed.
383    pub fn build_entry_function(self) -> AptosResult<EntryFunction> {
384        let module = self.module.map_err(AptosError::Transaction)?;
385
386        if !self.errors.is_empty() {
387            return Err(AptosError::Transaction(self.errors.join("; ")));
388        }
389
390        Ok(EntryFunction {
391            module,
392            function: self.function,
393            type_args: self.type_args,
394            args: self.args,
395        })
396    }
397}
398
399/// Trait for types that can be converted to entry function arguments.
400///
401/// This trait is automatically implemented for types that implement `Serialize`.
402pub trait IntoMoveArg {
403    /// Converts this value into BCS-encoded bytes.
404    ///
405    /// # Errors
406    ///
407    /// Returns an error if BCS serialization fails.
408    fn into_move_arg(self) -> AptosResult<Vec<u8>>;
409}
410
411impl<T: Serialize> IntoMoveArg for T {
412    fn into_move_arg(self) -> AptosResult<Vec<u8>> {
413        aptos_bcs::to_bytes(&self).map_err(AptosError::bcs)
414    }
415}
416
417/// Helper to create a vector argument for Move functions.
418///
419/// Move vectors are BCS-encoded with a length prefix followed by elements.
420///
421/// # Example
422///
423/// ```rust,ignore
424/// let addresses = move_vec(&[addr1, addr2, addr3]);
425/// let amounts = move_vec(&[100u64, 200u64, 300u64]);
426/// ```
427pub fn move_vec<T: Serialize>(items: &[T]) -> Vec<u8> {
428    aptos_bcs::to_bytes(items).unwrap_or_default()
429}
430
431/// Helper to create a string argument for Move functions.
432///
433/// Move strings are UTF-8 encoded vectors of bytes.
434///
435/// # Example
436///
437/// ```rust,ignore
438/// let name = move_string("Alice");
439/// ```
440pub fn move_string(s: &str) -> String {
441    s.to_string()
442}
443
444/// Helper to create an `Option::Some` argument for Move.
445///
446/// # Example
447///
448/// ```rust,ignore
449/// let maybe_value = move_some(42u64);
450/// ```
451pub fn move_some<T: Serialize>(value: T) -> Vec<u8> {
452    // BCS encodes Option as: 0x01 followed by the value bytes for Some
453    let mut bytes = vec![0x01];
454    if let Ok(value_bytes) = aptos_bcs::to_bytes(&value) {
455        bytes.extend(value_bytes);
456    }
457    bytes
458}
459
460/// Helper to create an `Option::None` argument for Move.
461///
462/// # Example
463///
464/// ```rust,ignore
465/// let maybe_value: Vec<u8> = move_none();
466/// ```
467pub fn move_none() -> Vec<u8> {
468    // BCS encodes Option as: 0x00 for None
469    vec![0x00]
470}
471
472/// A u256 value for Move arguments.
473///
474/// Move's u256 is a 256-bit unsigned integer, represented as 32 bytes in little-endian.
475#[derive(Debug, Clone, Copy, PartialEq, Eq)]
476pub struct MoveU256(pub [u8; 32]);
477
478impl MoveU256 {
479    /// Creates a `MoveU256` from a decimal string.
480    ///
481    /// # Errors
482    ///
483    /// Returns an error if the string cannot be parsed as a u256 value.
484    pub fn parse(s: &str) -> AptosResult<Self> {
485        // Parse as big integer and convert to little-endian bytes
486        let mut bytes = [0u8; 32];
487
488        // Simple parsing for small values
489        if let Ok(val) = s.parse::<u128>() {
490            bytes[..16].copy_from_slice(&val.to_le_bytes());
491            return Ok(Self(bytes));
492        }
493
494        Err(AptosError::Transaction(format!("Invalid u256: {s}")))
495    }
496
497    /// Creates a `MoveU256` from a u128.
498    pub fn from_u128(val: u128) -> Self {
499        let mut bytes = [0u8; 32];
500        bytes[..16].copy_from_slice(&val.to_le_bytes());
501        Self(bytes)
502    }
503
504    /// Creates a `MoveU256` from raw bytes (little-endian).
505    pub fn from_le_bytes(bytes: [u8; 32]) -> Self {
506        Self(bytes)
507    }
508}
509
510impl Serialize for MoveU256 {
511    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
512    where
513        S: serde::Serializer,
514    {
515        // BCS serializes u256 as 32 little-endian bytes (as a tuple of bytes, not a vec)
516        use serde::ser::SerializeTuple;
517        let mut tuple = serializer.serialize_tuple(32)?;
518        for byte in &self.0 {
519            tuple.serialize_element(byte)?;
520        }
521        tuple.end()
522    }
523}
524
525/// An i128 value for Move arguments.
526///
527/// Move's i128 is a 128-bit signed integer, represented as 16 bytes in little-endian
528/// using two's complement representation.
529#[derive(Debug, Clone, Copy, PartialEq, Eq)]
530pub struct MoveI128(pub i128);
531
532impl MoveI128 {
533    /// Creates a new `MoveI128` from an i128 value.
534    pub fn new(val: i128) -> Self {
535        Self(val)
536    }
537}
538
539impl Serialize for MoveI128 {
540    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
541    where
542        S: serde::Serializer,
543    {
544        // BCS serializes i128 as 16 little-endian bytes (two's complement)
545        use serde::ser::SerializeTuple;
546        let bytes = self.0.to_le_bytes();
547        let mut tuple = serializer.serialize_tuple(16)?;
548        for byte in &bytes {
549            tuple.serialize_element(byte)?;
550        }
551        tuple.end()
552    }
553}
554
555impl From<i128> for MoveI128 {
556    fn from(val: i128) -> Self {
557        Self(val)
558    }
559}
560
561/// An i256 value for Move arguments.
562///
563/// Move's i256 is a 256-bit signed integer, represented as 32 bytes in little-endian
564/// using two's complement representation.
565#[derive(Debug, Clone, Copy, PartialEq, Eq)]
566pub struct MoveI256(pub [u8; 32]);
567
568impl MoveI256 {
569    /// Creates a `MoveI256` from an i128 value.
570    pub fn from_i128(val: i128) -> Self {
571        let mut bytes = [0u8; 32];
572        let val_bytes = val.to_le_bytes();
573        bytes[..16].copy_from_slice(&val_bytes);
574        // Sign extend for negative values
575        if val < 0 {
576            bytes[16..].fill(0xFF);
577        }
578        Self(bytes)
579    }
580
581    /// Creates a `MoveI256` from raw bytes (little-endian, two's complement).
582    pub fn from_le_bytes(bytes: [u8; 32]) -> Self {
583        Self(bytes)
584    }
585}
586
587impl Serialize for MoveI256 {
588    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
589    where
590        S: serde::Serializer,
591    {
592        // BCS serializes i256 as 32 little-endian bytes (as a tuple of bytes, not a vec)
593        use serde::ser::SerializeTuple;
594        let mut tuple = serializer.serialize_tuple(32)?;
595        for byte in &self.0 {
596            tuple.serialize_element(byte)?;
597        }
598        tuple.end()
599    }
600}
601
602impl From<i128> for MoveI256 {
603    fn from(val: i128) -> Self {
604        Self::from_i128(val)
605    }
606}
607
608/// Common function IDs for convenience.
609pub mod functions {
610    /// APT transfer function.
611    pub const APT_TRANSFER: &str = "0x1::aptos_account::transfer";
612    /// Coin transfer function.
613    pub const COIN_TRANSFER: &str = "0x1::coin::transfer";
614    /// Account creation function.
615    pub const CREATE_ACCOUNT: &str = "0x1::aptos_account::create_account";
616    /// Register a coin store.
617    pub const REGISTER_COIN: &str = "0x1::managed_coin::register";
618    /// Publish a package.
619    pub const PUBLISH_PACKAGE: &str = "0x1::code::publish_package_txn";
620    /// Rotate authentication key.
621    pub const ROTATE_AUTH_KEY: &str = "0x1::account::rotate_authentication_key";
622}
623
624/// Common type tags for convenience.
625pub mod types {
626    /// APT coin type.
627    pub const APT_COIN: &str = "0x1::aptos_coin::AptosCoin";
628}
629
630#[cfg(test)]
631mod tests {
632    use super::*;
633
634    #[test]
635    fn test_simple_transfer() {
636        let recipient = AccountAddress::from_hex("0x123").unwrap();
637        let payload = InputEntryFunctionData::new("0x1::aptos_account::transfer")
638            .arg(recipient)
639            .arg(1_000_000u64)
640            .build()
641            .unwrap();
642
643        match payload {
644            TransactionPayload::EntryFunction(ef) => {
645                assert_eq!(ef.function, "transfer");
646                assert_eq!(ef.module.name.as_str(), "aptos_account");
647                assert!(ef.type_args.is_empty());
648                assert_eq!(ef.args.len(), 2);
649            }
650            _ => panic!("Expected EntryFunction"),
651        }
652    }
653
654    #[test]
655    fn test_with_type_args() {
656        let payload = InputEntryFunctionData::new("0x1::coin::transfer")
657            .type_arg("0x1::aptos_coin::AptosCoin")
658            .arg(AccountAddress::ONE)
659            .arg(1000u64)
660            .build()
661            .unwrap();
662
663        match payload {
664            TransactionPayload::EntryFunction(ef) => {
665                assert_eq!(ef.function, "transfer");
666                assert_eq!(ef.type_args.len(), 1);
667            }
668            _ => panic!("Expected EntryFunction"),
669        }
670    }
671
672    #[test]
673    fn test_invalid_function_id() {
674        let result = InputEntryFunctionData::new("invalid").arg(42u64).build();
675
676        assert!(result.is_err());
677    }
678
679    #[test]
680    fn test_invalid_type_arg() {
681        let result = InputEntryFunctionData::new("0x1::coin::transfer")
682            .type_arg("not a type")
683            .arg(AccountAddress::ONE)
684            .arg(1000u64)
685            .build();
686
687        assert!(result.is_err());
688    }
689
690    #[test]
691    fn test_transfer_apt_helper() {
692        let recipient = AccountAddress::from_hex("0x456").unwrap();
693        let payload = InputEntryFunctionData::transfer_apt(recipient, 5_000_000).unwrap();
694
695        match payload {
696            TransactionPayload::EntryFunction(ef) => {
697                assert_eq!(ef.function, "transfer");
698                assert_eq!(ef.module.name.as_str(), "aptos_account");
699            }
700            _ => panic!("Expected EntryFunction"),
701        }
702    }
703
704    #[test]
705    fn test_transfer_coin_helper() {
706        let recipient = AccountAddress::from_hex("0x789").unwrap();
707        let payload =
708            InputEntryFunctionData::transfer_coin("0x1::aptos_coin::AptosCoin", recipient, 1000)
709                .unwrap();
710
711        match payload {
712            TransactionPayload::EntryFunction(ef) => {
713                assert_eq!(ef.function, "transfer");
714                assert_eq!(ef.module.name.as_str(), "coin");
715                assert_eq!(ef.type_args.len(), 1);
716            }
717            _ => panic!("Expected EntryFunction"),
718        }
719    }
720
721    #[test]
722    fn test_various_arg_types() {
723        let payload = InputEntryFunctionData::new("0x1::test::test_function")
724            .arg(42u8)
725            .arg(1000u64)
726            .arg(true)
727            .arg("hello".to_string())
728            .arg(vec![1u8, 2u8, 3u8])
729            .arg(AccountAddress::ONE)
730            .build()
731            .unwrap();
732
733        match payload {
734            TransactionPayload::EntryFunction(ef) => {
735                assert_eq!(ef.args.len(), 6);
736            }
737            _ => panic!("Expected EntryFunction"),
738        }
739    }
740
741    #[test]
742    fn test_move_u256() {
743        let val = MoveU256::from_u128(12345);
744        let bytes = aptos_bcs::to_bytes(&val).unwrap();
745        assert_eq!(bytes.len(), 32);
746    }
747
748    #[test]
749    fn test_move_some_none() {
750        let some_bytes = move_some(42u64);
751        assert_eq!(some_bytes[0], 0x01);
752
753        let none_bytes = move_none();
754        assert_eq!(none_bytes, vec![0x00]);
755    }
756
757    #[test]
758    fn test_from_parts() {
759        let module = MoveModuleId::from_str_strict("0x1::coin").unwrap();
760        let payload = InputEntryFunctionData::from_parts(module, "transfer")
761            .type_arg("0x1::aptos_coin::AptosCoin")
762            .arg(AccountAddress::ONE)
763            .arg(1000u64)
764            .build()
765            .unwrap();
766
767        match payload {
768            TransactionPayload::EntryFunction(ef) => {
769                assert_eq!(ef.function, "transfer");
770                assert_eq!(ef.module.name.as_str(), "coin");
771            }
772            _ => panic!("Expected EntryFunction"),
773        }
774    }
775
776    #[test]
777    fn test_build_entry_function() {
778        let ef = InputEntryFunctionData::new("0x1::aptos_account::transfer")
779            .arg(AccountAddress::ONE)
780            .arg(1000u64)
781            .build_entry_function()
782            .unwrap();
783
784        assert_eq!(ef.function, "transfer");
785        assert_eq!(ef.args.len(), 2);
786    }
787
788    #[test]
789    fn test_function_constants() {
790        assert_eq!(functions::APT_TRANSFER, "0x1::aptos_account::transfer");
791        assert_eq!(functions::COIN_TRANSFER, "0x1::coin::transfer");
792    }
793
794    #[test]
795    fn test_move_u256_from_u128() {
796        let val = MoveU256::from_u128(123_456_789);
797        // First 16 bytes should contain the value in little-endian
798        let expected = 123_456_789_u128.to_le_bytes();
799        assert_eq!(&val.0[..16], &expected);
800        // Upper 16 bytes should be zero
801        assert_eq!(&val.0[16..], &[0u8; 16]);
802    }
803
804    #[test]
805    fn test_move_u256_from_le_bytes() {
806        let bytes = [0xab; 32];
807        let val = MoveU256::from_le_bytes(bytes);
808        assert_eq!(val.0, bytes);
809    }
810
811    #[test]
812    fn test_move_u256_parse() {
813        let val = MoveU256::parse("12345678901234567890").unwrap();
814        let expected = 12_345_678_901_234_567_890_u128;
815        let mut expected_bytes = [0u8; 32];
816        expected_bytes[..16].copy_from_slice(&expected.to_le_bytes());
817        assert_eq!(val.0, expected_bytes);
818    }
819
820    #[test]
821    fn test_move_u256_parse_invalid() {
822        // Value larger than u128 currently returns error
823        let result = MoveU256::parse("999999999999999999999999999999999999999999999");
824        assert!(result.is_err());
825    }
826
827    #[test]
828    fn test_move_u256_serialization() {
829        let val = MoveU256::from_u128(0x0102_0304_0506_0708);
830        let bcs = aptos_bcs::to_bytes(&val).unwrap();
831        // Should serialize as 32 bytes (tuple, not vector with length prefix)
832        assert_eq!(bcs.len(), 32);
833        // First 8 bytes should be our value in little-endian
834        assert_eq!(&bcs[..8], &[0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
835    }
836
837    #[test]
838    fn test_move_i128_new() {
839        let val = MoveI128::new(42);
840        assert_eq!(val.0, 42);
841    }
842
843    #[test]
844    fn test_move_i128_from_i128() {
845        let val: MoveI128 = (-100i128).into();
846        assert_eq!(val.0, -100);
847    }
848
849    #[test]
850    fn test_move_i128_serialization_positive() {
851        let val = MoveI128::new(0x0102_0304_0506_0708);
852        let bcs = aptos_bcs::to_bytes(&val).unwrap();
853        // Should serialize as 16 bytes (tuple, not vector)
854        assert_eq!(bcs.len(), 16);
855        // First 8 bytes should be our value in little-endian
856        assert_eq!(&bcs[..8], &[0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
857        // Upper 8 bytes should be zeros for positive value
858        assert_eq!(&bcs[8..], &[0, 0, 0, 0, 0, 0, 0, 0]);
859    }
860
861    #[test]
862    fn test_move_i128_serialization_negative() {
863        let val = MoveI128::new(-1);
864        let bcs = aptos_bcs::to_bytes(&val).unwrap();
865        assert_eq!(bcs.len(), 16);
866        // -1 in two's complement is all 0xFF bytes
867        assert_eq!(bcs, vec![0xFF; 16]);
868    }
869
870    #[test]
871    fn test_move_i256_from_i128_positive() {
872        let val = MoveI256::from_i128(42);
873        // First 16 bytes should contain the value
874        let expected = 42i128.to_le_bytes();
875        assert_eq!(&val.0[..16], &expected);
876        // Upper 16 bytes should be zeros for positive value
877        assert_eq!(&val.0[16..], &[0u8; 16]);
878    }
879
880    #[test]
881    fn test_move_i256_from_i128_negative() {
882        let val = MoveI256::from_i128(-1);
883        // -1 in two's complement should be all 0xFF bytes
884        assert_eq!(val.0, [0xFF; 32]);
885    }
886
887    #[test]
888    fn test_move_i256_from_le_bytes() {
889        let bytes = [0xcd; 32];
890        let val = MoveI256::from_le_bytes(bytes);
891        assert_eq!(val.0, bytes);
892    }
893
894    #[test]
895    fn test_move_i256_from_trait() {
896        let val: MoveI256 = (-100i128).into();
897        let expected = MoveI256::from_i128(-100);
898        assert_eq!(val, expected);
899    }
900
901    #[test]
902    fn test_move_i256_serialization() {
903        let val = MoveI256::from_i128(0x0102_0304_0506_0708);
904        let bcs = aptos_bcs::to_bytes(&val).unwrap();
905        // Should serialize as 32 bytes (tuple, not vector)
906        assert_eq!(bcs.len(), 32);
907        // First 8 bytes should be our value in little-endian
908        assert_eq!(&bcs[..8], &[0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
909    }
910
911    #[test]
912    fn test_move_i256_serialization_negative() {
913        let val = MoveI256::from_i128(-1);
914        let bcs = aptos_bcs::to_bytes(&val).unwrap();
915        assert_eq!(bcs.len(), 32);
916        // -1 in two's complement is all 0xFF bytes
917        assert_eq!(bcs, vec![0xFF; 32]);
918    }
919
920    #[test]
921    fn test_input_entry_function_data_new() {
922        let builder = InputEntryFunctionData::new("0x1::coin::transfer");
923        let result = builder.build();
924        // Should build successfully (no args required yet)
925        assert!(result.is_ok());
926    }
927
928    #[test]
929    fn test_input_entry_function_data_invalid_function_id() {
930        let builder = InputEntryFunctionData::new("invalid");
931        let result = builder.build();
932        assert!(result.is_err());
933        assert!(
934            result
935                .unwrap_err()
936                .to_string()
937                .contains("Invalid function ID")
938        );
939    }
940
941    #[test]
942    fn test_input_entry_function_data_type_arg() {
943        let builder = InputEntryFunctionData::new("0x1::coin::transfer")
944            .type_arg("0x1::aptos_coin::AptosCoin");
945        let result = builder.build();
946        assert!(result.is_ok());
947    }
948
949    #[test]
950    fn test_input_entry_function_data_invalid_type_arg() {
951        let builder =
952            InputEntryFunctionData::new("0x1::coin::transfer").type_arg("not a valid type");
953        let result = builder.build();
954        assert!(result.is_err());
955        assert!(result.unwrap_err().to_string().contains("type argument"));
956    }
957
958    #[test]
959    fn test_input_entry_function_data_type_arg_typed() {
960        use crate::types::TypeTag;
961
962        let builder =
963            InputEntryFunctionData::new("0x1::coin::transfer").type_arg_typed(TypeTag::U64);
964        let result = builder.build();
965        assert!(result.is_ok());
966    }
967
968    #[test]
969    fn test_input_entry_function_data_type_args() {
970        let builder = InputEntryFunctionData::new("0x1::coin::transfer").type_args(["u64", "u128"]);
971        let result = builder.build();
972        assert!(result.is_ok());
973    }
974
975    #[test]
976    fn test_input_entry_function_data_type_args_typed() {
977        use crate::types::TypeTag;
978
979        let builder = InputEntryFunctionData::new("0x1::coin::transfer")
980            .type_args_typed([TypeTag::U64, TypeTag::Bool]);
981        let result = builder.build();
982        assert!(result.is_ok());
983    }
984
985    #[test]
986    fn test_input_entry_function_data_arg() {
987        let builder = InputEntryFunctionData::new("0x1::coin::transfer")
988            .arg(42u64)
989            .arg(true)
990            .arg("hello".to_string());
991        let result = builder.build();
992        assert!(result.is_ok());
993    }
994
995    #[test]
996    fn test_input_entry_function_data_arg_raw() {
997        let raw_bytes = vec![0x01, 0x02, 0x03];
998        let builder = InputEntryFunctionData::new("0x1::coin::transfer").arg_raw(raw_bytes);
999        let result = builder.build();
1000        assert!(result.is_ok());
1001    }
1002
1003    #[test]
1004    fn test_input_entry_function_data_args() {
1005        let builder = InputEntryFunctionData::new("0x1::coin::transfer").args([1u64, 2u64, 3u64]);
1006        let result = builder.build();
1007        assert!(result.is_ok());
1008    }
1009
1010    #[test]
1011    fn test_input_entry_function_data_transfer_apt() {
1012        use crate::types::AccountAddress;
1013
1014        let recipient = AccountAddress::from_hex("0x123").unwrap();
1015        let result = InputEntryFunctionData::transfer_apt(recipient, 1000);
1016        assert!(result.is_ok());
1017    }
1018
1019    #[test]
1020    fn test_input_entry_function_data_builder_debug() {
1021        let builder = InputEntryFunctionData::new("0x1::coin::transfer");
1022        let debug = format!("{builder:?}");
1023        assert!(debug.contains("InputEntryFunctionDataBuilder"));
1024    }
1025
1026    #[test]
1027    fn test_input_entry_function_data_builder_clone() {
1028        let builder = InputEntryFunctionData::new("0x1::coin::transfer").arg(42u64);
1029        let cloned = builder.clone();
1030        assert!(cloned.build().is_ok());
1031    }
1032
1033    #[test]
1034    fn test_move_u256_debug() {
1035        let val = MoveU256::from_u128(123_456_789);
1036        let debug = format!("{val:?}");
1037        assert!(debug.contains("MoveU256"));
1038    }
1039
1040    #[test]
1041    fn test_move_i128_debug() {
1042        let val = MoveI128::new(-42);
1043        let debug = format!("{val:?}");
1044        assert!(debug.contains("MoveI128"));
1045    }
1046
1047    #[test]
1048    fn test_move_i256_debug() {
1049        let val = MoveI256::from_i128(-42);
1050        let debug = format!("{val:?}");
1051        assert!(debug.contains("MoveI256"));
1052    }
1053
1054    #[test]
1055    fn test_move_u256_equality() {
1056        let val1 = MoveU256::from_u128(100);
1057        let val2 = MoveU256::from_u128(100);
1058        let val3 = MoveU256::from_u128(200);
1059        assert_eq!(val1, val2);
1060        assert_ne!(val1, val3);
1061    }
1062
1063    #[test]
1064    fn test_move_i256_equality() {
1065        let val1 = MoveI256::from_i128(-50);
1066        let val2 = MoveI256::from_i128(-50);
1067        let val3 = MoveI256::from_i128(50);
1068        assert_eq!(val1, val2);
1069        assert_ne!(val1, val3);
1070    }
1071
1072    #[test]
1073    fn test_move_u256_clone() {
1074        let val1 = MoveU256::from_u128(999);
1075        let val2 = val1;
1076        assert_eq!(val1, val2);
1077    }
1078
1079    #[test]
1080    fn test_move_i256_clone() {
1081        let val1 = MoveI256::from_i128(-999);
1082        let val2 = val1;
1083        assert_eq!(val1, val2);
1084    }
1085}