radix_clis/resim/
cmd_call_method.rs

1#![allow(unused_must_use)]
2
3use clap::Parser;
4use radix_common::prelude::*;
5
6use crate::resim::*;
7use crate::utils::*;
8
9/// Call a method
10#[derive(Parser, Debug)]
11pub struct CallMethod {
12    /// The component that the method belongs to
13    pub component_address: SimulatorComponentAddress,
14
15    /// The method name
16    pub method_name: String,
17
18    /// The call arguments, such as "5", "hello", "<resource_address>:<amount>" and "<resource_address>:<nf_local_id1>,<nf_local_id2>"
19    pub arguments: Vec<String>,
20
21    /// The proofs to add to the auth zone, in form of "<resource_address>:<amount>" or "<resource_address>:<nf_local_id1>,<nf_local_id2>"
22    #[clap(short, long, multiple = true)]
23    pub proofs: Option<Vec<String>>,
24
25    /// The network to use when outputting manifest, [simulator | adapanet | nebunet | mainnet]
26    #[clap(short, long)]
27    pub network: Option<String>,
28
29    /// Output a transaction manifest without execution
30    #[clap(short, long)]
31    pub manifest: Option<PathBuf>,
32
33    /// The private keys used for signing, separated by comma
34    #[clap(short, long)]
35    pub signing_keys: Option<String>,
36
37    /// Turn on tracing
38    #[clap(short, long)]
39    pub trace: bool,
40}
41
42impl CallMethod {
43    pub fn run<O: std::io::Write>(&self, out: &mut O) -> Result<(), String> {
44        let address_bech32_decoder = AddressBech32Decoder::for_simulator();
45
46        let default_account = get_default_account()?;
47        let proofs = self.proofs.clone().unwrap_or_default();
48
49        let mut builder = ManifestBuilder::new().lock_fee_from_faucet();
50        for resource_specifier in proofs {
51            builder = create_proof_from_account(
52                builder,
53                &address_bech32_decoder,
54                default_account,
55                resource_specifier,
56            )
57            .map_err(Error::FailedToBuildArguments)?
58        }
59
60        let manifest = self
61            .add_call_method_instruction_with_schema(
62                builder,
63                &address_bech32_decoder,
64                self.component_address.0,
65                self.method_name.clone(),
66                self.arguments.clone(),
67                Some(default_account),
68            )?
69            .try_deposit_entire_worktop_or_refund(default_account, None)
70            .build();
71        handle_manifest(
72            manifest.into(),
73            &self.signing_keys,
74            &self.network,
75            &self.manifest,
76            self.trace,
77            true,
78            out,
79        )
80        .map(|_| ())
81    }
82
83    /// Calls a method.
84    ///
85    /// The implementation will automatically prepare the arguments based on the
86    /// method SCHEMA, including resource buckets and proofs.
87    ///
88    /// If an Account component address is provided, resources will be withdrawn from the given account;
89    /// otherwise, they will be taken from transaction worktop.
90    pub fn add_call_method_instruction_with_schema(
91        &self,
92        builder: ManifestBuilder,
93        address_bech32_decoder: &AddressBech32Decoder,
94        component_address: ComponentAddress,
95        method_name: String,
96        args: Vec<String>,
97        account: Option<ComponentAddress>,
98    ) -> Result<ManifestBuilder, Error> {
99        let object_info = export_object_info(component_address)?;
100        let bp_info = object_info.blueprint_info;
101        let bp_id = bp_info.blueprint_id;
102        let bp_interface =
103            export_blueprint_interface(bp_id.package_address, &bp_id.blueprint_name)?;
104
105        let function_schema = bp_interface
106            .find_method(method_name.as_str())
107            .ok_or_else(|| {
108                Error::TransactionConstructionError(BuildCallInstructionError::MethodNotFound(
109                    method_name.clone(),
110                ))
111            })?;
112
113        let (schema, index) = match function_schema.input {
114            BlueprintPayloadDef::Static(ScopedTypeId(schema_hash, index)) => {
115                let schema = export_schema(bp_id.package_address.as_node_id(), schema_hash)?;
116                (schema, index)
117            }
118            BlueprintPayloadDef::Generic(generic_index) => {
119                let type_subst_ref = bp_info
120                    .generic_substitutions
121                    .get(generic_index as usize)
122                    .ok_or_else(|| Error::InstanceSchemaNot(component_address, generic_index))?;
123
124                match type_subst_ref {
125                    GenericSubstitution::Local(type_id) => {
126                        let schema = export_schema(bp_id.package_address.as_node_id(), type_id.0)?;
127                        (schema, type_id.1)
128                    }
129                    GenericSubstitution::Remote(_) => {
130                        return Err(Error::RemoteGenericSubstitutionNotSupported);
131                    }
132                }
133            }
134        };
135
136        let (builder, built_args) = build_call_arguments(
137            builder,
138            address_bech32_decoder,
139            &schema,
140            index,
141            args,
142            account,
143        )
144        .map_err(|e| {
145            Error::TransactionConstructionError(BuildCallInstructionError::FailedToBuildArguments(
146                e,
147            ))
148        })?;
149
150        Ok(builder.call_method_raw(component_address, method_name, built_args))
151    }
152}