1use crate::error::{AptosError, AptosResult};
33use crate::transaction::{EntryFunction, TransactionPayload};
34use crate::types::{AccountAddress, EntryFunctionId, MoveModuleId, TypeTag};
35use serde::Serialize;
36
37#[allow(dead_code)] #[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 #[allow(clippy::new_ret_no_self)] pub fn new(function_id: &str) -> InputEntryFunctionDataBuilder {
76 InputEntryFunctionDataBuilder::new(function_id)
77 }
78
79 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 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 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 pub fn transfer_fungible_asset(
166 metadata: AccountAddress,
167 recipient: AccountAddress,
168 amount: u64,
169 ) -> AptosResult<TransactionPayload> {
170 InputEntryFunctionData::new("0x1::primary_fungible_store::transfer")
171 .type_arg("0x1::fungible_asset::Metadata")
172 .arg(metadata)
173 .arg(recipient)
174 .arg(amount)
175 .build()
176 }
177
178 pub fn create_account(auth_key: AccountAddress) -> AptosResult<TransactionPayload> {
188 InputEntryFunctionData::new("0x1::aptos_account::create_account")
189 .arg(auth_key)
190 .build()
191 }
192
193 pub fn rotate_authentication_key(
203 from_scheme: u8,
204 from_public_key_bytes: Vec<u8>,
205 to_scheme: u8,
206 to_public_key_bytes: Vec<u8>,
207 cap_rotate_key: Vec<u8>,
208 cap_update_table: Vec<u8>,
209 ) -> AptosResult<TransactionPayload> {
210 InputEntryFunctionData::new("0x1::account::rotate_authentication_key")
211 .arg(from_scheme)
212 .arg(from_public_key_bytes)
213 .arg(to_scheme)
214 .arg(to_public_key_bytes)
215 .arg(cap_rotate_key)
216 .arg(cap_update_table)
217 .build()
218 }
219
220 pub fn register_coin(coin_type: &str) -> AptosResult<TransactionPayload> {
230 InputEntryFunctionData::new("0x1::managed_coin::register")
231 .type_arg(coin_type)
232 .build()
233 }
234
235 pub fn publish_package(
246 metadata_serialized: Vec<u8>,
247 code: Vec<Vec<u8>>,
248 ) -> AptosResult<TransactionPayload> {
249 InputEntryFunctionData::new("0x1::code::publish_package_txn")
250 .arg(metadata_serialized)
251 .arg(code)
252 .build()
253 }
254
255 pub fn transfer_object(
267 object: AccountAddress,
268 to: AccountAddress,
269 ) -> AptosResult<TransactionPayload> {
270 InputEntryFunctionData::new("0x1::object::transfer_call")
271 .arg(object)
272 .arg(to)
273 .build()
274 }
275
276 pub fn transfer_digital_asset(
288 token: AccountAddress,
289 to: AccountAddress,
290 ) -> AptosResult<TransactionPayload> {
291 InputEntryFunctionData::new("0x1::object::transfer")
292 .type_arg("0x4::token::Token")
293 .arg(token)
294 .arg(to)
295 .build()
296 }
297
298 #[allow(clippy::too_many_arguments)]
311 pub fn create_collection(
312 description: &str,
313 max_supply: u64,
314 name: &str,
315 uri: &str,
316 config: CollectionConfig,
317 royalty_numerator: u64,
318 royalty_denominator: u64,
319 ) -> AptosResult<TransactionPayload> {
320 InputEntryFunctionData::new("0x4::aptos_token::create_collection")
321 .arg(description)
322 .arg(max_supply)
323 .arg(name)
324 .arg(uri)
325 .arg(config.mutable_description)
326 .arg(config.mutable_royalty)
327 .arg(config.mutable_uri)
328 .arg(config.mutable_token_description)
329 .arg(config.mutable_token_name)
330 .arg(config.mutable_token_properties)
331 .arg(config.mutable_token_uri)
332 .arg(config.tokens_burnable_by_creator)
333 .arg(config.tokens_freezable_by_creator)
334 .arg(royalty_numerator)
335 .arg(royalty_denominator)
336 .build()
337 }
338
339 pub fn mint_digital_asset(
350 collection: &str,
351 description: &str,
352 name: &str,
353 uri: &str,
354 property_keys: Vec<String>,
355 property_types: Vec<String>,
356 property_values: Vec<Vec<u8>>,
357 ) -> AptosResult<TransactionPayload> {
358 InputEntryFunctionData::new("0x4::aptos_token::mint")
359 .arg(collection)
360 .arg(description)
361 .arg(name)
362 .arg(uri)
363 .arg(property_keys)
364 .arg(property_types)
365 .arg(property_values)
366 .build()
367 }
368
369 pub fn mint_soul_bound_digital_asset(
376 collection: &str,
377 description: &str,
378 name: &str,
379 uri: &str,
380 property_keys: Vec<String>,
381 property_types: Vec<String>,
382 property_values: Vec<Vec<u8>>,
383 soul_bound_to: AccountAddress,
384 ) -> AptosResult<TransactionPayload> {
385 InputEntryFunctionData::new("0x4::aptos_token::mint_soul_bound")
386 .arg(collection)
387 .arg(description)
388 .arg(name)
389 .arg(uri)
390 .arg(property_keys)
391 .arg(property_types)
392 .arg(property_values)
393 .arg(soul_bound_to)
394 .build()
395 }
396
397 pub fn burn_digital_asset(token: AccountAddress) -> AptosResult<TransactionPayload> {
404 InputEntryFunctionData::new("0x4::aptos_token::burn")
405 .type_arg("0x4::token::Token")
406 .arg(token)
407 .build()
408 }
409
410 pub fn freeze_digital_asset_transfer(token: AccountAddress) -> AptosResult<TransactionPayload> {
418 InputEntryFunctionData::new("0x4::aptos_token::freeze_transfer")
419 .type_arg("0x4::token::Token")
420 .arg(token)
421 .build()
422 }
423
424 pub fn unfreeze_digital_asset_transfer(
432 token: AccountAddress,
433 ) -> AptosResult<TransactionPayload> {
434 InputEntryFunctionData::new("0x4::aptos_token::unfreeze_transfer")
435 .type_arg("0x4::token::Token")
436 .arg(token)
437 .build()
438 }
439
440 pub fn delegation_add_stake(
449 pool_address: AccountAddress,
450 amount: u64,
451 ) -> AptosResult<TransactionPayload> {
452 InputEntryFunctionData::new("0x1::delegation_pool::add_stake")
453 .arg(pool_address)
454 .arg(amount)
455 .build()
456 }
457
458 pub fn delegation_unlock(
466 pool_address: AccountAddress,
467 amount: u64,
468 ) -> AptosResult<TransactionPayload> {
469 InputEntryFunctionData::new("0x1::delegation_pool::unlock")
470 .arg(pool_address)
471 .arg(amount)
472 .build()
473 }
474
475 pub fn delegation_reactivate_stake(
482 pool_address: AccountAddress,
483 amount: u64,
484 ) -> AptosResult<TransactionPayload> {
485 InputEntryFunctionData::new("0x1::delegation_pool::reactivate_stake")
486 .arg(pool_address)
487 .arg(amount)
488 .build()
489 }
490
491 pub fn delegation_withdraw(
498 pool_address: AccountAddress,
499 amount: u64,
500 ) -> AptosResult<TransactionPayload> {
501 InputEntryFunctionData::new("0x1::delegation_pool::withdraw")
502 .arg(pool_address)
503 .arg(amount)
504 .build()
505 }
506
507 pub fn add_authentication_function(
519 module_address: AccountAddress,
520 module_name: &str,
521 function_name: &str,
522 ) -> AptosResult<TransactionPayload> {
523 InputEntryFunctionData::new("0x1::account_abstraction::add_authentication_function")
524 .arg(module_address)
525 .arg(module_name)
526 .arg(function_name)
527 .build()
528 }
529
530 pub fn remove_authentication_function(
537 module_address: AccountAddress,
538 module_name: &str,
539 function_name: &str,
540 ) -> AptosResult<TransactionPayload> {
541 InputEntryFunctionData::new("0x1::account_abstraction::remove_authentication_function")
542 .arg(module_address)
543 .arg(module_name)
544 .arg(function_name)
545 .build()
546 }
547
548 pub fn remove_authenticator() -> AptosResult<TransactionPayload> {
556 InputEntryFunctionData::new("0x1::account_abstraction::remove_authenticator").build()
557 }
558}
559
560#[derive(Debug, Clone, Copy, PartialEq, Eq)]
567pub struct CollectionConfig {
568 pub mutable_description: bool,
570 pub mutable_royalty: bool,
572 pub mutable_uri: bool,
574 pub mutable_token_description: bool,
576 pub mutable_token_name: bool,
578 pub mutable_token_properties: bool,
580 pub mutable_token_uri: bool,
582 pub tokens_burnable_by_creator: bool,
584 pub tokens_freezable_by_creator: bool,
586}
587
588impl Default for CollectionConfig {
589 fn default() -> Self {
590 Self {
591 mutable_description: true,
592 mutable_royalty: true,
593 mutable_uri: true,
594 mutable_token_description: true,
595 mutable_token_name: true,
596 mutable_token_properties: true,
597 mutable_token_uri: true,
598 tokens_burnable_by_creator: true,
599 tokens_freezable_by_creator: true,
600 }
601 }
602}
603
604#[derive(Debug, Clone)]
606pub struct InputEntryFunctionDataBuilder {
607 module: Result<MoveModuleId, String>,
608 function: String,
609 type_args: Vec<TypeTag>,
610 args: Vec<Vec<u8>>,
611 errors: Vec<String>,
612}
613
614impl InputEntryFunctionDataBuilder {
615 #[must_use]
617 fn new(function_id: &str) -> Self {
618 match EntryFunctionId::from_str_strict(function_id) {
619 Ok(func_id) => Self {
620 module: Ok(func_id.module),
621 function: func_id.name.as_str().to_string(),
622 type_args: Vec::new(),
623 args: Vec::new(),
624 errors: Vec::new(),
625 },
626 Err(e) => Self {
627 module: Err(format!("Invalid function ID '{function_id}': {e}")),
628 function: String::new(),
629 type_args: Vec::new(),
630 args: Vec::new(),
631 errors: Vec::new(),
632 },
633 }
634 }
635
636 #[must_use]
649 pub fn type_arg(mut self, type_arg: &str) -> Self {
650 match TypeTag::from_str_strict(type_arg) {
651 Ok(tag) => self.type_args.push(tag),
652 Err(e) => self
653 .errors
654 .push(format!("Invalid type argument '{type_arg}': {e}")),
655 }
656 self
657 }
658
659 #[must_use]
661 pub fn type_arg_typed(mut self, type_arg: TypeTag) -> Self {
662 self.type_args.push(type_arg);
663 self
664 }
665
666 #[must_use]
668 pub fn type_args(mut self, type_args: impl IntoIterator<Item = &'static str>) -> Self {
669 for type_arg in type_args {
670 self = self.type_arg(type_arg);
671 }
672 self
673 }
674
675 #[must_use]
677 pub fn type_args_typed(mut self, type_args: impl IntoIterator<Item = TypeTag>) -> Self {
678 self.type_args.extend(type_args);
679 self
680 }
681
682 #[must_use]
705 pub fn arg<T: Serialize>(mut self, value: T) -> Self {
706 match aptos_bcs::to_bytes(&value) {
707 Ok(bytes) => self.args.push(bytes),
708 Err(e) => self
709 .errors
710 .push(format!("Failed to serialize argument: {e}")),
711 }
712 self
713 }
714
715 #[must_use]
719 pub fn arg_raw(mut self, bytes: Vec<u8>) -> Self {
720 self.args.push(bytes);
721 self
722 }
723
724 #[must_use]
726 pub fn args<T: Serialize>(mut self, values: impl IntoIterator<Item = T>) -> Self {
727 for value in values {
728 self = self.arg(value);
729 }
730 self
731 }
732
733 pub fn build(self) -> AptosResult<TransactionPayload> {
744 let module = self.module.map_err(AptosError::Transaction)?;
746
747 if !self.errors.is_empty() {
749 return Err(AptosError::Transaction(self.errors.join("; ")));
750 }
751
752 Ok(TransactionPayload::EntryFunction(EntryFunction {
753 module,
754 function: self.function,
755 type_args: self.type_args,
756 args: self.args,
757 }))
758 }
759
760 pub fn build_entry_function(self) -> AptosResult<EntryFunction> {
766 let module = self.module.map_err(AptosError::Transaction)?;
767
768 if !self.errors.is_empty() {
769 return Err(AptosError::Transaction(self.errors.join("; ")));
770 }
771
772 Ok(EntryFunction {
773 module,
774 function: self.function,
775 type_args: self.type_args,
776 args: self.args,
777 })
778 }
779}
780
781pub trait IntoMoveArg {
785 fn into_move_arg(self) -> AptosResult<Vec<u8>>;
791}
792
793impl<T: Serialize> IntoMoveArg for T {
794 fn into_move_arg(self) -> AptosResult<Vec<u8>> {
795 aptos_bcs::to_bytes(&self).map_err(AptosError::bcs)
796 }
797}
798
799pub fn move_vec<T: Serialize>(items: &[T]) -> Vec<u8> {
810 aptos_bcs::to_bytes(items).unwrap_or_default()
811}
812
813pub fn move_string(s: &str) -> String {
823 s.to_string()
824}
825
826pub fn move_some<T: Serialize>(value: T) -> Vec<u8> {
834 let mut bytes = vec![0x01];
836 if let Ok(value_bytes) = aptos_bcs::to_bytes(&value) {
837 bytes.extend(value_bytes);
838 }
839 bytes
840}
841
842pub fn move_none() -> Vec<u8> {
850 vec![0x00]
852}
853
854#[derive(Debug, Clone, Copy, PartialEq, Eq)]
858pub struct MoveU256(pub [u8; 32]);
859
860impl MoveU256 {
861 pub fn parse(s: &str) -> AptosResult<Self> {
867 let mut bytes = [0u8; 32];
869
870 if let Ok(val) = s.parse::<u128>() {
872 bytes[..16].copy_from_slice(&val.to_le_bytes());
873 return Ok(Self(bytes));
874 }
875
876 Err(AptosError::Transaction(format!("Invalid u256: {s}")))
877 }
878
879 pub fn from_u128(val: u128) -> Self {
881 let mut bytes = [0u8; 32];
882 bytes[..16].copy_from_slice(&val.to_le_bytes());
883 Self(bytes)
884 }
885
886 pub fn from_le_bytes(bytes: [u8; 32]) -> Self {
888 Self(bytes)
889 }
890}
891
892impl Serialize for MoveU256 {
893 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
894 where
895 S: serde::Serializer,
896 {
897 use serde::ser::SerializeTuple;
899 let mut tuple = serializer.serialize_tuple(32)?;
900 for byte in &self.0 {
901 tuple.serialize_element(byte)?;
902 }
903 tuple.end()
904 }
905}
906
907#[derive(Debug, Clone, Copy, PartialEq, Eq)]
912pub struct MoveI128(pub i128);
913
914impl MoveI128 {
915 pub fn new(val: i128) -> Self {
917 Self(val)
918 }
919}
920
921impl Serialize for MoveI128 {
922 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
923 where
924 S: serde::Serializer,
925 {
926 use serde::ser::SerializeTuple;
928 let bytes = self.0.to_le_bytes();
929 let mut tuple = serializer.serialize_tuple(16)?;
930 for byte in &bytes {
931 tuple.serialize_element(byte)?;
932 }
933 tuple.end()
934 }
935}
936
937impl From<i128> for MoveI128 {
938 fn from(val: i128) -> Self {
939 Self(val)
940 }
941}
942
943#[derive(Debug, Clone, Copy, PartialEq, Eq)]
948pub struct MoveI256(pub [u8; 32]);
949
950impl MoveI256 {
951 pub fn from_i128(val: i128) -> Self {
953 let mut bytes = [0u8; 32];
954 let val_bytes = val.to_le_bytes();
955 bytes[..16].copy_from_slice(&val_bytes);
956 if val < 0 {
958 bytes[16..].fill(0xFF);
959 }
960 Self(bytes)
961 }
962
963 pub fn from_le_bytes(bytes: [u8; 32]) -> Self {
965 Self(bytes)
966 }
967}
968
969impl Serialize for MoveI256 {
970 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
971 where
972 S: serde::Serializer,
973 {
974 use serde::ser::SerializeTuple;
976 let mut tuple = serializer.serialize_tuple(32)?;
977 for byte in &self.0 {
978 tuple.serialize_element(byte)?;
979 }
980 tuple.end()
981 }
982}
983
984impl From<i128> for MoveI256 {
985 fn from(val: i128) -> Self {
986 Self::from_i128(val)
987 }
988}
989
990pub mod functions {
992 pub const APT_TRANSFER: &str = "0x1::aptos_account::transfer";
994 pub const COIN_TRANSFER: &str = "0x1::coin::transfer";
996 pub const CREATE_ACCOUNT: &str = "0x1::aptos_account::create_account";
998 pub const REGISTER_COIN: &str = "0x1::managed_coin::register";
1000 pub const PUBLISH_PACKAGE: &str = "0x1::code::publish_package_txn";
1002 pub const ROTATE_AUTH_KEY: &str = "0x1::account::rotate_authentication_key";
1004}
1005
1006pub mod types {
1008 pub const APT_COIN: &str = "0x1::aptos_coin::AptosCoin";
1010}
1011
1012#[cfg(test)]
1013mod tests {
1014 use super::*;
1015
1016 #[test]
1017 fn test_simple_transfer() {
1018 let recipient = AccountAddress::from_hex("0x123").unwrap();
1019 let payload = InputEntryFunctionData::new("0x1::aptos_account::transfer")
1020 .arg(recipient)
1021 .arg(1_000_000u64)
1022 .build()
1023 .unwrap();
1024
1025 match payload {
1026 TransactionPayload::EntryFunction(ef) => {
1027 assert_eq!(ef.function, "transfer");
1028 assert_eq!(ef.module.name.as_str(), "aptos_account");
1029 assert!(ef.type_args.is_empty());
1030 assert_eq!(ef.args.len(), 2);
1031 }
1032 _ => panic!("Expected EntryFunction"),
1033 }
1034 }
1035
1036 #[test]
1037 fn test_with_type_args() {
1038 let payload = InputEntryFunctionData::new("0x1::coin::transfer")
1039 .type_arg("0x1::aptos_coin::AptosCoin")
1040 .arg(AccountAddress::ONE)
1041 .arg(1000u64)
1042 .build()
1043 .unwrap();
1044
1045 match payload {
1046 TransactionPayload::EntryFunction(ef) => {
1047 assert_eq!(ef.function, "transfer");
1048 assert_eq!(ef.type_args.len(), 1);
1049 }
1050 _ => panic!("Expected EntryFunction"),
1051 }
1052 }
1053
1054 #[test]
1055 fn test_invalid_function_id() {
1056 let result = InputEntryFunctionData::new("invalid").arg(42u64).build();
1057
1058 assert!(result.is_err());
1059 }
1060
1061 #[test]
1062 fn test_invalid_type_arg() {
1063 let result = InputEntryFunctionData::new("0x1::coin::transfer")
1064 .type_arg("not a type")
1065 .arg(AccountAddress::ONE)
1066 .arg(1000u64)
1067 .build();
1068
1069 assert!(result.is_err());
1070 }
1071
1072 #[test]
1073 fn test_transfer_apt_helper() {
1074 let recipient = AccountAddress::from_hex("0x456").unwrap();
1075 let payload = InputEntryFunctionData::transfer_apt(recipient, 5_000_000).unwrap();
1076
1077 match payload {
1078 TransactionPayload::EntryFunction(ef) => {
1079 assert_eq!(ef.function, "transfer");
1080 assert_eq!(ef.module.name.as_str(), "aptos_account");
1081 }
1082 _ => panic!("Expected EntryFunction"),
1083 }
1084 }
1085
1086 #[test]
1087 fn test_transfer_coin_helper() {
1088 let recipient = AccountAddress::from_hex("0x789").unwrap();
1089 let payload =
1090 InputEntryFunctionData::transfer_coin("0x1::aptos_coin::AptosCoin", recipient, 1000)
1091 .unwrap();
1092
1093 match payload {
1094 TransactionPayload::EntryFunction(ef) => {
1095 assert_eq!(ef.function, "transfer");
1096 assert_eq!(ef.module.name.as_str(), "coin");
1097 assert_eq!(ef.type_args.len(), 1);
1098 }
1099 _ => panic!("Expected EntryFunction"),
1100 }
1101 }
1102
1103 #[test]
1104 fn test_transfer_fungible_asset_helper() {
1105 let metadata = AccountAddress::from_hex("0xa").unwrap();
1106 let recipient = AccountAddress::from_hex("0x789").unwrap();
1107 let payload =
1108 InputEntryFunctionData::transfer_fungible_asset(metadata, recipient, 1000).unwrap();
1109
1110 match payload {
1111 TransactionPayload::EntryFunction(ef) => {
1112 assert_eq!(ef.function, "transfer");
1113 assert_eq!(ef.module.name.as_str(), "primary_fungible_store");
1114 assert_eq!(ef.type_args.len(), 1);
1116 assert_eq!(ef.args.len(), 3);
1118 assert_eq!(ef.args[0], aptos_bcs::to_bytes(&metadata).unwrap());
1120 assert_eq!(ef.args[2], aptos_bcs::to_bytes(&1000u64).unwrap());
1121 }
1122 _ => panic!("Expected EntryFunction"),
1123 }
1124 }
1125
1126 #[test]
1127 fn test_transfer_object_helper() {
1128 let object = AccountAddress::from_hex("0xbeef").unwrap();
1129 let to = AccountAddress::from_hex("0x789").unwrap();
1130 let payload = InputEntryFunctionData::transfer_object(object, to).unwrap();
1131 match payload {
1132 TransactionPayload::EntryFunction(ef) => {
1133 assert_eq!(ef.module.name.as_str(), "object");
1134 assert_eq!(ef.function, "transfer_call");
1135 assert_eq!(ef.args.len(), 2);
1136 assert!(ef.type_args.is_empty());
1137 }
1138 _ => panic!("Expected EntryFunction"),
1139 }
1140 }
1141
1142 #[test]
1143 fn test_transfer_digital_asset_helper() {
1144 let token = AccountAddress::from_hex("0xdead").unwrap();
1145 let to = AccountAddress::from_hex("0x789").unwrap();
1146 let payload = InputEntryFunctionData::transfer_digital_asset(token, to).unwrap();
1147 match payload {
1148 TransactionPayload::EntryFunction(ef) => {
1149 assert_eq!(ef.module.name.as_str(), "object");
1150 assert_eq!(ef.function, "transfer");
1151 assert_eq!(ef.type_args.len(), 1, "should carry the Token object type");
1152 assert_eq!(ef.args.len(), 2);
1153 }
1154 _ => panic!("Expected EntryFunction"),
1155 }
1156 }
1157
1158 #[test]
1159 fn test_create_collection_arg_shape() {
1160 let payload = InputEntryFunctionData::create_collection(
1161 "desc",
1162 100,
1163 "My Collection",
1164 "https://example.com",
1165 CollectionConfig::default(),
1166 5,
1167 100,
1168 )
1169 .unwrap();
1170 match payload {
1171 TransactionPayload::EntryFunction(ef) => {
1172 assert_eq!(ef.module.address, AccountAddress::from_hex("0x4").unwrap());
1173 assert_eq!(ef.module.name.as_str(), "aptos_token");
1174 assert_eq!(ef.function, "create_collection");
1175 assert_eq!(ef.args.len(), 15);
1177 }
1178 _ => panic!("Expected EntryFunction"),
1179 }
1180 }
1181
1182 #[test]
1183 fn test_mint_digital_asset_arg_shape() {
1184 let payload = InputEntryFunctionData::mint_digital_asset(
1185 "My Collection",
1186 "desc",
1187 "Token #1",
1188 "https://example.com/1",
1189 vec!["level".to_string()],
1190 vec!["u64".to_string()],
1191 vec![aptos_bcs::to_bytes(&1u64).unwrap()],
1192 )
1193 .unwrap();
1194 match payload {
1195 TransactionPayload::EntryFunction(ef) => {
1196 assert_eq!(ef.function, "mint");
1197 assert_eq!(ef.args.len(), 7);
1198 }
1199 _ => panic!("Expected EntryFunction"),
1200 }
1201 }
1202
1203 #[test]
1204 fn test_delegation_helpers() {
1205 let pool = AccountAddress::from_hex("0x5").unwrap();
1206 for (payload, func) in [
1207 (
1208 InputEntryFunctionData::delegation_add_stake(pool, 1000).unwrap(),
1209 "add_stake",
1210 ),
1211 (
1212 InputEntryFunctionData::delegation_unlock(pool, 1000).unwrap(),
1213 "unlock",
1214 ),
1215 (
1216 InputEntryFunctionData::delegation_reactivate_stake(pool, 1000).unwrap(),
1217 "reactivate_stake",
1218 ),
1219 (
1220 InputEntryFunctionData::delegation_withdraw(pool, 1000).unwrap(),
1221 "withdraw",
1222 ),
1223 ] {
1224 match payload {
1225 TransactionPayload::EntryFunction(ef) => {
1226 assert_eq!(ef.module.name.as_str(), "delegation_pool");
1227 assert_eq!(ef.function, func);
1228 assert_eq!(ef.args.len(), 2);
1229 }
1230 _ => panic!("Expected EntryFunction"),
1231 }
1232 }
1233 }
1234
1235 #[test]
1236 fn test_account_abstraction_helpers() {
1237 let module = AccountAddress::from_hex("0x123").unwrap();
1238 let add =
1239 InputEntryFunctionData::add_authentication_function(module, "my_auth", "authenticate")
1240 .unwrap();
1241 match add {
1242 TransactionPayload::EntryFunction(ef) => {
1243 assert_eq!(ef.module.name.as_str(), "account_abstraction");
1244 assert_eq!(ef.function, "add_authentication_function");
1245 assert_eq!(ef.args.len(), 3);
1246 }
1247 _ => panic!("Expected EntryFunction"),
1248 }
1249
1250 let remove_all = InputEntryFunctionData::remove_authenticator().unwrap();
1251 match remove_all {
1252 TransactionPayload::EntryFunction(ef) => {
1253 assert_eq!(ef.function, "remove_authenticator");
1254 assert!(ef.args.is_empty());
1255 }
1256 _ => panic!("Expected EntryFunction"),
1257 }
1258 }
1259
1260 #[test]
1261 fn test_various_arg_types() {
1262 let payload = InputEntryFunctionData::new("0x1::test::test_function")
1263 .arg(42u8)
1264 .arg(1000u64)
1265 .arg(true)
1266 .arg("hello".to_string())
1267 .arg(vec![1u8, 2u8, 3u8])
1268 .arg(AccountAddress::ONE)
1269 .build()
1270 .unwrap();
1271
1272 match payload {
1273 TransactionPayload::EntryFunction(ef) => {
1274 assert_eq!(ef.args.len(), 6);
1275 }
1276 _ => panic!("Expected EntryFunction"),
1277 }
1278 }
1279
1280 #[test]
1281 fn test_move_u256() {
1282 let val = MoveU256::from_u128(12345);
1283 let bytes = aptos_bcs::to_bytes(&val).unwrap();
1284 assert_eq!(bytes.len(), 32);
1285 }
1286
1287 #[test]
1288 fn test_move_some_none() {
1289 let some_bytes = move_some(42u64);
1290 assert_eq!(some_bytes[0], 0x01);
1291
1292 let none_bytes = move_none();
1293 assert_eq!(none_bytes, vec![0x00]);
1294 }
1295
1296 #[test]
1297 fn test_from_parts() {
1298 let module = MoveModuleId::from_str_strict("0x1::coin").unwrap();
1299 let payload = InputEntryFunctionData::from_parts(module, "transfer")
1300 .type_arg("0x1::aptos_coin::AptosCoin")
1301 .arg(AccountAddress::ONE)
1302 .arg(1000u64)
1303 .build()
1304 .unwrap();
1305
1306 match payload {
1307 TransactionPayload::EntryFunction(ef) => {
1308 assert_eq!(ef.function, "transfer");
1309 assert_eq!(ef.module.name.as_str(), "coin");
1310 }
1311 _ => panic!("Expected EntryFunction"),
1312 }
1313 }
1314
1315 #[test]
1316 fn test_build_entry_function() {
1317 let ef = InputEntryFunctionData::new("0x1::aptos_account::transfer")
1318 .arg(AccountAddress::ONE)
1319 .arg(1000u64)
1320 .build_entry_function()
1321 .unwrap();
1322
1323 assert_eq!(ef.function, "transfer");
1324 assert_eq!(ef.args.len(), 2);
1325 }
1326
1327 #[test]
1328 fn test_function_constants() {
1329 assert_eq!(functions::APT_TRANSFER, "0x1::aptos_account::transfer");
1330 assert_eq!(functions::COIN_TRANSFER, "0x1::coin::transfer");
1331 }
1332
1333 #[test]
1334 fn test_move_u256_from_u128() {
1335 let val = MoveU256::from_u128(123_456_789);
1336 let expected = 123_456_789_u128.to_le_bytes();
1338 assert_eq!(&val.0[..16], &expected);
1339 assert_eq!(&val.0[16..], &[0u8; 16]);
1341 }
1342
1343 #[test]
1344 fn test_move_u256_from_le_bytes() {
1345 let bytes = [0xab; 32];
1346 let val = MoveU256::from_le_bytes(bytes);
1347 assert_eq!(val.0, bytes);
1348 }
1349
1350 #[test]
1351 fn test_move_u256_parse() {
1352 let val = MoveU256::parse("12345678901234567890").unwrap();
1353 let expected = 12_345_678_901_234_567_890_u128;
1354 let mut expected_bytes = [0u8; 32];
1355 expected_bytes[..16].copy_from_slice(&expected.to_le_bytes());
1356 assert_eq!(val.0, expected_bytes);
1357 }
1358
1359 #[test]
1360 fn test_move_u256_parse_invalid() {
1361 let result = MoveU256::parse("999999999999999999999999999999999999999999999");
1363 assert!(result.is_err());
1364 }
1365
1366 #[test]
1367 fn test_move_u256_serialization() {
1368 let val = MoveU256::from_u128(0x0102_0304_0506_0708);
1369 let bcs = aptos_bcs::to_bytes(&val).unwrap();
1370 assert_eq!(bcs.len(), 32);
1372 assert_eq!(&bcs[..8], &[0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1374 }
1375
1376 #[test]
1377 fn test_move_i128_new() {
1378 let val = MoveI128::new(42);
1379 assert_eq!(val.0, 42);
1380 }
1381
1382 #[test]
1383 fn test_move_i128_from_i128() {
1384 let val: MoveI128 = (-100i128).into();
1385 assert_eq!(val.0, -100);
1386 }
1387
1388 #[test]
1389 fn test_move_i128_serialization_positive() {
1390 let val = MoveI128::new(0x0102_0304_0506_0708);
1391 let bcs = aptos_bcs::to_bytes(&val).unwrap();
1392 assert_eq!(bcs.len(), 16);
1394 assert_eq!(&bcs[..8], &[0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1396 assert_eq!(&bcs[8..], &[0, 0, 0, 0, 0, 0, 0, 0]);
1398 }
1399
1400 #[test]
1401 fn test_move_i128_serialization_negative() {
1402 let val = MoveI128::new(-1);
1403 let bcs = aptos_bcs::to_bytes(&val).unwrap();
1404 assert_eq!(bcs.len(), 16);
1405 assert_eq!(bcs, vec![0xFF; 16]);
1407 }
1408
1409 #[test]
1410 fn test_move_i256_from_i128_positive() {
1411 let val = MoveI256::from_i128(42);
1412 let expected = 42i128.to_le_bytes();
1414 assert_eq!(&val.0[..16], &expected);
1415 assert_eq!(&val.0[16..], &[0u8; 16]);
1417 }
1418
1419 #[test]
1420 fn test_move_i256_from_i128_negative() {
1421 let val = MoveI256::from_i128(-1);
1422 assert_eq!(val.0, [0xFF; 32]);
1424 }
1425
1426 #[test]
1427 fn test_move_i256_from_le_bytes() {
1428 let bytes = [0xcd; 32];
1429 let val = MoveI256::from_le_bytes(bytes);
1430 assert_eq!(val.0, bytes);
1431 }
1432
1433 #[test]
1434 fn test_move_i256_from_trait() {
1435 let val: MoveI256 = (-100i128).into();
1436 let expected = MoveI256::from_i128(-100);
1437 assert_eq!(val, expected);
1438 }
1439
1440 #[test]
1441 fn test_move_i256_serialization() {
1442 let val = MoveI256::from_i128(0x0102_0304_0506_0708);
1443 let bcs = aptos_bcs::to_bytes(&val).unwrap();
1444 assert_eq!(bcs.len(), 32);
1446 assert_eq!(&bcs[..8], &[0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1448 }
1449
1450 #[test]
1451 fn test_move_i256_serialization_negative() {
1452 let val = MoveI256::from_i128(-1);
1453 let bcs = aptos_bcs::to_bytes(&val).unwrap();
1454 assert_eq!(bcs.len(), 32);
1455 assert_eq!(bcs, vec![0xFF; 32]);
1457 }
1458
1459 fn payload_as_entry_function(
1462 p: crate::transaction::TransactionPayload,
1463 ) -> crate::transaction::EntryFunction {
1464 match p {
1465 crate::transaction::TransactionPayload::EntryFunction(ef) => ef,
1466 other => panic!(
1467 "expected TransactionPayload::EntryFunction, got: {:?}",
1468 std::mem::discriminant(&other)
1469 ),
1470 }
1471 }
1472
1473 #[test]
1474 fn test_input_entry_function_data_new() {
1475 let builder = InputEntryFunctionData::new("0x1::coin::transfer");
1476 let entry_fn = payload_as_entry_function(builder.build().expect("build should succeed"));
1477 assert_eq!(entry_fn.module.name.as_str(), "coin");
1478 assert_eq!(entry_fn.function, "transfer");
1479 assert!(entry_fn.type_args.is_empty());
1480 assert!(entry_fn.args.is_empty());
1481 }
1482
1483 #[test]
1484 fn test_input_entry_function_data_invalid_function_id() {
1485 let builder = InputEntryFunctionData::new("invalid");
1486 let result = builder.build();
1487 assert!(result.is_err());
1488 assert!(
1489 result
1490 .unwrap_err()
1491 .to_string()
1492 .contains("Invalid function ID")
1493 );
1494 }
1495
1496 #[test]
1497 fn test_input_entry_function_data_type_arg() {
1498 let entry_fn = payload_as_entry_function(
1499 InputEntryFunctionData::new("0x1::coin::transfer")
1500 .type_arg("0x1::aptos_coin::AptosCoin")
1501 .build()
1502 .expect("build should succeed"),
1503 );
1504 assert_eq!(entry_fn.type_args.len(), 1);
1505 assert_eq!(
1506 entry_fn.type_args[0].to_string(),
1507 "0x1::aptos_coin::AptosCoin"
1508 );
1509 }
1510
1511 #[test]
1512 fn test_input_entry_function_data_invalid_type_arg() {
1513 let builder =
1514 InputEntryFunctionData::new("0x1::coin::transfer").type_arg("not a valid type");
1515 let result = builder.build();
1516 assert!(result.is_err());
1517 assert!(result.unwrap_err().to_string().contains("type argument"));
1518 }
1519
1520 #[test]
1521 fn test_input_entry_function_data_type_arg_typed() {
1522 use crate::types::TypeTag;
1523
1524 let entry_fn = payload_as_entry_function(
1525 InputEntryFunctionData::new("0x1::coin::transfer")
1526 .type_arg_typed(TypeTag::U64)
1527 .build()
1528 .expect("build should succeed"),
1529 );
1530 assert_eq!(entry_fn.type_args, vec![TypeTag::U64]);
1531 }
1532
1533 #[test]
1534 fn test_input_entry_function_data_type_args() {
1535 let entry_fn = payload_as_entry_function(
1536 InputEntryFunctionData::new("0x1::coin::transfer")
1537 .type_args(["u64", "u128"])
1538 .build()
1539 .expect("build should succeed"),
1540 );
1541 assert_eq!(entry_fn.type_args.len(), 2);
1542 assert_eq!(entry_fn.type_args[0].to_string(), "u64");
1543 assert_eq!(entry_fn.type_args[1].to_string(), "u128");
1544 }
1545
1546 #[test]
1547 fn test_input_entry_function_data_type_args_typed() {
1548 use crate::types::TypeTag;
1549
1550 let entry_fn = payload_as_entry_function(
1551 InputEntryFunctionData::new("0x1::coin::transfer")
1552 .type_args_typed([TypeTag::U64, TypeTag::Bool])
1553 .build()
1554 .expect("build should succeed"),
1555 );
1556 assert_eq!(entry_fn.type_args, vec![TypeTag::U64, TypeTag::Bool]);
1557 }
1558
1559 #[test]
1560 fn test_input_entry_function_data_arg() {
1561 let entry_fn = payload_as_entry_function(
1562 InputEntryFunctionData::new("0x1::coin::transfer")
1563 .arg(42u64)
1564 .arg(true)
1565 .arg("hello".to_string())
1566 .build()
1567 .expect("build should succeed"),
1568 );
1569 assert_eq!(entry_fn.args.len(), 3, "all three args must be present");
1570 assert_eq!(entry_fn.args[0][0], 42);
1572 assert_eq!(entry_fn.args[1], vec![0x01]);
1574 }
1575
1576 #[test]
1577 fn test_input_entry_function_data_arg_raw() {
1578 let raw_bytes = vec![0x01, 0x02, 0x03];
1579 let entry_fn = payload_as_entry_function(
1580 InputEntryFunctionData::new("0x1::coin::transfer")
1581 .arg_raw(raw_bytes.clone())
1582 .build()
1583 .expect("build should succeed"),
1584 );
1585 assert_eq!(entry_fn.args.len(), 1);
1586 assert_eq!(entry_fn.args[0], raw_bytes);
1587 }
1588
1589 #[test]
1590 fn test_input_entry_function_data_args() {
1591 let entry_fn = payload_as_entry_function(
1592 InputEntryFunctionData::new("0x1::coin::transfer")
1593 .args([1u64, 2u64, 3u64])
1594 .build()
1595 .expect("build should succeed"),
1596 );
1597 assert_eq!(entry_fn.args.len(), 3);
1598 assert_eq!(entry_fn.args[0][0], 1);
1599 assert_eq!(entry_fn.args[1][0], 2);
1600 assert_eq!(entry_fn.args[2][0], 3);
1601 }
1602
1603 #[test]
1604 fn test_input_entry_function_data_transfer_apt() {
1605 use crate::types::AccountAddress;
1606
1607 let recipient = AccountAddress::from_hex("0x123").unwrap();
1608 let entry_fn = payload_as_entry_function(
1609 InputEntryFunctionData::transfer_apt(recipient, 1000)
1610 .expect("transfer_apt should succeed"),
1611 );
1612 assert_eq!(entry_fn.module.address, AccountAddress::ONE);
1613 assert_eq!(entry_fn.module.name.as_str(), "aptos_account");
1614 assert_eq!(entry_fn.function, "transfer");
1615 assert_eq!(entry_fn.args.len(), 2);
1616 }
1617
1618 #[test]
1619 fn test_input_entry_function_data_builder_debug() {
1620 let builder = InputEntryFunctionData::new("0x1::coin::transfer");
1621 let debug = format!("{builder:?}");
1622 assert!(debug.contains("InputEntryFunctionDataBuilder"));
1623 }
1624
1625 #[test]
1626 fn test_input_entry_function_data_builder_clone() {
1627 let builder = InputEntryFunctionData::new("0x1::coin::transfer").arg(42u64);
1628 let cloned = builder.clone();
1629 let original_built = payload_as_entry_function(builder.build().expect("original builds"));
1630 let cloned_built = payload_as_entry_function(cloned.build().expect("clone builds"));
1631 assert_eq!(original_built.module.name, cloned_built.module.name);
1633 assert_eq!(original_built.function, cloned_built.function);
1634 assert_eq!(original_built.args, cloned_built.args);
1635 }
1636
1637 #[test]
1638 fn test_move_u256_debug() {
1639 let val = MoveU256::from_u128(123_456_789);
1640 let debug = format!("{val:?}");
1641 assert!(debug.contains("MoveU256"));
1642 }
1643
1644 #[test]
1645 fn test_move_i128_debug() {
1646 let val = MoveI128::new(-42);
1647 let debug = format!("{val:?}");
1648 assert!(debug.contains("MoveI128"));
1649 }
1650
1651 #[test]
1652 fn test_move_i256_debug() {
1653 let val = MoveI256::from_i128(-42);
1654 let debug = format!("{val:?}");
1655 assert!(debug.contains("MoveI256"));
1656 }
1657
1658 #[test]
1659 fn test_move_u256_equality() {
1660 let val1 = MoveU256::from_u128(100);
1661 let val2 = MoveU256::from_u128(100);
1662 let val3 = MoveU256::from_u128(200);
1663 assert_eq!(val1, val2);
1664 assert_ne!(val1, val3);
1665 }
1666
1667 #[test]
1668 fn test_move_i256_equality() {
1669 let val1 = MoveI256::from_i128(-50);
1670 let val2 = MoveI256::from_i128(-50);
1671 let val3 = MoveI256::from_i128(50);
1672 assert_eq!(val1, val2);
1673 assert_ne!(val1, val3);
1674 }
1675
1676 #[test]
1677 fn test_move_u256_clone() {
1678 let val1 = MoveU256::from_u128(999);
1679 let val2 = val1;
1680 assert_eq!(val1, val2);
1681 }
1682
1683 #[test]
1684 fn test_move_i256_clone() {
1685 let val1 = MoveI256::from_i128(-999);
1686 let val2 = val1;
1687 assert_eq!(val1, val2);
1688 }
1689}