Repository: tangle-network/gadget (language: rust)

Issue:
Runtime Call ID Injection (for the job function)
Tangle Listeners should require context structs to contain the `call_id` field. That way, at runtime, the call_id can be dynamically injected. By doing this, we prevent race conditions.

```
#[job(
    id = 0,
    params(x),
    event_listener(
        listener = TangleEventListener<MyContext, JobCalled>,
        pre_processor = services_pre_processor,
        post_processor = services_post_processor,
    ),
)]
/// Returns x^2 saturating to [`u64::MAX`] if overflow occurs.
pub fn xsquare(x: u64, context: MyContext) -> Result<u64, gadget_sdk::Error> {
    assert!(context.call_id.is_some());
    Ok(x.saturating_pow(2))
}
```

This task does not address the injection of the call_id into the post-processor. To achieve that, a different approach will be required. For now, providing the call ID to the job function has the immediate advantage of giving related tasks between nodes the ability to create symmetric hashes.



Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 75e66a36b..edee5219c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4861,6 +4861,7 @@ dependencies = [
  "gadget-sdk",
  "indexmap 2.6.0",
  "itertools 0.13.0",
+ "parking_lot 0.12.3",
  "proc-macro2",
  "quote",
  "serde_json",
@@ -12272,8 +12273,7 @@ dependencies = [
 [[package]]
 name = "tangle-subxt"
 version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0cad370e154d2297ed658b566b3ed5320362dfebdcc0f1f38f5194d85bf85f02"
+source = "git+https://github.com/tangle-network/tangle#83f587fb4a720ac3936200abb568a346e5045277"
 dependencies = [
  "parity-scale-codec",
  "scale-info",
diff --git a/Cargo.toml b/Cargo.toml
index 377adf7ef..17e730ddc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -63,7 +63,7 @@ cargo-tangle = { path = "./cli", version = "0.3.2" }
 cargo_metadata = { version = "0.18.1" }
 
 # Tangle-related dependencies
-tangle-subxt = { version = "0.7.0", default-features = false }
+tangle-subxt = { git = "https://github.com/tangle-network/tangle", default-features = false }
 subxt-signer = { version = "0.37.0", default-features = false }
 subxt = { version = "0.37.0", default-features = false }
 subxt-core = { version = "0.37.0", default-features = false }
diff --git a/README.md b/README.md
index bc7b08a74..ccc05a507 100644
--- a/README.md
+++ b/README.md
@@ -77,7 +77,7 @@ passing `--clean` as an argument to reset the chain and any keys.
 Then, you can run:
 
 ```bash
-cargo test --package blueprint-test-utils tests_standard::test_externalities_gadget_starts -- --nocapture
+cargo test --package blueprint-test-utils test_incredible_squaring -- --nocapture
 ```
 
 Since testing is in beta stage, each time the blueprint is run, you
diff --git a/blueprint-manager/src/executor/event_handler.rs b/blueprint-manager/src/executor/event_handler.rs
index a12e582d6..77d9c1638 100644
--- a/blueprint-manager/src/executor/event_handler.rs
+++ b/blueprint-manager/src/executor/event_handler.rs
@@ -266,11 +266,10 @@ pub(crate) async fn handle_tangle_event(
 
             // Ensure that we have a test fetcher if we are in test mode
             if gadget_manager_opts.test_mode && test_fetcher_idx.is_none() {
-                warn!(
+                return Err(color_eyre::Report::msg(format!(
                     "No testing fetcher found for blueprint `{}` despite operating in TEST MODE",
                     blueprint.name,
-                );
-                continue;
+                )));
             }
 
             // Ensure that we have only one fetcher if we are in test mode
diff --git a/blueprint-metadata/src/lib.rs b/blueprint-metadata/src/lib.rs
index 60efe5874..f9d9fd5b4 100644
--- a/blueprint-metadata/src/lib.rs
+++ b/blueprint-metadata/src/lib.rs
@@ -316,22 +316,44 @@ fn generate_gadget(package: &Package) -> Gadget<'static> {
     let root = Path::new(&root)
         .canonicalize()
         .expect("Failed to canonicalize root dir");
-    let Some(gadget) = package.metadata.get("gadget") else {
+    let mut sources = vec![];
+    if let Some(gadget) = package.metadata.get("gadget") {
+        let gadget: Gadget<'static> =
+            serde_json::from_value(gadget.clone()).expect("Failed to deserialize gadget.");
+        if let Gadget::Native(NativeGadget { sources: fetchers }) = gadget {
+            sources.extend(fetchers);
+        } else {
+            panic!("Currently unsupported gadget type has been parsed")
+        }
+    } else {
         eprintln!("No gadget metadata found in the Cargo.toml.");
         eprintln!("For more information, see:");
         eprintln!("<TODO>");
-        // For now, we just return an empty gadget
-        return Gadget::Native(NativeGadget {
-            sources: vec![GadgetSource {
-                fetcher: GadgetSourceFetcher::Testing(TestFetcher {
-                    cargo_package: package.name.clone().into(),
-                    cargo_bin: "main".into(),
-                    base_path: format!("{}", root.display()).into(),
-                }),
-            }],
-        });
     };
-    serde_json::from_value(gadget.clone()).expect("Failed to deserialize gadget.")
+
+    let has_test_fetcher = sources.iter().any(|fetcher| {
+        matches!(
+            fetcher,
+            GadgetSource {
+                fetcher: GadgetSourceFetcher::Testing(..)
+            }
+        )
+    });
+
+    if !has_test_fetcher {
+        println!("Adding test fetcher since none exists");
+        sources.push(GadgetSource {
+            fetcher: GadgetSourceFetcher::Testing(TestFetcher {
+                cargo_package: package.name.clone().into(),
+                cargo_bin: "main".into(),
+                base_path: format!("{}", root.display()).into(),
+            }),
+        })
+    }
+
+    assert_ne!(sources.len(), 0, "No sources found for the gadget");
+
+    Gadget::Native(NativeGadget { sources })
 }
 
 fn generate_rustdoc() -> Crate {
diff --git a/blueprints/examples/src/raw_tangle_events.rs b/blueprints/examples/src/raw_tangle_events.rs
index 1a004c9d7..ec16a11a9 100644
--- a/blueprints/examples/src/raw_tangle_events.rs
+++ b/blueprints/examples/src/raw_tangle_events.rs
@@ -1,11 +1,17 @@
 use gadget_sdk::config::StdGadgetConfiguration;
+use gadget_sdk::contexts::TangleClientContext;
 use gadget_sdk::event_listener::tangle::{TangleEvent, TangleEventListener};
 use gadget_sdk::event_utils::InitializableEventHandler;
 use gadget_sdk::job;
 use gadget_sdk::tangle_subxt::tangle_testnet_runtime::api;
 
-#[derive(Clone)]
-pub struct MyContext;
+#[derive(Clone, TangleClientContext)]
+pub struct MyContext {
+    #[config]
+    sdk_config: StdGadgetConfiguration,
+    #[call_id]
+    call_id: Option<u64>,
+}
 
 pub async fn constructor(
     env: StdGadgetConfiguration,
@@ -17,9 +23,15 @@ pub async fn constructor(
         .map_err(|e| color_eyre::eyre::eyre!(e))?;
 
     gadget_sdk::info!("Starting the event watcher for {} ...", signer.account_id());
-    RawEventHandler::new(&env, MyContext)
-        .await
-        .map_err(|e| color_eyre::eyre::eyre!(e))
+    RawEventHandler::new(
+        &env,
+        MyContext {
+            sdk_config: env.clone(),
+            call_id: None,
+        },
+    )
+    .await
+    .map_err(|e| color_eyre::eyre::eyre!(e))
 }
 
 #[job(
diff --git a/blueprints/examples/src/services_context.rs b/blueprints/examples/src/services_context.rs
index 6ff4255ed..383830278 100644
--- a/blueprints/examples/src/services_context.rs
+++ b/blueprints/examples/src/services_context.rs
@@ -11,6 +11,8 @@ use gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::services::events::Job
 pub struct ExampleServiceContext {
     #[config]
     sdk_config: StdGadgetConfiguration,
+    #[call_id]
+    call_id: Option<u64>,
 }
 
 pub async fn constructor(
@@ -24,9 +26,15 @@ pub async fn constructor(
         .map_err(|e| color_eyre::eyre::eyre!(e))?;
 
     gadget_sdk::info!("Starting the event watcher for {} ...", signer.account_id());
-    HandleJobEventHandler::new(&env.clone(), ExampleServiceContext { sdk_config: env })
-        .await
-        .map_err(|e| color_eyre::eyre::eyre!(e))
+    HandleJobEventHandler::new(
+        &env.clone(),
+        ExampleServiceContext {
+            sdk_config: env,
+            call_id: None,
+        },
+    )
+    .await
+    .map_err(|e| color_eyre::eyre::eyre!(e))
 }
 
 #[job(
@@ -39,8 +47,8 @@ pub async fn constructor(
     ),
 )]
 pub async fn handle_job(
-    job_details: Vec<u8>,
     context: ExampleServiceContext,
+    job_details: Vec<u8>,
 ) -> Result<u64, gadget_sdk::Error> {
     let client = context.tangle_client().await.unwrap();
     let blueprint_owner = context.current_blueprint_owner(&client).await.unwrap();
diff --git a/blueprints/incredible-squaring/contracts/foundry.toml b/blueprints/incredible-squaring/contracts/foundry.toml
index 21570e20b..41f3dfb1f 100644
--- a/blueprints/incredible-squaring/contracts/foundry.toml
+++ b/blueprints/incredible-squaring/contracts/foundry.toml
@@ -2,4 +2,4 @@
 src = "src"
 out = "out"
 libs = ["lib"]
-solc_version = "0.8.20"
\ No newline at end of file
+solc_version = "0.8.20"
diff --git a/blueprints/incredible-squaring/contracts/remappings.txt b/blueprints/incredible-squaring/contracts/remappings.txt
index e159d0062..8b9a7a0a6 100644
--- a/blueprints/incredible-squaring/contracts/remappings.txt
+++ b/blueprints/incredible-squaring/contracts/remappings.txt
@@ -1,4 +1,4 @@
 ds-test/=lib/ds-test/src/
 forge-std/=lib/forge-std/src/
+incredible-squaring/=src/
 tnt-core/=lib/tnt-core/src/
-incredible-squaring/=src/
\ No newline at end of file
diff --git a/blueprints/incredible-squaring/contracts/src/IncredibleSquaringBlueprint.sol b/blueprints/incredible-squaring/contracts/src/IncredibleSquaringBlueprint.sol
index 687a7d7a9..59ee7c1c1 100644
--- a/blueprints/incredible-squaring/contracts/src/IncredibleSquaringBlueprint.sol
+++ b/blueprints/incredible-squaring/contracts/src/IncredibleSquaringBlueprint.sol
@@ -23,46 +23,62 @@ contract IncredibleSquaringBlueprint is BlueprintServiceManagerBase {
     mapping(uint64 => address) public serviceInstances;
 
     /**
-     * @dev Hook for service instance requests. Called when a user requests a service
-     * instance from the blueprint.
-     * @param serviceId The ID of the requested service.
-     * @param operatorsOfService The operators involved in the service in bytes array format.
-     * @param requestInputs Inputs required for the service request in bytes format.
+     * @dev Hook for service initialization. Called when a service is initialized.
+     * This hook is called after the service is approved from all of the operators.
+     *
+     * @param requestId The ID of the request.
+     * @param serviceId The ID of the service.
+     * @param owner The owner of the service.
+     * @param permittedCallers  The list of permitted callers for the service.
+     * @param ttl The time-to-live for the service.
      */
-    function onRequest(uint64 serviceId, bytes[] calldata operatorsOfService, bytes calldata requestInputs)
-        public
-        payable
-        virtual
-        override
-        onlyFromRootChain
+    function onServiceInitialized(
+        uint64 requestId,
+        uint64 serviceId,
+        address owner,
+        address[] calldata permittedCallers,
+        uint64 ttl
+    )
+    external
+    virtual
+    override
+    onlyFromMaster
     {
         IncredibleSquaringInstance deployed = new IncredibleSquaringInstance(serviceId);
         serviceInstances[serviceId] = address(deployed);
     }
 
     /**
-     * @dev Verifies the result of a job call. This function is used to validate the
-     * outputs of a job execution against the expected results.
+     * @dev Hook for handling job result. Called when operators send the result
+     * of a job execution.
      * @param serviceId The ID of the service related to the job.
      * @param job The job identifier.
      * @param jobCallId The unique ID for the job call.
-     * @param participant The participant (operator) whose result is being verified.
-     * @param inputs Inputs used for the job execution.
-     * @param outputs Outputs resulting from the job execution.
+     * @param operator The operator sending the result in bytes format.
+     * @param inputs Inputs used for the job execution in bytes format.
+     * @param outputs Outputs resulting from the job execution in bytes format.
      */
     function onJobResult(
         uint64 serviceId,
         uint8 job,
         uint64 jobCallId,
-        bytes calldata participant,
+        ServiceOperators.OperatorPreferences calldata operator,
         bytes calldata inputs,
         bytes calldata outputs
-    ) public payable override {
-        // Decode the inputs and outputs
-        uint256 input = abi.decode(inputs, (uint256));
-        uint256 output = abi.decode(outputs, (uint256));
-        // Check if the output is the square of the input
-        bool isValid = output == input * input;
-        require(isValid, "Invalid result");
+    )
+    external
+    payable
+    virtual
+    override
+    onlyFromMaster
+    {
+        if (jobCallId == 0) {
+            // Decode the inputs and outputs
+            uint256 input = abi.decode(inputs, (uint256));
+            uint256 output = abi.decode(outputs, (uint256));
+            // Check if the output is the square of the input
+            bool isValid = output == input * input;
+            require(isValid, "Invalid result");
+        }
     }
 }
diff --git a/blueprints/incredible-squaring/src/lib.rs b/blueprints/incredible-squaring/src/lib.rs
index cd49f92a9..0e88f7481 100644
--- a/blueprints/incredible-squaring/src/lib.rs
+++ b/blueprints/incredible-squaring/src/lib.rs
@@ -1,10 +1,16 @@
+use gadget_sdk::contexts::TangleClientContext;
 use gadget_sdk::event_listener::tangle::jobs::{services_post_processor, services_pre_processor};
 use gadget_sdk::event_listener::tangle::TangleEventListener;
 use gadget_sdk::job;
 use gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::services::events::JobCalled;
 
-#[derive(Clone)]
-pub struct MyContext;
+#[derive(Clone, TangleClientContext)]
+pub struct MyContext {
+    #[config]
+    pub config: gadget_sdk::config::StdGadgetConfiguration,
+    #[call_id]
+    pub call_id: Option<u64>,
+}
 
 #[job(
     id = 0,
@@ -17,5 +23,24 @@ pub struct MyContext;
 )]
 /// Returns x^2 saturating to [`u64::MAX`] if overflow occurs.
 pub fn xsquare(x: u64, context: MyContext) -> Result<u64, gadget_sdk::Error> {
+    assert!(context.call_id.is_some());
     Ok(x.saturating_pow(2))
 }
+
+#[job(
+    id = 1,
+    params(call_id),
+    event_listener(
+        listener = TangleEventListener<MyContext, JobCalled>,
+        pre_processor = services_pre_processor,
+        post_processor = services_post_processor,
+    ),
+)]
+pub fn test_call_id(context: MyContext, call_id: u64) -> Result<u64, gadget_sdk::Error> {
+    gadget_sdk::info!("JOB CALL ID: {:?}, {}", context.call_id, call_id);
+    if context.call_id.unwrap() != call_id {
+        return Err(gadget_sdk::Error::Other(String::from("Call ID mismatch!")));
+    }
+
+    Ok(0)
+}
diff --git a/blueprints/incredible-squaring/src/main.rs b/blueprints/incredible-squaring/src/main.rs
index aefa42876..0bfc6c150 100644
--- a/blueprints/incredible-squaring/src/main.rs
+++ b/blueprints/incredible-squaring/src/main.rs
@@ -7,7 +7,23 @@ use incredible_squaring_blueprint as blueprint;
 
 #[gadget_sdk::main(env)]
 async fn main() {
-    let x_square = blueprint::XsquareEventHandler::new(&env, blueprint::MyContext).await?;
+    let x_square = blueprint::XsquareEventHandler::new(
+        &env,
+        blueprint::MyContext {
+            config: env.clone(),
+            call_id: None,
+        },
+    )
+    .await?;
+
+    let test_call_id = blueprint::TestCallIdEventHandler::new(
+        &env,
+        blueprint::MyContext {
+            config: env.clone(),
+            call_id: None,
+        },
+    )
+    .await?;
 
     info!(
         "Starting the event watcher for {} ...",
@@ -18,6 +34,7 @@ async fn main() {
     let tangle_config = TangleConfig::default();
     BlueprintRunner::new(tangle_config, env)
         .job(x_square)
+        .job(test_call_id)
         .run()
         .await?;
 
diff --git a/macros/blueprint-proc-macro/Cargo.toml b/macros/blueprint-proc-macro/Cargo.toml
index 798d09b28..14a59dade 100644
--- a/macros/blueprint-proc-macro/Cargo.toml
+++ b/macros/blueprint-proc-macro/Cargo.toml
@@ -19,6 +19,7 @@ proc-macro2 = { workspace = true }
 serde_json = { workspace = true }
 gadget-blueprint-proc-macro-core = { workspace = true, default-features = false }
 indexmap = { workspace = true }
+parking_lot = { workspace = true }
 
 [dev-dependencies]
 trybuild = { workspace = true }
diff --git a/macros/blueprint-proc-macro/src/job.rs b/macros/blueprint-proc-macro/src/job.rs
index cb401933b..9454ca4d7 100644
--- a/macros/blueprint-proc-macro/src/job.rs
+++ b/macros/blueprint-proc-macro/src/job.rs
@@ -230,21 +230,21 @@ pub(crate) fn generate_event_workflow_tokenstream(
             // convert the listener var, which is just a struct name, to an ident
             let listener = listener_meta.listener.to_token_stream();
 
+            // Raw events have no pre-processor, therefore their inputs are passed directly into the job function
+            // and NOT as job params
+            let is_raw = listener_meta.is_raw();
+
             // Generate the variable that we are passing as the context into EventListener::create(&mut ctx)
             // We assume the first supplied event handler arg is the context we are injecting into the event listener
             // Then, pass that into the EventFlowWrapper
             let fn_name_ident = &input.sig.ident;
             let static_ctx_get_override = quote! { CTX.get().unwrap() };
-            let mut ordered_inputs =
-                get_fn_call_ordered(param_types, params, Some(static_ctx_get_override))?;
+            let (ctx_pos_in_ordered_inputs, mut ordered_inputs) =
+                get_fn_call_ordered(param_types, params, Some(static_ctx_get_override), is_raw)?;
 
             let asyncness = get_asyncness(input);
             let call_id_static_name = get_current_call_id_field_name(input);
 
-            // Raw events have no pre-processor, therefore their inputs are passed directly into the job function
-            // and NOT as job params
-            let is_raw = listener_meta.is_raw();
-
             // TODO: task 001: find better way to identify which ident is the raw event
             // for now, we assume the raw event is always listed first
             if is_raw {
@@ -257,7 +257,12 @@ pub(crate) fn generate_event_workflow_tokenstream(
                     // If is_raw, assume the actual context is the second param
                     quote! { ctx. #field_in_self .clone() }
                 })
-                .ok_or_else(|| syn::Error::new(Span::call_site(), "Must specify a context"))?;
+                .ok_or_else(|| {
+                    syn::Error::new(
+                        Span::call_site(),
+                        "Must specify a context (field_in_self_getter)",
+                    )
+                })?;
 
             let autogen_struct_name = quote! { #struct_name };
 
@@ -292,9 +297,9 @@ pub(crate) fn generate_event_workflow_tokenstream(
                     event_listeners,
                     &mut ordered_inputs,
                     fn_name_ident,
-                    &call_id_static_name,
                     &asyncness,
                     &return_type,
+                    ctx_pos_in_ordered_inputs,
                 )?,
 
                 ListenerType::Evm => get_evm_job_processor_wrapper(
@@ -312,7 +317,7 @@ pub(crate) fn generate_event_workflow_tokenstream(
 
                     quote! {
                         move |param0| async move {
-                            let res = #fn_name_ident (#(#ordered_inputs)*) #asyncness;
+                            let res = #fn_name_ident (#(#ordered_inputs),*) #asyncness;
                             #job_processor_call_return
                         }
                     }
@@ -329,7 +334,7 @@ pub(crate) fn generate_event_workflow_tokenstream(
                                 let tangle_job_result = gadget_sdk::event_listener::tangle::TangleResult::<_> {
                                     results: job_result,
                                     service_id: ctx.service_id,
-                                    call_id: #call_id_static_name.load(std::sync::atomic::Ordering::Relaxed),
+                                    call_id: #call_id_static_name.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
                                     client: ctx.client.clone(),
                                     signer: ctx.signer.clone(),
                                 };
@@ -377,8 +382,7 @@ pub(crate) fn generate_event_workflow_tokenstream(
             let next_listener = quote! {
                 async fn #listener_function_name (ctx: &#autogen_struct_name) -> Option<gadget_sdk::tokio::sync::oneshot::Receiver<Result<(), gadget_sdk::Error>>> {
                     static ONCE: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
-                    if !ONCE.load(std::sync::atomic::Ordering::Relaxed) {
-                        ONCE.store(true, std::sync::atomic::Ordering::Relaxed);
+                    if !ONCE.fetch_or(true, std::sync::atomic::Ordering::Relaxed) {
                         let (tx, rx) = gadget_sdk::tokio::sync::oneshot::channel();
 
                         static CTX: gadget_sdk::tokio::sync::OnceCell<#autogen_struct_name> = gadget_sdk::tokio::sync::OnceCell::const_new();
@@ -568,7 +572,8 @@ pub fn get_fn_call_ordered(
     param_types: &IndexMap<Ident, Type>,
     params_from_job_args: &[Ident],
     replacement_for_self: Option<proc_macro2::TokenStream>,
-) -> syn::Result<Vec<proc_macro2::TokenStream>> {
+    is_raw: bool,
+) -> syn::Result<(usize, Vec<proc_macro2::TokenStream>)> {
     let (event_handler_args, _) = get_event_handler_args(param_types, params_from_job_args)?;
 
     let additional_var_indexes: Vec<usize> =
@@ -583,6 +588,8 @@ pub fn get_fn_call_ordered(
 
     // This has all params
     let mut job_var_idx = 0;
+    let mut non_job_var_count = 0;
+    let mut ctx_pos = None;
     let this = replacement_for_self.unwrap_or_else(|| quote! { self });
     let ret = param_types
         .iter()
@@ -595,7 +602,7 @@ pub fn get_fn_call_ordered(
             if is_job_var {
                 let ident = format_ident!("param{job_var_idx}");
                 job_var_idx += 1;
-                return quote! { #ident, };
+                return quote! { #ident };
             }
 
             let (is_ref, is_ref_mut) = match ty {
@@ -603,17 +610,35 @@ pub fn get_fn_call_ordered(
                 _ => (false, false),
             };
 
+            if non_job_var_count == 0 {
+                // Assume first non-job var is the context
+                ctx_pos = Some(pos_in_all_args);
+            }
+
+            non_job_var_count += 1;
+
             if is_ref && is_ref_mut {
-                quote! { &mut #this .#ident, }
+                quote! { &mut #this .#ident }
             } else if is_ref {
-                quote! { &#this .#ident, }
+                quote! { &#this .#ident }
             } else {
-                quote! { #this .#ident.clone(), }
+                quote! { #this .#ident.clone() }
             }
         })
         .collect::<Vec<_>>();
 
-    Ok(ret)
+    if is_raw {
+        ctx_pos = Some(1); // Raw events have only two events: 0 = the raw event, 1 = the context
+    }
+
+    let ctx_pos = ctx_pos.ok_or_else(|| {
+        syn::Error::new(
+            Span::call_site(),
+            "Could not find the context in the function signature",
+        )
+    })?;
+
+    Ok((ctx_pos, ret))
 }
 
 fn get_asyncness(input: &ItemFn) -> proc_macro2::TokenStream {
@@ -640,12 +665,9 @@ pub fn generate_specialized_logic(
             generate_evm_specific_impl(&struct_name, event_listener_args, param_map, job_params)
         }
 
-        ListenerType::Tangle => Ok(generate_tangle_specific_impl(
-            &struct_name,
-            param_map,
-            job_params,
-            event_listener_args,
-        )),
+        ListenerType::Tangle => {
+            generate_tangle_specific_impl(&struct_name, param_map, job_params, event_listener_args)
+        }
 
         ListenerType::Custom => Ok(proc_macro2::TokenStream::default()),
     }
diff --git a/macros/blueprint-proc-macro/src/special_impls/evm.rs b/macros/blueprint-proc-macro/src/special_impls/evm.rs
index f89e3393c..1f34992fc 100644
--- a/macros/blueprint-proc-macro/src/special_impls/evm.rs
+++ b/macros/blueprint-proc-macro/src/special_impls/evm.rs
@@ -124,7 +124,7 @@ pub(crate) fn get_evm_job_processor_wrapper(
         quote! {
             let inputs = param0;
             #(#params_tokens)*
-            let res = #fn_name_ident (#(#ordered_inputs)*) #asyncness;
+            let res = #fn_name_ident (#(#ordered_inputs),*) #asyncness;
         }
     };
 
diff --git a/macros/blueprint-proc-macro/src/special_impls/tangle.rs b/macros/blueprint-proc-macro/src/special_impls/tangle.rs
index 469972ae7..4782f0267 100644
--- a/macros/blueprint-proc-macro/src/special_impls/tangle.rs
+++ b/macros/blueprint-proc-macro/src/special_impls/tangle.rs
@@ -11,7 +11,7 @@ pub(crate) fn generate_tangle_specific_impl(
     param_map: &IndexMap<Ident, Type>,
     job_params: &[Ident],
     event_listener_args: &EventListenerArgs,
-) -> TokenStream {
+) -> syn::Result<TokenStream> {
     let mut non_job_param_map = get_non_job_arguments(param_map, job_params);
     let mut new_function_signature = vec![];
     let mut constructor_args = vec![];
@@ -45,7 +45,7 @@ pub(crate) fn generate_tangle_specific_impl(
 
     let struct_name_as_literal = struct_name.to_string();
 
-    quote! {
+    Ok(quote! {
         impl #struct_name {
             /// Create a new
             #[doc = "[`"]
@@ -69,7 +69,7 @@ pub(crate) fn generate_tangle_specific_impl(
 
         #[automatically_derived]
         impl gadget_sdk::event_listener::markers::IsTangle for #struct_name {}
-    }
+    })
 }
 
 #[allow(clippy::too_many_arguments)]
@@ -79,9 +79,9 @@ pub(crate) fn get_tangle_job_processor_wrapper(
     event_listeners: &EventListenerArgs,
     ordered_inputs: &mut Vec<TokenStream>,
     fn_name_ident: &Ident,
-    call_id_static_name: &Ident,
     asyncness: &TokenStream,
     return_type: &Type,
+    ctx_post_in_ordered_inputs: usize,
 ) -> syn::Result<TokenStream> {
     let params = declared_params_to_field_types(job_params, param_map)?;
     let params_tokens = event_listeners.get_param_name_tokenstream(&params);
@@ -91,37 +91,48 @@ pub(crate) fn get_tangle_job_processor_wrapper(
         const PARAMETER_COUNT: usize = #parameter_count;
     };
 
+    let injected_context = ordered_inputs[ctx_post_in_ordered_inputs].clone();
+    let call_id_injector = quote! {
+        let mut injected_context = #injected_context;
+        if let Some(call_id) = tangle_event.call_id {
+            gadget_sdk::contexts::TangleClientContext::set_call_id(&mut injected_context, call_id);
+        }
+    };
+
+    ordered_inputs[ctx_post_in_ordered_inputs] = quote! { injected_context };
+
     let job_processor_call = if params_tokens.is_empty() {
         let second_param = ordered_inputs
             .pop()
             .ok_or_else(|| syn::Error::new(Span::call_site(), "Context type required"))?;
         quote! {
+            #call_id_injector
             // If no args are specified, assume this job has no parameters and thus takes in the raw event
-            let res = #fn_name_ident (param0, #second_param) #asyncness;
+            let res = #fn_name_ident (tangle_event, #second_param) #asyncness;
         }
     } else {
         quote! {
             #parameter_count_const
 
-            if param0.args.len() != PARAMETER_COUNT {
+            if tangle_event.args.len() != PARAMETER_COUNT {
                 return Err(
-                    ::gadget_sdk::Error::BadArgumentDecoding(format!("Parameter count mismatch, got `{}`, expected `{PARAMETER_COUNT}`", param0.args.len()))
+                    ::gadget_sdk::Error::BadArgumentDecoding(format!("Parameter count mismatch, got `{}`, expected `{PARAMETER_COUNT}`", tangle_event.args.len()))
                 );
             }
 
-            let mut args = param0.args.into_iter();
+            let mut args = tangle_event.args.clone().into_iter();
             #(#params_tokens)*
-            let res = #fn_name_ident (#(#ordered_inputs)*) #asyncness;
+
+            #call_id_injector
+
+            let res = #fn_name_ident (#(#ordered_inputs),*) #asyncness;
         }
     };
 
     let job_processor_call_return = get_return_type_wrapper(return_type);
 
     Ok(quote! {
-        move |param0: gadget_sdk::event_listener::tangle::TangleEvent<_, _>| async move {
-            if let Some(call_id) = param0.call_id {
-                #call_id_static_name.store(call_id, std::sync::atomic::Ordering::Relaxed);
-            }
+        move |tangle_event: gadget_sdk::event_listener::tangle::TangleEvent<_, _>| async move {
 
             #job_processor_call
             #job_processor_call_return
diff --git a/macros/context-derive/src/cfg.rs b/macros/context-derive/src/cfg.rs
index 8e9a31220..67bf0a973 100644
--- a/macros/context-derive/src/cfg.rs
+++ b/macros/context-derive/src/cfg.rs
@@ -5,7 +5,12 @@ pub enum FieldInfo {
     Unnamed(Index),
 }
 
-pub fn find_config_field(input_ident: &Ident, data: &Data) -> Result<FieldInfo> {
+pub fn find_config_field(
+    input_ident: &Ident,
+    data: &Data,
+    tag_name: &'static str,
+    tag_ty: &'static str,
+) -> Result<FieldInfo> {
     match data {
         Data::Struct(data_struct) => match &data_struct.fields {
             Fields::Named(fields) => {
@@ -13,19 +18,19 @@ pub fn find_config_field(input_ident: &Ident, data: &Data) -> Result<FieldInfo>
                     if field
                         .attrs
                         .iter()
-                        .any(|attr| attr.path().is_ident("config"))
+                        .any(|attr| attr.path().is_ident(tag_name))
                     {
                         return field.ident.clone().map(FieldInfo::Named).ok_or_else(|| {
                             Error::new_spanned(
                                 field,
-                                "Expected named field with #[config] attribute",
+                                format!("Expected named field with #[{tag_name}] attribute"),
                             )
                         });
                     }
                 }
                 Err(Error::new_spanned(
                     input_ident,
-                    "No field with #[config] attribute found, please add #[config] to the field that holds the `gadget_sdk::config::GadgetConfiguration`",
+                    format!("No field with #[{tag_name}] attribute found, please add #[{tag_name}] to the field that holds the `{tag_ty}`"),
                 ))
             }
             Fields::Unnamed(fields) => {
@@ -33,14 +38,14 @@ pub fn find_config_field(input_ident: &Ident, data: &Data) -> Result<FieldInfo>
                     if field
                         .attrs
                         .iter()
-                        .any(|attr| attr.path().is_ident("config"))
+                        .any(|attr| attr.path().is_ident(tag_name))
                     {
                         return Ok(FieldInfo::Unnamed(Index::from(i)));
                     }
                 }
                 Err(Error::new_spanned(
                     input_ident,
-                    "No field with #[config] attribute found, please add #[config] to the field that holds the `gadget_sdk::config::GadgetConfiguration`",
+                    format!("No field with #[{tag_name}] attribute found, please add #[{tag_name}] to the field that holds the `{tag_ty}`"),
                 ))
             }
             Fields::Unit => Err(Error::new_spanned(
diff --git a/macros/context-derive/src/lib.rs b/macros/context-derive/src/lib.rs
index 04a416501..5a2f0e0b7 100644
--- a/macros/context-derive/src/lib.rs
+++ b/macros/context-derive/src/lib.rs
@@ -26,12 +26,18 @@ mod services;
 /// Tangle Subxt Client context extension implementation.
 mod subxt;
 
+const CONFIG_TAG_NAME: &str = "config";
+const CONFIG_TAG_TYPE: &str = "gadget_sdk::config::GadgetConfiguration";
+const CALL_ID_TAG_NAME: &str = "call_id";
+const CALL_ID_TAG_TYPE: &str = "Option<u64>";
+
 /// Derive macro for generating Context Extensions trait implementation for `KeystoreContext`.
 #[proc_macro_derive(KeystoreContext, attributes(config))]
 pub fn derive_keystore_context(input: TokenStream) -> TokenStream {
     let input = syn::parse_macro_input!(input as syn::DeriveInput);
-    let result = cfg::find_config_field(&input.ident, &input.data)
-        .map(|config_field| keystore::generate_context_impl(input, config_field));
+    let result =
+        cfg::find_config_field(&input.ident, &input.data, CONFIG_TAG_NAME, CONFIG_TAG_TYPE)
+            .map(|config_field| keystore::generate_context_impl(input, config_field));
 
     match result {
         Ok(expanded) => TokenStream::from(expanded),
@@ -43,8 +49,9 @@ pub fn derive_keystore_context(input: TokenStream) -> TokenStream {
 #[proc_macro_derive(EVMProviderContext, attributes(config))]
 pub fn derive_evm_provider_context(input: TokenStream) -> TokenStream {
     let input = syn::parse_macro_input!(input as syn::DeriveInput);
-    let result = cfg::find_config_field(&input.ident, &input.data)
-        .map(|config_field| evm::generate_context_impl(input, config_field));
+    let result =
+        cfg::find_config_field(&input.ident, &input.data, CONFIG_TAG_NAME, CONFIG_TAG_TYPE)
+            .map(|config_field| evm::generate_context_impl(input, config_field));
 
     match result {
         Ok(expanded) => TokenStream::from(expanded),
@@ -53,11 +60,23 @@ pub fn derive_evm_provider_context(input: TokenStream) -> TokenStream {
 }
 
 /// Derive macro for generating Context Extensions trait implementation for `TangleClientContext`.
-#[proc_macro_derive(TangleClientContext, attributes(config))]
+#[proc_macro_derive(TangleClientContext, attributes(config, call_id))]
 pub fn derive_tangle_client_context(input: TokenStream) -> TokenStream {
     let input = syn::parse_macro_input!(input as syn::DeriveInput);
-    let result = cfg::find_config_field(&input.ident, &input.data)
-        .map(|config_field| subxt::generate_context_impl(input, config_field));
+    let result =
+        cfg::find_config_field(&input.ident, &input.data, CONFIG_TAG_NAME, CONFIG_TAG_TYPE)
+            .and_then(|res| {
+                let call_id_field = cfg::find_config_field(
+                    &input.ident,
+                    &input.data,
+                    CALL_ID_TAG_NAME,
+                    CALL_ID_TAG_TYPE,
+                )?;
+                Ok((res, call_id_field))
+            })
+            .map(|(config_field, call_id_field)| {
+                subxt::generate_context_impl(input, config_field, call_id_field)
+            });
 
     match result {
         Ok(expanded) => TokenStream::from(expanded),
@@ -69,8 +88,9 @@ pub fn derive_tangle_client_context(input: TokenStream) -> TokenStream {
 #[proc_macro_derive(ServicesContext, attributes(config))]
 pub fn derive_services_context(input: TokenStream) -> TokenStream {
     let input = syn::parse_macro_input!(input as syn::DeriveInput);
-    let result = cfg::find_config_field(&input.ident, &input.data)
-        .map(|config_field| services::generate_context_impl(input, config_field));
+    let result =
+        cfg::find_config_field(&input.ident, &input.data, CONFIG_TAG_NAME, CONFIG_TAG_TYPE)
+            .map(|config_field| services::generate_context_impl(input, config_field));
 
     match result {
         Ok(expanded) => TokenStream::from(expanded),
@@ -82,8 +102,9 @@ pub fn derive_services_context(input: TokenStream) -> TokenStream {
 #[proc_macro_derive(EigenlayerContext, attributes(config))]
 pub fn derive_eigenlayer_context(input: TokenStream) -> TokenStream {
     let input = syn::parse_macro_input!(input as syn::DeriveInput);
-    let result = cfg::find_config_field(&input.ident, &input.data)
-        .map(|config_field| eigenlayer::generate_context_impl(input, config_field));
+    let result =
+        cfg::find_config_field(&input.ident, &input.data, CONFIG_TAG_NAME, CONFIG_TAG_TYPE)
+            .map(|config_field| eigenlayer::generate_context_impl(input, config_field));
 
     match result {
         Ok(expanded) => TokenStream::from(expanded),
@@ -95,8 +116,9 @@ pub fn derive_eigenlayer_context(input: TokenStream) -> TokenStream {
 #[proc_macro_derive(MPCContext, attributes(config))]
 pub fn derive_mpc_context(input: TokenStream) -> TokenStream {
     let input = syn::parse_macro_input!(input as syn::DeriveInput);
-    let result = cfg::find_config_field(&input.ident, &input.data)
-        .map(|config_field| mpc::generate_context_impl(input, config_field));
+    let result =
+        cfg::find_config_field(&input.ident, &input.data, CONFIG_TAG_NAME, CONFIG_TAG_TYPE)
+            .map(|config_field| mpc::generate_context_impl(input, config_field));
 
     match result {
         Ok(expanded) => TokenStream::from(expanded),
diff --git a/macros/context-derive/src/subxt.rs b/macros/context-derive/src/subxt.rs
index b8e7f2999..753093027 100644
--- a/macros/context-derive/src/subxt.rs
+++ b/macros/context-derive/src/subxt.rs
@@ -11,8 +11,14 @@ pub fn generate_context_impl(
         ..
     }: DeriveInput,
     config_field: FieldInfo,
+    call_id_field: FieldInfo,
 ) -> proc_macro2::TokenStream {
-    let field_access = match config_field {
+    let field_access_config = match config_field {
+        FieldInfo::Named(ident) => quote! { self.#ident },
+        FieldInfo::Unnamed(index) => quote! { self.#index },
+    };
+
+    let field_access_call_id = match call_id_field {
         FieldInfo::Named(ident) => quote! { self.#ident },
         FieldInfo::Unnamed(index) => quote! { self.#index },
     };
@@ -22,6 +28,11 @@ pub fn generate_context_impl(
     quote! {
         impl #impl_generics gadget_sdk::contexts::TangleClientContext for #name #ty_generics #where_clause {
             type Config = gadget_sdk::ext::subxt::PolkadotConfig;
+
+            fn set_call_id(&mut self, call_id: u64) {
+                #field_access_call_id = Some(call_id);
+            }
+
             fn tangle_client(&self) -> impl core::future::Future<Output = Result<gadget_sdk::ext::subxt::OnlineClient<Self::Config>, gadget_sdk::ext::subxt::Error>> {
                 use gadget_sdk::ext::subxt;
 
@@ -31,7 +42,7 @@ pub fn generate_context_impl(
                     match CLIENT.get() {
                         Some(client) => Ok(client.clone()),
                         None => {
-                            let rpc_url = #field_access.ws_rpc_endpoint.as_str();
+                            let rpc_url = #field_access_config.ws_rpc_endpoint.as_str();
                             let client = subxt::OnlineClient::from_url(rpc_url).await?;
                             CLIENT.set(client.clone()).map(|_| client).map_err(|_| {
                                 subxt::Error::Io(std::io::Error::new(
diff --git a/macros/playground/src/lib.rs b/macros/playground/src/lib.rs
index b38f57da1..8e8a57b43 100644
--- a/macros/playground/src/lib.rs
+++ b/macros/playground/src/lib.rs
@@ -1,5 +1,6 @@
 #![allow(dead_code)]
-use gadget_sdk::clients::tangle::runtime::TangleClient;
+use gadget_sdk::config::StdGadgetConfiguration;
+use gadget_sdk::contexts::TangleClientContext;
 use gadget_sdk::event_listener::tangle::jobs::{services_post_processor, services_pre_processor};
 use gadget_sdk::event_listener::tangle::TangleEventListener;
 use gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::services::events::{
@@ -26,16 +27,21 @@ impl std::fmt::Display for Error {
 }
 
 impl std::error::Error for Error {}
-#[derive(Copy, Clone)]
-pub struct MyContext;
+#[derive(Clone, TangleClientContext)]
+pub struct MyContext {
+    #[config]
+    pub config: StdGadgetConfiguration,
+    #[call_id]
+    pub call_id: Option<u64>,
+}
 
 // ==================
 //       Jobs
 // ==================
 
 /// Simple Threshold (t) Keygen Job for n parties.
-#[job(id = 0, params(n, t), event_listener(listener = TangleEventListener<TangleClient, JobCalled>, pre_processor = services_pre_processor))]
-pub fn keygen(context: TangleClient, n: u16, t: u16) -> Result<Vec<u8>, Error> {
+#[job(id = 0, params(n, t), event_listener(listener = TangleEventListener<MyContext, JobCalled>, pre_processor = services_pre_processor))]
+pub fn keygen(context: MyContext, n: u16, t: u16) -> Result<Vec<u8>, Error> {
     let _ = (n, t, context);
     Ok(vec![0; 33])
 }
@@ -44,9 +50,9 @@ pub fn keygen(context: TangleClient, n: u16, t: u16) -> Result<Vec<u8>, Error> {
 #[job(
     id = 1,
     params(keygen_id, data),
-    event_listener(listener = TangleEventListener<TangleClient, JobCalled>, pre_processor = services_pre_processor),
+    event_listener(listener = TangleEventListener<MyContext, JobCalled>, pre_processor = services_pre_processor),
 )]
-pub async fn sign(context: TangleClient, keygen_id: u64, data: Vec<u8>) -> Result<Vec<u8>, Error> {
+pub async fn sign(context: MyContext, keygen_id: u64, data: Vec<u8>) -> Result<Vec<u8>, Error> {
     let _ = (keygen_id, data);
     Ok(vec![0; 65])
 }
@@ -55,16 +61,12 @@ pub async fn sign(context: TangleClient, keygen_id: u64, data: Vec<u8>) -> Resul
     id = 2,
     params(keygen_id, new_t),
     event_listener(
-        listener = TangleEventListener<TangleClient, JobCalled>,
+        listener = TangleEventListener<MyContext, JobCalled>,
         pre_processor = services_pre_processor,
         post_processor = services_post_processor,
     ),
 )]
-pub fn refresh(
-    context: TangleClient,
-    keygen_id: u64,
-    new_t: Option<u8>,
-) -> Result<Vec<u64>, Error> {
+pub fn refresh(context: MyContext, keygen_id: u64, new_t: Option<u8>) -> Result<Vec<u64>, Error> {
     let _ = (keygen_id, new_t);
     Ok(vec![0; 33])
 }
@@ -72,11 +74,11 @@ pub fn refresh(
 /// Say hello to someone or the world.
 #[job(id = 3, params(who),
     event_listener(
-        listener = TangleEventListener<TangleClient, JobCalled>,
+        listener = TangleEventListener<MyContext, JobCalled>,
         pre_processor = services_pre_processor,
         post_processor = services_post_processor,
     ))]
-pub fn say_hello(context: TangleClient, who: Option<String>) -> Result<String, Error> {
+pub fn say_hello(context: MyContext, who: Option<String>) -> Result<String, Error> {
     match who {
         Some(who) => Ok(format!("Hello, {}!", who)),
         None => Ok("Hello, World!".to_string()),
@@ -102,7 +104,7 @@ pub fn on_request(nft_id: u64);
     job_id = 0,
     params(n, t, msgs),
     event_listener(
-        listener = TangleEventListener<TangleClient, JobCalled>,
+        listener = TangleEventListener<MyContext, JobCalled>,
         pre_processor = services_pre_processor,
         post_processor = services_post_processor,
     ),
@@ -111,7 +113,7 @@ pub fn on_request(nft_id: u64);
     verifier(evm = "KeygenContract")
 )]
 fn report_keygen(
-    context: TangleClient,
+    context: MyContext,
     n: u16,
     t: u16,
     msgs: Vec<Vec<u8>>,
@@ -122,13 +124,13 @@ fn report_keygen(
 
 #[report(
     params(uptime, response_time, error_rate),
-    event_listener(listener = TangleEventListener<TangleClient, JobResultSubmitted>, pre_processor = services_pre_processor,),
+    event_listener(listener = TangleEventListener<MyContext, JobResultSubmitted>, pre_processor = services_pre_processor,),
     report_type = "qos",
     interval = 3600,
     metric_thresholds(uptime = 99, response_time = 1000, error_rate = 5)
 )]
 fn report_service_health(
-    context: TangleClient,
+    context: MyContext,
     uptime: u64,
     response_time: u64,
     error_rate: u64,
diff --git a/sdk/src/contexts/mod.rs b/sdk/src/contexts/mod.rs
index 8e4a801fb..73f01c5f5 100644
--- a/sdk/src/contexts/mod.rs
+++ b/sdk/src/contexts/mod.rs
@@ -8,6 +8,7 @@
 //! # Example
 //!
 //! ```rust,no_run
+//! use gadget_context_derive::TangleClientContext;
 //! use gadget_sdk::config::StdGadgetConfiguration;
 //! use gadget_sdk::contexts::KeystoreContext;
 //! use gadget_sdk::event_listener::tangle::jobs::{
@@ -19,12 +20,14 @@
 //!
 //! // This your struct that encapsulates all the things you need from outside world.
 //! // By deriving KeystoreContext, you can now access the keystore client from your struct.
-//! #[derive(Clone, Debug, KeystoreContext)]
+//! #[derive(Clone, Debug, KeystoreContext, TangleClientContext)]
 //! struct MyContext {
 //!     foo: String,
 //!     bar: u64,
 //!     #[config]
 //!     sdk_config: StdGadgetConfiguration,
+//!     #[call_id]
+//!     call_id: Option<u64>,
 //! }
 //!
 //! #[job(
diff --git a/sdk/src/contexts/tangle_client.rs b/sdk/src/contexts/tangle_client.rs
index 031dbb5c2..3c24c3c39 100644
--- a/sdk/src/contexts/tangle_client.rs
+++ b/sdk/src/contexts/tangle_client.rs
@@ -7,4 +7,6 @@ pub trait TangleClientContext {
     fn tangle_client(
         &self,
     ) -> impl Future<Output = color_eyre::Result<subxt::OnlineClient<Self::Config>, subxt::Error>>;
+
+    fn set_call_id(&mut self, call_id: u64);
 }
diff --git a/sdk/src/event_listener/tangle/jobs.rs b/sdk/src/event_listener/tangle/jobs.rs
index 9592b402d..3aa052381 100644
--- a/sdk/src/event_listener/tangle/jobs.rs
+++ b/sdk/src/event_listener/tangle/jobs.rs
@@ -1,15 +1,16 @@
+use crate::contexts::TangleClientContext;
 use crate::event_listener::tangle::{EventMatcher, TangleEvent, TangleResult};
 use crate::Error;
 use std::any::Any;
 use tangle_subxt::tangle_testnet_runtime::api;
 use tangle_subxt::tangle_testnet_runtime::api::services::events::{JobCalled, JobResultSubmitted};
 
-pub async fn services_pre_processor<C, E: EventMatcher<Output: Clone>>(
+pub async fn services_pre_processor<C: TangleClientContext, E: EventMatcher<Output: Clone>>(
     event: TangleEvent<C, E>,
 ) -> Result<TangleEvent<C, E>, Error> {
     let TangleEvent {
         evt,
-        context,
+        mut context,
         block_number,
         signer,
         client,
@@ -29,10 +30,14 @@ pub async fn services_pre_processor<C, E: EventMatcher<Output: Clone>>(
             return Err(Error::SkipPreProcessedType);
         };
 
-    crate::info!("Pre-processing event for sid/bid = {service_id}/{job_id} ...");
+    crate::info!("Pre-processing event for service-id/job-id = {service_id}/{job_id} ...");
 
     if event_job_id == job_id && event_service_id == service_id {
-        crate::info!("Found actionable event for sid/bid = {event_service_id}/{event_job_id} ...");
+        crate::info!(
+            "Found actionable event for service-id/job-id = {event_service_id}/{event_job_id} ..."
+        );
+        // Set the call ID that way the user can access it in the job function
+        context.set_call_id(event_call_id);
         return Ok(TangleEvent {
             evt: *boxed_item.downcast().unwrap(),
             context,
@@ -64,7 +69,7 @@ pub async fn services_post_processor<R: serde::Serialize>(
     let response = api::tx().services().submit_result(
         service_id,
         call_id,
-        vec![dbg!(blueprint_serde::to_field(results)?)],
+        vec![blueprint_serde::to_field(results)?],
     );
     let _ = crate::tx::tangle::send(&client, &signer, &response)
         .await
diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs
index a2a32617a..2f78b2e46 100644
--- a/sdk/src/lib.rs
+++ b/sdk/src/lib.rs
@@ -77,6 +77,7 @@ pub use color_eyre;
 pub use error::Error;
 pub use futures;
 pub use gadget_blueprint_proc_macro::*;
+pub use k256;
 pub use libp2p;
 pub use parking_lot;
 pub use round_based;
diff --git a/sdk/src/utils/hashing.rs b/sdk/src/utils/hashing.rs
index 6c16ea2d0..66c10498f 100644
--- a/sdk/src/utils/hashing.rs
+++ b/sdk/src/utils/hashing.rs
@@ -2,7 +2,7 @@
 macro_rules! compute_sha256_hash {
     ($($data:expr),*) => {
         {
-            use k256::sha2::{Digest, Sha256};
+            use gadget_sdk::k256::sha2::{Digest, Sha256};
             let mut hasher = Sha256::default();
             $(hasher.update($data);)*
             let result = hasher.finalize();


Test patch:
diff --git a/blueprint-test-utils/src/tangle/mod.rs b/blueprint-test-utils/src/tangle/mod.rs
index 2a0d05818..6fdf8bac5 100644
--- a/blueprint-test-utils/src/tangle/mod.rs
+++ b/blueprint-test-utils/src/tangle/mod.rs
@@ -12,6 +12,7 @@ pub fn run() -> Result<SubstrateNode, Error> {
             &tangle_from_env,
             "../tangle/target/release/tangle",
             "../../tangle/target/release/tangle",
+            "../../../tangle/target/release/tangle",
         ])
         .arg("validator")
         .arg_val("rpc-cors", "all")
@@ -56,7 +57,8 @@ macro_rules! test_tangle_blueprint {
         $T:tt,
         $job_id:tt,
         [$($inputs:expr),*],
-        [$($expected_output:expr),*]
+        [$($expected_output:expr),*],
+        $call_id:expr,
     ) => {
         ::blueprint_test_utils::tangle_blueprint_test_template!(
             $N,
@@ -77,6 +79,7 @@ macro_rules! test_tangle_blueprint {
                     service_id,
                     $job_id as ::blueprint_test_utils::Job,
                     job_args,
+                    $call_id,
                 )
                 .await
                 .expect("Failed to submit job");
@@ -113,7 +116,8 @@ macro_rules! test_tangle_blueprint {
         $job_id:tt,
         [$($input:expr),*],
         [$($expected_output:expr),*]
+        $call_id:expr,
     ) => {
-        ::blueprint_test_utils::test_tangle_blueprint!($N, $N, $job_id, [$($input),+], [$($expected_output),+]);
+        ::blueprint_test_utils::test_tangle_blueprint!($N, $N, $job_id, [$($input),+], [$($expected_output),+], $call_id);
     };
 }
diff --git a/blueprint-test-utils/src/tangle/transactions.rs b/blueprint-test-utils/src/tangle/transactions.rs
index 41ae5786d..76a710c18 100644
--- a/blueprint-test-utils/src/tangle/transactions.rs
+++ b/blueprint-test-utils/src/tangle/transactions.rs
@@ -19,6 +19,8 @@ use gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::services::events::{Jo
 use subxt::tx::TxProgress;
 use gadget_sdk::clients::tangle::runtime::TangleConfig;
 use gadget_sdk::subxt_core::tx::signer::Signer;
+use gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::Asset;
+use gadget_sdk::tangle_subxt::tangle_testnet_runtime::api::services::calls::types::request::{Assets, PaymentAsset};
 use crate::TestClient;
 
 /// Deploy a new MBSM revision and returns the result.
@@ -138,6 +140,7 @@ pub async fn submit_job(
     service_id: u64,
     job_id: Job,
     job_params: Args,
+    call_id: u64,
 ) -> Result<JobCalled, Box<dyn Error>> {
     let call = api::tx().services().call(service_id, job_id, job_params);
     let events = client
@@ -153,6 +156,7 @@ pub async fn submit_job(
         if job_called.service_id == service_id
             && job_called.job == job_id
             && user.account_id() == job_called.caller
+            && job_called.call_id == call_id
         {
             return Ok(job_called);
         }
@@ -175,8 +179,9 @@ pub async fn request_service(
         test_nodes.clone(),
         test_nodes,
         Default::default(),
-        vec![0],
+        Assets::from([0]),
         1000,
+        PaymentAsset::from(Asset::Custom(0)),
         value,
     );
     let res = client
diff --git a/blueprint-test-utils/tnt-core/MasterBlueprintServiceManager.hex b/blueprint-test-utils/tnt-core/MasterBlueprintServiceManager.hex
index 2e3d5d3ef..5af0510ec 100644
--- a/blueprint-test-utils/tnt-core/MasterBlueprintServiceManager.hex
+++ b/blueprint-test-utils/tnt-core/MasterBlueprintServiceManager.hex
@@ -1,1 +1,1 @@
-0x608060405234801561001057600080fd5b506002805460ff191690556124b18061002a6000396000f3fe60806040526004361061019c5760003560e01c8063727391f2116100ec578063a4d91fe91161008a578063b89af90411610064578063b89af9041461047c578063b9315d3a1461048f578063c97008e0146104a2578063d547741f146104c257600080fd5b8063a4d91fe914610438578063a6a7853814610456578063a8f7c5ed1461046957600080fd5b80638e6f8c60116100c65780638e6f8c60146103c257806391d14854146103e2578063987ab9db14610402578063a217fddf1461042357600080fd5b8063727391f21461036d57806382a1ece414610380578063884673ac146103a057600080fd5b80632f2ff15d11610159578063410fdec811610133578063410fdec81461030f5780634984ee6b1461032f5780634efb4b8b146103425780635c975abb1461035557600080fd5b80632f2ff15d146102af57806333db3920146102cf57806336568abe146102ef57600080fd5b806301ffc9a7146101a15780630633da77146101d65780630d0dd399146101f85780631fb27b3414610230578063248a9ca314610250578063274ef0151461028f575b600080fd5b3480156101ad57600080fd5b506101c16101bc3660046116e7565b6104e2565b60405190151581526020015b60405180910390f35b3480156101e257600080fd5b506101f66101f136600461174d565b610519565b005b34801561020457600080fd5b50600054610218906001600160a01b031681565b6040516001600160a01b0390911681526020016101cd565b34801561023c57600080fd5b506101f661024b3660046117ac565b610640565b34801561025c57600080fd5b5061028161026b3660046117f9565b6000908152600160208190526040909120015490565b6040519081526020016101cd565b34801561029b57600080fd5b506102186102aa366004611812565b610745565b3480156102bb57600080fd5b506101f66102ca366004611845565b6107d9565b3480156102db57600080fd5b506101f66102ea3660046118ce565b610805565b3480156102fb57600080fd5b506101f661030a366004611845565b610926565b34801561031b57600080fd5b506101f661032a36600461194b565b61095e565b6101f661033d3660046119a8565b610a62565b6101f6610350366004611a71565b610b72565b34801561036157600080fd5b5060025460ff166101c1565b6101f661037b366004611b4e565b610c9d565b34801561038c57600080fd5b506101f661039b366004611bc4565b610da8565b3480156103ac57600080fd5b5061021860008051602061248583398151915281565b3480156103ce57600080fd5b506102186103dd366004611812565b610ed1565b3480156103ee57600080fd5b506101c16103fd366004611845565b610f20565b34801561040e57600080fd5b50600080516020612485833981519152610218565b34801561042f57600080fd5b50610281600081565b34801561044457600080fd5b506000546001600160a01b0316610218565b6101f66104643660046117ac565b610f4b565b6101f6610477366004611c5c565b611043565b6101f661048a366004611d34565b61116b565b6101f661049d366004611d8f565b611240565b3480156104ae57600080fd5b506101f66104bd3660046118ce565b611357565b3480156104ce57600080fd5b506101f66104dd366004611845565b611467565b60006001600160e01b03198216637965db0b60e01b148061051357506301ffc9a760e01b6001600160e01b03198316145b92915050565b336000805160206124858339815191521461056257336000805160206124858339815191526040516359afe8af60e11b8152600401610559929190611dfd565b60405180910390fd5b61056a61148d565b600061058060036001600160401b0386166114b3565b604051630a24e8a960e41b81526001600160401b03851660048201526001600160a01b0384811660248301529192509082169063a24e8a9090604401600060405180830381600087803b1580156105d657600080fd5b505af11580156105ea573d6000803e3d6000fd5b50506040516001600160a01b03851681526001600160401b038087169350871691507f5267932e6c1b678e74efba1e4d6c8b3fd7504cf0fa8eea462efac2c590a21d56906020015b60405180910390a350505050565b336000805160206124858339815191521461068057336000805160206124858339815191526040516359afe8af60e11b8152600401610559929190611dfd565b61068861148d565b600061069e60036001600160401b0385166114b3565b60405163434698bb60e01b81529091506001600160a01b0382169063434698bb906106cd908590600401611f18565b600060405180830381600087803b1580156106e757600080fd5b505af11580156106fb573d6000803e3d6000fd5b50505050826001600160401b03167fde25c2b146db36cbc91715fdf29cb0eb556eed7678e0bf13563bf7050e8f0bbd836040516107389190611f18565b60405180910390a2505050565b60008061075c60036001600160401b0386166114b3565b6040516374ceeb5560e01b81526001600160401b03851660048201529091506001600160a01b038216906374ceeb55906024015b602060405180830381865afa1580156107ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d19190611f2b565b949350505050565b600082815260016020819052604090912001546107f5816114c6565b6107ff83836114d3565b50505050565b336000805160206124858339815191521461084557336000805160206124858339815191526040516359afe8af60e11b8152600401610559929190611dfd565b61084d61148d565b600061086360036001600160401b0389166114b3565b604051630af7d74360e01b81529091506001600160a01b03821690630af7d7439061089a9089908990899089908990600401611f48565b600060405180830381600087803b1580156108b457600080fd5b505af11580156108c8573d6000803e3d6000fd5b50505050856001600160401b0316876001600160401b03167f7d2ac4c1544e07516def74ce3ada12ff791868ed07d6da82694ae2c0342c8371878787876040516109159493929190611f82565b60405180910390a350505050505050565b6001600160a01b038116331461094f5760405163334bd91960e11b815260040160405180910390fd5b610959828261154c565b505050565b336000805160206124858339815191521461099e57336000805160206124858339815191526040516359afe8af60e11b8152600401610559929190611dfd565b6109a661148d565b60006109bc60036001600160401b0386166114b3565b60405163410e3df160e11b81529091506001600160a01b0382169063821c7be2906109ed9086908690600401611fac565b600060405180830381600087803b158015610a0757600080fd5b505af1158015610a1b573d6000803e3d6000fd5b50505050816001600160401b0316846001600160401b03167f4ca48d7cb411035a931da7a686ea77bc413069de12d844c47daf9242b765cba4856040516106329190611f18565b3360008051602061248583398151915214610aa257336000805160206124858339815191526040516359afe8af60e11b8152600401610559929190611dfd565b610aaa61148d565b6000610ac060036001600160401b0389166114b3565b604051639838caa360e01b81529091506001600160a01b03821690639838caa390610af79089908990899089908990600401611fd7565b600060405180830381600087803b158015610b1157600080fd5b505af1158015610b25573d6000803e3d6000fd5b50506040805160ff891681526001600160401b038881166020830152808b1694508b1692507fa9aaaef624d196d961d053fc29d4331e5cb45d62195d99814d29a94f46fa9d4b9101610915565b3360008051602061248583398151915214610bb257336000805160206124858339815191526040516359afe8af60e11b8152600401610559929190611dfd565b610bba61148d565b6000610bd060036001600160401b038d166114b3565b6040516332e72cfd60e11b81529091506001600160a01b038216906365ce59fa90610c0f908d908d908d908d908d908d908d908d908d90600401612061565b600060405180830381600087803b158015610c2957600080fd5b505af1158015610c3d573d6000803e3d6000fd5b50506040516001600160401b0385811682526001600160a01b038d169350808e1692508e16907f43200f1363ce7ac3780a4c2c5835bf74800937ca5cbc69c4f4d666cf92b0c54c9060200160405180910390a45050505050505050505050565b3360008051602061248583398151915214610cdd57336000805160206124858339815191526040516359afe8af60e11b8152600401610559929190611dfd565b610ce561148d565b6000610cfb60036001600160401b0387166114b3565b604051634e82087760e11b81529091506001600160a01b03821690639d0410ee90610d2e90879087908790600401612136565b600060405180830381600087803b158015610d4857600080fd5b505af1158015610d5c573d6000803e3d6000fd5b50505050846001600160401b03167f7814301100cb38a35027dd63a29aa14c3f73188f15cf9f027655257c45016e8785604051610d999190611f18565b60405180910390a25050505050565b3360008051602061248583398151915214610de857336000805160206124858339815191526040516359afe8af60e11b8152600401610559929190611dfd565b610df061148d565b6000610e0660036001600160401b038a166114b3565b604051636bef5a4160e11b81529091506001600160a01b0382169063d7deb48290610e3f908a908a908a908a908a908a90600401612166565b600060405180830381600087803b158015610e5957600080fd5b505af1158015610e6d573d6000803e3d6000fd5b5050604080516001600160a01b03891681526001600160401b038681166020830152808b1694508b811693508c16917f93927390d124732c66289c6e22370514ddd612a9cc6bff5671cbf2cbc15ddfe3910160405180910390a45050505050505050565b600080610ee860036001600160401b0386166114b3565b60405163052d37d360e21b81526001600160401b03851660048201529091506001600160a01b038216906314b4df4c90602401610790565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b3360008051602061248583398151915214610f8b57336000805160206124858339815191526040516359afe8af60e11b8152600401610559929190611dfd565b610f9361148d565b6000610fa960036001600160401b0385166114b3565b60405163fe0dd37160e01b81529091506001600160a01b0382169063fe0dd37190610fd8908590600401611f18565b600060405180830381600087803b158015610ff257600080fd5b505af1158015611006573d6000803e3d6000fd5b50505050826001600160401b03167febc442018e78570634a88dbac83d4677ae1a5cf8491fb0fc166a7306ba1ff051836040516107389190611f18565b336000805160206124858339815191521461108357336000805160206124858339815191526040516359afe8af60e11b8152600401610559929190611dfd565b61108b61148d565b60006110a160036001600160401b038c166114b3565b604051631be14b3160e11b81529091506001600160a01b038216906337c29662906110de908c908c908c908c908c908c908c908c906004016121b6565b600060405180830381600087803b1580156110f857600080fd5b505af115801561110c573d6000803e3d6000fd5b50505050886001600160401b03168a6001600160401b03167f08e18861659838cad8b04f4687aca1dfb93336e750acf5c3699637ed8ca4c27a8a8a8a60405161115793929190612222565b60405180910390a350505050505050505050565b33600080516020612485833981519152146111ab57336000805160206124858339815191526040516359afe8af60e11b8152600401610559929190611dfd565b6111b361148d565b6111d96001600160401b0384166111d06040840160208501612256565b600391906115b9565b506111ef60066001600160401b038516846115b9565b50826001600160401b0316826001600160a01b03167f278018182b7c51e2da72c1f465c26c12d9606099af0f08db420378344b352223836040516112339190612287565b60405180910390a3505050565b336000805160206124858339815191521461128057336000805160206124858339815191526040516359afe8af60e11b8152600401610559929190611dfd565b61128861148d565b600061129e60036001600160401b0387166114b3565b60405163bb43abd960e01b81529091506001600160a01b0382169063bb43abd9906112d19087908790879060040161242a565b600060405180830381600087803b1580156112eb57600080fd5b505af11580156112ff573d6000803e3d6000fd5b50505050826001600160401b0316856001600160401b03167f219c2fdfbe4f111dda584eefbcb4eb2370cec850e2a79e4fed3a8f708006e7a9868560405161134892919061245f565b60405180910390a35050505050565b336000805160206124858339815191521461139757336000805160206124858339815191526040516359afe8af60e11b8152600401610559929190611dfd565b61139f61148d565b60006113b560036001600160401b0389166114b3565b60405163e926cbd160e01b81529091506001600160a01b0382169063e926cbd1906113ec9089908990899089908990600401611f48565b600060405180830381600087803b15801561140657600080fd5b505af115801561141a573d6000803e3d6000fd5b50505050856001600160401b0316876001600160401b03167f0145a8acdb5e19e431ea924e2c8997b01f0869cad2a2c6927c8645a5938cf0c3878787876040516109159493929190611f82565b60008281526001602081905260409091200154611483816114c6565b6107ff838361154c565b60025460ff16156114b15760405163d93c066560e01b815260040160405180910390fd5b565b60006114bf83836115cf565b9392505050565b6114d08133611616565b50565b60006114df8383610f20565b6115445760008381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a4506001610513565b506000610513565b60006115588383610f20565b156115445760008381526001602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610513565b60006107d184846001600160a01b038516611653565b6000818152600283016020526040812054801580156115f557506115f38484611670565b155b156114bf5760405163015ab34360e11b815260048101849052602401610559565b6116208282610f20565b61164f5760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610559565b5050565b600082815260028401602052604081208290556107d1848461167c565b60006114bf8383611688565b60006114bf83836116a0565b600081815260018301602052604081205415156114bf565b600081815260018301602052604081205461154457508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610513565b6000602082840312156116f957600080fd5b81356001600160e01b0319811681146114bf57600080fd5b80356001600160401b038116811461172857600080fd5b919050565b6001600160a01b03811681146114d057600080fd5b80356117288161172d565b60008060006060848603121561176257600080fd5b61176b84611711565b925061177960208501611711565b915060408401356117898161172d565b809150509250925092565b600060c082840312156117a657600080fd5b50919050565b600080604083850312156117bf57600080fd5b6117c883611711565b915060208301356001600160401b038111156117e357600080fd5b6117ef85828601611794565b9150509250929050565b60006020828403121561180b57600080fd5b5035919050565b6000806040838503121561182557600080fd5b61182e83611711565b915061183c60208401611711565b90509250929050565b6000806040838503121561185857600080fd5b82359150602083013561186a8161172d565b809150509250929050565b60008083601f84011261188757600080fd5b5081356001600160401b0381111561189e57600080fd5b6020830191508360208285010111156118b657600080fd5b9250929050565b803560ff8116811461172857600080fd5b60008060008060008060a087890312156118e757600080fd5b6118f087611711565b95506118fe60208801611711565b945060408701356001600160401b0381111561191957600080fd5b61192589828a01611875565b90955093506119389050606088016118bd565b9150608087013590509295509295509295565b60008060006060848603121561196057600080fd5b61196984611711565b925060208401356001600160401b0381111561198457600080fd5b61199086828701611794565b92505061199f60408501611711565b90509250925092565b60008060008060008060a087890312156119c157600080fd5b6119ca87611711565b95506119d860208801611711565b94506119e6604088016118bd565b93506119f460608801611711565b925060808701356001600160401b03811115611a0f57600080fd5b611a1b89828a01611875565b979a9699509497509295939492505050565b60008083601f840112611a3f57600080fd5b5081356001600160401b03811115611a5657600080fd5b6020830191508360208260051b85010111156118b657600080fd5b60008060008060008060008060008060e08b8d031215611a9057600080fd5b611a998b611711565b9950611aa760208c01611711565b985060408b0135611ab78161172d565b975060608b01356001600160401b0380821115611ad357600080fd5b611adf8e838f01611a2d565b909950975060808d0135915080821115611af857600080fd5b611b048e838f01611875565b909750955060a08d0135915080821115611b1d57600080fd5b50611b2a8d828e01611a2d565b9094509250611b3d905060c08c01611711565b90509295989b9194979a5092959850565b60008060008060608587031215611b6457600080fd5b611b6d85611711565b935060208501356001600160401b0380821115611b8957600080fd5b611b9588838901611794565b94506040870135915080821115611bab57600080fd5b50611bb887828801611875565b95989497509550505050565b600080600080600080600060c0888a031215611bdf57600080fd5b611be888611711565b9650611bf660208901611711565b9550611c0460408901611711565b94506060880135611c148161172d565b935060808801356001600160401b03811115611c2f57600080fd5b611c3b8a828b01611a2d565b9094509250611c4e905060a08901611711565b905092959891949750929550565b600080600080600080600080600060e08a8c031215611c7a57600080fd5b611c838a611711565b9850611c9160208b01611711565b9750611c9f60408b016118bd565b9650611cad60608b01611711565b955060808a01356001600160401b0380821115611cc957600080fd5b611cd58d838e01611794565b965060a08c0135915080821115611ceb57600080fd5b611cf78d838e01611875565b909650945060c08c0135915080821115611d1057600080fd5b50611d1d8c828d01611875565b915080935050809150509295985092959850929598565b600080600060608486031215611d4957600080fd5b611d5284611711565b92506020840135611d628161172d565b915060408401356001600160401b03811115611d7d57600080fd5b84016060818703121561178957600080fd5b60008060008060808587031215611da557600080fd5b611dae85611711565b935060208501356001600160401b03811115611dc957600080fd5b611dd587828801611794565b935050611de460408601611711565b9150611df2606086016118bd565b905092959194509250565b6001600160a01b0392831681529116602082015260400190565b6000808335601e19843603018112611e2e57600080fd5b83016020810192503590506001600160401b03811115611e4d57600080fd5b8036038213156118b657600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000611e918283611e17565b60c08552611ea360c086018284611e5c565b9150506001600160401b0380611ebb60208601611711565b16602086015280611ece60408601611711565b16604086015280611ee160608601611711565b16606086015280611ef460808601611711565b16608086015280611f0760a08601611711565b1660a0860152508091505092915050565b6020815260006114bf6020830184611e85565b600060208284031215611f3d57600080fd5b81516114bf8161172d565b6001600160401b0386168152608060208201526000611f6b608083018688611e5c565b60ff94909416604083015250606001529392505050565b606081526000611f96606083018688611e5c565b60ff949094166020830152506040015292915050565b604081526000611fbf6040830185611e85565b90506001600160401b03831660208301529392505050565b60006001600160401b03808816835260ff871660208401528086166040840152506080606083015261200d608083018486611e5c565b979650505050505050565b8183526000602080850194508260005b8581101561205657813561203b8161172d565b6001600160a01b031687529582019590820190600101612028565b509495945050505050565b6001600160401b038a1681526001600160a01b038916602082015260c0604082018190528101879052600060e0600589901b830181019083018a835b8b8110156120e55785840360df190183528135368e900360be190181126120c357600080fd5b6120cf858f8301611e85565b945050602092830192919091019060010161209d565b50505082810360608401526120fb81888a611e5c565b90508281036080840152612110818688612018565b91505061212860a08301846001600160401b03169052565b9a9950505050505050505050565b6040815260006121496040830186611e85565b828103602084015261215c818587611e5c565b9695505050505050565b60006001600160401b038089168352808816602084015260018060a01b038716604084015260a060608401526121a060a084018688612018565b9150808416608084015250979650505050505050565b60006001600160401b03808b16835260ff8a16602084015280891660408401525060c060608301526121eb60c0830188611e85565b82810360808401526121fe818789611e5c565b905082810360a0840152612213818587611e5c565b9b9a5050505050505050505050565b60ff841681526001600160401b038316602082015260606040820152600061224d6060830184611e85565b95945050505050565b60006020828403121561226857600080fd5b81356114bf8161172d565b803563ffffffff8116811461172857600080fd5b602081526000823560fe198436030181126122a157600080fd5b6060602084015283016122b48180611e17565b6101008060808701526122cc61018087018385611e5c565b92506122db6020850185611e17565b9250607f19808886030160a08901526122f5858584611e5c565b94506123046040870187611e17565b94509150808886030160c089015261231d858584611e5c565b945061232c6060870187611e17565b94509150808886030160e0890152612345858584611e5c565b94506123546080870187611e17565b9450915080888603018389015261236c858584611e5c565b945061237b60a0870187611e17565b945092508088860301610120890152612395858585611e5c565b94506123a460c0870187611e17565b9450925080888603016101408901526123be858585611e5c565b94506123cd60e0870187611e17565b9650935080888603016101608901525050506123ea828483611e5c565b925050506123fa60208501611742565b6001600160a01b03811660408501525061241660408501612273565b63ffffffff81166060850152509392505050565b60608152600061243d6060830186611e85565b90506001600160401b038416602083015260ff83166040830152949350505050565b6040815260006124726040830185611e85565b905060ff83166020830152939250505056fe0000000000000000000000001111111111111111111111111111111111111111a164736f6c6343000814000a
+0x608060405234801561001057600080fd5b506002805460ff191690556125968061002a6000396000f3fe60806040526004361061019c5760003560e01c806382a1ece4116100ec578063a6a785381161008a578063b9315d3a11610064578063b9315d3a1461047c578063c97008e01461048f578063d547741f146104af578063f9e13845146104cf57600080fd5b8063a6a7853814610443578063a8f7c5ed14610456578063b89af9041461046957600080fd5b806391d14854116100c657806391d14854146103cf578063987ab9db146103ef578063a217fddf14610410578063a4d91fe91461042557600080fd5b806382a1ece41461036d578063884673ac1461038d5780638e6f8c60146103af57600080fd5b80632f2ff15d11610159578063410fdec811610133578063410fdec81461030f5780634984ee6b1461032f5780635c975abb14610342578063727391f21461035a57600080fd5b80632f2ff15d146102af57806333db3920146102cf57806336568abe146102ef57600080fd5b806301ffc9a7146101a15780630633da77146101d65780630d0dd399146101f85780631fb27b3414610230578063248a9ca314610250578063274ef0151461028f575b600080fd5b3480156101ad57600080fd5b506101c16101bc366004611707565b6104e2565b60405190151581526020015b60405180910390f35b3480156101e257600080fd5b506101f66101f136600461176d565b610519565b005b34801561020457600080fd5b50600054610218906001600160a01b031681565b6040516001600160a01b0390911681526020016101cd565b34801561023c57600080fd5b506101f661024b3660046117cc565b610640565b34801561025c57600080fd5b5061028161026b366004611819565b6000908152600160208190526040909120015490565b6040519081526020016101cd565b34801561029b57600080fd5b506102186102aa366004611832565b610745565b3480156102bb57600080fd5b506101f66102ca366004611865565b6107d9565b3480156102db57600080fd5b506101f66102ea3660046118ee565b610805565b3480156102fb57600080fd5b506101f661030a366004611865565b610926565b34801561031b57600080fd5b506101f661032a36600461196b565b61095e565b6101f661033d3660046119c8565b610a62565b34801561034e57600080fd5b5060025460ff166101c1565b6101f6610368366004611a4d565b610b72565b34801561037957600080fd5b506101f6610388366004611ac3565b610c7d565b34801561039957600080fd5b5061021860008051602061256a83398151915281565b3480156103bb57600080fd5b506102186103ca366004611832565b610da6565b3480156103db57600080fd5b506101c16103ea366004611865565b610df5565b3480156103fb57600080fd5b5060008051602061256a833981519152610218565b34801561041c57600080fd5b50610281600081565b34801561043157600080fd5b506000546001600160a01b0316610218565b6101f66104513660046117cc565b610e20565b6101f6610464366004611b8d565b610f18565b6101f6610477366004611c65565b611040565b6101f661048a366004611cc0565b611115565b34801561049b57600080fd5b506101f66104aa3660046118ee565b61122c565b3480156104bb57600080fd5b506101f66104ca366004611865565b61133c565b6101f66104dd366004611d2e565b611362565b60006001600160e01b03198216637965db0b60e01b148061051357506301ffc9a760e01b6001600160e01b03198316145b92915050565b3360008051602061256a83398151915214610562573360008051602061256a8339815191526040516359afe8af60e11b8152600401610559929190611d78565b60405180910390fd5b61056a6114ad565b600061058060036001600160401b0386166114d3565b604051630a24e8a960e41b81526001600160401b03851660048201526001600160a01b0384811660248301529192509082169063a24e8a9090604401600060405180830381600087803b1580156105d657600080fd5b505af11580156105ea573d6000803e3d6000fd5b50506040516001600160a01b03851681526001600160401b038087169350871691507f5267932e6c1b678e74efba1e4d6c8b3fd7504cf0fa8eea462efac2c590a21d56906020015b60405180910390a350505050565b3360008051602061256a83398151915214610680573360008051602061256a8339815191526040516359afe8af60e11b8152600401610559929190611d78565b6106886114ad565b600061069e60036001600160401b0385166114d3565b60405163434698bb60e01b81529091506001600160a01b0382169063434698bb906106cd908590600401611e93565b600060405180830381600087803b1580156106e757600080fd5b505af11580156106fb573d6000803e3d6000fd5b50505050826001600160401b03167fde25c2b146db36cbc91715fdf29cb0eb556eed7678e0bf13563bf7050e8f0bbd836040516107389190611e93565b60405180910390a2505050565b60008061075c60036001600160401b0386166114d3565b6040516374ceeb5560e01b81526001600160401b03851660048201529091506001600160a01b038216906374ceeb55906024015b602060405180830381865afa1580156107ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d19190611ea6565b949350505050565b600082815260016020819052604090912001546107f5816114e6565b6107ff83836114f3565b50505050565b3360008051602061256a83398151915214610845573360008051602061256a8339815191526040516359afe8af60e11b8152600401610559929190611d78565b61084d6114ad565b600061086360036001600160401b0389166114d3565b604051630af7d74360e01b81529091506001600160a01b03821690630af7d7439061089a9089908990899089908990600401611ec3565b600060405180830381600087803b1580156108b457600080fd5b505af11580156108c8573d6000803e3d6000fd5b50505050856001600160401b0316876001600160401b03167f7d2ac4c1544e07516def74ce3ada12ff791868ed07d6da82694ae2c0342c8371878787876040516109159493929190611efd565b60405180910390a350505050505050565b6001600160a01b038116331461094f5760405163334bd91960e11b815260040160405180910390fd5b610959828261156c565b505050565b3360008051602061256a8339815191521461099e573360008051602061256a8339815191526040516359afe8af60e11b8152600401610559929190611d78565b6109a66114ad565b60006109bc60036001600160401b0386166114d3565b60405163410e3df160e11b81529091506001600160a01b0382169063821c7be2906109ed9086908690600401611f27565b600060405180830381600087803b158015610a0757600080fd5b505af1158015610a1b573d6000803e3d6000fd5b50505050816001600160401b0316846001600160401b03167f4ca48d7cb411035a931da7a686ea77bc413069de12d844c47daf9242b765cba4856040516106329190611e93565b3360008051602061256a83398151915214610aa2573360008051602061256a8339815191526040516359afe8af60e11b8152600401610559929190611d78565b610aaa6114ad565b6000610ac060036001600160401b0389166114d3565b604051639838caa360e01b81529091506001600160a01b03821690639838caa390610af79089908990899089908990600401611f52565b600060405180830381600087803b158015610b1157600080fd5b505af1158015610b25573d6000803e3d6000fd5b50506040805160ff891681526001600160401b038881166020830152808b1694508b1692507fa9aaaef624d196d961d053fc29d4331e5cb45d62195d99814d29a94f46fa9d4b9101610915565b3360008051602061256a83398151915214610bb2573360008051602061256a8339815191526040516359afe8af60e11b8152600401610559929190611d78565b610bba6114ad565b6000610bd060036001600160401b0387166114d3565b604051634e82087760e11b81529091506001600160a01b03821690639d0410ee90610c0390879087908790600401611f93565b600060405180830381600087803b158015610c1d57600080fd5b505af1158015610c31573d6000803e3d6000fd5b50505050846001600160401b03167f7814301100cb38a35027dd63a29aa14c3f73188f15cf9f027655257c45016e8785604051610c6e9190611e93565b60405180910390a25050505050565b3360008051602061256a83398151915214610cbd573360008051602061256a8339815191526040516359afe8af60e11b8152600401610559929190611d78565b610cc56114ad565b6000610cdb60036001600160401b038a166114d3565b604051636bef5a4160e11b81529091506001600160a01b0382169063d7deb48290610d14908a908a908a908a908a908a90600401611fc3565b600060405180830381600087803b158015610d2e57600080fd5b505af1158015610d42573d6000803e3d6000fd5b5050604080516001600160a01b03891681526001600160401b038681166020830152808b1694508b811693508c16917f93927390d124732c66289c6e22370514ddd612a9cc6bff5671cbf2cbc15ddfe3910160405180910390a45050505050505050565b600080610dbd60036001600160401b0386166114d3565b60405163052d37d360e21b81526001600160401b03851660048201529091506001600160a01b038216906314b4df4c90602401610790565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b3360008051602061256a83398151915214610e60573360008051602061256a8339815191526040516359afe8af60e11b8152600401610559929190611d78565b610e686114ad565b6000610e7e60036001600160401b0385166114d3565b60405163fe0dd37160e01b81529091506001600160a01b0382169063fe0dd37190610ead908590600401611e93565b600060405180830381600087803b158015610ec757600080fd5b505af1158015610edb573d6000803e3d6000fd5b50505050826001600160401b03167febc442018e78570634a88dbac83d4677ae1a5cf8491fb0fc166a7306ba1ff051836040516107389190611e93565b3360008051602061256a83398151915214610f58573360008051602061256a8339815191526040516359afe8af60e11b8152600401610559929190611d78565b610f606114ad565b6000610f7660036001600160401b038c166114d3565b604051631be14b3160e11b81529091506001600160a01b038216906337c2966290610fb3908c908c908c908c908c908c908c908c9060040161204a565b600060405180830381600087803b158015610fcd57600080fd5b505af1158015610fe1573d6000803e3d6000fd5b50505050886001600160401b03168a6001600160401b03167f08e18861659838cad8b04f4687aca1dfb93336e750acf5c3699637ed8ca4c27a8a8a8a60405161102c939291906120b6565b60405180910390a350505050505050505050565b3360008051602061256a83398151915214611080573360008051602061256a8339815191526040516359afe8af60e11b8152600401610559929190611d78565b6110886114ad565b6110ae6001600160401b0384166110a560408401602085016120ea565b600391906115d9565b506110c460066001600160401b038516846115d9565b50826001600160401b0316826001600160a01b03167f278018182b7c51e2da72c1f465c26c12d9606099af0f08db420378344b35222383604051611108919061211b565b60405180910390a3505050565b3360008051602061256a83398151915214611155573360008051602061256a8339815191526040516359afe8af60e11b8152600401610559929190611d78565b61115d6114ad565b600061117360036001600160401b0387166114d3565b60405163bb43abd960e01b81529091506001600160a01b0382169063bb43abd9906111a6908790879087906004016122be565b600060405180830381600087803b1580156111c057600080fd5b505af11580156111d4573d6000803e3d6000fd5b50505050826001600160401b0316856001600160401b03167f219c2fdfbe4f111dda584eefbcb4eb2370cec850e2a79e4fed3a8f708006e7a9868560405161121d9291906122f3565b60405180910390a35050505050565b3360008051602061256a8339815191521461126c573360008051602061256a8339815191526040516359afe8af60e11b8152600401610559929190611d78565b6112746114ad565b600061128a60036001600160401b0389166114d3565b60405163e926cbd160e01b81529091506001600160a01b0382169063e926cbd1906112c19089908990899089908990600401611ec3565b600060405180830381600087803b1580156112db57600080fd5b505af11580156112ef573d6000803e3d6000fd5b50505050856001600160401b0316876001600160401b03167f0145a8acdb5e19e431ea924e2c8997b01f0869cad2a2c6927c8645a5938cf0c3878787876040516109159493929190611efd565b60008281526001602081905260409091200154611358816114e6565b6107ff838361156c565b3360008051602061256a833981519152146113a2573360008051602061256a8339815191526040516359afe8af60e11b8152600401610559929190611d78565b6113aa6114ad565b60006113c060036001600160401b0385166114d3565b604051638b24806560e01b81529091506001600160a01b03821690638b248065906113ef90859060040161242d565b600060405180830381600087803b15801561140957600080fd5b505af115801561141d573d6000803e3d6000fd5b506114329250505060408301602084016120ea565b6001600160a01b03166114486020840184612523565b6001600160401b039081169085167f4def5362750954077cbc9dad1ab88058dd6d0ed894ba05f8f147a7a2fe204b2261148760c0870160a08801612523565b8660c0018761010001356040516114a09392919061253e565b60405180910390a4505050565b60025460ff16156114d15760405163d93c066560e01b815260040160405180910390fd5b565b60006114df83836115ef565b9392505050565b6114f08133611636565b50565b60006114ff8383610df5565b6115645760008381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a4506001610513565b506000610513565b60006115788383610df5565b156115645760008381526001602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610513565b60006107d184846001600160a01b038516611673565b60008181526002830160205260408120548015801561161557506116138484611690565b155b156114df5760405163015ab34360e11b815260048101849052602401610559565b6116408282610df5565b61166f5760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610559565b5050565b600082815260028401602052604081208290556107d1848461169c565b60006114df83836116a8565b60006114df83836116c0565b600081815260018301602052604081205415156114df565b600081815260018301602052604081205461156457508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610513565b60006020828403121561171957600080fd5b81356001600160e01b0319811681146114df57600080fd5b80356001600160401b038116811461174857600080fd5b919050565b6001600160a01b03811681146114f057600080fd5b80356117488161174d565b60008060006060848603121561178257600080fd5b61178b84611731565b925061179960208501611731565b915060408401356117a98161174d565b809150509250925092565b600060c082840312156117c657600080fd5b50919050565b600080604083850312156117df57600080fd5b6117e883611731565b915060208301356001600160401b0381111561180357600080fd5b61180f858286016117b4565b9150509250929050565b60006020828403121561182b57600080fd5b5035919050565b6000806040838503121561184557600080fd5b61184e83611731565b915061185c60208401611731565b90509250929050565b6000806040838503121561187857600080fd5b82359150602083013561188a8161174d565b809150509250929050565b60008083601f8401126118a757600080fd5b5081356001600160401b038111156118be57600080fd5b6020830191508360208285010111156118d657600080fd5b9250929050565b803560ff8116811461174857600080fd5b60008060008060008060a0878903121561190757600080fd5b61191087611731565b955061191e60208801611731565b945060408701356001600160401b0381111561193957600080fd5b61194589828a01611895565b90955093506119589050606088016118dd565b9150608087013590509295509295509295565b60008060006060848603121561198057600080fd5b61198984611731565b925060208401356001600160401b038111156119a457600080fd5b6119b0868287016117b4565b9250506119bf60408501611731565b90509250925092565b60008060008060008060a087890312156119e157600080fd5b6119ea87611731565b95506119f860208801611731565b9450611a06604088016118dd565b9350611a1460608801611731565b925060808701356001600160401b03811115611a2f57600080fd5b611a3b89828a01611895565b979a9699509497509295939492505050565b60008060008060608587031215611a6357600080fd5b611a6c85611731565b935060208501356001600160401b0380821115611a8857600080fd5b611a94888389016117b4565b94506040870135915080821115611aaa57600080fd5b50611ab787828801611895565b95989497509550505050565b600080600080600080600060c0888a031215611ade57600080fd5b611ae788611731565b9650611af560208901611731565b9550611b0360408901611731565b94506060880135611b138161174d565b935060808801356001600160401b0380821115611b2f57600080fd5b818a0191508a601f830112611b4357600080fd5b813581811115611b5257600080fd5b8b60208260051b8501011115611b6757600080fd5b602083019550809450505050611b7f60a08901611731565b905092959891949750929550565b600080600080600080600080600060e08a8c031215611bab57600080fd5b611bb48a611731565b9850611bc260208b01611731565b9750611bd060408b016118dd565b9650611bde60608b01611731565b955060808a01356001600160401b0380821115611bfa57600080fd5b611c068d838e016117b4565b965060a08c0135915080821115611c1c57600080fd5b611c288d838e01611895565b909650945060c08c0135915080821115611c4157600080fd5b50611c4e8c828d01611895565b915080935050809150509295985092959850929598565b600080600060608486031215611c7a57600080fd5b611c8384611731565b92506020840135611c938161174d565b915060408401356001600160401b03811115611cae57600080fd5b8401606081870312156117a957600080fd5b60008060008060808587031215611cd657600080fd5b611cdf85611731565b935060208501356001600160401b03811115611cfa57600080fd5b611d06878288016117b4565b935050611d1560408601611731565b9150611d23606086016118dd565b905092959194509250565b60008060408385031215611d4157600080fd5b611d4a83611731565b915060208301356001600160401b03811115611d6557600080fd5b8301610120818603121561188a57600080fd5b6001600160a01b0392831681529116602082015260400190565b6000808335601e19843603018112611da957600080fd5b83016020810192503590506001600160401b03811115611dc857600080fd5b8036038213156118d657600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000611e0c8283611d92565b60c08552611e1e60c086018284611dd7565b9150506001600160401b0380611e3660208601611731565b16602086015280611e4960408601611731565b16604086015280611e5c60608601611731565b16606086015280611e6f60808601611731565b16608086015280611e8260a08601611731565b1660a0860152508091505092915050565b6020815260006114df6020830184611e00565b600060208284031215611eb857600080fd5b81516114df8161174d565b6001600160401b0386168152608060208201526000611ee6608083018688611dd7565b60ff94909416604083015250606001529392505050565b606081526000611f11606083018688611dd7565b60ff949094166020830152506040015292915050565b604081526000611f3a6040830185611e00565b90506001600160401b03831660208301529392505050565b60006001600160401b03808816835260ff8716602084015280861660408401525060806060830152611f88608083018486611dd7565b979650505050505050565b604081526000611fa66040830186611e00565b8281036020840152611fb9818587611dd7565b9695505050505050565b6001600160401b0387811682528681166020808401919091526001600160a01b03878116604085015260a06060850181905284018690526000928792909160c08601855b8981101561202e57853561201a8161174d565b831682529483019490830190600101612007565b5080955050505080851660808501525050979650505050505050565b60006001600160401b03808b16835260ff8a16602084015280891660408401525060c0606083015261207f60c0830188611e00565b8281036080840152612092818789611dd7565b905082810360a08401526120a7818587611dd7565b9b9a5050505050505050505050565b60ff841681526001600160401b03831660208201526060604082015260006120e16060830184611e00565b95945050505050565b6000602082840312156120fc57600080fd5b81356114df8161174d565b803563ffffffff8116811461174857600080fd5b602081526000823560fe1984360301811261213557600080fd5b6060602084015283016121488180611d92565b61010080608087015261216061018087018385611dd7565b925061216f6020850185611d92565b9250607f19808886030160a0890152612189858584611dd7565b94506121986040870187611d92565b94509150808886030160c08901526121b1858584611dd7565b94506121c06060870187611d92565b94509150808886030160e08901526121d9858584611dd7565b94506121e86080870187611d92565b94509150808886030183890152612200858584611dd7565b945061220f60a0870187611d92565b945092508088860301610120890152612229858585611dd7565b945061223860c0870187611d92565b945092508088860301610140890152612252858585611dd7565b945061226160e0870187611d92565b96509350808886030161016089015250505061227e828483611dd7565b9250505061228e60208501611762565b6001600160a01b0381166040850152506122aa60408501612107565b63ffffffff81166060850152509392505050565b6060815260006122d16060830186611e00565b90506001600160401b038416602083015260ff83166040830152949350505050565b6040815260006123066040830185611e00565b905060ff831660208301529392505050565b6000808335601e1984360301811261232f57600080fd5b83016020810192503590506001600160401b0381111561234e57600080fd5b8060051b36038213156118d657600080fd5b81835260006020808501808196508560051b81019150846000805b888110156123ba578385038a52823560be1989360301811261239b578283fd5b6123a7868a8301611e00565b9a87019a9550509185019160010161237b565b509298975050505050505050565b8183526000602080850194508260005b858110156124065781356123eb8161174d565b6001600160a01b0316875295820195908201906001016123d8565b509495945050505050565b80356002811061242057600080fd5b8252602090810135910152565b602081526001600160401b0361244283611731565b166020820152600061245660208401611762565b6001600160a01b0381166040840152506124736040840184612318565b61012080606086015261248b61014086018385612360565b925061249a6060870187611d92565b9250601f19808786030160808801526124b4858584611dd7565b94506124c36080890189612318565b94509150808786030160a0880152506124dd8484836123c8565b9350506124ec60a08701611731565b6001600160401b03811660c0870152915061250d60e0860160c08801612411565b6101008601358186015250508091505092915050565b60006020828403121561253557600080fd5b6114df82611731565b6001600160401b03841681526080810161255b6020830185612411565b82606083015294935050505056fe0000000000000000000000001111111111111111111111111111111111111111a164736f6c6343000814000a
\ No newline at end of file
diff --git a/blueprints/incredible-squaring/tests/call_id.rs b/blueprints/incredible-squaring/tests/call_id.rs
new file mode 100644
index 000000000..ff2e257ac
--- /dev/null
+++ b/blueprints/incredible-squaring/tests/call_id.rs
@@ -0,0 +1,87 @@
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering;
+
+pub use blueprint_test_utils::{
+    get_blueprint_base_dir, read_cargo_toml_file, run_test_blueprint_manager, setup_log,
+    submit_job, tangle, wait_for_completion_of_tangle_job, Args, Job, Opts,
+};
+
+use blueprint_test_utils::test_ext::new_test_ext_blueprint_manager;
+use blueprint_test_utils::{InputValue, OutputValue};
+
+#[gadget_sdk::tokio::test(crate = "::gadget_sdk::tokio")]
+async fn call_id_valid() {
+    const ITERATIONS: u8 = 5;
+    static CALL_ID: AtomicU64 = AtomicU64::new(0);
+
+    setup_log();
+
+    const N: usize = 1;
+    const JOB_ID: usize = 1;
+
+    new_test_ext_blueprint_manager::<N, 1, String, _, _>(String::new(), run_test_blueprint_manager)
+        .await
+        .execute_with_async(|client, handles, blueprint| async move {
+            let keypair = handles[0].sr25519_id().clone();
+            let selected_service = &blueprint.services[0];
+            let service_id = selected_service.id;
+
+            for _ in 0..ITERATIONS {
+                gadget_sdk::info!("Submitting job {JOB_ID} with service ID {service_id}");
+
+                let expected_call_id = CALL_ID.load(Ordering::Relaxed);
+                let job_args = vec![InputValue::Uint64(expected_call_id)];
+
+                let job = submit_job(
+                    client,
+                    &keypair,
+                    service_id,
+                    Job::from(JOB_ID as u8),
+                    job_args,
+                    expected_call_id,
+                )
+                .await
+                .expect("Failed to submit job");
+
+                let call_id = job.call_id;
+
+                gadget_sdk::info!(
+                    "Submitted job {JOB_ID} with service ID {service_id} has call id {call_id}"
+                );
+
+                let job_results = wait_for_completion_of_tangle_job(client, service_id, call_id, N)
+                    .await
+                    .expect("Failed to wait for job completion");
+
+                assert_eq!(job_results.service_id, service_id);
+                assert_eq!(job_results.call_id, call_id);
+
+                let expected_outputs = vec![OutputValue::Uint64(0)];
+                if expected_outputs.is_empty() {
+                    gadget_sdk::info!("No expected outputs specified, skipping verification");
+                    return;
+                }
+
+                assert_eq!(
+                    job_results.result.len(),
+                    expected_outputs.len(),
+                    "Number of outputs doesn't match expected"
+                );
+
+                for (result, expected) in job_results
+                    .result
+                    .into_iter()
+                    .zip(expected_outputs.into_iter())
+                {
+                    assert_eq!(result, expected);
+                }
+
+                gadget_sdk::info!(
+                    "*** Finished iteration {} ***",
+                    CALL_ID.load(Ordering::Relaxed)
+                );
+                CALL_ID.fetch_add(1, Ordering::Relaxed);
+            }
+        })
+        .await
+}
diff --git a/blueprints/incredible-squaring/tests/e2e.rs b/blueprints/incredible-squaring/tests/e2e.rs
index 7446c2c67..3c5ce5f05 100644
--- a/blueprints/incredible-squaring/tests/e2e.rs
+++ b/blueprints/incredible-squaring/tests/e2e.rs
@@ -4,8 +4,10 @@ const SQUARING_JOB_ID: usize = 0;
 const N: usize = 5;
 
 test_tangle_blueprint!(
-    N,                         // Number of nodes
+    N, // Number of nodes
+    N,
     SQUARING_JOB_ID,           // Job ID
     [InputValue::Uint64(5)],   // Inputs
-    [OutputValue::Uint64(25)]  // Expected output: input squared
+    [OutputValue::Uint64(25)], // Expected output: input squared
+    0,
 );
diff --git a/macros/context-derive/tests/ui/basic.rs b/macros/context-derive/tests/ui/basic.rs
index a07eeb6bc..89d1dda3e 100644
--- a/macros/context-derive/tests/ui/basic.rs
+++ b/macros/context-derive/tests/ui/basic.rs
@@ -20,6 +20,8 @@ struct MyContext {
     #[config]
     config: StdGadgetConfiguration,
     store: Arc<LocalDatabase<u64>>,
+    #[call_id]
+    call_id: Option<u64>,
 }
 
 #[allow(dead_code)]
@@ -29,6 +31,7 @@ fn main() {
             foo: "bar".to_string(),
             config: GadgetConfiguration::default(),
             store: Arc::new(LocalDatabase::open("test.json")),
+            call_id: None,
         };
 
         // Test existing context functions
diff --git a/macros/context-derive/tests/ui/generic_struct.rs b/macros/context-derive/tests/ui/generic_struct.rs
index c0eecfd47..6ee6f46b4 100644
--- a/macros/context-derive/tests/ui/generic_struct.rs
+++ b/macros/context-derive/tests/ui/generic_struct.rs
@@ -10,6 +10,8 @@ struct MyContext<T, U> {
     bar: U,
     #[config]
     sdk_config: StdGadgetConfiguration,
+    #[call_id]
+    call_id: Option<u64>,
 }
 
 #[allow(dead_code)]
@@ -19,6 +21,7 @@ fn main() {
             foo: "bar".to_string(),
             bar: 42,
             sdk_config: GadgetConfiguration::default(),
+            call_id: None,
         };
         let _keystore = ctx.keystore();
         let _evm_provider = ctx.evm_provider().await.unwrap();
diff --git a/macros/context-derive/tests/ui/unnamed_fields.rs b/macros/context-derive/tests/ui/unnamed_fields.rs
index fb63f8ae2..7189ec466 100644
--- a/macros/context-derive/tests/ui/unnamed_fields.rs
+++ b/macros/context-derive/tests/ui/unnamed_fields.rs
@@ -5,12 +5,16 @@ use gadget_sdk::contexts::{
 
 #[derive(KeystoreContext, EVMProviderContext, TangleClientContext, ServicesContext)]
 #[allow(dead_code)]
-struct MyContext(String, #[config] StdGadgetConfiguration);
+struct MyContext(
+    String,
+    #[config] StdGadgetConfiguration,
+    #[call_id] Option<u64>,
+);
 
 #[allow(dead_code)]
 fn main() {
     let body = async {
-        let ctx = MyContext("bar".to_string(), GadgetConfiguration::default());
+        let ctx = MyContext("bar".to_string(), GadgetConfiguration::default(), None);
         let _keystore = ctx.keystore();
         let _evm_provider = ctx.evm_provider().await;
         let tangle_client = ctx.tangle_client().await.unwrap();