radix_clis/utils/
common_instructions.rs

1//! This module implements a number of useful utility functions used to prepare instructions for
2//! calling methods and functions when a known SCHEMA is provided. This module implements all of the
3//! parsing logic as well as the logic needed ot add these instructions to the original manifest
4//! builder that is being used.
5
6use radix_common::prelude::*;
7use radix_engine_interface::prelude::*;
8use radix_transactions::data::{from_decimal, from_non_fungible_local_id, from_precise_decimal};
9use radix_transactions::prelude::*;
10
11use super::{parse_resource_specifier, ResourceSpecifier};
12
13/// Represents an error when building a transaction.
14#[derive(Debug, Clone)]
15pub enum BuildCallInstructionError {
16    /// The given blueprint function does not exist.
17    FunctionNotFound(String),
18
19    /// The given component method does not exist.
20    MethodNotFound(String),
21
22    /// The provided arguments do not match SCHEMA.
23    FailedToBuildArguments(BuildCallArgumentsError),
24
25    /// Failed to export the SCHEMA of a function.
26    FailedToExportFunctionSchema(PackageAddress, String, String),
27
28    /// Failed to export the SCHEMA of a method.
29    FailedToExportMethodSchema(ComponentAddress, String),
30
31    /// Account is required but not provided.
32    AccountNotProvided,
33}
34
35/// Represents an error when parsing arguments.
36#[derive(Debug, Clone)]
37pub enum BuildCallArgumentsError {
38    WrongNumberOfArguments(usize, usize),
39    BuildCallArgumentError(BuildCallArgumentError),
40    RustToManifestValueError(RustToManifestValueError),
41}
42
43/// Represents an error when parsing an argument.
44#[derive(Debug, Clone)]
45pub enum BuildCallArgumentError {
46    /// The argument is of unsupported type.
47    UnsupportedType(ScryptoTypeKind<LocalTypeId>),
48
49    /// Failure when parsing an argument.
50    FailedToParse(String),
51
52    /// Failed to interpret this string as a resource specifier
53    InvalidResourceSpecifier(String),
54}
55
56impl From<BuildCallArgumentsError> for BuildCallInstructionError {
57    fn from(error: BuildCallArgumentsError) -> Self {
58        Self::FailedToBuildArguments(error)
59    }
60}
61
62impl From<BuildCallArgumentError> for BuildCallArgumentsError {
63    fn from(error: BuildCallArgumentError) -> Self {
64        Self::BuildCallArgumentError(error)
65    }
66}
67
68impl From<RustToManifestValueError> for BuildCallArgumentsError {
69    fn from(error: RustToManifestValueError) -> Self {
70        Self::RustToManifestValueError(error)
71    }
72}
73
74/// Creates resource proof from an account.
75pub fn create_proof_from_account(
76    builder: ManifestBuilder,
77    address_bech32_decoder: &AddressBech32Decoder,
78    account: ComponentAddress,
79    resource_specifier: String,
80) -> Result<ManifestBuilder, BuildCallArgumentError> {
81    let resource_specifier = parse_resource_specifier(&resource_specifier, address_bech32_decoder)
82        .map_err(|_| BuildCallArgumentError::InvalidResourceSpecifier(resource_specifier))?;
83    let builder = match resource_specifier {
84        ResourceSpecifier::Amount(amount, resource_address) => {
85            builder.create_proof_from_account_of_amount(account, resource_address, amount)
86        }
87        ResourceSpecifier::Ids(non_fungible_local_ids, resource_address) => builder
88            .create_proof_from_account_of_non_fungibles(
89                account,
90                resource_address,
91                non_fungible_local_ids,
92            ),
93    };
94    Ok(builder)
95}
96
97pub fn build_call_arguments(
98    mut builder: ManifestBuilder,
99    address_bech32_decoder: &AddressBech32Decoder,
100    schema: &VersionedScryptoSchema,
101    type_id: LocalTypeId,
102    args: Vec<String>,
103    account: Option<ComponentAddress>,
104) -> Result<(ManifestBuilder, ManifestValue), BuildCallArgumentsError> {
105    let mut built_args = Vec::<ManifestValue>::new();
106    match schema.v1().resolve_type_kind(type_id) {
107        Some(TypeKind::Tuple { field_types }) => {
108            if args.len() != field_types.len() {
109                return Err(BuildCallArgumentsError::WrongNumberOfArguments(
110                    args.len(),
111                    field_types.len(),
112                ));
113            }
114
115            for (i, f) in field_types.iter().enumerate() {
116                let (returned_builder, value) = build_call_argument(
117                    builder,
118                    address_bech32_decoder,
119                    schema
120                        .v1()
121                        .resolve_type_kind(*f)
122                        .expect("Inconsistent schema"),
123                    schema
124                        .v1()
125                        .resolve_type_validation(*f)
126                        .expect("Inconsistent schema"),
127                    args[i].clone(),
128                    account,
129                )?;
130                builder = returned_builder;
131                built_args.push(value);
132            }
133        }
134        _ => panic!("Inconsistent schema"),
135    }
136    let manifest_value = to_manifest_value(&ManifestValue::Tuple { fields: built_args })?;
137    Ok((builder, manifest_value))
138}
139
140macro_rules! parse_basic_type {
141    ($builder:expr, $argument:expr, $type:tt) => {
142        Ok((
143            $builder,
144            ManifestValue::$type {
145                value: $argument
146                    .parse()
147                    .map_err(|_| BuildCallArgumentError::FailedToParse($argument))?,
148            },
149        ))
150    };
151}
152
153macro_rules! matches_bucket {
154    ($type_validation:expr) => {
155        matches!(
156            $type_validation,
157            TypeValidation::Custom(
158                ScryptoCustomTypeValidation::Own(OwnValidation::IsBucket)
159            )
160        )
161    };
162    ($type_validation:expr, $bucket_blueprint:expr) => {
163        matches!(
164            $type_validation,
165            TypeValidation::Custom(
166                ScryptoCustomTypeValidation::Own(
167                    OwnValidation::IsTypedObject(Some(RESOURCE_PACKAGE), blueprint)
168                )
169            ) if blueprint == $bucket_blueprint
170        )
171    };
172}
173
174fn build_call_argument(
175    mut builder: ManifestBuilder,
176    address_bech32_decoder: &AddressBech32Decoder,
177    type_kind: &ScryptoTypeKind<LocalTypeId>,
178    type_validation: &TypeValidation<ScryptoCustomTypeValidation>,
179    argument: String,
180    account: Option<ComponentAddress>,
181) -> Result<(ManifestBuilder, ManifestValue), BuildCallArgumentError> {
182    match type_kind {
183        ScryptoTypeKind::Bool => parse_basic_type!(builder, argument, Bool),
184        ScryptoTypeKind::I8 => parse_basic_type!(builder, argument, I8),
185        ScryptoTypeKind::I16 => parse_basic_type!(builder, argument, I16),
186        ScryptoTypeKind::I32 => parse_basic_type!(builder, argument, I32),
187        ScryptoTypeKind::I64 => parse_basic_type!(builder, argument, I64),
188        ScryptoTypeKind::I128 => parse_basic_type!(builder, argument, I128),
189        ScryptoTypeKind::U8 => parse_basic_type!(builder, argument, U8),
190        ScryptoTypeKind::U16 => parse_basic_type!(builder, argument, U16),
191        ScryptoTypeKind::U32 => parse_basic_type!(builder, argument, U32),
192        ScryptoTypeKind::U64 => parse_basic_type!(builder, argument, U64),
193        ScryptoTypeKind::U128 => parse_basic_type!(builder, argument, U128),
194        ScryptoTypeKind::String => Ok((builder, ManifestValue::String { value: argument })),
195        ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Decimal) => Ok((
196            builder,
197            ManifestValue::Custom {
198                value: ManifestCustomValue::Decimal(from_decimal(
199                    argument
200                        .parse()
201                        .map_err(|_| BuildCallArgumentError::FailedToParse(argument))?,
202                )),
203            },
204        )),
205        ScryptoTypeKind::Custom(ScryptoCustomTypeKind::PreciseDecimal) => Ok((
206            builder,
207            ManifestValue::Custom {
208                value: ManifestCustomValue::PreciseDecimal(from_precise_decimal(
209                    argument
210                        .parse()
211                        .map_err(|_| BuildCallArgumentError::FailedToParse(argument))?,
212                )),
213            },
214        )),
215        ScryptoTypeKind::Custom(ScryptoCustomTypeKind::NonFungibleLocalId) => Ok((
216            builder,
217            ManifestValue::Custom {
218                value: ManifestCustomValue::NonFungibleLocalId(from_non_fungible_local_id(
219                    argument
220                        .parse()
221                        .map_err(|_| BuildCallArgumentError::FailedToParse(argument))?,
222                )),
223            },
224        )),
225        ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Reference)
226            if matches!(
227                type_validation,
228                TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
229                    ReferenceValidation::IsGlobalPackage
230                ))
231            ) =>
232        {
233            let value = PackageAddress::try_from_bech32(address_bech32_decoder, &argument)
234                .ok_or(BuildCallArgumentError::FailedToParse(argument))?;
235            Ok((
236                builder,
237                ManifestValue::Custom {
238                    value: ManifestCustomValue::Address(value.into()),
239                },
240            ))
241        }
242        ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Reference)
243            if matches!(
244                type_validation,
245                TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
246                    ReferenceValidation::IsGlobalComponent
247                ))
248            ) =>
249        {
250            let value = ComponentAddress::try_from_bech32(address_bech32_decoder, &argument)
251                .ok_or(BuildCallArgumentError::FailedToParse(argument))?;
252            Ok((
253                builder,
254                ManifestValue::Custom {
255                    value: ManifestCustomValue::Address(value.into()),
256                },
257            ))
258        }
259        ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Reference)
260            if matches!(
261                type_validation,
262                TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
263                    ReferenceValidation::IsGlobalResourceManager
264                ))
265            ) =>
266        {
267            let value = ResourceAddress::try_from_bech32(address_bech32_decoder, &argument)
268                .ok_or(BuildCallArgumentError::FailedToParse(argument))?;
269            Ok((
270                builder,
271                ManifestValue::Custom {
272                    value: ManifestCustomValue::Address(value.into()),
273                },
274            ))
275        }
276        ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Reference)
277            if matches!(
278                type_validation,
279                TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
280                    ReferenceValidation::IsGlobal
281                ))
282            ) =>
283        {
284            let value = GlobalAddress::try_from_bech32(address_bech32_decoder, &argument)
285                .ok_or(BuildCallArgumentError::FailedToParse(argument))?;
286            Ok((
287                builder,
288                ManifestValue::Custom {
289                    value: ManifestCustomValue::Address(value.into()),
290                },
291            ))
292        }
293        ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Reference)
294            if matches!(
295                type_validation,
296                TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
297                    ReferenceValidation::IsGlobalTyped(_package_address, _bp_name)
298                ))
299            ) =>
300        {
301            let value = GlobalAddress::try_from_bech32(address_bech32_decoder, &argument)
302                .ok_or(BuildCallArgumentError::FailedToParse(argument))?;
303            Ok((
304                builder,
305                ManifestValue::Custom {
306                    value: ManifestCustomValue::Address(value.into()),
307                },
308            ))
309        }
310        ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Own)
311            if matches_bucket!(type_validation)
312                || matches_bucket!(type_validation, FUNGIBLE_BUCKET_BLUEPRINT)
313                || matches_bucket!(type_validation, NON_FUNGIBLE_BUCKET_BLUEPRINT) =>
314        {
315            let resource_specifier = parse_resource_specifier(&argument, address_bech32_decoder)
316                .map_err(|_| BuildCallArgumentError::FailedToParse(argument))?;
317
318            let bucket_name = builder.generate_bucket_name("taken");
319            let builder = match resource_specifier {
320                ResourceSpecifier::Amount(amount, resource_address) => {
321                    if let Some(account) = account {
322                        builder = builder.withdraw_from_account(account, resource_address, amount);
323                    }
324                    builder.take_from_worktop(resource_address, amount, &bucket_name)
325                }
326                ResourceSpecifier::Ids(ids, resource_address) => {
327                    if let Some(account) = account {
328                        builder = builder.withdraw_non_fungibles_from_account(
329                            account,
330                            resource_address,
331                            ids.clone(),
332                        );
333                    }
334                    builder.take_non_fungibles_from_worktop(resource_address, ids, &bucket_name)
335                }
336            };
337            let bucket = builder.bucket(bucket_name);
338            Ok((
339                builder,
340                ManifestValue::Custom {
341                    value: ManifestCustomValue::Bucket(bucket),
342                },
343            ))
344        }
345        ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Own)
346            if matches!(
347                type_validation,
348                TypeValidation::Custom(ScryptoCustomTypeValidation::Own(OwnValidation::IsProof))
349            ) =>
350        {
351            let resource_specifier = parse_resource_specifier(&argument, address_bech32_decoder)
352                .map_err(|_| BuildCallArgumentError::FailedToParse(argument))?;
353            let proof_name = builder.generate_proof_name("proof");
354            let builder = match resource_specifier {
355                ResourceSpecifier::Amount(amount, resource_address) => {
356                    if let Some(account) = account {
357                        builder
358                            .create_proof_from_account_of_amount(account, resource_address, amount)
359                            .pop_from_auth_zone(&proof_name)
360                    } else {
361                        todo!("Take from worktop and create proof")
362                    }
363                }
364                ResourceSpecifier::Ids(ids, resource_address) => {
365                    if let Some(account) = account {
366                        builder
367                            .create_proof_from_account_of_non_fungibles(
368                                account,
369                                resource_address,
370                                ids,
371                            )
372                            .pop_from_auth_zone(&proof_name)
373                    } else {
374                        todo!("Take from worktop and create proof")
375                    }
376                }
377            };
378            let proof = builder.proof(proof_name);
379            Ok((
380                builder,
381                ManifestValue::Custom {
382                    value: ManifestCustomValue::Proof(proof),
383                },
384            ))
385        }
386        _ => Err(BuildCallArgumentError::UnsupportedType(type_kind.clone())),
387    }
388}
389
390#[cfg(test)]
391mod test {
392    use super::*;
393    use radix_engine_interface::blueprints::identity::IDENTITY_BLUEPRINT;
394    use radix_transactions::manifest::*;
395    use radix_transactions::model::InstructionV1;
396
397    #[test]
398    pub fn parsing_of_u8_succeeds() {
399        // Arrange
400        let arg = "12";
401        let type_kind = ScryptoTypeKind::U8;
402
403        // Act
404        let parsed_arg: u8 = build_and_decode_arg(arg, type_kind, TypeValidation::None)
405            .expect("Failed to parse arg");
406
407        // Assert
408        assert_eq!(parsed_arg, 12u8)
409    }
410
411    #[test]
412    pub fn parsing_of_u16_succeeds() {
413        // Arrange
414        let arg = "12";
415        let type_kind = ScryptoTypeKind::U16;
416
417        // Act
418        let parsed_arg: u16 = build_and_decode_arg(arg, type_kind, TypeValidation::None)
419            .expect("Failed to parse arg");
420
421        // Assert
422        assert_eq!(parsed_arg, 12u16)
423    }
424
425    #[test]
426    pub fn parsing_of_u32_succeeds() {
427        // Arrange
428        let arg = "12";
429        let type_kind = ScryptoTypeKind::U32;
430
431        // Act
432        let parsed_arg: u32 = build_and_decode_arg(arg, type_kind, TypeValidation::None)
433            .expect("Failed to parse arg");
434
435        // Assert
436        assert_eq!(parsed_arg, 12u32)
437    }
438
439    #[test]
440    pub fn parsing_of_u64_succeeds() {
441        // Arrange
442        let arg = "12";
443        let type_kind = ScryptoTypeKind::U64;
444
445        // Act
446        let parsed_arg: u64 = build_and_decode_arg(arg, type_kind, TypeValidation::None)
447            .expect("Failed to parse arg");
448
449        // Assert
450        assert_eq!(parsed_arg, 12u64)
451    }
452
453    #[test]
454    pub fn parsing_of_u128_succeeds() {
455        // Arrange
456        let arg = "12";
457        let type_kind = ScryptoTypeKind::U128;
458
459        // Act
460        let parsed_arg: u128 = build_and_decode_arg(arg, type_kind, TypeValidation::None)
461            .expect("Failed to parse arg");
462
463        // Assert
464        assert_eq!(parsed_arg, 12u128)
465    }
466
467    #[test]
468    pub fn parsing_of_i8_succeeds() {
469        // Arrange
470        let arg = "12";
471        let type_kind = ScryptoTypeKind::I8;
472
473        // Act
474        let parsed_arg: i8 = build_and_decode_arg(arg, type_kind, TypeValidation::None)
475            .expect("Failed to parse arg");
476
477        // Assert
478        assert_eq!(parsed_arg, 12i8)
479    }
480
481    #[test]
482    pub fn parsing_of_i16_succeeds() {
483        // Arrange
484        let arg = "12";
485        let type_kind = ScryptoTypeKind::I16;
486
487        // Act
488        let parsed_arg: i16 = build_and_decode_arg(arg, type_kind, TypeValidation::None)
489            .expect("Failed to parse arg");
490
491        // Assert
492        assert_eq!(parsed_arg, 12i16)
493    }
494
495    #[test]
496    pub fn parsing_of_i32_succeeds() {
497        // Arrange
498        let arg = "12";
499        let type_kind = ScryptoTypeKind::I32;
500
501        // Act
502        let parsed_arg: i32 = build_and_decode_arg(arg, type_kind, TypeValidation::None)
503            .expect("Failed to parse arg");
504
505        // Assert
506        assert_eq!(parsed_arg, 12i32)
507    }
508
509    #[test]
510    pub fn parsing_of_i64_succeeds() {
511        // Arrange
512        let arg = "12";
513        let type_kind = ScryptoTypeKind::I64;
514
515        // Act
516        let parsed_arg: i64 = build_and_decode_arg(arg, type_kind, TypeValidation::None)
517            .expect("Failed to parse arg");
518
519        // Assert
520        assert_eq!(parsed_arg, 12i64)
521    }
522
523    #[test]
524    pub fn parsing_of_i128_succeeds() {
525        // Arrange
526        let arg = "12";
527        let type_kind = ScryptoTypeKind::I128;
528
529        // Act
530        let parsed_arg: i128 = build_and_decode_arg(arg, type_kind, TypeValidation::None)
531            .expect("Failed to parse arg");
532
533        // Assert
534        assert_eq!(parsed_arg, 12i128)
535    }
536
537    #[test]
538    pub fn parsing_of_decimal_succeeds() {
539        // Arrange
540        let arg = "12";
541        let type_kind = ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Decimal);
542
543        // Act
544        let parsed_arg: Decimal = build_and_decode_arg(arg, type_kind, TypeValidation::None)
545            .expect("Failed to parse arg");
546
547        // Assert
548        assert_eq!(parsed_arg, Decimal::from_str("12").unwrap())
549    }
550
551    #[test]
552    pub fn parsing_of_component_address_succeeds() {
553        // Arrange
554        let component_address = component_address(EntityType::GlobalAccount, 5);
555
556        let arg = AddressBech32Encoder::for_simulator()
557            .encode(component_address.as_ref())
558            .unwrap();
559        let type_kind = ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Reference);
560        let type_validation = TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
561            ReferenceValidation::IsGlobalComponent,
562        ));
563
564        // Act
565        let parsed_arg: ComponentAddress =
566            build_and_decode_arg(arg, type_kind, type_validation).expect("Failed to parse arg");
567
568        // Assert
569        assert_eq!(parsed_arg, component_address)
570    }
571
572    #[test]
573    pub fn parsing_of_global_address_succeeds() {
574        // Arrange
575        let component_address = component_address(EntityType::GlobalAccount, 5);
576        let global_address: GlobalAddress = component_address.into();
577
578        let arg = AddressBech32Encoder::for_simulator()
579            .encode(global_address.as_ref())
580            .unwrap();
581        let type_kind = ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Reference);
582        let type_validation = TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
583            ReferenceValidation::IsGlobal,
584        ));
585
586        // Act
587        let parsed_arg: GlobalAddress =
588            build_and_decode_arg(arg, type_kind, type_validation).expect("Failed to parse arg");
589
590        // Assert
591        assert_eq!(parsed_arg, global_address)
592    }
593
594    #[test]
595    pub fn parsing_of_global_address_typed_succeeds() {
596        // Arrange
597        let package_address = package_address(EntityType::GlobalPackage, 5);
598        let global_address: GlobalAddress = package_address.into();
599
600        let arg = AddressBech32Encoder::for_simulator()
601            .encode(global_address.as_ref())
602            .unwrap();
603
604        let type_kind = ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Reference);
605        let type_validation = TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
606            ReferenceValidation::IsGlobalTyped(
607                Some(IDENTITY_PACKAGE),
608                IDENTITY_BLUEPRINT.to_string(),
609            ),
610        ));
611
612        // Act
613        let parsed_arg: GlobalAddress =
614            build_and_decode_arg(arg, type_kind, type_validation).expect("Failed to parse arg");
615
616        // Assert
617        assert_eq!(parsed_arg, global_address)
618    }
619
620    #[test]
621    pub fn parsing_of_package_address_succeeds() {
622        // Arrange
623        let package_address = package_address(EntityType::GlobalPackage, 5);
624
625        let arg = AddressBech32Encoder::for_simulator()
626            .encode(package_address.as_ref())
627            .unwrap();
628        let type_kind = ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Reference);
629        let type_validation = TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
630            ReferenceValidation::IsGlobalPackage,
631        ));
632
633        // Act
634        let parsed_arg: PackageAddress =
635            build_and_decode_arg(arg, type_kind, type_validation).expect("Failed to parse arg");
636
637        // Assert
638        assert_eq!(parsed_arg, package_address)
639    }
640
641    #[test]
642    pub fn parsing_of_resource_address_succeeds() {
643        // Arrange
644        let resource_address = resource_address(EntityType::GlobalFungibleResourceManager, 5);
645
646        let arg = AddressBech32Encoder::for_simulator()
647            .encode(resource_address.as_ref())
648            .unwrap();
649        let type_kind = ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Reference);
650        let type_validation = TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
651            ReferenceValidation::IsGlobalResourceManager,
652        ));
653
654        // Act
655        let parsed_arg: ResourceAddress =
656            build_and_decode_arg(arg, type_kind, type_validation).expect("Failed to parse arg");
657
658        // Assert
659        assert_eq!(parsed_arg, resource_address)
660    }
661
662    #[test]
663    pub fn parsing_of_precise_decimal_succeeds() {
664        // Arrange
665        let arg = "12";
666        let type_kind = ScryptoTypeKind::Custom(ScryptoCustomTypeKind::PreciseDecimal);
667
668        // Act
669        let parsed_arg: PreciseDecimal = build_and_decode_arg(arg, type_kind, TypeValidation::None)
670            .expect("Failed to parse arg");
671
672        // Assert
673        assert_eq!(parsed_arg, PreciseDecimal::from_str("12").unwrap())
674    }
675
676    #[test]
677    pub fn parsing_of_string_non_fungible_local_id_succeeds() {
678        // Arrange
679        let arg = "<HelloWorld>";
680        let type_kind = ScryptoTypeKind::Custom(ScryptoCustomTypeKind::NonFungibleLocalId);
681
682        // Act
683        let parsed_arg: NonFungibleLocalId =
684            build_and_decode_arg(arg, type_kind, TypeValidation::None)
685                .expect("Failed to parse arg");
686
687        // Assert
688        assert_eq!(
689            parsed_arg,
690            NonFungibleLocalId::string("HelloWorld").unwrap()
691        )
692    }
693
694    #[test]
695    pub fn parsing_of_bytes_non_fungible_local_id_succeeds() {
696        // Arrange
697        let arg = "[c41fa9ef2ab31f5db2614c1c4c626e9c279349b240af7cb939ead29058fdff2c]";
698        let type_kind = ScryptoTypeKind::Custom(ScryptoCustomTypeKind::NonFungibleLocalId);
699
700        // Act
701        let parsed_arg: NonFungibleLocalId =
702            build_and_decode_arg(arg, type_kind, TypeValidation::None)
703                .expect("Failed to parse arg");
704
705        // Assert
706        assert_eq!(
707            parsed_arg,
708            NonFungibleLocalId::bytes(vec![
709                196, 31, 169, 239, 42, 179, 31, 93, 178, 97, 76, 28, 76, 98, 110, 156, 39, 147, 73,
710                178, 64, 175, 124, 185, 57, 234, 210, 144, 88, 253, 255, 44
711            ])
712            .unwrap()
713        )
714    }
715
716    #[test]
717    pub fn parsing_of_u64_non_fungible_local_id_succeeds() {
718        // Arrange
719        let arg = "#12#";
720        let type_kind = ScryptoTypeKind::Custom(ScryptoCustomTypeKind::NonFungibleLocalId);
721
722        // Act
723        let parsed_arg: NonFungibleLocalId =
724            build_and_decode_arg(arg, type_kind, TypeValidation::None)
725                .expect("Failed to parse arg");
726
727        // Assert
728        assert_eq!(parsed_arg, NonFungibleLocalId::integer(12))
729    }
730
731    #[test]
732    pub fn parsing_of_ruid_non_fungible_local_id_succeeds() {
733        // Arrange
734        let arg = "{1111111111111111-2222222222222222-3333333333333333-4444444444444444}";
735        let type_kind = ScryptoTypeKind::Custom(ScryptoCustomTypeKind::NonFungibleLocalId);
736
737        // Act
738        let parsed_arg: NonFungibleLocalId =
739            build_and_decode_arg(arg, type_kind, TypeValidation::None)
740                .expect("Failed to parse arg");
741
742        // Assert
743        assert_eq!(
744            parsed_arg,
745            NonFungibleLocalId::ruid([
746                0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
747                0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, 0x44, 0x44,
748                0x44, 0x44, 0x44, 0x44,
749            ])
750        )
751    }
752
753    #[test]
754    pub fn parsing_of_fungible_bucket_succeeds() {
755        let amount = 2000;
756        let arg = format!(
757            "{}:{}",
758            AddressBech32Encoder::for_simulator()
759                .encode(XRD.as_ref())
760                .unwrap(),
761            amount
762        );
763
764        let type_kind = ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Own);
765        let type_validations = vec![
766            TypeValidation::Custom(ScryptoCustomTypeValidation::Own(OwnValidation::IsBucket)),
767            TypeValidation::Custom(ScryptoCustomTypeValidation::Own(
768                OwnValidation::IsTypedObject(
769                    Some(RESOURCE_PACKAGE),
770                    FUNGIBLE_BUCKET_BLUEPRINT.to_string(),
771                ),
772            )),
773        ];
774
775        for type_validation in type_validations {
776            // Act
777            let (builder, parsed_arg): (ManifestBuilder, ManifestBucket) =
778                build_and_decode(&arg, type_kind.clone(), type_validation)
779                    .expect("Failed to parse arg");
780            let instructions = builder.build_no_validate().instructions;
781
782            // Assert
783            assert_eq!(
784                instructions.first().unwrap(),
785                &InstructionV1::TakeFromWorktop(TakeFromWorktop {
786                    resource_address: XRD,
787                    amount: amount.into()
788                })
789            );
790            assert_eq!(parsed_arg, ManifestBucket(0u32));
791        }
792    }
793
794    #[test]
795    pub fn parsing_of_non_fungible_bucket_succeeds() {
796        // Arrange
797        let local_ids: [u64; 3] = [12, 600, 123];
798        let resource_address = resource_address(EntityType::GlobalNonFungibleResourceManager, 5);
799
800        let arg = format!(
801            "{}:#{}#,#{}#,#{}#",
802            AddressBech32Encoder::for_simulator()
803                .encode(resource_address.as_ref())
804                .unwrap(),
805            local_ids[0],
806            local_ids[1],
807            local_ids[2]
808        );
809
810        let type_kind = ScryptoTypeKind::Custom(ScryptoCustomTypeKind::Own);
811        let type_validations = vec![
812            TypeValidation::Custom(ScryptoCustomTypeValidation::Own(OwnValidation::IsBucket)),
813            TypeValidation::Custom(ScryptoCustomTypeValidation::Own(
814                OwnValidation::IsTypedObject(
815                    Some(RESOURCE_PACKAGE),
816                    NON_FUNGIBLE_BUCKET_BLUEPRINT.to_string(),
817                ),
818            )),
819        ];
820
821        for type_validation in type_validations {
822            // Act
823            let (builder, parsed_arg): (ManifestBuilder, ManifestBucket) =
824                build_and_decode(&arg, type_kind.clone(), type_validation)
825                    .expect("Failed to parse arg");
826            let instructions = builder.build_no_validate().instructions;
827            let ids = local_ids
828                .map(|id| NonFungibleLocalId::Integer(IntegerNonFungibleLocalId::new(id)))
829                .to_vec();
830
831            // Assert
832            assert_eq!(
833                instructions.first().unwrap(),
834                &InstructionV1::TakeNonFungiblesFromWorktop(TakeNonFungiblesFromWorktop {
835                    resource_address,
836                    ids
837                })
838            );
839            assert_eq!(parsed_arg, ManifestBucket(0u32));
840        }
841    }
842
843    pub fn build_and_decode<S: AsRef<str>, T: ManifestDecode>(
844        arg: S,
845        type_kind: ScryptoTypeKind<LocalTypeId>,
846        type_validation: TypeValidation<ScryptoCustomTypeValidation>,
847    ) -> Result<(ManifestBuilder, T), BuildAndDecodeArgError> {
848        let builder = ManifestBuilder::new();
849        let (builder, built_arg) = build_call_argument(
850            builder,
851            &AddressBech32Decoder::for_simulator(),
852            &type_kind,
853            &type_validation,
854            arg.as_ref().to_owned(),
855            None,
856        )
857        .map_err(BuildAndDecodeArgError::BuildCallArgumentError)?;
858
859        let bytes = manifest_encode(&built_arg).map_err(BuildAndDecodeArgError::EncodeError)?;
860
861        Ok((
862            builder,
863            manifest_decode(&bytes).map_err(BuildAndDecodeArgError::DecodeError)?,
864        ))
865    }
866
867    pub fn build_and_decode_arg<S: AsRef<str>, T: ManifestDecode>(
868        arg: S,
869        type_kind: ScryptoTypeKind<LocalTypeId>,
870        type_validation: TypeValidation<ScryptoCustomTypeValidation>,
871    ) -> Result<T, BuildAndDecodeArgError> {
872        build_and_decode(arg, type_kind, type_validation).map(|(_, arg)| arg)
873    }
874
875    #[allow(dead_code)] // As it's used for displaying the error messages
876    #[derive(Debug, Clone)]
877    #[allow(clippy::enum_variant_names)]
878    pub enum BuildAndDecodeArgError {
879        BuildCallArgumentError(BuildCallArgumentError),
880        EncodeError(EncodeError),
881        DecodeError(DecodeError),
882    }
883}