Repository: scs/substrate-api-client (language: rust)

Issue:
Split `compose_call` into  two functions
The current compose_call! macro expects the full metadata and the pallet name as input. But in case multiple calls for one pallet are created, the current code is forced to get the `PalletMetada` each time for each call. That's not necessary.

Therefore I'm proposing to split it into two functions:
```rust
/// Generates the extrinsic's call field for a given module and call passed as &str
/// # Arguments
///
/// * 'node_metadata' - This crate's parsed node metadata as field of the API.
/// * 'pallet' - Pallet name as &str for which the call is composed.
/// * 'call_name' - Call name as &str
/// * 'args' - Optional sequence of arguments of the call. They are not checked against the metadata.
/// As of now the user needs to check himself that the correct arguments are supplied.
#[macro_export]
macro_rules! compose_call {
($node_metadata: expr, $pallet: expr, $call_name: expr $(, $args: expr) *) => {
        {
            let pallet_metadata = $node_metadata.pallet_by_name($pallet).unwrap();
            $crate::compose_call_for_pallet_metadata !(pallet_metadata, $call $(, ($args)) *);
        }
    };
}

/// Generates the extrinsic's call field for the given PalletMetadata
/// # Arguments
///
/// * 'pallet_metadata' - This crate's parsed pallet metadata as field of the API.
/// * 'call_name' - Call name as &str
/// * 'args' - Optional sequence of arguments of the call. They are not checked against the metadata.
/// As of now the user needs to check himself that the correct arguments are supplied.
#[macro_export]
macro_rules! compose_call_for_pallet_metadata {
($pallet_metadata: expr, $call_name: expr $(, $args: expr) *) => {
        {
            let call_index = pallet.call_variant_by_name($call_name).unwrap().index;
            ([pallet.index(), call_index as u8] $(, ($args)) *)
        }
    };
}

```


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 7a311fa87..4cc596fd0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -16,9 +16,12 @@ dependencies = [
 name = "ac-compose-macros"
 version = "0.4.2"
 dependencies = [
+ "ac-node-api",
  "ac-primitives",
+ "frame-metadata 16.0.0",
  "log",
  "maybe-async",
+ "parity-scale-codec",
 ]
 
 [[package]]
diff --git a/compose-macros/Cargo.toml b/compose-macros/Cargo.toml
index 41b7c9e71..dc604e642 100644
--- a/compose-macros/Cargo.toml
+++ b/compose-macros/Cargo.toml
@@ -17,6 +17,12 @@ maybe-async = { version = "0.2.7" }
 # local
 ac-primitives = { path = "../primitives", default-features = false }
 
+[dev-dependencies]
+ac-node-api = { path = "../node-api" }
+frame-metadata = { version = "16.0" }
+codec = { package = "parity-scale-codec", version = "3.6.1" }
+
+
 [features]
 default = ["std", "sync-api"]
 sync-api = ["maybe-async/is_sync"]
diff --git a/compose-macros/src/lib.rs b/compose-macros/src/lib.rs
index 8ddd67646..4895235d4 100644
--- a/compose-macros/src/lib.rs
+++ b/compose-macros/src/lib.rs
@@ -37,11 +37,25 @@ mod rpc;
 macro_rules! compose_call {
 ($node_metadata: expr, $pallet: expr, $call_name: expr $(, $args: expr) *) => {
         {
-            let pallet = $node_metadata.pallet_by_name($pallet).unwrap().to_owned();
-
-            let call_index = pallet.call_variant_by_name($call_name).unwrap().index;
+			let pallet_metadata = $node_metadata.pallet_by_name($pallet).unwrap().to_owned();
+            $crate::compose_call_for_pallet_metadata!(pallet_metadata, $call_name $(, ($args)) *)
+        }
+    };
+}
 
-            ([pallet.index(), call_index as u8] $(, ($args)) *)
+/// Generates the extrinsic's call field for the given PalletMetadata
+/// # Arguments
+///
+/// * 'pallet_metadata' - This crate's parsed pallet metadata as field of the API.
+/// * 'call_name' - Call name as &str
+/// * 'args' - Optional sequence of arguments of the call. They are not checked against the metadata.
+/// As of now the user needs to check himself that the correct arguments are supplied.
+#[macro_export]
+macro_rules! compose_call_for_pallet_metadata {
+($pallet_metadata: expr, $call_name: expr $(, $args: expr) *) => {
+        {
+            let call_index = $pallet_metadata.call_variant_by_name($call_name).unwrap().index;
+            ([$pallet_metadata.index(), call_index as u8] $(, ($args)) *)
         }
     };
 }
@@ -143,3 +157,38 @@ macro_rules! compose_extrinsic {
 		}
 	};
 }
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+	use ac_node_api::Metadata;
+	use codec::Decode;
+	use frame_metadata::RuntimeMetadataPrefixed;
+	use std::fs;
+
+	#[test]
+	fn macro_compose_call_for_pallet_metadata_works() {
+		let encoded_metadata = fs::read("../ksm_metadata_v14.bin").unwrap();
+		let runtime_metadata_prefixed =
+			RuntimeMetadataPrefixed::decode(&mut encoded_metadata.as_slice()).unwrap();
+		let metadata = Metadata::try_from(runtime_metadata_prefixed).unwrap();
+
+		let pallet_metadata = metadata.pallet_by_name("Balances").unwrap();
+
+		let extra_parameter = 10000;
+		let expected_call_one = ([4, 0], extra_parameter);
+		let call_one = compose_call_for_pallet_metadata!(
+			&pallet_metadata,
+			"transfer_allow_death",
+			extra_parameter
+		);
+		assert_eq!(expected_call_one, call_one);
+		let expected_call_two = ([4, 8], extra_parameter);
+		let call_two = compose_call_for_pallet_metadata!(
+			&pallet_metadata,
+			"force_set_balance",
+			extra_parameter
+		);
+		assert_eq!(expected_call_two, call_two);
+	}
+}


Test patch: