1use crate::types::{MoveModuleId, TypeTag};
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
14pub enum TransactionPayload {
15 Script(Script),
17 #[doc(hidden)]
20 ModuleBundle(DeprecatedModuleBundle),
21 EntryFunction(EntryFunction),
23 Multisig(Multisig),
25}
26
27#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
30pub struct DeprecatedModuleBundle {
31 #[doc(hidden)]
32 _private: (),
33}
34
35#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
37pub struct Multisig {
38 pub multisig_address: crate::types::AccountAddress,
40 pub transaction_payload: Option<MultisigTransactionPayload>,
42}
43
44#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
46pub enum MultisigTransactionPayload {
47 EntryFunction(EntryFunction),
49}
50
51#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
53pub struct Script {
54 #[serde(with = "serde_bytes")]
56 pub code: Vec<u8>,
57 pub type_args: Vec<TypeTag>,
59 pub args: Vec<ScriptArgument>,
61}
62
63impl Script {
64 pub fn new(code: Vec<u8>, type_args: Vec<TypeTag>, args: Vec<ScriptArgument>) -> Self {
66 Self {
67 code,
68 type_args,
69 args,
70 }
71 }
72}
73
74#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
79pub enum ScriptArgument {
80 U8(u8),
82 U64(u64),
84 U128(u128),
86 Address(crate::types::AccountAddress),
88 U8Vector(#[serde(with = "serde_bytes")] Vec<u8>),
90 Bool(bool),
92 U16(u16),
94 U32(u32),
96 U256([u8; 32]),
98 Serialized(#[serde(with = "serde_bytes")] Vec<u8>),
100 I8(i8),
102 I16(i16),
104 I32(i32),
106 I64(i64),
108 I128(i128),
110 I256([u8; 32]),
112}
113
114#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
139pub struct EntryFunction {
140 pub module: MoveModuleId,
142 pub function: String,
144 pub type_args: Vec<TypeTag>,
146 pub args: Vec<Vec<u8>>,
148}
149
150impl EntryFunction {
151 pub fn new(
153 module: MoveModuleId,
154 function: impl Into<String>,
155 type_args: Vec<TypeTag>,
156 args: Vec<Vec<u8>>,
157 ) -> Self {
158 Self {
159 module,
160 function: function.into(),
161 type_args,
162 args,
163 }
164 }
165
166 pub fn from_function_id(
178 function_id: &str,
179 type_args: Vec<TypeTag>,
180 args: Vec<Vec<u8>>,
181 ) -> crate::error::AptosResult<Self> {
182 let func_id = crate::types::EntryFunctionId::from_str_strict(function_id)?;
183 Ok(Self {
184 module: func_id.module,
185 function: func_id.name.as_str().to_string(),
186 type_args,
187 args,
188 })
189 }
190
191 pub fn apt_transfer(
202 recipient: crate::types::AccountAddress,
203 amount: u64,
204 ) -> crate::error::AptosResult<Self> {
205 let module = MoveModuleId::from_str_strict("0x1::aptos_account")?;
206 Ok(Self {
207 module,
208 function: "transfer".to_string(),
209 type_args: vec![],
210 args: vec![
211 aptos_bcs::to_bytes(&recipient).map_err(crate::error::AptosError::bcs)?,
212 aptos_bcs::to_bytes(&amount).map_err(crate::error::AptosError::bcs)?,
213 ],
214 })
215 }
216
217 pub fn coin_transfer(
229 coin_type: TypeTag,
230 recipient: crate::types::AccountAddress,
231 amount: u64,
232 ) -> crate::error::AptosResult<Self> {
233 let module = MoveModuleId::from_str_strict("0x1::coin")?;
234 Ok(Self {
235 module,
236 function: "transfer".to_string(),
237 type_args: vec![coin_type],
238 args: vec![
239 aptos_bcs::to_bytes(&recipient).map_err(crate::error::AptosError::bcs)?,
240 aptos_bcs::to_bytes(&amount).map_err(crate::error::AptosError::bcs)?,
241 ],
242 })
243 }
244}
245
246impl From<EntryFunction> for TransactionPayload {
247 fn from(entry_function: EntryFunction) -> Self {
248 TransactionPayload::EntryFunction(entry_function)
249 }
250}
251
252impl From<Script> for TransactionPayload {
253 fn from(script: Script) -> Self {
254 TransactionPayload::Script(script)
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261 use crate::types::AccountAddress;
262
263 #[test]
264 fn test_apt_transfer() {
265 let recipient = AccountAddress::from_hex("0x123").unwrap();
266 let entry_fn = EntryFunction::apt_transfer(recipient, 1000).unwrap();
267
268 assert_eq!(entry_fn.function, "transfer");
269 assert!(entry_fn.type_args.is_empty());
270 assert_eq!(entry_fn.args.len(), 2);
271 }
272
273 #[test]
274 fn test_from_function_id() {
275 let entry_fn = EntryFunction::from_function_id(
276 "0x1::coin::transfer",
277 vec![TypeTag::aptos_coin()],
278 vec![],
279 )
280 .unwrap();
281
282 assert_eq!(entry_fn.module.address, AccountAddress::ONE);
283 assert_eq!(entry_fn.module.name.as_str(), "coin");
284 assert_eq!(entry_fn.function, "transfer");
285 }
286
287 #[test]
288 fn test_entry_function_new() {
289 let module = MoveModuleId::from_str_strict("0x1::test_module").unwrap();
290 let entry_fn = EntryFunction::new(
291 module.clone(),
292 "test_function",
293 vec![TypeTag::U64],
294 vec![vec![1, 2, 3]],
295 );
296
297 assert_eq!(entry_fn.module, module);
298 assert_eq!(entry_fn.function, "test_function");
299 assert_eq!(entry_fn.type_args.len(), 1);
300 assert_eq!(entry_fn.args.len(), 1);
301 }
302
303 #[test]
304 fn test_coin_transfer() {
305 let recipient = AccountAddress::from_hex("0x456").unwrap();
306 let coin_type = TypeTag::aptos_coin();
307 let entry_fn = EntryFunction::coin_transfer(coin_type, recipient, 5000).unwrap();
308
309 assert_eq!(entry_fn.module.address, AccountAddress::ONE);
310 assert_eq!(entry_fn.module.name.as_str(), "coin");
311 assert_eq!(entry_fn.function, "transfer");
312 assert_eq!(entry_fn.type_args.len(), 1);
313 assert_eq!(entry_fn.args.len(), 2);
314 }
315
316 #[test]
317 fn test_script_new() {
318 let code = vec![0x01, 0x02, 0x03];
319 let type_args = vec![TypeTag::U64];
320 let args = vec![ScriptArgument::U64(100)];
321 let script = Script::new(code.clone(), type_args.clone(), args);
322
323 assert_eq!(script.code, code);
324 assert_eq!(script.type_args.len(), 1);
325 assert_eq!(script.args.len(), 1);
326 }
327
328 #[test]
329 fn test_script_argument_variants() {
330 let u8_arg = ScriptArgument::U8(255);
331 let u64_arg = ScriptArgument::U64(18_446_744_073_709_551_615);
332 let u128_arg = ScriptArgument::U128(340_282_366_920_938_463_463_374_607_431_768_211_455);
333 let addr_arg = ScriptArgument::Address(AccountAddress::ONE);
334 let bytes_arg = ScriptArgument::U8Vector(vec![1, 2, 3]);
335 let bool_arg = ScriptArgument::Bool(true);
336 let u16_arg = ScriptArgument::U16(65535);
337 let u32_arg = ScriptArgument::U32(4_294_967_295);
338 let u256_arg = ScriptArgument::U256([0xff; 32]);
339 let serialized_arg = ScriptArgument::Serialized(vec![1, 2, 3]);
340 let i64_arg = ScriptArgument::I64(-1i64);
341
342 let args = vec![
344 u8_arg.clone(),
345 u64_arg.clone(),
346 u128_arg.clone(),
347 addr_arg.clone(),
348 bytes_arg.clone(),
349 bool_arg.clone(),
350 u16_arg.clone(),
351 u32_arg.clone(),
352 u256_arg.clone(),
353 serialized_arg.clone(),
354 i64_arg.clone(),
355 ];
356
357 for arg in args {
358 let serialized = aptos_bcs::to_bytes(&arg).unwrap();
359 let deserialized: ScriptArgument = aptos_bcs::from_bytes(&serialized).unwrap();
360 assert_eq!(deserialized, arg);
361 }
362 }
363
364 #[test]
365 fn test_transaction_payload_from_entry_function() {
366 let entry_fn = EntryFunction::apt_transfer(AccountAddress::ONE, 100).unwrap();
367 let payload: TransactionPayload = entry_fn.into();
368
369 match payload {
370 TransactionPayload::EntryFunction(ef) => {
371 assert_eq!(ef.function, "transfer");
372 }
373 _ => panic!("Expected EntryFunction variant"),
374 }
375 }
376
377 #[test]
378 fn test_transaction_payload_from_script() {
379 let script = Script::new(vec![0x01], vec![], vec![]);
380 let payload: TransactionPayload = script.into();
381
382 match payload {
383 TransactionPayload::Script(s) => {
384 assert_eq!(s.code, vec![0x01]);
385 }
386 _ => panic!("Expected Script variant"),
387 }
388 }
389
390 #[test]
391 fn test_multisig_payload() {
392 let entry_fn = EntryFunction::apt_transfer(AccountAddress::ONE, 100).unwrap();
393 let multisig = Multisig {
394 multisig_address: AccountAddress::from_hex("0x999").unwrap(),
395 transaction_payload: Some(MultisigTransactionPayload::EntryFunction(entry_fn)),
396 };
397
398 let payload = TransactionPayload::Multisig(multisig.clone());
399 match payload {
400 TransactionPayload::Multisig(m) => {
401 assert_eq!(m.multisig_address, multisig.multisig_address);
402 assert!(m.transaction_payload.is_some());
403 }
404 _ => panic!("Expected Multisig variant"),
405 }
406 }
407
408 #[test]
409 fn test_entry_function_bcs_serialization() {
410 let entry_fn = EntryFunction::apt_transfer(AccountAddress::ONE, 1000).unwrap();
411 let serialized = aptos_bcs::to_bytes(&entry_fn).unwrap();
412 let deserialized: EntryFunction = aptos_bcs::from_bytes(&serialized).unwrap();
413
414 assert_eq!(entry_fn.module, deserialized.module);
415 assert_eq!(entry_fn.function, deserialized.function);
416 assert_eq!(entry_fn.type_args, deserialized.type_args);
417 assert_eq!(entry_fn.args, deserialized.args);
418 }
419
420 #[test]
421 fn test_transaction_payload_bcs_serialization() {
422 let entry_fn = EntryFunction::apt_transfer(AccountAddress::ONE, 1000).unwrap();
423 let payload = TransactionPayload::EntryFunction(entry_fn);
424
425 let serialized = aptos_bcs::to_bytes(&payload).unwrap();
426 let deserialized: TransactionPayload = aptos_bcs::from_bytes(&serialized).unwrap();
427
428 assert_eq!(payload, deserialized);
429 }
430
431 #[test]
432 fn test_from_function_id_invalid() {
433 let result = EntryFunction::from_function_id("0x1coin::transfer", vec![], vec![]);
435 assert!(result.is_err());
436
437 let result = EntryFunction::from_function_id("invalid::module::function", vec![], vec![]);
439 assert!(result.is_err());
440 }
441}