How can I pack different types (like Principal, Text, Nat, Blob, Vec and so on) to blob for wallet_call's args

In wallet_call, the args is blob. How can I pack different types (like Principal, Text, Nat, Blob, Vec and so on) to blob. Is there any words about the pack rule(such as abi.encode in etherum)?

  // Call Forwarding
  wallet_call: (record {
    canister: principal;
    method_name: text;
    args: blob;
    cycles: nat64;
  }) -> (WalletResultCall);

in dfinity/sdk/src/dfx/util/mod.rs, blob_from_arguments() is provided to encoded the parameters to blob, any one can help to write case for funciton multiple_sha256(text, text) → text.


#[context("Failed to create argument blob.")]
pub fn blob_from_arguments(
    dfx_env: Option<&dyn Environment>,
    arguments: Option<&str>,
    random: Option<&str>,
    arg_type: Option<&str>,
    method_type: &Option<(TypeEnv, Function)>,
    is_init_arg: bool,
    always_assist: bool,
) -> DfxResult<Vec<u8>> {
    let arg_type = arg_type.unwrap_or("idl");
    match arg_type {
        "raw" => {
            let bytes = hex::decode(arguments.unwrap_or("")).map_err(|e| {
                error_invalid_argument!("Argument is not a valid hex string: {}", e)
            })?;
            Ok(bytes)
        }
        "idl" => {
            let typed_args = match method_type {
                None => {
                    let arguments = arguments.unwrap_or("()");
                    pretty_wrap("Candid argument", arguments, candid_parser::parse_idl_args)
                        .map_err(|e| error_invalid_argument!("Invalid Candid values: {}", e))?
                        .to_bytes()
                }
                Some((env, func)) => {
                    let is_terminal =
                        stdin().is_terminal() && stdout().is_terminal() && stderr().is_terminal();
                    if let Some(arguments) = arguments {
                        fuzzy_parse_argument(arguments, env, &func.args)
                    } else if func.args.is_empty() {
                        use candid::Encode;
                        Encode!()
                    } else if func
                        .args
                        .iter()
                        .all(|t| matches!(t.as_ref(), TypeInner::Opt(_)))
                        && !always_assist
                    {
                        // If the user provided no arguments, and if all the expected arguments are
                        // optional, then use null values.
                        let nulls = vec![IDLValue::Null; func.args.len()];
                        let args = IDLArgs::new(&nulls);
                        args.to_bytes_with_types(env, &func.args)
                    } else if let Some(random) = random {
                        let random = if random.is_empty() {
                            eprintln!("Random schema is empty, using any random value instead.");
                            "{=}"
                        } else {
                            random
                        };
                        use rand::Rng;
                        let mut rng = rand::thread_rng();
                        let seed: Vec<u8> = (0..2048).map(|_| rng.gen::<u8>()).collect();
                        let config = candid_parser::configs::Configs::from_dhall(random)
                            .context("Failed to create candid parser config.")?;
                        let args = candid_parser::random::any(&seed, &config, env, &func.args)
                            .context("Failed to create idl args.")?;
                        eprintln!("Sending the following random argument:\n{}\n", args);
                        args.to_bytes_with_types(env, &func.args)
                    } else if is_terminal {
                        use candid_parser::assist::{input_args, Context};
                        let mut ctx = Context::new(env.clone());
                        if let Some(env) = dfx_env {
                            let principals = gather_principals_from_env(env);
                            if !principals.is_empty() {
                                let mut map = BTreeMap::new();
                                map.insert("principal".to_string(), principals);
                                ctx.set_completion(map);
                            }
                        }
                        if is_init_arg {
                            eprintln!("This canister requires an initialization argument.");
                        } else {
                            eprintln!("This method requires arguments.");
                        }
                        let args = input_args(&ctx, &func.args)?;
                        eprintln!("Sending the following argument:\n{}\n", args);
                        if is_init_arg {
                            eprintln!(
                                "Do you want to initialize the canister with this argument? [y/N]"
                            );
                        } else {
                            eprintln!("Do you want to send this message? [y/N]");
                        }
                        let mut input = String::new();
                        stdin().read_line(&mut input)?;
                        if !["y", "Y", "yes", "Yes", "YES"].contains(&input.trim()) {
                            return Err(error_invalid_data!("User cancelled."));
                        }
                        args.to_bytes_with_types(env, &func.args)
                    } else {
                        return Err(error_invalid_data!("Expected arguments but found none."));
                    }
                }
            }
            .map_err(|e| error_invalid_data!("Unable to serialize Candid values: {}", e))?;
            Ok(typed_args)
        }
        v => Err(error_unknown!("Invalid type: {}", v)),
    }
}

You are looking for candid::Encode to build the args, and candid::Decode to translate the result back to a struct. But TBH this is more intended to be called via ic_utils::interfaces::wallet::WalletCanister, which will do the en- and decoding for you automatically.

The following code show how to pack/unpack parameters.

    fn test_idl_encode_decode(){
       let  args1 = candid::Encode!().unwrap();
       assert_eq!([68, 73, 68, 76, 0, 0].to_vec(), args1);

       let args2 = candid::Encode!(&"hello", &"world").unwrap();
       assert_eq!([68, 73, 68, 76, 0, 2, 113, 113, 5, 104, 101, 108, 108, 111, 5, 119, 111, 114, 108, 100].to_vec(), args2);
       let decoded_agrs2 = candid::Decode!(&args2, String, String).unwrap();
       assert_eq!("hello", decoded_agrs2.0);
       assert_eq!("world", decoded_agrs2.1);

       let args3 = candid::Encode!(&"hello,world", &2u32).unwrap();
       assert_eq!([68, 73, 68, 76, 0, 2, 113, 121, 11, 104, 101, 108, 108, 111, 44, 119, 111, 114, 108, 100, 2, 0, 0, 0].to_vec(), args3);
       let decoded_agrs3 = candid::Decode!(&args3, String, u32).unwrap();
       assert_eq!("hello,world", decoded_agrs3.0);
       assert_eq!(2, decoded_agrs3.1);
    }