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

Issue:
[TASK] Overhaul `tangle-test-utils` to support a locally-running live node, rename to `blueprint-test-utils`



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/validate_pr.yml b/.github/workflows/validate_pr.yml
index 55bf1fbbe..0e4e993ed 100644
--- a/.github/workflows/validate_pr.yml
+++ b/.github/workflows/validate_pr.yml
@@ -41,7 +41,7 @@ jobs:
             package: [
               gadget-core,
               gadget-common,
-              shell-manager,
+              blueprint-manager,
               shell-sdk,
             ]
     steps:
@@ -72,7 +72,7 @@ jobs:
             package: [
               gadget-core,
               gadget-common,
-              shell-manager,
+              blueprint-manager,
               shell-sdk,
             ]
     steps:
diff --git a/Cargo.lock b/Cargo.lock
index 14b537fdc..ff2c66902 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -93,7 +93,7 @@ dependencies = [
  "getrandom",
  "once_cell",
  "version_check",
- "zerocopy 0.7.35",
+ "zerocopy",
 ]
 
 [[package]]
@@ -122,9 +122,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
 
 [[package]]
 name = "alloy-chains"
-version = "0.1.25"
+version = "0.1.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3312b2a48f29abe7c3ea7c7fbc1f8cc6ea09b85d74b6232e940df35f2f3826fd"
+checksum = "5b515e82c8468ddb6ff8db21c78a5997442f113fd8471fd5b2261b2602dd0c67"
 dependencies = [
  "num_enum",
  "strum",
@@ -349,7 +349,7 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -458,7 +458,7 @@ dependencies = [
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -470,11 +470,11 @@ dependencies = [
  "alloy-sol-macro-input",
  "const-hex",
  "heck 0.5.0",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
  "syn-solidity",
  "tiny-keccak",
 ]
@@ -490,7 +490,7 @@ dependencies = [
  "heck 0.5.0",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
  "syn-solidity",
 ]
 
@@ -673,7 +673,7 @@ dependencies = [
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -1184,7 +1184,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
  "synstructure 0.13.1",
 ]
 
@@ -1207,7 +1207,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -1278,7 +1278,7 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c"
 dependencies = [
  "async-channel 2.3.1",
  "async-executor",
- "async-io 2.3.3",
+ "async-io 2.3.4",
  "async-lock 3.4.0",
  "blocking",
  "futures-lite 2.3.0",
@@ -1307,9 +1307,9 @@ dependencies = [
 
 [[package]]
 name = "async-io"
-version = "2.3.3"
+version = "2.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964"
+checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8"
 dependencies = [
  "async-lock 3.4.0",
  "cfg-if",
@@ -1317,11 +1317,11 @@ dependencies = [
  "futures-io",
  "futures-lite 2.3.0",
  "parking",
- "polling 3.7.2",
+ "polling 3.7.3",
  "rustix 0.38.34",
  "slab",
  "tracing",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -1350,19 +1350,19 @@ version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
 dependencies = [
- "async-io 2.3.3",
+ "async-io 2.3.4",
  "blocking",
  "futures-lite 2.3.0",
 ]
 
 [[package]]
 name = "async-process"
-version = "2.2.3"
+version = "2.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a"
+checksum = "a8a07789659a4d385b79b18b9127fc27e1a59e1e89117c78c5ea3b806f016374"
 dependencies = [
  "async-channel 2.3.1",
- "async-io 2.3.3",
+ "async-io 2.3.4",
  "async-lock 3.4.0",
  "async-signal",
  "async-task",
@@ -1372,16 +1372,16 @@ dependencies = [
  "futures-lite 2.3.0",
  "rustix 0.38.34",
  "tracing",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "async-signal"
-version = "0.2.9"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfb3634b73397aa844481f814fad23bbf07fdb0eabec10f2eb95e58944b1ec32"
+checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3"
 dependencies = [
- "async-io 2.3.3",
+ "async-io 2.3.4",
  "async-lock 3.4.0",
  "atomic-waker",
  "cfg-if",
@@ -1390,7 +1390,7 @@ dependencies = [
  "rustix 0.38.34",
  "signal-hook-registry",
  "slab",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -1438,7 +1438,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -1468,7 +1468,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -1582,7 +1582,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -1871,6 +1871,27 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "blueprint-manager"
+version = "0.1.0"
+dependencies = [
+ "color-eyre",
+ "futures",
+ "gadget-common",
+ "gadget-io",
+ "hex",
+ "reqwest",
+ "serde",
+ "sha2 0.10.8",
+ "shell-sdk",
+ "sp-core 29.0.0",
+ "structopt",
+ "tangle-environment",
+ "tangle-subxt",
+ "tokio",
+ "toml 0.8.19",
+]
+
 [[package]]
 name = "blueprint-metadata"
 version = "0.1.0"
@@ -1882,6 +1903,55 @@ dependencies = [
  "typed-builder",
 ]
 
+[[package]]
+name = "blueprint-test-utils"
+version = "0.1.0"
+dependencies = [
+ "async-trait",
+ "blueprint-manager",
+ "cargo-tangle",
+ "cargo_metadata",
+ "environment-utils",
+ "frame-support 29.0.2",
+ "frame-system 29.0.0",
+ "futures",
+ "gadget-blueprint-proc-macro-core",
+ "gadget-common",
+ "gadget-core",
+ "gadget-io",
+ "getrandom",
+ "libp2p",
+ "log",
+ "pallet-balances",
+ "pallet-evm",
+ "pallet-services",
+ "pallet-session",
+ "pallet-staking",
+ "pallet-timestamp",
+ "parity-scale-codec 3.6.12",
+ "parking_lot",
+ "sc-client-api",
+ "sc-utils",
+ "scale-info",
+ "serde",
+ "sp-api 27.0.1",
+ "sp-application-crypto 31.0.0",
+ "sp-core 29.0.0",
+ "sp-externalities 0.26.0",
+ "sp-io 31.0.0",
+ "sp-keystore 0.35.0",
+ "sp-npos-elections",
+ "sp-runtime 32.0.0",
+ "sp-session",
+ "sp-staking 27.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tangle-environment",
+ "tangle-primitives",
+ "tokio",
+ "tracing-subscriber 0.3.18",
+ "url",
+]
+
 [[package]]
 name = "bounded-collections"
 version = "0.2.0"
@@ -1970,32 +2040,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "cargo-gadget"
-version = "0.1.0"
-dependencies = [
- "alloy-contract",
- "alloy-json-abi",
- "alloy-network",
- "alloy-primitives",
- "alloy-provider",
- "alloy-rpc-types",
- "alloy-signer",
- "alloy-signer-local",
- "cargo-generate",
- "cargo_metadata",
- "clap 4.5.13",
- "clap-cargo",
- "color-eyre",
- "escargot",
- "gadget-blueprint-proc-macro-core",
- "serde_json",
- "tangle-subxt",
- "tokio",
- "tracing",
- "tracing-subscriber 0.3.18",
-]
-
 [[package]]
 name = "cargo-generate"
 version = "0.21.3"
@@ -2005,17 +2049,17 @@ dependencies = [
  "anstyle",
  "anyhow",
  "auth-git2",
- "clap 4.5.13",
+ "clap 4.5.15",
  "console",
  "dialoguer",
- "env_logger",
+ "env_logger 0.11.5",
  "fs-err",
  "git2",
  "gix-config",
  "heck 0.5.0",
  "home",
  "ignore",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
  "indicatif",
  "liquid",
  "liquid-core",
@@ -2047,6 +2091,33 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "cargo-tangle"
+version = "0.1.0"
+dependencies = [
+ "alloy-contract",
+ "alloy-json-abi",
+ "alloy-network",
+ "alloy-primitives",
+ "alloy-provider",
+ "alloy-rpc-types",
+ "alloy-signer",
+ "alloy-signer-local",
+ "cargo-generate",
+ "cargo_metadata",
+ "clap 4.5.15",
+ "clap-cargo",
+ "color-eyre",
+ "escargot",
+ "gadget-blueprint-proc-macro-core",
+ "k256",
+ "serde_json",
+ "tangle-subxt",
+ "tokio",
+ "tracing",
+ "tracing-subscriber 0.3.18",
+]
+
 [[package]]
 name = "cargo_metadata"
 version = "0.18.1"
@@ -2081,12 +2152,13 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.1.7"
+version = "1.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
+checksum = "68064e60dbf1f17005c2fde4d07c16d8baa506fd7ffed8ccab702d93617975c7"
 dependencies = [
  "jobserver",
  "libc",
+ "shlex",
 ]
 
 [[package]]
@@ -2198,9 +2270,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.13"
+version = "4.5.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
+checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -2213,14 +2285,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "23b2ea69cefa96b848b73ad516ad1d59a195cdf9263087d977f648a818c8b43e"
 dependencies = [
  "anstyle",
- "clap 4.5.13",
+ "clap 4.5.15",
 ]
 
 [[package]]
 name = "clap_builder"
-version = "4.5.13"
+version = "4.5.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
+checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
 dependencies = [
  "anstream",
  "anstyle",
@@ -2238,7 +2310,7 @@ dependencies = [
  "heck 0.5.0",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -2430,9 +2502,9 @@ dependencies = [
 
 [[package]]
 name = "core-foundation-sys"
-version = "0.8.6"
+version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 
 [[package]]
 name = "core2"
@@ -2454,13 +2526,57 @@ dependencies = [
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.12"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
 dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "cranelift-bforest"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1277fbfa94bc82c8ec4af2ded3e639d49ca5f7f3c7eeab2c66accd135ece4e70"
+dependencies = [
+ "cranelift-entity",
+]
+
+[[package]]
+name = "cranelift-codegen"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6e8c31ad3b2270e9aeec38723888fe1b0ace3bea2b06b3f749ccf46661d3220"
+dependencies = [
+ "bumpalo",
+ "cranelift-bforest",
+ "cranelift-codegen-meta",
+ "cranelift-codegen-shared",
+ "cranelift-entity",
+ "cranelift-isle",
+ "gimli 0.27.3",
+ "hashbrown 0.13.2",
+ "log",
+ "regalloc2",
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cranelift-codegen-meta"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8ac5ac30d62b2d66f12651f6b606dbdfd9c2cfd0908de6b387560a277c5c9da"
+dependencies = [
+ "cranelift-codegen-shared",
+]
+
+[[package]]
+name = "cranelift-codegen-shared"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd82b8b376247834b59ed9bdc0ddeb50f517452827d4a11bccf5937b213748b8"
+
 [[package]]
 name = "cranelift-entity"
 version = "0.95.1"
@@ -2470,6 +2586,51 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "cranelift-frontend"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64a25d9d0a0ae3079c463c34115ec59507b4707175454f0eee0891e83e30e82d"
+dependencies = [
+ "cranelift-codegen",
+ "log",
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cranelift-isle"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80de6a7d0486e4acbd5f9f87ec49912bf4c8fb6aea00087b989685460d4469ba"
+
+[[package]]
+name = "cranelift-native"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb6b03e0e03801c4b3fd8ce0758a94750c07a44e7944cc0ffbf0d3f2e7c79b00"
+dependencies = [
+ "cranelift-codegen",
+ "libc",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cranelift-wasm"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff3220489a3d928ad91e59dd7aeaa8b3de18afb554a6211213673a71c90737ac"
+dependencies = [
+ "cranelift-codegen",
+ "cranelift-entity",
+ "cranelift-frontend",
+ "itertools 0.10.5",
+ "log",
+ "smallvec",
+ "wasmparser",
+ "wasmtime-types",
+]
+
 [[package]]
 name = "crc"
 version = "3.2.1"
@@ -2623,7 +2784,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -2680,7 +2841,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strsim 0.11.1",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -2702,7 +2863,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
 dependencies = [
  "darling_core 0.20.10",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -2821,7 +2982,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -2832,7 +2993,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -2845,7 +3006,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustc_version 0.4.0",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -2882,6 +3043,16 @@ dependencies = [
  "subtle",
 ]
 
+[[package]]
+name = "directories-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
 [[package]]
 name = "dirs"
 version = "5.0.1"
@@ -2903,6 +3074,17 @@ dependencies = [
  "windows-sys 0.48.0",
 ]
 
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
 [[package]]
 name = "displaydoc"
 version = "0.2.5"
@@ -2911,7 +3093,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -2957,7 +3139,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "regex",
- "syn 2.0.72",
+ "syn 2.0.74",
  "termcolor",
  "toml 0.8.19",
  "walkdir",
@@ -3105,7 +3287,7 @@ dependencies = [
  "enum-ordinalize 4.3.0",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -3162,7 +3344,7 @@ dependencies = [
  "heck 0.4.1",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -3175,7 +3357,7 @@ dependencies = [
  "num-traits",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -3195,7 +3377,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -3208,6 +3390,19 @@ dependencies = [
  "regex",
 ]
 
+[[package]]
+name = "env_logger"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
+dependencies = [
+ "humantime",
+ "is-terminal",
+ "log",
+ "regex",
+ "termcolor",
+]
+
 [[package]]
 name = "env_logger"
 version = "0.11.5"
@@ -3471,7 +3666,7 @@ dependencies = [
  "prettyplease",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -3583,6 +3778,16 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "file-per-thread-logger"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866"
+dependencies = [
+ "env_logger 0.10.2",
+ "log",
+]
+
 [[package]]
 name = "finito"
 version = "0.1.0"
@@ -3658,13 +3863,30 @@ dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "fp-account"
+version = "1.0.0-dev"
+source = "git+https://github.com/paritytech/frontier.git?branch=polkadot-v1.7.0#c5d6daa9ffd46c0a85915526aa26d200fd635e30"
+dependencies = [
+ "hex",
+ "libsecp256k1",
+ "log",
+ "parity-scale-codec 3.6.12",
+ "scale-info",
+ "sp-core 28.0.0",
+ "sp-io 30.0.0",
+ "sp-runtime 31.0.1",
+ "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
+ "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
+]
+
 [[package]]
 name = "fp-evm"
 version = "3.0.0-dev"
 source = "git+https://github.com/paritytech/frontier.git?branch=polkadot-v1.7.0#c5d6daa9ffd46c0a85915526aa26d200fd635e30"
 dependencies = [
  "evm",
- "frame-support",
+ "frame-support 28.0.0",
  "num_enum",
  "parity-scale-codec 3.6.12",
  "scale-info",
@@ -3679,9 +3901,9 @@ name = "frame-benchmarking"
 version = "28.0.0"
 source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0#851c824dc7a8e7e6db2d0c9fb29d232f2f45198a"
 dependencies = [
- "frame-support",
- "frame-support-procedural",
- "frame-system",
+ "frame-support 28.0.0",
+ "frame-support-procedural 23.0.0",
+ "frame-system 28.0.0",
  "linregress",
  "log",
  "parity-scale-codec 3.6.12",
@@ -3699,6 +3921,62 @@ dependencies = [
  "static_assertions",
 ]
 
+[[package]]
+name = "frame-benchmarking"
+version = "29.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4090659c6aaa3c4d5b6c6ec909b4b0a25dec10ad92aad5f729efa8d5bd4d806a"
+dependencies = [
+ "frame-support 29.0.2",
+ "frame-support-procedural 24.0.0",
+ "frame-system 29.0.0",
+ "linregress",
+ "log",
+ "parity-scale-codec 3.6.12",
+ "paste",
+ "scale-info",
+ "serde",
+ "sp-api 27.0.1",
+ "sp-application-crypto 31.0.0",
+ "sp-core 29.0.0",
+ "sp-io 31.0.0",
+ "sp-runtime 32.0.0",
+ "sp-runtime-interface 25.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sp-storage 20.0.0",
+ "static_assertions",
+]
+
+[[package]]
+name = "frame-election-provider-solution-type"
+version = "13.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5c3bff645e46577c69c272733c53fa3a77d1ee6e40dfb66157bc94b0740b8fc"
+dependencies = [
+ "proc-macro-crate 3.1.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.74",
+]
+
+[[package]]
+name = "frame-election-provider-support"
+version = "29.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87da19ee99e6473cd057ead84337d20011fe5e299c6750e88e43b8b7963b8852"
+dependencies = [
+ "frame-election-provider-solution-type",
+ "frame-support 29.0.2",
+ "frame-system 29.0.0",
+ "parity-scale-codec 3.6.12",
+ "scale-info",
+ "sp-arithmetic 24.0.0",
+ "sp-core 29.0.0",
+ "sp-npos-elections",
+ "sp-runtime 32.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "frame-metadata"
 version = "15.1.0"
@@ -3733,7 +4011,7 @@ dependencies = [
  "docify",
  "environmental",
  "frame-metadata 16.0.0",
- "frame-support-procedural",
+ "frame-support-procedural 23.0.0",
  "impl-trait-for-tuples",
  "k256",
  "log",
@@ -3749,12 +4027,12 @@ dependencies = [
  "sp-core 28.0.0",
  "sp-crypto-hashing-proc-macro 0.0.0",
  "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
- "sp-genesis-builder",
- "sp-inherents",
+ "sp-genesis-builder 0.7.0",
+ "sp-inherents 26.0.0",
  "sp-io 30.0.0",
  "sp-metadata-ir 0.6.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
  "sp-runtime 31.0.1",
- "sp-staking",
+ "sp-staking 26.0.0",
  "sp-state-machine 0.35.0",
  "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
  "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
@@ -3763,6 +4041,48 @@ dependencies = [
  "tt-call",
 ]
 
+[[package]]
+name = "frame-support"
+version = "29.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e52c84b611d2049d9253f83a62ab0f093e4be5c42a7ef42ea5bb16d6611e32"
+dependencies = [
+ "aquamarine",
+ "array-bytes",
+ "bitflags 1.3.2",
+ "docify",
+ "environmental",
+ "frame-metadata 16.0.0",
+ "frame-support-procedural 24.0.0",
+ "impl-trait-for-tuples",
+ "k256",
+ "log",
+ "macro_magic",
+ "parity-scale-codec 3.6.12",
+ "paste",
+ "scale-info",
+ "serde",
+ "serde_json",
+ "smallvec",
+ "sp-api 27.0.1",
+ "sp-arithmetic 24.0.0",
+ "sp-core 29.0.0",
+ "sp-crypto-hashing-proc-macro 0.1.0",
+ "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sp-genesis-builder 0.8.0",
+ "sp-inherents 27.0.0",
+ "sp-io 31.0.0",
+ "sp-metadata-ir 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sp-runtime 32.0.0",
+ "sp-staking 27.0.0",
+ "sp-state-machine 0.36.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sp-tracing 16.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sp-weights 28.0.0",
+ "static_assertions",
+ "tt-call",
+]
+
 [[package]]
 name = "frame-support-procedural"
 version = "23.0.0"
@@ -3772,14 +4092,47 @@ dependencies = [
  "cfg-expr",
  "derive-syn-parse 0.1.5",
  "expander",
- "frame-support-procedural-tools",
+ "frame-support-procedural-tools 10.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
  "itertools 0.10.5",
  "macro_magic",
  "proc-macro-warning",
  "proc-macro2",
  "quote",
  "sp-crypto-hashing 0.0.0",
- "syn 2.0.72",
+ "syn 2.0.74",
+]
+
+[[package]]
+name = "frame-support-procedural"
+version = "24.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3bf1d648c4007d421b9677b3c893256913498fff159dc2d85022cdd9cc432f3c"
+dependencies = [
+ "Inflector",
+ "cfg-expr",
+ "derive-syn-parse 0.1.5",
+ "expander",
+ "frame-support-procedural-tools 10.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itertools 0.10.5",
+ "macro_magic",
+ "proc-macro-warning",
+ "proc-macro2",
+ "quote",
+ "sp-crypto-hashing 0.1.0",
+ "syn 2.0.74",
+]
+
+[[package]]
+name = "frame-support-procedural-tools"
+version = "10.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3363df38464c47a73eb521a4f648bfcc7537a82d70347ef8af3f73b6d019e910"
+dependencies = [
+ "frame-support-procedural-tools-derive 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro-crate 3.1.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -3787,11 +4140,22 @@ name = "frame-support-procedural-tools"
 version = "10.0.0"
 source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0#851c824dc7a8e7e6db2d0c9fb29d232f2f45198a"
 dependencies = [
- "frame-support-procedural-tools-derive",
+ "frame-support-procedural-tools-derive 11.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
  "proc-macro-crate 3.1.0",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
+]
+
+[[package]]
+name = "frame-support-procedural-tools-derive"
+version = "11.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68672b9ec6fe72d259d3879dc212c5e42e977588cdac830c76f54d9f492aeb58"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -3801,7 +4165,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -3811,7 +4175,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot
 dependencies = [
  "cfg-if",
  "docify",
- "frame-support",
+ "frame-support 28.0.0",
  "log",
  "parity-scale-codec 3.6.12",
  "scale-info",
@@ -3824,6 +4188,27 @@ dependencies = [
  "sp-weights 27.0.0",
 ]
 
+[[package]]
+name = "frame-system"
+version = "29.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bc20a793c3cec0b11165c1075fe11a255b2491f3eef8230bb3073cb296e7383"
+dependencies = [
+ "cfg-if",
+ "docify",
+ "frame-support 29.0.2",
+ "log",
+ "parity-scale-codec 3.6.12",
+ "scale-info",
+ "serde",
+ "sp-core 29.0.0",
+ "sp-io 31.0.0",
+ "sp-runtime 32.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sp-version 30.0.0",
+ "sp-weights 28.0.0",
+]
+
 [[package]]
 name = "fs-err"
 version = "2.11.0"
@@ -3975,7 +4360,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -4046,6 +4431,15 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9"
 
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
 [[package]]
 name = "gadget-blueprint-proc-macro"
 version = "0.1.0"
@@ -4054,7 +4448,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "serde_json",
- "syn 2.0.72",
+ "syn 2.0.74",
  "trybuild",
 ]
 
@@ -4125,15 +4519,12 @@ dependencies = [
  "futures",
  "futures-timer",
  "gadget-io",
- "getrandom",
  "hex",
  "log",
  "parking_lot",
  "serde",
  "sp-core 29.0.0",
  "tokio",
- "wasm-bindgen-futures",
- "wasm-bindgen-test",
 ]
 
 [[package]]
@@ -4620,7 +5011,7 @@ dependencies = [
  "futures-sink",
  "futures-util",
  "http 0.2.12",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
  "slab",
  "tokio",
  "tokio-util",
@@ -4639,7 +5030,7 @@ dependencies = [
  "futures-core",
  "futures-sink",
  "http 1.1.0",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
  "slab",
  "tokio",
  "tokio-util",
@@ -5050,9 +5441,9 @@ dependencies = [
 
 [[package]]
 name = "hyper-util"
-version = "0.1.6"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
+checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -5133,7 +5524,7 @@ version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e"
 dependencies = [
- "async-io 2.3.3",
+ "async-io 2.3.4",
  "core-foundation",
  "fnv",
  "futures",
@@ -5280,9 +5671,9 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "2.3.0"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
+checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
 dependencies = [
  "equivalent",
  "hashbrown 0.14.5",
@@ -5388,6 +5779,17 @@ version = "2.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
 
+[[package]]
+name = "is-terminal"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
+dependencies = [
+ "hermit-abi 0.4.0",
+ "libc",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "is_terminal_polyfill"
 version = "1.70.1"
@@ -5458,9 +5860,9 @@ dependencies = [
 
 [[package]]
 name = "js-sys"
-version = "0.3.69"
+version = "0.3.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -5702,7 +6104,16 @@ version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
 dependencies = [
- "log",
+ "log",
+]
+
+[[package]]
+name = "kvdb"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7d770dcb02bf6835887c3a979b5107a04ff4bbde97a5f0928d27404a155add9"
+dependencies = [
+ "smallvec",
 ]
 
 [[package]]
@@ -6187,7 +6598,7 @@ dependencies = [
  "heck 0.5.0",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -6342,9 +6753,9 @@ dependencies = [
 
 [[package]]
 name = "libz-sys"
-version = "1.1.18"
+version = "1.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e"
+checksum = "fdc53a7799a7496ebc9fd29f31f7df80e83c9bda5299768af5f9e59eeea74647"
 dependencies = [
  "cc",
  "libc",
@@ -6424,7 +6835,7 @@ checksum = "3b51f1d220e3fa869e24cfd75915efe3164bd09bb11b3165db3f37f57bf673e3"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -6497,7 +6908,7 @@ dependencies = [
  "macro_magic_core",
  "macro_magic_macros",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -6511,7 +6922,7 @@ dependencies = [
  "macro_magic_core_macros",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -6522,7 +6933,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -6533,7 +6944,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869"
 dependencies = [
  "macro_magic_core",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -6692,6 +7103,16 @@ version = "0.3.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 
+[[package]]
+name = "minicov"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c71e683cd655513b99affab7d317deb690528255a0d5f717f1024093c12b169"
+dependencies = [
+ "cc",
+ "walkdir",
+]
+
 [[package]]
 name = "minimal-lexical"
 version = "0.2.1"
@@ -6709,9 +7130,9 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "1.0.1"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
+checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
 dependencies = [
  "hermit-abi 0.3.9",
  "libc",
@@ -6797,7 +7218,7 @@ checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -7117,7 +7538,7 @@ dependencies = [
  "proc-macro-crate 3.1.0",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -7200,7 +7621,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -7263,16 +7684,70 @@ dependencies = [
  "sha2 0.10.8",
 ]
 
+[[package]]
+name = "pallet-authorship"
+version = "29.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d83773e731a1760f99684b09961ed7b92acafe335f36f08ebb8313d3b9c72e2"
+dependencies = [
+ "frame-support 29.0.2",
+ "frame-system 29.0.0",
+ "impl-trait-for-tuples",
+ "parity-scale-codec 3.6.12",
+ "scale-info",
+ "sp-runtime 32.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "pallet-balances"
+version = "29.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9a54b5d0c7c4c3731883d6b1ac18aff44db20c3d0a3470c8861001a17afdc85"
+dependencies = [
+ "docify",
+ "frame-benchmarking 29.0.0",
+ "frame-support 29.0.2",
+ "frame-system 29.0.0",
+ "log",
+ "parity-scale-codec 3.6.12",
+ "scale-info",
+ "sp-runtime 32.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "pallet-evm"
+version = "6.0.0-dev"
+source = "git+https://github.com/paritytech/frontier.git?branch=polkadot-v1.7.0#c5d6daa9ffd46c0a85915526aa26d200fd635e30"
+dependencies = [
+ "evm",
+ "fp-account",
+ "fp-evm",
+ "frame-support 28.0.0",
+ "frame-system 28.0.0",
+ "hash-db",
+ "hex-literal",
+ "impl-trait-for-tuples",
+ "log",
+ "parity-scale-codec 3.6.12",
+ "scale-info",
+ "sp-core 28.0.0",
+ "sp-io 30.0.0",
+ "sp-runtime 31.0.1",
+ "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
+]
+
 [[package]]
 name = "pallet-services"
 version = "2.0.0"
-source = "git+https://github.com/webb-tools/tangle.git#e4a92c8ca1c399b8ce547679d5ba0c7f1fe14664"
+source = "git+https://github.com/webb-tools/tangle.git#050d607fe9fc663d69e545a8d55a17974039ed04"
 dependencies = [
  "ethabi",
  "fp-evm",
- "frame-benchmarking",
- "frame-support",
- "frame-system",
+ "frame-benchmarking 28.0.0",
+ "frame-support 28.0.0",
+ "frame-system 28.0.0",
  "hex",
  "itertools 0.12.1",
  "log",
@@ -7286,6 +7761,73 @@ dependencies = [
  "tangle-primitives",
 ]
 
+[[package]]
+name = "pallet-session"
+version = "29.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7412ac59247b300feee53709f7009a23d1c6f8c70528599f48f44e102d896d03"
+dependencies = [
+ "frame-support 29.0.2",
+ "frame-system 29.0.0",
+ "impl-trait-for-tuples",
+ "log",
+ "pallet-timestamp",
+ "parity-scale-codec 3.6.12",
+ "scale-info",
+ "sp-core 29.0.0",
+ "sp-io 31.0.0",
+ "sp-runtime 32.0.0",
+ "sp-session",
+ "sp-staking 27.0.0",
+ "sp-state-machine 0.36.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sp-trie 30.0.0",
+]
+
+[[package]]
+name = "pallet-staking"
+version = "29.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "061b00814eb794a40df4eca7972a7c67b26473cd85cc7c54f5816ae49ad6e11b"
+dependencies = [
+ "frame-benchmarking 29.0.0",
+ "frame-election-provider-support",
+ "frame-support 29.0.2",
+ "frame-system 29.0.0",
+ "log",
+ "pallet-authorship",
+ "pallet-session",
+ "parity-scale-codec 3.6.12",
+ "scale-info",
+ "serde",
+ "sp-application-crypto 31.0.0",
+ "sp-io 31.0.0",
+ "sp-runtime 32.0.0",
+ "sp-staking 27.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "pallet-timestamp"
+version = "28.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1b8810ddfb254c7fb8cd7698229cce513d309a43ff117b38798dae6120f477b"
+dependencies = [
+ "docify",
+ "frame-benchmarking 29.0.0",
+ "frame-support 29.0.2",
+ "frame-system 29.0.0",
+ "log",
+ "parity-scale-codec 3.6.12",
+ "scale-info",
+ "sp-inherents 27.0.0",
+ "sp-io 31.0.0",
+ "sp-runtime 32.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sp-storage 20.0.0",
+ "sp-timestamp 27.0.0",
+]
+
 [[package]]
 name = "parity-scale-codec"
 version = "2.3.1"
@@ -7321,7 +7863,7 @@ version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27"
 dependencies = [
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate 1.3.1",
  "proc-macro2",
  "quote",
  "syn 1.0.109",
@@ -7484,7 +8026,7 @@ dependencies = [
  "pest_meta",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -7534,7 +8076,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -7551,9 +8093,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
 [[package]]
 name = "piper"
-version = "0.2.3"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391"
+checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
 dependencies = [
  "atomic-waker",
  "fastrand 2.1.0",
@@ -7611,7 +8153,7 @@ dependencies = [
  "polkavm-common",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -7621,7 +8163,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429"
 dependencies = [
  "polkavm-derive-impl",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -7642,9 +8184,9 @@ dependencies = [
 
 [[package]]
 name = "polling"
-version = "3.7.2"
+version = "3.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b"
+checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511"
 dependencies = [
  "cfg-if",
  "concurrent-queue",
@@ -7652,7 +8194,7 @@ dependencies = [
  "pin-project-lite",
  "rustix 0.38.34",
  "tracing",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -7692,11 +8234,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.18"
+version = "0.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
 dependencies = [
- "zerocopy 0.6.6",
+ "zerocopy",
 ]
 
 [[package]]
@@ -7706,7 +8248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
 dependencies = [
  "proc-macro2",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -7745,12 +8287,12 @@ dependencies = [
 
 [[package]]
 name = "proc-macro-crate"
-version = "1.1.3"
+version = "1.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
+checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
 dependencies = [
- "thiserror",
- "toml 0.5.11",
+ "once_cell",
+ "toml_edit 0.19.15",
 ]
 
 [[package]]
@@ -7794,7 +8336,7 @@ checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -7846,7 +8388,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -7875,7 +8417,7 @@ version = "0.1.0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -8145,7 +8687,19 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
+]
+
+[[package]]
+name = "regalloc2"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80535183cae11b149d618fbd3c37e38d7cda589d82d7769e196ca9a9042d7621"
+dependencies = [
+ "fxhash",
+ "log",
+ "slice-group-by",
+ "smallvec",
 ]
 
 [[package]]
@@ -8233,7 +8787,7 @@ dependencies = [
  "once_cell",
  "percent-encoding",
  "pin-project-lite",
- "rustls-pemfile 2.1.2",
+ "rustls-pemfile 2.1.3",
  "serde",
  "serde_json",
  "serde_urlencoded",
@@ -8294,7 +8848,7 @@ checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -8642,7 +9196,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba"
 dependencies = [
  "openssl-probe",
- "rustls-pemfile 2.1.2",
+ "rustls-pemfile 2.1.3",
  "rustls-pki-types",
  "schannel",
  "security-framework",
@@ -8659,9 +9213,9 @@ dependencies = [
 
 [[package]]
 name = "rustls-pemfile"
-version = "2.1.2"
+version = "2.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
+checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
 dependencies = [
  "base64 0.22.1",
  "rustls-pki-types",
@@ -8669,9 +9223,9 @@ dependencies = [
 
 [[package]]
 name = "rustls-pki-types"
-version = "1.7.0"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
+checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
 
 [[package]]
 name = "rustls-platform-verifier"
@@ -8795,6 +9349,101 @@ dependencies = [
  "regex",
 ]
 
+[[package]]
+name = "sc-allocator"
+version = "24.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "357127c91373ed6d1ae582f6e3300ab5b13bcde43bbf270a891f44194ef48b70"
+dependencies = [
+ "log",
+ "sp-core 29.0.0",
+ "sp-wasm-interface 20.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-client-api"
+version = "29.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e914dfadaaf384d8869ae47f3ec783bf6a1ac24e7827f5fec2e0e649a450a91"
+dependencies = [
+ "fnv",
+ "futures",
+ "log",
+ "parity-scale-codec 3.6.12",
+ "parking_lot",
+ "sc-executor",
+ "sc-transaction-pool-api",
+ "sc-utils",
+ "sp-api 27.0.1",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-core 29.0.0",
+ "sp-database",
+ "sp-externalities 0.26.0",
+ "sp-runtime 32.0.0",
+ "sp-state-machine 0.36.0",
+ "sp-statement-store",
+ "sp-storage 20.0.0",
+ "sp-trie 30.0.0",
+ "substrate-prometheus-endpoint",
+]
+
+[[package]]
+name = "sc-executor"
+version = "0.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa2ac6c356538d67987bbb867e11a12a84ba87250c70fd50005b6d74f570a4f7"
+dependencies = [
+ "parity-scale-codec 3.6.12",
+ "parking_lot",
+ "sc-executor-common",
+ "sc-executor-wasmtime",
+ "schnellru",
+ "sp-api 27.0.1",
+ "sp-core 29.0.0",
+ "sp-externalities 0.26.0",
+ "sp-io 31.0.0",
+ "sp-panic-handler 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sp-runtime-interface 25.0.0",
+ "sp-trie 30.0.0",
+ "sp-version 30.0.0",
+ "sp-wasm-interface 20.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tracing",
+]
+
+[[package]]
+name = "sc-executor-common"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07498138dee3ddf2c71299ca372d8449880bb3a8a8a299a483094e9c26b0823e"
+dependencies = [
+ "sc-allocator",
+ "sp-maybe-compressed-blob",
+ "sp-wasm-interface 20.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thiserror",
+ "wasm-instrument",
+]
+
+[[package]]
+name = "sc-executor-wasmtime"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30a387779ab54ec1ffce0bf3a6631faada079459d42796c1895683767918a642"
+dependencies = [
+ "anyhow",
+ "cfg-if",
+ "libc",
+ "log",
+ "parking_lot",
+ "rustix 0.36.17",
+ "sc-allocator",
+ "sc-executor-common",
+ "sp-runtime-interface 25.0.0",
+ "sp-wasm-interface 20.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasmtime",
+]
+
 [[package]]
 name = "sc-keystore"
 version = "26.0.0"
@@ -8810,6 +9459,39 @@ dependencies = [
  "thiserror",
 ]
 
+[[package]]
+name = "sc-transaction-pool-api"
+version = "29.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93ae888ce3491acb1b489c3dba930d0c46c7ef9f9893ba0ab8af9125362f3d14"
+dependencies = [
+ "async-trait",
+ "futures",
+ "log",
+ "parity-scale-codec 3.6.12",
+ "serde",
+ "sp-blockchain",
+ "sp-core 29.0.0",
+ "sp-runtime 32.0.0",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-utils"
+version = "15.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28b1a238f5baa56405db4e440e2d2f697583736fa2e2f1aac345c438a42975f1"
+dependencies = [
+ "async-channel 1.9.0",
+ "futures",
+ "futures-timer",
+ "lazy_static",
+ "log",
+ "parking_lot",
+ "prometheus",
+ "sp-arithmetic 24.0.0",
+]
+
 [[package]]
 name = "scale-bits"
 version = "0.6.0"
@@ -8871,7 +9553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "82ab7e60e2d9c8d47105f44527b26f04418e5e624ffc034f6b4a86c0ba19c5bf"
 dependencies = [
  "darling 0.14.4",
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate 1.3.1",
  "proc-macro2",
  "quote",
  "syn 1.0.109",
@@ -8922,15 +9604,15 @@ dependencies = [
  "proc-macro2",
  "quote",
  "scale-info",
- "syn 2.0.72",
+ "syn 2.0.74",
  "thiserror",
 ]
 
 [[package]]
 name = "scale-value"
-version = "0.16.1"
+version = "0.16.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ab68da501822d2769c4c5823535f6104a6d4cd15f0d3eba3e647e725294ae22"
+checksum = "ba4d772cfb7569e03868400344a1695d16560bf62b86b918604773607d39ec84"
 dependencies = [
  "base58",
  "blake2",
@@ -9126,9 +9808,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
 
 [[package]]
 name = "serde"
-version = "1.0.204"
+version = "1.0.208"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
+checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
 dependencies = [
  "serde_derive",
 ]
@@ -9155,13 +9837,13 @@ dependencies = [
 
 [[package]]
 name = "serde_derive"
-version = "1.0.204"
+version = "1.0.208"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
+checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -9172,14 +9854,14 @@ checksum = "e578a843d40b4189a4d66bba51d7684f57da5bd7c304c64e14bd63efbef49509"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.122"
+version = "1.0.125"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
+checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
 dependencies = [
  "itoa",
  "memchr",
@@ -9303,26 +9985,6 @@ dependencies = [
  "lazy_static",
 ]
 
-[[package]]
-name = "shell-manager"
-version = "0.1.0"
-dependencies = [
- "color-eyre",
- "gadget-common",
- "gadget-io",
- "hex",
- "reqwest",
- "serde",
- "sha2 0.10.8",
- "shell-sdk",
- "sp-core 29.0.0",
- "structopt",
- "tangle-environment",
- "tangle-subxt",
- "tokio",
- "toml 0.8.19",
-]
-
 [[package]]
 name = "shell-sdk"
 version = "0.1.0"
@@ -9333,7 +9995,7 @@ dependencies = [
  "color-eyre",
  "console_error_panic_hook",
  "console_log",
- "env_logger",
+ "env_logger 0.11.5",
  "environment-utils",
  "futures",
  "futures-timer",
@@ -9380,6 +10042,12 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
 
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
 [[package]]
 name = "signal-hook-registry"
 version = "1.4.2"
@@ -9438,6 +10106,12 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "slice-group-by"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
+
 [[package]]
 name = "smallvec"
 version = "1.13.2"
@@ -9457,14 +10131,14 @@ dependencies = [
 
 [[package]]
 name = "smol"
-version = "2.0.0"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e635339259e51ef85ac7aa29a1cd991b957047507288697a690e80ab97d07cad"
+checksum = "aad24f41392790e6ac67f4f4cd871da61f7d758e07b5622431e491e897d9c8a7"
 dependencies = [
  "async-channel 2.3.1",
  "async-executor",
  "async-fs",
- "async-io 2.3.3",
+ "async-io 2.3.4",
  "async-lock 3.4.0",
  "async-net",
  "async-process",
@@ -9693,7 +10367,7 @@ dependencies = [
  "proc-macro-crate 3.1.0",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -9708,7 +10382,7 @@ dependencies = [
  "proc-macro-crate 3.1.0",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -9772,17 +10446,52 @@ name = "sp-ark-bls12-381"
 version = "0.4.2"
 source = "git+https://github.com/paritytech/arkworks-substrate#caa2eed74beb885dd07c7db5f916f2281dad818f"
 dependencies = [
- "ark-bls12-381-ext",
- "sp-crypto-ec-utils",
+ "ark-bls12-381-ext",
+ "sp-crypto-ec-utils",
+]
+
+[[package]]
+name = "sp-ark-ed-on-bls12-381-bandersnatch"
+version = "0.4.2"
+source = "git+https://github.com/paritytech/arkworks-substrate#caa2eed74beb885dd07c7db5f916f2281dad818f"
+dependencies = [
+ "ark-ed-on-bls12-381-bandersnatch-ext",
+ "sp-crypto-ec-utils",
+]
+
+[[package]]
+name = "sp-blockchain"
+version = "29.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31303e766d2e53812641bbc1f1cec03a85793fc9e627e55f0a6854b28708758"
+dependencies = [
+ "futures",
+ "log",
+ "parity-scale-codec 3.6.12",
+ "parking_lot",
+ "schnellru",
+ "sp-api 27.0.1",
+ "sp-consensus",
+ "sp-database",
+ "sp-runtime 32.0.0",
+ "sp-state-machine 0.36.0",
+ "thiserror",
 ]
 
 [[package]]
-name = "sp-ark-ed-on-bls12-381-bandersnatch"
-version = "0.4.2"
-source = "git+https://github.com/paritytech/arkworks-substrate#caa2eed74beb885dd07c7db5f916f2281dad818f"
+name = "sp-consensus"
+version = "0.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb6e512b862c4ff7a26cdcd364898cc42e181ff5cb35fbb226ff27d88c81569a"
 dependencies = [
- "ark-ed-on-bls12-381-bandersnatch-ext",
- "sp-crypto-ec-utils",
+ "async-trait",
+ "futures",
+ "log",
+ "sp-core 29.0.0",
+ "sp-inherents 27.0.0",
+ "sp-runtime 32.0.0",
+ "sp-state-machine 0.36.0",
+ "thiserror",
 ]
 
 [[package]]
@@ -9798,10 +10507,10 @@ dependencies = [
  "sp-application-crypto 30.0.0",
  "sp-consensus-slots",
  "sp-core 28.0.0",
- "sp-inherents",
+ "sp-inherents 26.0.0",
  "sp-runtime 31.0.1",
  "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
- "sp-timestamp",
+ "sp-timestamp 26.0.0",
 ]
 
 [[package]]
@@ -9813,7 +10522,7 @@ dependencies = [
  "scale-info",
  "serde",
  "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
- "sp-timestamp",
+ "sp-timestamp 26.0.0",
 ]
 
 [[package]]
@@ -9911,7 +10620,7 @@ dependencies = [
 [[package]]
 name = "sp-crypto-ec-utils"
 version = "0.10.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#1f49358db0033e57a790eac6daccc45beba81863"
+source = "git+https://github.com/paritytech/polkadot-sdk#b5029eb4fd6c7ffd8164b2fe12b71bad0c59c9f2"
 dependencies = [
  "ark-bls12-377",
  "ark-bls12-377-ext",
@@ -9962,7 +10671,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot
 dependencies = [
  "quote",
  "sp-crypto-hashing 0.0.0",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -9973,7 +10682,17 @@ checksum = "b85d0f1f1e44bd8617eb2a48203ee854981229e3e79e6f468c7175d5fd37489b"
 dependencies = [
  "quote",
  "sp-crypto-hashing 0.1.0",
- "syn 2.0.72",
+ "syn 2.0.74",
+]
+
+[[package]]
+name = "sp-database"
+version = "10.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "722cbecdbf5b94578137dbd07feb51e95f7de221be0c1ff4dcfe0bb4cd986929"
+dependencies = [
+ "kvdb",
+ "parking_lot",
 ]
 
 [[package]]
@@ -9984,7 +10703,7 @@ checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -9994,17 +10713,17 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
 name = "sp-debug-derive"
 version = "14.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#1f49358db0033e57a790eac6daccc45beba81863"
+source = "git+https://github.com/paritytech/polkadot-sdk#b5029eb4fd6c7ffd8164b2fe12b71bad0c59c9f2"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -10021,7 +10740,7 @@ dependencies = [
 [[package]]
 name = "sp-externalities"
 version = "0.25.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#1f49358db0033e57a790eac6daccc45beba81863"
+source = "git+https://github.com/paritytech/polkadot-sdk#b5029eb4fd6c7ffd8164b2fe12b71bad0c59c9f2"
 dependencies = [
  "environmental",
  "parity-scale-codec 3.6.12",
@@ -10051,6 +10770,18 @@ dependencies = [
  "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
 ]
 
+[[package]]
+name = "sp-genesis-builder"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd865540ec19479c7349b584ccd78cc34c3f3a628a2a69dbb6365ceec36295ee"
+dependencies = [
+ "serde_json",
+ "sp-api 27.0.1",
+ "sp-runtime 32.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "sp-inherents"
 version = "26.0.0"
@@ -10065,6 +10796,21 @@ dependencies = [
  "thiserror",
 ]
 
+[[package]]
+name = "sp-inherents"
+version = "27.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "607c9e35e96966645ff180a9e9f976433b96e905d0a91d8d5315e605a21f4bc0"
+dependencies = [
+ "async-trait",
+ "impl-trait-for-tuples",
+ "parity-scale-codec 3.6.12",
+ "scale-info",
+ "sp-runtime 32.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thiserror",
+]
+
 [[package]]
 name = "sp-io"
 version = "30.0.0"
@@ -10141,6 +10887,16 @@ dependencies = [
  "thiserror",
 ]
 
+[[package]]
+name = "sp-maybe-compressed-blob"
+version = "11.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0c768c11afbe698a090386876911da4236af199cd38a5866748df4d8628aeff"
+dependencies = [
+ "thiserror",
+ "zstd 0.12.4",
+]
+
 [[package]]
 name = "sp-metadata-ir"
 version = "0.6.0"
@@ -10164,6 +10920,21 @@ dependencies = [
  "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
 ]
 
+[[package]]
+name = "sp-npos-elections"
+version = "27.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "195d7e1154c91cce5c3abc8c778689c3e5799da6411328dd32ac7a974c68e526"
+dependencies = [
+ "parity-scale-codec 3.6.12",
+ "scale-info",
+ "serde",
+ "sp-arithmetic 24.0.0",
+ "sp-core 29.0.0",
+ "sp-runtime 32.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "sp-panic-handler"
 version = "13.0.0"
@@ -10255,7 +11026,7 @@ dependencies = [
 [[package]]
 name = "sp-runtime-interface"
 version = "24.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#1f49358db0033e57a790eac6daccc45beba81863"
+source = "git+https://github.com/paritytech/polkadot-sdk#b5029eb4fd6c7ffd8164b2fe12b71bad0c59c9f2"
 dependencies = [
  "bytes",
  "impl-trait-for-tuples",
@@ -10301,7 +11072,7 @@ dependencies = [
  "proc-macro-crate 3.1.0",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -10314,20 +11085,36 @@ dependencies = [
  "proc-macro-crate 3.1.0",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
 name = "sp-runtime-interface-proc-macro"
 version = "17.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#1f49358db0033e57a790eac6daccc45beba81863"
+source = "git+https://github.com/paritytech/polkadot-sdk#b5029eb4fd6c7ffd8164b2fe12b71bad0c59c9f2"
 dependencies = [
  "Inflector",
  "expander",
  "proc-macro-crate 3.1.0",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
+]
+
+[[package]]
+name = "sp-session"
+version = "28.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b86531090cc04d2ab3535df07146258e2fb3ab6257b0a77ef14aa08282c3d4a"
+dependencies = [
+ "parity-scale-codec 3.6.12",
+ "scale-info",
+ "sp-api 27.0.1",
+ "sp-core 29.0.0",
+ "sp-keystore 0.35.0",
+ "sp-runtime 32.0.0",
+ "sp-staking 27.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -10344,6 +11131,21 @@ dependencies = [
  "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
 ]
 
+[[package]]
+name = "sp-staking"
+version = "27.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e14d003ecf0b610bf1305a92bdab875289b39d514c073f30e75e78c2763a788"
+dependencies = [
+ "impl-trait-for-tuples",
+ "parity-scale-codec 3.6.12",
+ "scale-info",
+ "serde",
+ "sp-core 29.0.0",
+ "sp-runtime 32.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "sp-state-machine"
 version = "0.35.0"
@@ -10387,6 +11189,32 @@ dependencies = [
  "trie-db",
 ]
 
+[[package]]
+name = "sp-statement-store"
+version = "11.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "309a9ae4e8134bbed8ffc510cf4d461a4a651f9250b556de782cedd876abe1ff"
+dependencies = [
+ "aes-gcm",
+ "curve25519-dalek 4.1.3",
+ "ed25519-dalek",
+ "hkdf",
+ "parity-scale-codec 3.6.12",
+ "rand",
+ "scale-info",
+ "sha2 0.10.8",
+ "sp-api 27.0.1",
+ "sp-application-crypto 31.0.0",
+ "sp-core 29.0.0",
+ "sp-crypto-hashing 0.1.0",
+ "sp-externalities 0.26.0",
+ "sp-runtime 32.0.0",
+ "sp-runtime-interface 25.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thiserror",
+ "x25519-dalek",
+]
+
 [[package]]
 name = "sp-std"
 version = "14.0.0"
@@ -10401,7 +11229,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot
 [[package]]
 name = "sp-std"
 version = "14.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#1f49358db0033e57a790eac6daccc45beba81863"
+source = "git+https://github.com/paritytech/polkadot-sdk#b5029eb4fd6c7ffd8164b2fe12b71bad0c59c9f2"
 
 [[package]]
 name = "sp-storage"
@@ -10419,7 +11247,7 @@ dependencies = [
 [[package]]
 name = "sp-storage"
 version = "19.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#1f49358db0033e57a790eac6daccc45beba81863"
+source = "git+https://github.com/paritytech/polkadot-sdk#b5029eb4fd6c7ffd8164b2fe12b71bad0c59c9f2"
 dependencies = [
  "impl-serde",
  "parity-scale-codec 3.6.12",
@@ -10449,12 +11277,26 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot
 dependencies = [
  "async-trait",
  "parity-scale-codec 3.6.12",
- "sp-inherents",
+ "sp-inherents 26.0.0",
  "sp-runtime 31.0.1",
  "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
  "thiserror",
 ]
 
+[[package]]
+name = "sp-timestamp"
+version = "27.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "249cd06624f2edb53b25af528ab216a508dc9d0870e158b43caac3a97e86699f"
+dependencies = [
+ "async-trait",
+ "parity-scale-codec 3.6.12",
+ "sp-inherents 27.0.0",
+ "sp-runtime 32.0.0",
+ "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thiserror",
+]
+
 [[package]]
 name = "sp-tracing"
 version = "16.0.0"
@@ -10483,7 +11325,7 @@ dependencies = [
 [[package]]
 name = "sp-tracing"
 version = "16.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#1f49358db0033e57a790eac6daccc45beba81863"
+source = "git+https://github.com/paritytech/polkadot-sdk#b5029eb4fd6c7ffd8164b2fe12b71bad0c59c9f2"
 dependencies = [
  "parity-scale-codec 3.6.12",
  "tracing",
@@ -10584,7 +11426,7 @@ dependencies = [
  "parity-scale-codec 3.6.12",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -10595,7 +11437,7 @@ dependencies = [
  "parity-scale-codec 3.6.12",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -10628,7 +11470,7 @@ dependencies = [
 [[package]]
 name = "sp-wasm-interface"
 version = "20.0.0"
-source = "git+https://github.com/paritytech/polkadot-sdk#1f49358db0033e57a790eac6daccc45beba81863"
+source = "git+https://github.com/paritytech/polkadot-sdk#b5029eb4fd6c7ffd8164b2fe12b71bad0c59c9f2"
 dependencies = [
  "impl-trait-for-tuples",
  "log",
@@ -10736,7 +11578,7 @@ dependencies = [
  "futures-util",
  "hashlink",
  "hex",
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
  "log",
  "memchr",
  "once_cell",
@@ -10999,7 +11841,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustversion",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -11116,7 +11958,7 @@ dependencies = [
  "scale-info",
  "scale-typegen",
  "subxt-metadata",
- "syn 2.0.72",
+ "syn 2.0.74",
  "thiserror",
  "tokio",
 ]
@@ -11187,7 +12029,7 @@ dependencies = [
  "quote",
  "scale-typegen",
  "subxt-codegen",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -11239,9 +12081,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.72"
+version = "2.0.74"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
+checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -11257,7 +12099,7 @@ dependencies = [
  "paste",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -11286,7 +12128,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -11346,7 +12188,7 @@ dependencies = [
 [[package]]
 name = "tangle-primitives"
 version = "2.0.0"
-source = "git+https://github.com/webb-tools/tangle.git#e4a92c8ca1c399b8ce547679d5ba0c7f1fe14664"
+source = "git+https://github.com/webb-tools/tangle.git#050d607fe9fc663d69e545a8d55a17974039ed04"
 dependencies = [
  "ark-bn254",
  "ark-crypto-primitives",
@@ -11357,7 +12199,7 @@ dependencies = [
  "ark-std 0.4.0",
  "educe 0.5.11",
  "ethabi",
- "frame-support",
+ "frame-support 28.0.0",
  "log",
  "parity-scale-codec 3.6.12",
  "scale-info",
@@ -11367,7 +12209,7 @@ dependencies = [
  "sp-consensus-babe",
  "sp-core 28.0.0",
  "sp-runtime 31.0.1",
- "sp-staking",
+ "sp-staking 26.0.0",
  "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.0)",
 ]
 
@@ -11469,7 +12311,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -11574,7 +12416,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -11690,7 +12532,7 @@ version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
 dependencies = [
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
  "serde",
  "serde_spanned",
  "toml_datetime",
@@ -11706,13 +12548,24 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "toml_edit"
+version = "0.19.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
+dependencies = [
+ "indexmap 2.4.0",
+ "toml_datetime",
+ "winnow 0.5.40",
+]
+
 [[package]]
 name = "toml_edit"
 version = "0.21.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
 dependencies = [
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
  "toml_datetime",
  "winnow 0.5.40",
 ]
@@ -11723,7 +12576,7 @@ version = "0.22.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
 dependencies = [
- "indexmap 2.3.0",
+ "indexmap 2.4.0",
  "serde",
  "serde_spanned",
  "toml_datetime",
@@ -11748,15 +12601,15 @@ dependencies = [
 
 [[package]]
 name = "tower-layer"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
 
 [[package]]
 name = "tower-service"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
 
 [[package]]
 name = "tracing"
@@ -11778,7 +12631,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -11950,7 +12803,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "serde_derive_internals",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -12029,7 +12882,7 @@ checksum = "f9534daa9fd3ed0bd911d462a37f172228077e7abf18c18a5f67199d959205f8"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -12310,34 +13163,35 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
 dependencies = [
  "cfg-if",
+ "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
 dependencies = [
  "bumpalo",
  "log",
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.42"
+version = "0.4.43"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
+checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -12347,9 +13201,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -12357,31 +13211,32 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
 
 [[package]]
 name = "wasm-bindgen-test"
-version = "0.3.42"
+version = "0.3.43"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b"
+checksum = "68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9"
 dependencies = [
  "console_error_panic_hook",
  "js-sys",
+ "minicov",
  "scoped-tls",
  "wasm-bindgen",
  "wasm-bindgen-futures",
@@ -12390,13 +13245,22 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-test-macro"
-version = "0.3.42"
+version = "0.3.43"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0"
+checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
+]
+
+[[package]]
+name = "wasm-instrument"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a47ecb37b9734d1085eaa5ae1a81e60801fd8c28d4cabdd8aedb982021918bc"
+dependencies = [
+ "parity-wasm",
 ]
 
 [[package]]
@@ -12465,9 +13329,12 @@ dependencies = [
  "once_cell",
  "paste",
  "psm",
+ "rayon",
  "serde",
  "target-lexicon",
  "wasmparser",
+ "wasmtime-cache",
+ "wasmtime-cranelift",
  "wasmtime-environ",
  "wasmtime-jit",
  "wasmtime-runtime",
@@ -12483,6 +13350,63 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "wasmtime-cache"
+version = "8.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c86437fa68626fe896e5afc69234bb2b5894949083586535f200385adfd71213"
+dependencies = [
+ "anyhow",
+ "base64 0.21.7",
+ "bincode",
+ "directories-next",
+ "file-per-thread-logger",
+ "log",
+ "rustix 0.36.17",
+ "serde",
+ "sha2 0.10.8",
+ "toml 0.5.11",
+ "windows-sys 0.45.0",
+ "zstd 0.11.2+zstd.1.5.2",
+]
+
+[[package]]
+name = "wasmtime-cranelift"
+version = "8.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1cefde0cce8cb700b1b21b6298a3837dba46521affd7b8c38a9ee2c869eee04"
+dependencies = [
+ "anyhow",
+ "cranelift-codegen",
+ "cranelift-entity",
+ "cranelift-frontend",
+ "cranelift-native",
+ "cranelift-wasm",
+ "gimli 0.27.3",
+ "log",
+ "object 0.30.4",
+ "target-lexicon",
+ "thiserror",
+ "wasmparser",
+ "wasmtime-cranelift-shared",
+ "wasmtime-environ",
+]
+
+[[package]]
+name = "wasmtime-cranelift-shared"
+version = "8.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd041e382ef5aea1b9fc78442394f1a4f6d676ce457e7076ca4cb3f397882f8b"
+dependencies = [
+ "anyhow",
+ "cranelift-codegen",
+ "cranelift-native",
+ "gimli 0.27.3",
+ "object 0.30.4",
+ "target-lexicon",
+ "wasmtime-environ",
+]
+
 [[package]]
 name = "wasmtime-environ"
 version = "8.0.1"
@@ -12520,6 +13444,7 @@ dependencies = [
  "serde",
  "target-lexicon",
  "wasmtime-environ",
+ "wasmtime-jit-debug",
  "wasmtime-jit-icache-coherence",
  "wasmtime-runtime",
  "windows-sys 0.45.0",
@@ -12531,7 +13456,9 @@ version = "8.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846"
 dependencies = [
+ "object 0.30.4",
  "once_cell",
+ "rustix 0.36.17",
 ]
 
 [[package]]
@@ -12597,9 +13524,9 @@ dependencies = [
 
 [[package]]
 name = "web-sys"
-version = "0.3.69"
+version = "0.3.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -12870,9 +13797,9 @@ dependencies = [
 
 [[package]]
 name = "wide"
-version = "0.7.26"
+version = "0.7.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "901e8597c777fa042e9e245bd56c0dc4418c5db3f845b6ff94fbac732c6a0692"
+checksum = "b828f995bf1e9622031f8009f8481a85406ce1f4d4588ff746d872043e855690"
 dependencies = [
  "bytemuck",
  "safe_arch",
@@ -12902,11 +13829,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
 name = "winapi-util"
-version = "0.1.8"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
 dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -13271,9 +14198,9 @@ dependencies = [
 
 [[package]]
 name = "xml-rs"
-version = "0.8.20"
+version = "0.8.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193"
+checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601"
 
 [[package]]
 name = "xmltree"
@@ -13330,34 +14257,14 @@ dependencies = [
  "time",
 ]
 
-[[package]]
-name = "zerocopy"
-version = "0.6.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6"
-dependencies = [
- "byteorder",
- "zerocopy-derive 0.6.6",
-]
-
 [[package]]
 name = "zerocopy"
 version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
 dependencies = [
- "zerocopy-derive 0.7.35",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.6.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.72",
+ "byteorder",
+ "zerocopy-derive",
 ]
 
 [[package]]
@@ -13368,7 +14275,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
 ]
 
 [[package]]
@@ -13388,5 +14295,53 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.72",
+ "syn 2.0.74",
+]
+
+[[package]]
+name = "zstd"
+version = "0.11.2+zstd.1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
+dependencies = [
+ "zstd-safe 5.0.2+zstd.1.5.2",
+]
+
+[[package]]
+name = "zstd"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c"
+dependencies = [
+ "zstd-safe 6.0.6",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "5.0.2+zstd.1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db"
+dependencies = [
+ "libc",
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "6.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581"
+dependencies = [
+ "libc",
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.13+zstd.1.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
+dependencies = [
+ "cc",
+ "pkg-config",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 6c76be9ab..e3bf31bad 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,21 +1,22 @@
 [workspace]
 resolver = "2"
 members = [
-  "blueprint-metadata",
-  "blueprints/*",
-  "cli",
-  "core",
-  "common",
-  "executor",
-  "environments/tangle",
-  "environments/utils",
-  "gadget-io",
-  "protocol-macros",
-  "shell-manager",
-  "shell-sdk",
-  "sdk",
-  # Commented out, since it uses jobs-v1 code
-  #"tangle-test-utils",
+    "blueprint-metadata",
+    "blueprints/*",
+    "cli",
+    "core",
+    "common",
+    "executor",
+    "environments/tangle",
+    "environments/utils",
+    "gadget-io",
+    "blueprint-test-utils",
+    "protocol-macros",
+    "blueprint-manager",
+    "shell-sdk",
+    "sdk",
+    # Commented out, since it uses jobs-v1 code
+    #"tangle-test-utils",
 
   "macros/blueprint-proc-macro",
   "macros/blueprint-proc-macro-core",
@@ -34,18 +35,20 @@ repository = "https://github.com/webb-tools/gadget"
 gadget-core = { version = "0.0.1", path = "./core", default-features = false }
 gadget-common = { version = "0.1.0", path = "./common", default-features = false }
 gadget-io = { version = "0.0.1",  path = "./gadget-io", default-features = false }
-gadget-executor = { version = "0.1.0", path = "./executor", default-features = false }
-gadget-sdk = { version = "0.1.0", path = "./sdk", default-features = false }
-# tangle-test-utils = { path = "./tangle-test-utils", default-features = false }
-protocol-macros = { path = "./protocol-macros", default-features = false }
+gadget-executor = { version = "0.1.0", path = "./executor" }
+blueprint-manager = { version = "0.1.0", path = "./blueprint-manager"}
+blueprint-test-utils = { path = "./blueprint-test-utils" }
+gadget-sdk = { path = "./sdk", default-features = false }
+protocol-macros = { path = "./protocol-macros" }
 shell-sdk = { path = "./shell-sdk", default-features = false }
-shell-manager = { path = "./shell-manager", default-features = false }
 tangle-environment = { path = "./environments/tangle", default-features = false }
 environment-utils = { path = "./environments/utils", default-features = false }
 
 gadget-blueprint-proc-macro = { path = "./macros/blueprint-proc-macro", default-features = false, version = "0.1.0" }
 gadget-blueprint-proc-macro-core = { path = "./macros/blueprint-proc-macro-core", default-features = false, version = "0.1.0" }
 blueprint-metadata = { path = "./blueprint-metadata", default-features = false, version = "0.1.0" }
+cargo-tangle = { path = "./cli" }
+cargo_metadata = { version = "0.18.1" }
 
 # Tangle-related dependencies
 pallet-services = { git = "https://github.com/webb-tools/tangle.git" }
@@ -261,3 +264,4 @@ wasm-bindgen-test = { version = "0.3.42" }
 
 [profile.dev.package.backtrace]
 opt-level = 3
+
diff --git a/README.md b/README.md
index d4418e51a..9d8de747b 100644
--- a/README.md
+++ b/README.md
@@ -46,14 +46,14 @@ cd gadget
 cargo build --release
 ```
 
-### Running the Gadget Shell
-The gadget shell is a standalone application that provides a command-line interface for interacting with the Gadget and the blockchain. To run the shell, use the following command:
+### Running the Gadget Binary
+The gadget binary is a standalone application that provides a command-line interface for interacting with the Gadget and the blockchain. To run the shell, use the following command:
 
 ```bash
-./target/release/shell-manager --protocols-config ./global_protocols.toml --shell-config shell-configs/local-testnet-0.toml -vvv
+./target/release/blueprint-manager --protocols-config ./global_protocols.toml --gadget-config gadget-configs/local-testnet-0.toml -vvv
 ```
 
-Before running that command, make sure that a [tangle](https://github.com/webb-tools/tangle/) node is running, otherwise, the shell will fail.
+Before running that command, make sure that a [tangle](https://github.com/webb-tools/tangle/) node is running, otherwise, the gadget binary will fail.
 
 ### Creating a New Protocol
 To create a new protocol using Gadget, clone the [protocol-template](https://github.com/webb-tools/protocol-template) and begin hacking!
@@ -70,7 +70,7 @@ RUST_LOG=gadget=trace cargo nextest run
 ```
 
 ### Integration testing
-Integration testing involves a multi-step process that includes running a tangle node, running multiple gadget shells each with a separate config, and submitting jobs manually.
+Integration testing involves a multi-step process that includes running a tangle node, running multiple gadget binaries each with a separate config, and submitting jobs manually.
 To run the integration tests, make sure the aforementioned steps are followed for building the executables, then, follow these steps:
 
 1. Run a tangle node from the [tangle repository](https://github.com/webb-tools/tangle/)
@@ -83,7 +83,7 @@ bash ./scripts/run-standalone-local.sh --clean
 bash ./scripts/run-gadget-local.sh
 ```
 
-3. Once the shells are running, open a separate terminal, navigate to the tangle directory, and run:
+3. Once the gadgets are running, open a separate terminal, navigate to the tangle directory, and run:
 ```bash
 cd types && yarn && ts-node playground.ts 
 ```
@@ -105,7 +105,19 @@ brew install gmp
 export LIBRARY_PATH=$LIBRARY_PATH:/opt/homebrew/lib
 export INCLUDE_PATH=$INCLUDE_PATH:/opt/homebrew/include
 ```
-Note: You need to set these environment variables each time you start a new shell session or append them to your .zshrc file.
+Note: You need to set these environment variables each time you start a new gadget session or append them to your .zshrc file.
+
+### Foundry
+In order to use the incredible-squaring blueprint, you need to have the Foundry CLI installed. You can install it here: [Foundry CLI](https://ethereum-blockchain-developer.com/2022-06-nft-truffle-hardhat-foundry/14-foundry-setup/)
+
+Then, run:
+```bash
+cd blueprints/incredible-squaring
+yarn install
+forge build --root ./contracts
+```
+
+This is required for testing and GitHub pipelines.
 
 ## License
 Gadget is licensed under either of
diff --git a/shell-manager/Cargo.toml b/blueprint-manager/Cargo.toml
similarity index 88%
rename from shell-manager/Cargo.toml
rename to blueprint-manager/Cargo.toml
index dc78cbd3e..b4fc01dcd 100644
--- a/shell-manager/Cargo.toml
+++ b/blueprint-manager/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "shell-manager"
+name = "blueprint-manager"
 version = "0.1.0"
 edition = "2021"
 
@@ -18,6 +18,10 @@ wasm = [
     "gadget-io/wasm"
 ]
 
+[[bin]]
+name = "blueprint-manager"
+path = "src/cli.rs"
+
 [dependencies]
 sp-core = { workspace = true }
 shell-sdk = { workspace = true }
@@ -33,3 +37,4 @@ tokio = { workspace = true, features = ["process", "io-util", "signal"] }
 reqwest = { version = "0.12.3" }
 sha2 = { workspace = true }
 tangle-environment = { workspace = true }
+futures = { workspace = true }
diff --git a/blueprint-manager/src/cli.rs b/blueprint-manager/src/cli.rs
new file mode 100644
index 000000000..f7f79a4a0
--- /dev/null
+++ b/blueprint-manager/src/cli.rs
@@ -0,0 +1,40 @@
+use blueprint_manager::config::BlueprintManagerConfig;
+use blueprint_manager::run_blueprint_manager;
+use blueprint_manager::utils;
+use gadget_common::gadget_io;
+use gadget_io::GadgetConfig;
+use shell_sdk::entry;
+use structopt::StructOpt;
+
+#[tokio::main]
+async fn main() -> color_eyre::Result<()> {
+    color_eyre::install()?;
+    let blueprint_manager_config = BlueprintManagerConfig::from_args();
+
+    entry::setup_blueprint_manager_logger(
+        blueprint_manager_config.verbose,
+        blueprint_manager_config.pretty,
+        "gadget",
+    )?;
+
+    if let Some(gadget_config) = blueprint_manager_config.gadget_config.as_ref() {
+        let gadget_config_settings = std::fs::read_to_string(gadget_config)?;
+        let gadget_config: GadgetConfig = toml::from_str(&gadget_config_settings)
+            .map_err(|err| utils::msg_to_error(err.to_string()))?;
+
+        // Allow CTRL-C to shutdown this CLI application instance
+        let shutdown_signal = async move {
+            let _ = tokio::signal::ctrl_c().await;
+        };
+
+        let handle =
+            run_blueprint_manager(blueprint_manager_config, gadget_config, shutdown_signal).await?;
+
+        handle.await
+    } else {
+        Err(utils::msg_to_error(
+            "Gadget config file is required when running the blueprint manager in CLI mode"
+                .to_string(),
+        ))
+    }
+}
diff --git a/shell-manager/src/config.rs b/blueprint-manager/src/config.rs
similarity index 52%
rename from shell-manager/src/config.rs
rename to blueprint-manager/src/config.rs
index a44c107ca..c0c41e806 100644
--- a/shell-manager/src/config.rs
+++ b/blueprint-manager/src/config.rs
@@ -3,15 +3,13 @@ use structopt::StructOpt;
 
 #[derive(Debug, StructOpt)]
 #[structopt(
-    name = "Shell Manager",
+    name = "Blueprint Manager",
     about = "An program executor that connects to the Tangle network and runs protocols dynamically on the fly"
 )]
-pub struct ShellManagerOpts {
-    /// The path to the shell configuration file
-    #[structopt(parse(from_os_str), short = "s", long = "shell-config")]
-    pub shell_config: PathBuf,
-    #[structopt(parse(from_os_str), short = "p", long = "protocols-config")]
-    pub protocols_config: PathBuf,
+pub struct BlueprintManagerConfig {
+    /// The path to the gadget configuration file
+    #[structopt(parse(from_os_str), short = "s", long = "gadget-config")]
+    pub gadget_config: Option<PathBuf>,
     /// The path to the keystore
     #[structopt(short = "k", long = "keystore-uri")]
     pub keystore_uri: String,
@@ -21,4 +19,8 @@ pub struct ShellManagerOpts {
     /// Whether to use pretty logging
     #[structopt(long)]
     pub pretty: bool,
+    /// An optional unique string identifier for the blueprint manager to differentiate between multiple
+    /// running instances of a BlueprintManager (mostly for debugging purposes)
+    #[structopt(long, short = "id")]
+    pub instance_id: Option<String>,
 }
diff --git a/shell-manager/src/error.rs b/blueprint-manager/src/error.rs
similarity index 100%
rename from shell-manager/src/error.rs
rename to blueprint-manager/src/error.rs
diff --git a/blueprint-manager/src/executor/event_handler.rs b/blueprint-manager/src/executor/event_handler.rs
new file mode 100644
index 000000000..c7b883f84
--- /dev/null
+++ b/blueprint-manager/src/executor/event_handler.rs
@@ -0,0 +1,477 @@
+use crate::config::BlueprintManagerConfig;
+use crate::gadget::native::{get_gadget_binary, FilteredBlueprint};
+use crate::gadget::ActiveGadgets;
+use crate::protocols::resolver::NativeGithubMetadata;
+use crate::utils;
+use crate::utils::{get_service_str, github_fetcher_to_native_github_metadata};
+use color_eyre::eyre::OptionExt;
+use gadget_common::prelude::DebugLogger;
+use gadget_common::tangle_runtime::AccountId32;
+use gadget_io::GadgetConfig;
+use std::fmt::Write;
+use std::sync::atomic::Ordering;
+use tangle_environment::api::{RpcServicesWithBlueprint, ServicesClient};
+use tangle_environment::gadget::TangleEvent;
+use tangle_subxt::subxt::SubstrateConfig;
+use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::{
+    Gadget, GithubFetcher,
+};
+use tangle_subxt::tangle_testnet_runtime::api::services::events::{
+    JobCalled, JobResultSubmitted, PreRegistration, Registered, ServiceInitiated, Unregistered,
+};
+use tokio::io::AsyncWriteExt;
+
+#[allow(clippy::too_many_arguments)]
+pub async fn maybe_handle(
+    blueprints: &[FilteredBlueprint],
+    onchain_services: &[NativeGithubMetadata],
+    onchain_gh_fetchers: &[GithubFetcher],
+    gadget_config: &GadgetConfig,
+    blueprint_manager_opts: &BlueprintManagerConfig,
+    active_gadgets: &mut ActiveGadgets,
+    logger: &DebugLogger,
+) -> color_eyre::Result<()> {
+    for (gh, fetcher) in onchain_services.iter().zip(onchain_gh_fetchers) {
+        let native_github_metadata = NativeGithubMetadata {
+            git: gh.git.clone(),
+            tag: gh.tag.clone(),
+            owner: gh.owner.clone(),
+            repo: gh.repo.clone(),
+            gadget_binaries: fetcher.binaries.0.clone(),
+            blueprint_id: gh.blueprint_id,
+        };
+
+        if let Err(err) = handle_github_source(
+            blueprints,
+            &native_github_metadata,
+            gadget_config,
+            blueprint_manager_opts,
+            fetcher,
+            active_gadgets,
+            logger,
+        )
+        .await
+        {
+            logger.warn(err)
+        }
+    }
+
+    Ok(())
+}
+
+#[allow(clippy::too_many_arguments)]
+async fn handle_github_source(
+    blueprints: &[FilteredBlueprint],
+    service: &NativeGithubMetadata,
+    gadget_config: &GadgetConfig,
+    blueprint_manager_opts: &BlueprintManagerConfig,
+    github: &GithubFetcher,
+    active_gadgets: &mut ActiveGadgets,
+    logger: &DebugLogger,
+) -> color_eyre::Result<()> {
+    let blueprint_id = service.blueprint_id;
+    let service_str = get_service_str(service);
+    if !active_gadgets.contains_key(&blueprint_id) {
+        // Maybe add in the protocol to the active shells
+        let relevant_binary =
+            get_gadget_binary(&github.binaries.0).ok_or_eyre("Unable to find matching binary")?;
+        let expected_hash = slice_32_to_sha_hex_string(relevant_binary.sha256);
+
+        let current_dir = std::env::current_dir()?;
+        let mut binary_download_path =
+            format!("{}/protocol-{:?}", current_dir.display(), github.tag);
+
+        if utils::is_windows() {
+            binary_download_path += ".exe"
+        }
+
+        logger.info(format!("Downloading to {binary_download_path}"));
+
+        // Check if the binary exists, if not download it
+        let retrieved_hash =
+            if !utils::valid_file_exists(&binary_download_path, &expected_hash).await {
+                let url = utils::get_download_url(&service.git, &service.tag);
+
+                let download = reqwest::get(&url)
+                    .await
+                    .map_err(|err| utils::msg_to_error(err.to_string()))?
+                    .bytes()
+                    .await
+                    .map_err(|err| utils::msg_to_error(err.to_string()))?;
+                let retrieved_hash = utils::hash_bytes_to_hex(&download);
+
+                // Write the binary to disk
+                let mut file = tokio::fs::File::create(&binary_download_path).await?;
+                file.write_all(&download).await?;
+                file.flush().await?;
+                Some(retrieved_hash)
+            } else {
+                None
+            };
+
+        if let Some(retrieved_hash) = retrieved_hash {
+            if retrieved_hash.trim() != expected_hash.trim() {
+                logger.error(format!(
+                    "Binary hash {} mismatched expected hash of {} for protocol: {}",
+                    retrieved_hash, expected_hash, service_str
+                ));
+                return Ok(());
+            }
+        }
+
+        if !utils::is_windows() {
+            if let Err(err) = utils::chmod_x_file(&binary_download_path).await {
+                logger.warn(format!("Failed to chmod +x the binary: {err}"));
+            }
+        }
+
+        for blueprint in blueprints {
+            if blueprint.blueprint_id == blueprint_id {
+                for service_id in &blueprint.services {
+                    let sub_service_str = format!("{service_str}-{service_id}");
+                    // Each spawned binary will effectively run a single "RoleType" in old parlance
+                    let arguments = utils::generate_process_arguments(
+                        gadget_config,
+                        blueprint_manager_opts,
+                        blueprint_id,
+                        *service_id,
+                    )?;
+
+                    let env_vars = if blueprint.registration_mode {
+                        let mut env_vars = std::env::vars().collect::<Vec<_>>();
+                        // RPC_URL: The remote RPC url for tangle
+                        // KEYSTORE_URI: the keystore file where the keys are stored and could be retrieved.
+                        // DATA_DIR: is an isolated path for where this gadget should store its data (database, secrets, …etc)
+                        // BLUEPRINT_ID: the active blueprint ID for this gadget
+                        // REGISTRATION_MODE_ON set to any value.
+                        env_vars.push(("RPC_URL".to_string(), gadget_config.url.to_string()));
+                        env_vars.push((
+                            "KEYSTORE_URI".to_string(),
+                            blueprint_manager_opts.keystore_uri.clone(),
+                        ));
+                        env_vars.push((
+                            "DATA_DIR".to_string(),
+                            format!("{}", gadget_config.base_path.display()),
+                        ));
+                        env_vars.push(("BLUEPRINT_ID".to_string(), format!("{}", blueprint_id)));
+                        env_vars.push(("REGISTRATION_MODE_ON".to_string(), "true".to_string()));
+                        env_vars
+                    } else {
+                        std::env::vars().collect::<Vec<_>>()
+                    };
+
+                    logger.info(format!("Starting protocol: {sub_service_str}"));
+
+                    // Now that the file is loaded, spawn the process
+                    let process_handle = tokio::process::Command::new(&binary_download_path)
+                        .kill_on_drop(true)
+                        .stdout(std::process::Stdio::inherit()) // Inherit the stdout of this process
+                        .stderr(std::process::Stdio::inherit()) // Inherit the stderr of this process
+                        .stdin(std::process::Stdio::null())
+                        .current_dir(&std::env::current_dir()?)
+                        .envs(env_vars)
+                        .args(arguments)
+                        .spawn()?;
+
+                    if blueprint.registration_mode {
+                        // We must wait for the process to exit successfully
+                        let status = process_handle.wait_with_output().await?;
+                        if !status.status.success() {
+                            logger.error(format!(
+                                "Protocol (registration mode) {sub_service_str} failed to execute: {status:?}"
+                            ));
+                        } else {
+                            logger.info(format!(
+                                "***Protocol (registration mode) {sub_service_str} executed successfully***"
+                            ));
+                        }
+                    } else {
+                        // A normal running gadget binary. Store the process handle and let the event loop handle the rest
+                        let (status_handle, abort) = utils::generate_running_process_status_handle(
+                            process_handle,
+                            logger,
+                            &sub_service_str,
+                        );
+
+                        active_gadgets
+                            .entry(blueprint_id)
+                            .or_default()
+                            .insert(*service_id, (status_handle, Some(abort)));
+                    }
+                }
+            }
+        }
+    }
+
+    Ok(())
+}
+
+fn slice_32_to_sha_hex_string(hash: [u8; 32]) -> String {
+    hash.iter().fold(String::new(), |mut acc, byte| {
+        write!(&mut acc, "{:02x}", byte).expect("Should be able to write");
+        acc
+    })
+}
+
+#[derive(Default, Debug)]
+pub struct EventPollResult {
+    pub needs_update: bool,
+    // A vec of blueprints we have not yet become registered to
+    pub blueprint_registrations: Vec<u64>,
+}
+
+pub(crate) async fn check_blueprint_events(
+    event: &TangleEvent,
+    logger: &DebugLogger,
+    active_gadgets: &mut ActiveGadgets,
+    account_id: &AccountId32,
+) -> EventPollResult {
+    let pre_registation_events = event.events.find::<PreRegistration>();
+    let registered_events = event.events.find::<Registered>();
+    let unregistered_events = event.events.find::<Unregistered>();
+    let service_initiated_events = event.events.find::<ServiceInitiated>();
+    let job_called_events = event.events.find::<JobCalled>();
+    let job_result_submitted_events = event.events.find::<JobResultSubmitted>();
+
+    let mut result = EventPollResult::default();
+
+    for evt in pre_registation_events {
+        match evt {
+            Ok(evt) => {
+                if &evt.operator == account_id {
+                    result.blueprint_registrations.push(evt.blueprint_id);
+                    logger.info(format!("Pre-registered event: {evt:?}"));
+                }
+            }
+            Err(err) => {
+                logger.warn(format!("Error handling pre-registered event: {err:?}"));
+            }
+        }
+    }
+
+    // Handle registered events
+    for evt in registered_events {
+        match evt {
+            Ok(evt) => {
+                logger.info(format!("Registered event: {evt:?}"));
+                result.needs_update = true;
+            }
+            Err(err) => {
+                logger.warn(format!("Error handling registered event: {err:?}"));
+            }
+        }
+    }
+
+    // Handle unregistered events
+    for evt in unregistered_events {
+        match evt {
+            Ok(evt) => {
+                logger.info(format!("Unregistered event: {evt:?}"));
+                if &evt.operator == account_id && active_gadgets.remove(&evt.blueprint_id).is_some()
+                {
+                    logger.info(format!(
+                        "Removed services for blueprint_id: {}",
+                        evt.blueprint_id,
+                    ));
+
+                    result.needs_update = true;
+                }
+            }
+            Err(err) => {
+                logger.warn(format!("Error handling unregistered event: {err:?}"));
+            }
+        }
+    }
+
+    // Handle service initiated events
+    for evt in service_initiated_events {
+        match evt {
+            Ok(evt) => {
+                logger.info(format!("Service initiated event: {evt:?}"));
+            }
+            Err(err) => {
+                logger.warn(format!("Error handling service initiated event: {err:?}"));
+            }
+        }
+    }
+
+    // Handle job called events
+    for evt in job_called_events {
+        match evt {
+            Ok(evt) => {
+                logger.info(format!("Job called event: {evt:?}"));
+            }
+            Err(err) => {
+                logger.warn(format!("Error handling job called event: {err:?}"));
+            }
+        }
+    }
+
+    // Handle job result submitted events
+    for evt in job_result_submitted_events {
+        match evt {
+            Ok(evt) => {
+                logger.info(format!("Job result submitted event: {evt:?}"));
+            }
+            Err(err) => {
+                logger.warn(format!(
+                    "Error handling job result submitted event: {err:?}"
+                ));
+            }
+        }
+    }
+
+    result
+}
+
+#[allow(clippy::too_many_arguments)]
+pub(crate) async fn handle_tangle_event(
+    event: &TangleEvent,
+    blueprints: &[RpcServicesWithBlueprint],
+    logger: &DebugLogger,
+    gadget_config: &GadgetConfig,
+    gadget_manager_opts: &BlueprintManagerConfig,
+    active_gadgets: &mut ActiveGadgets,
+    poll_result: EventPollResult,
+    client: &ServicesClient<SubstrateConfig>,
+) -> color_eyre::Result<()> {
+    logger.info(format!("Received notification {}", event.number));
+
+    let mut registration_blueprints = vec![];
+    // First, check to see if we need to register any new services invoked by the PreRegistration event
+    if !poll_result.blueprint_registrations.is_empty() {
+        for blueprint_id in &poll_result.blueprint_registrations {
+            let blueprint = client
+                .get_blueprint_by_id(event.hash, *blueprint_id)
+                .await?
+                .ok_or_eyre("Unable to retrieve blueprint for registration mode")?;
+
+            let general_blueprint = FilteredBlueprint {
+                blueprint_id: *blueprint_id,
+                services: vec![0], // Add a dummy service id for now, since it does not matter for registration mode
+                gadget: blueprint.gadget,
+                registration_mode: true,
+            };
+
+            registration_blueprints.push(general_blueprint);
+        }
+    }
+
+    // TODO: Refactor into Vec<SourceMetadata<T>> where T: NativeGithubMetadata + [...]
+    let mut onchain_services = vec![];
+    let mut fetchers = vec![];
+    let mut service_ids = vec![];
+    let mut valid_blueprint_ids = vec![];
+
+    let mut blueprints_filtered = vec![];
+
+    for blueprint in blueprints
+        .iter()
+        .map(|r| FilteredBlueprint {
+            blueprint_id: r.blueprint_id,
+            services: r.services.iter().map(|r| r.id).collect(),
+            gadget: r.blueprint.gadget.clone(),
+            registration_mode: false,
+        })
+        .chain(registration_blueprints)
+    {
+        let mut services_for_this_blueprint = vec![];
+        if let Gadget::Native(gadget) = &blueprint.gadget {
+            let gadget_source = &gadget.soruces.0[0];
+            if let gadget_common::tangle_runtime::api::runtime_types::tangle_primitives::services::GadgetSourceFetcher::Github(gh) = &gadget_source.fetcher {
+                let metadata = github_fetcher_to_native_github_metadata(gh, blueprint.blueprint_id);
+                onchain_services.push(metadata);
+                fetchers.push(gh.clone());
+                valid_blueprint_ids.push(blueprint.blueprint_id);
+
+                for service in &blueprint.services {
+                    services_for_this_blueprint.push(*service);
+                }
+
+                blueprints_filtered.push(blueprint);
+            } else {
+                logger.warn(
+                    "Blueprint does not contain a Github fetcher and thus currently unsupported",
+                );
+            }
+
+            service_ids.push(services_for_this_blueprint);
+        } else {
+            logger
+                .warn("Blueprint does not contain a native gadget and thus currently unsupported");
+        }
+    }
+
+    logger.trace(format!(
+        "OnChain services: {:?}",
+        onchain_services
+            .iter()
+            .map(|r| r.git.clone())
+            .collect::<Vec<_>>()
+    ));
+
+    // Step 3: Check to see if we need to start any new services
+    maybe_handle(
+        &blueprints_filtered,
+        &onchain_services,
+        &fetchers,
+        gadget_config,
+        gadget_manager_opts,
+        active_gadgets,
+        logger,
+    )
+    .await?;
+
+    // Check to see if local is running services that are not on-chain
+    let mut to_remove: Vec<(u64, u64)> = vec![];
+
+    // Loop through every (blueprint_id, service_id) running. See if the service is still on-chain. If not, kill it and add it to to_remove
+    for (blueprint_id, process_handles) in &mut *active_gadgets {
+        for (service_id, process_handle) in process_handles {
+            if !onchain_services
+                .iter()
+                .zip(&service_ids)
+                .all(|(r, s)| r.blueprint_id != *blueprint_id && !s.contains(service_id))
+            {
+                logger.warn(format!(
+                    "Killing service that is no longer on-chain: bid={blueprint_id}//sid={service_id}",
+                ));
+                if let Some(abort_handle) = process_handle.1.take() {
+                    if abort_handle.send(()).is_err() {
+                        logger.error(format!(
+                            "Failed to send abort signal to service: bid={blueprint_id}//sid={service_id}",
+                        ));
+                    }
+                }
+
+                to_remove.push((*blueprint_id, *service_id));
+            }
+        }
+    }
+
+    // Check to see if any process handles have died
+    for (blueprint_id, process_handles) in &mut *active_gadgets {
+        for (service_id, process_handle) in process_handles {
+            if !process_handle.0.load(Ordering::Relaxed) {
+                // By removing any killed processes, we will auto-restart them on the next finality notification if required
+                to_remove.push((*blueprint_id, *service_id));
+            }
+        }
+    }
+
+    for (blueprint_id, service_id) in to_remove {
+        let mut should_delete_blueprint = false;
+        if let Some(gadgets) = active_gadgets.get_mut(&blueprint_id) {
+            gadgets.remove(&service_id);
+            if gadgets.is_empty() {
+                should_delete_blueprint = true;
+            }
+        }
+
+        if should_delete_blueprint {
+            active_gadgets.remove(&blueprint_id);
+        }
+    }
+
+    Ok(())
+}
diff --git a/blueprint-manager/src/executor/mod.rs b/blueprint-manager/src/executor/mod.rs
new file mode 100644
index 000000000..ccdb8143b
--- /dev/null
+++ b/blueprint-manager/src/executor/mod.rs
@@ -0,0 +1,273 @@
+use crate::config::BlueprintManagerConfig;
+use crate::utils;
+use color_eyre::eyre::OptionExt;
+use color_eyre::Report;
+use gadget_common::config::DebugLogger;
+use gadget_common::environments::GadgetEnvironment;
+use gadget_common::subxt_signer::sr25519;
+use gadget_common::tangle_runtime::AccountId32;
+use gadget_io::{GadgetConfig, SubstrateKeystore};
+use shell_sdk::entry::keystore_from_base_path;
+use shell_sdk::keystore::load_keys_from_keystore;
+use shell_sdk::{Client, SendFuture};
+use sp_core::{ecdsa, Pair};
+use std::collections::HashMap;
+use std::future::Future;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+use tangle_environment::api::ServicesClient;
+use tangle_environment::gadget::SubxtConfig;
+use tangle_environment::TangleEnvironment;
+use tokio::task::JoinHandle;
+
+pub(crate) mod event_handler;
+
+pub struct BlueprintManagerHandle {
+    shutdown_call: Option<tokio::sync::oneshot::Sender<()>>,
+    start_tx: Option<tokio::sync::oneshot::Sender<()>>,
+    process: JoinHandle<color_eyre::Result<()>>,
+    logger: DebugLogger,
+    sr25519_id: sr25519::Keypair,
+    ecdsa_id: ecdsa::Pair,
+}
+
+impl BlueprintManagerHandle {
+    /// Send a start signal to the blueprint manager
+    pub fn start(&mut self) -> color_eyre::Result<()> {
+        match self.start_tx.take() {
+            Some(tx) => match tx.send(()) {
+                Ok(_) => {
+                    self.logger.info("Start signal sent to Blueprint Manager");
+                    Ok(())
+                }
+                Err(_) => Err(Report::msg(
+                    "Failed to send start signal to Blueprint Manager",
+                )),
+            },
+            None => Err(Report::msg("Blueprint Manager Already Started")),
+        }
+    }
+
+    /// Returns the SR25519 keypair for this blueprint manager
+    pub fn sr25519_id(&self) -> &sr25519::Keypair {
+        &self.sr25519_id
+    }
+
+    /// Returns the ECDSA keypair for this blueprint manager
+    pub fn ecdsa_id(&self) -> &ecdsa::Pair {
+        &self.ecdsa_id
+    }
+
+    /// Shutdown the blueprint manager
+    pub async fn shutdown(&mut self) -> color_eyre::Result<()> {
+        self.shutdown_call
+            .take()
+            .map(|tx| tx.send(()))
+            .ok_or_eyre("Shutdown already called")?
+            .map_err(|_| Report::msg("Failed to send shutdown signal to Blueprint Manager"))
+    }
+}
+
+/// Add default behavior for unintentional dropping of the BlueprintManagerHandle
+/// This will ensure that the BlueprintManagerHandle is executed even if the handle
+/// is dropped, which is similar behavior to the tokio SpawnHandle
+impl Drop for BlueprintManagerHandle {
+    fn drop(&mut self) {
+        let _ = self.start();
+    }
+}
+
+/// Implement the Future trait for the BlueprintManagerHandle to allow
+/// for the handle to be awaited on
+impl Future for BlueprintManagerHandle {
+    type Output = color_eyre::Result<()>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        // Start the blueprint manager if it has not been started
+        let this = self.get_mut();
+        if this.start_tx.is_some() {
+            if let Err(err) = this.start() {
+                return Poll::Ready(Err(err));
+            }
+        }
+
+        let result = futures::ready!(Pin::new(&mut this.process).poll(cx));
+
+        match result {
+            Ok(res) => Poll::Ready(res),
+            Err(err) => Poll::Ready(Err(Report::msg(format!(
+                "Blueprint Manager Closed Unexpectedly (JoinError): {err:?}"
+            )))),
+        }
+    }
+}
+
+pub async fn run_blueprint_manager<F: SendFuture<'static, ()>>(
+    blueprint_manager_config: BlueprintManagerConfig,
+    gadget_config: GadgetConfig,
+    shutdown_cmd: F,
+) -> color_eyre::Result<BlueprintManagerHandle> {
+    let logger_id = if let Some(custom_id) = &blueprint_manager_config.instance_id {
+        custom_id.as_str()
+    } else {
+        "local"
+    };
+
+    let logger = DebugLogger {
+        id: format!("blueprint-manager-{}", logger_id),
+    };
+
+    logger.info("Starting blueprint manager ...");
+
+    let logger_clone = logger.clone();
+
+    let keystore = keystore_from_base_path(
+        &gadget_config.base_path,
+        gadget_config.chain,
+        gadget_config.keystore_password.clone(),
+    );
+
+    let sr25519_keypair = keystore.sr25519_key()?;
+
+    let mut secret_key = [0u8; 32];
+    // The `secret` field is 64 bytes. The first 32 bytes are the secret key, the last 32 bytes are seeds to the nonces.
+    secret_key.clone_from_slice(&sr25519_keypair.as_ref().secret.to_bytes()[..32]);
+
+    let sr25519_private_key = sr25519::Keypair::from_secret_key(secret_key)
+        .map_err(|err| Report::msg(format!("Failed to create SR25519 keypair: {err:?}")))?;
+
+    let (role_key, acco_key) = load_keys_from_keystore(&keystore)?;
+    let ecdsa_key = role_key.clone();
+
+    let sub_account_id = AccountId32(acco_key.public().0);
+    let subxt_config = SubxtConfig {
+        endpoint: gadget_config.url.clone(),
+    };
+
+    let tangle_environment = TangleEnvironment::new(subxt_config, acco_key, logger.clone());
+
+    let tangle_runtime = tangle_environment.setup_runtime().await?;
+    let runtime = ServicesClient::new(logger.clone(), tangle_runtime.client());
+    let mut active_gadgets = HashMap::new();
+
+    // With the basics setup, we must now implement the main logic
+    //
+    // * Query to get Vec<RpcServicesWithBlueprint>
+    // * For each RpcServicesWithBlueprint, fetch the associated gadget binary (fetch/download)
+    //   -> If the services field is empty, just emit and log inside the executed binary "that states a new service instance got created by one of these blueprints"
+    //   -> If the services field is not empty, for each service in RpcServicesWithBlueprint.services, spawn the gadget binary, using params to set the job type to listen to (in terms of our old language, each spawned service represents a single "RoleType")
+
+    let (mut operator_subscribed_blueprints, init_event) =
+        if let Some(event) = tangle_runtime.next_event().await {
+            (
+                utils::get_blueprints(&runtime, event.hash, sub_account_id.clone()).await?,
+                event,
+            )
+        } else {
+            return Err(Report::msg("Failed to get initial block hash"));
+        };
+
+    logger.info(format!(
+        "Received {} initial blueprints this operator is registered to",
+        operator_subscribed_blueprints.len()
+    ));
+
+    // Immediately poll, handling the initial state
+    let poll_result = event_handler::check_blueprint_events(
+        &init_event,
+        &logger,
+        &mut active_gadgets,
+        &sub_account_id,
+    )
+    .await;
+    event_handler::handle_tangle_event(
+        &init_event,
+        &operator_subscribed_blueprints,
+        &logger,
+        &gadget_config,
+        &blueprint_manager_config,
+        &mut active_gadgets,
+        poll_result,
+        &runtime,
+    )
+    .await?;
+
+    let logger_manager = logger.clone();
+    let manager_task = async move {
+        // Listen to FinalityNotifications and poll for new/deleted services that correspond to the blueprints above
+        while let Some(event) = tangle_runtime.next_event().await {
+            let result = event_handler::check_blueprint_events(
+                &event,
+                &logger_manager,
+                &mut active_gadgets,
+                &sub_account_id,
+            )
+            .await;
+
+            if result.needs_update {
+                operator_subscribed_blueprints =
+                    utils::get_blueprints(&runtime, event.hash, sub_account_id.clone()).await?;
+            }
+
+            event_handler::handle_tangle_event(
+                &event,
+                &operator_subscribed_blueprints,
+                &logger_manager,
+                &gadget_config,
+                &blueprint_manager_config,
+                &mut active_gadgets,
+                result,
+                &runtime,
+            )
+            .await?;
+        }
+
+        Err::<(), _>(utils::msg_to_error("Finality Notification stream died"))
+    };
+
+    let (tx_stop, rx_stop) = tokio::sync::oneshot::channel::<()>();
+
+    let logger = logger.clone();
+    let shutdown_task = async move {
+        tokio::select! {
+            _res0 = shutdown_cmd => {
+                logger.info("Shutdown-1 command received, closing application");
+            },
+
+            _res1 = rx_stop => {
+                logger.info("Manual shutdown signal received, closing application");
+            }
+        }
+    };
+
+    let (start_tx, start_rx) = tokio::sync::oneshot::channel::<()>();
+
+    let combined_task = async move {
+        start_rx
+            .await
+            .map_err(|_err| Report::msg("Failed to receive start signal"))?;
+
+        tokio::select! {
+            res0 = manager_task => {
+                Err(Report::msg(format!("Blueprint Manager Closed Unexpectedly: {res0:?}")))
+            },
+
+            _ = shutdown_task => {
+                Ok(())
+            }
+        }
+    };
+
+    let handle = tokio::spawn(combined_task);
+
+    let handle = BlueprintManagerHandle {
+        start_tx: Some(start_tx),
+        shutdown_call: Some(tx_stop),
+        process: handle,
+        logger: logger_clone,
+        sr25519_id: sr25519_private_key,
+        ecdsa_id: ecdsa_key,
+    };
+
+    Ok(handle)
+}
diff --git a/shell-manager/src/gadget/mod.rs b/blueprint-manager/src/gadget/mod.rs
similarity index 88%
rename from shell-manager/src/gadget/mod.rs
rename to blueprint-manager/src/gadget/mod.rs
index 11d3e475a..ccc08f19d 100644
--- a/shell-manager/src/gadget/mod.rs
+++ b/blueprint-manager/src/gadget/mod.rs
@@ -2,6 +2,6 @@ use std::collections::HashMap;
 use std::sync::atomic::AtomicBool;
 use std::sync::Arc;
 
-pub type ActiveShells =
+pub type ActiveGadgets =
     HashMap<u64, HashMap<u64, (Arc<AtomicBool>, Option<tokio::sync::oneshot::Sender<()>>)>>;
 pub mod native;
diff --git a/blueprint-manager/src/gadget/native.rs b/blueprint-manager/src/gadget/native.rs
new file mode 100644
index 000000000..19e0d1a1a
--- /dev/null
+++ b/blueprint-manager/src/gadget/native.rs
@@ -0,0 +1,34 @@
+use crate::utils;
+use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::{
+    Gadget, GadgetBinary,
+};
+
+pub struct FilteredBlueprint {
+    pub blueprint_id: u64,
+    pub services: Vec<u64>,
+    pub gadget: Gadget,
+    pub registration_mode: bool,
+}
+
+pub fn get_gadget_binary(gadget_binaries: &[GadgetBinary]) -> Option<&GadgetBinary> {
+    let os = utils::get_formatted_os_string().to_lowercase();
+    let arch = std::env::consts::ARCH.to_lowercase();
+    for binary in gadget_binaries {
+        let binary_str = format!("{:?}", binary.os).to_lowercase();
+        if binary_str.contains(&os) || os.contains(&binary_str) || binary_str == os {
+            let mut arch_str = format!("{:?}", binary.arch).to_lowercase();
+
+            if arch_str == "amd" {
+                arch_str = "x86".to_string()
+            } else if arch_str == "amd64" {
+                arch_str = "x86_64".to_string()
+            }
+
+            if arch_str == arch {
+                return Some(binary);
+            }
+        }
+    }
+
+    None
+}
diff --git a/blueprint-manager/src/lib.rs b/blueprint-manager/src/lib.rs
new file mode 100644
index 000000000..53e2e6299
--- /dev/null
+++ b/blueprint-manager/src/lib.rs
@@ -0,0 +1,8 @@
+pub mod config;
+pub mod error;
+pub mod executor;
+pub mod gadget;
+pub mod protocols;
+pub mod utils;
+
+pub use executor::run_blueprint_manager;
diff --git a/shell-manager/src/protocols/config.rs b/blueprint-manager/src/protocols/config.rs
similarity index 100%
rename from shell-manager/src/protocols/config.rs
rename to blueprint-manager/src/protocols/config.rs
diff --git a/shell-manager/src/protocols/mod.rs b/blueprint-manager/src/protocols/mod.rs
similarity index 100%
rename from shell-manager/src/protocols/mod.rs
rename to blueprint-manager/src/protocols/mod.rs
diff --git a/shell-manager/src/protocols/resolver.rs b/blueprint-manager/src/protocols/resolver.rs
similarity index 100%
rename from shell-manager/src/protocols/resolver.rs
rename to blueprint-manager/src/protocols/resolver.rs
diff --git a/shell-manager/src/utils.rs b/blueprint-manager/src/utils.rs
similarity index 88%
rename from shell-manager/src/utils.rs
rename to blueprint-manager/src/utils.rs
index 38f8b6d14..681a11492 100644
--- a/shell-manager/src/utils.rs
+++ b/blueprint-manager/src/utils.rs
@@ -1,8 +1,8 @@
-use crate::config::ShellManagerOpts;
+use crate::config::BlueprintManagerConfig;
 use crate::protocols::resolver::NativeGithubMetadata;
 use gadget_common::config::DebugLogger;
 use gadget_common::tangle_runtime::AccountId32;
-use gadget_io::{defaults, ShellTomlConfig};
+use gadget_io::GadgetConfig;
 use sha2::Digest;
 use sp_core::H256;
 use std::path::Path;
@@ -47,39 +47,32 @@ pub fn github_fetcher_to_native_github_metadata(
 }
 
 pub fn generate_process_arguments(
-    shell_config: &ShellTomlConfig,
-    opt: &ShellManagerOpts,
+    gadget_config: &GadgetConfig,
+    opt: &BlueprintManagerConfig,
     blueprint_id: u64,
     service_id: u64,
 ) -> color_eyre::Result<Vec<String>> {
     let mut arguments = vec![
-        format!("--bind-ip={}", shell_config.bind_ip),
-        format!("--url={}", shell_config.url),
+        format!("--bind-ip={}", gadget_config.bind_ip),
+        format!("--url={}", gadget_config.url),
         format!(
             "--bootnodes={}",
-            shell_config
+            gadget_config
                 .bootnodes
                 .iter()
                 .map(|r| r.to_string())
                 .collect::<Vec<String>>()
                 .join(",")
         ),
-        format!(
-            "--node-key={}",
-            shell_config
-                .node_key
-                .clone()
-                .unwrap_or_else(|| { hex::encode(defaults::generate_node_key()) })
-        ),
         format!("--blueprint-id={}", blueprint_id),
         format!("--service-id={}", service_id),
-        format!("--base-path={}", shell_config.base_path.display()),
-        format!("--chain={}", shell_config.chain.to_string()),
+        format!("--base-path={}", gadget_config.base_path.display()),
+        format!("--chain={}", gadget_config.chain.to_string()),
         format!("--verbose={}", opt.verbose),
         format!("--pretty={}", opt.pretty),
     ];
 
-    if let Some(keystore_password) = &shell_config.keystore_password {
+    if let Some(keystore_password) = &gadget_config.keystore_password {
         arguments.push(format!("--keystore-password={}", keystore_password));
     }
 
diff --git a/blueprints/incredible-squaring/build.rs b/blueprints/incredible-squaring/build.rs
index 6ad623a41..19606ba0d 100644
--- a/blueprints/incredible-squaring/build.rs
+++ b/blueprints/incredible-squaring/build.rs
@@ -1,5 +1,5 @@
 fn main() {
-    println!("cargo:rerun-if-changed=src/main.rs");
+    println!("cargo:rerun-if-changed=src/cli");
     println!("cargo:rerun-if-changed=src/lib.rs");
     blueprint_metadata::generate_json();
 }
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index ba5aad839..b1608dd6c 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -12,9 +12,9 @@ repository.workspace = true
 clap = { workspace = true, features = ["derive", "wrap_help"] }
 clap-cargo = { version = "0.14" }
 cargo-generate = { version = "0.21.3" }
-cargo_metadata = { version = "0.18.1" }
+cargo_metadata = { workspace = true }
 escargot = { version = "0.5.12" }
-
+k256 = { workspace = true, features = ["ecdsa"] }
 tracing = { workspace = true }
 tracing-subscriber = { workspace = true, features = ["env-filter", "fmt", "ansi"] }
 color-eyre = { workspace = true }
diff --git a/cli/src/deploy.rs b/cli/src/deploy.rs
index 518b7772a..e6aac377d 100644
--- a/cli/src/deploy.rs
+++ b/cli/src/deploy.rs
@@ -1,13 +1,19 @@
 use alloy_provider::network::TransactionBuilder;
 use alloy_provider::Provider;
+pub use alloy_signer_local::PrivateKeySigner;
 use color_eyre::eyre::{self, Context, ContextCompat, OptionExt, Result};
 use gadget_blueprint_proc_macro_core::{
     JobResultVerifier, ServiceBlueprint, ServiceRegistrationHook, ServiceRequestHook,
 };
+pub use k256;
+use std::fmt::Debug;
+use std::path::PathBuf;
 use tangle_subxt::subxt::{self, PolkadotConfig};
+use tangle_subxt::subxt_signer::sr25519;
 use tangle_subxt::tangle_testnet_runtime::api as TangleApi;
+use tangle_subxt::tangle_testnet_runtime::api::services::calls::types;
 
-#[derive(Debug, Clone)]
+#[derive(Clone)]
 pub struct Opts {
     /// The name of the package to deploy (if the workspace has multiple packages)
     pub pkg_name: Option<String>,
@@ -15,31 +21,64 @@ pub struct Opts {
     pub rpc_url: String,
     /// The path to the manifest file
     pub manifest_path: std::path::PathBuf,
+    /// The signer for deploying the blueprint
+    pub signer: Option<sr25519::Keypair>,
+    /// The signer for deploying the smart contract
+    pub signer_evm: Option<PrivateKeySigner>,
 }
 
-pub async fn deploy_to_tangle(
-    Opts {
-        pkg_name,
-        rpc_url,
-        manifest_path,
-    }: Opts,
-) -> Result<()> {
-    // Load the manifest file into cargo metadata
+impl Debug for Opts {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("Opts")
+            .field("pkg_name", &self.pkg_name)
+            .field("rpc_url", &self.rpc_url)
+            .field("manifest_path", &self.manifest_path)
+            .finish()
+    }
+}
+
+pub async fn generate_service_blueprint<P: Into<PathBuf>, T: AsRef<str>>(
+    manifest_metadata_path: P,
+    pkg_name: Option<&String>,
+    rpc_url: T,
+    signer_evm: Option<PrivateKeySigner>,
+) -> Result<types::create_blueprint::Blueprint> {
+    let manifest_path = manifest_metadata_path.into();
     let metadata = cargo_metadata::MetadataCommand::new()
         .manifest_path(manifest_path)
         .no_deps()
         .exec()
         .context("Getting Metadata about the workspace")?;
-    let package = find_package(&metadata, pkg_name.as_ref())?;
-    let mut blueprint = load_blueprnt_metadata(package)?;
+
+    let package = find_package(&metadata, pkg_name)?;
+    let mut blueprint = load_blueprint_metadata(package)?;
 
     build_contracts_if_needed(package, &blueprint).context("Building contracts")?;
 
-    deploy_contracts_to_tangle(&rpc_url, package, &mut blueprint).await?;
+    deploy_contracts_to_tangle(rpc_url.as_ref(), package, &mut blueprint, signer_evm).await?;
+
+    bake_blueprint(blueprint)
+}
+
+pub async fn deploy_to_tangle(
+    Opts {
+        pkg_name,
+        rpc_url,
+        manifest_path,
+        signer,
+        signer_evm,
+    }: Opts,
+) -> Result<u64> {
+    // Load the manifest file into cargo metadata
+    let blueprint =
+        generate_service_blueprint(&manifest_path, pkg_name.as_ref(), &rpc_url, signer_evm).await?;
 
-    let blueprint = bake_blueprint(blueprint)?;
+    let signer = if let Some(signer) = signer {
+        signer
+    } else {
+        crate::signer::load_signer_from_env()?
+    };
 
-    let signer = crate::signer::load_signer_from_env()?;
     let my_account_id = signer.public_key().to_account_id();
     let client = subxt::OnlineClient::<PolkadotConfig>::from_url(rpc_url).await?;
 
@@ -67,10 +106,11 @@ pub async fn deploy_to_tangle(
         event.owner,
         result.extrinsic_hash(),
     );
-    Ok(())
+
+    Ok(event.blueprint_id)
 }
 
-fn load_blueprnt_metadata(package: &cargo_metadata::Package) -> Result<ServiceBlueprint> {
+pub fn load_blueprint_metadata(package: &cargo_metadata::Package) -> Result<ServiceBlueprint> {
     let blueprint_json_path = package
         .manifest_path
         .parent()
@@ -88,7 +128,7 @@ fn load_blueprnt_metadata(package: &cargo_metadata::Package) -> Result<ServiceBl
     }
     // should have the blueprnt.json
     let blueprint_json =
-        std::fs::read_to_string(blueprint_json_path).context("Reading blueprnt.json file")?;
+        std::fs::read_to_string(blueprint_json_path).context("Reading blueprint.json file")?;
     let blueprint = serde_json::from_str::<ServiceBlueprint<'_>>(&blueprint_json)?;
     Ok(blueprint)
 }
@@ -97,6 +137,7 @@ async fn deploy_contracts_to_tangle(
     rpc_url: &str,
     package: &cargo_metadata::Package,
     blueprint: &mut ServiceBlueprint<'_>,
+    signer_evm: Option<PrivateKeySigner>,
 ) -> Result<()> {
     enum ContractKind {
         RegistrationHook,
@@ -152,7 +193,12 @@ async fn deploy_contracts_to_tangle(
         return Ok(());
     }
 
-    let signer = crate::signer::load_evm_signer_from_env()?;
+    let signer = if let Some(signer) = signer_evm {
+        signer
+    } else {
+        crate::signer::load_evm_signer_from_env()?
+    };
+
     let wallet = alloy_provider::network::EthereumWallet::from(signer);
     let provider = alloy_provider::ProviderBuilder::new()
         .with_recommended_fillers()
@@ -253,6 +299,7 @@ fn bake_blueprint(
         convert_to_bytes_or_null(&mut job["metadata"]["name"]);
         convert_to_bytes_or_null(&mut job["metadata"]["description"]);
     }
+    println!("Job: {blueprint_json}");
     let blueprint = serde_json::from_value(blueprint_json)?;
     Ok(blueprint)
 }
diff --git a/cli/src/foundry/forge.rs b/cli/src/foundry/forge.rs
index e13812932..6f671b531 100644
--- a/cli/src/foundry/forge.rs
+++ b/cli/src/foundry/forge.rs
@@ -7,6 +7,12 @@ pub struct Forge {
     cmd: Command,
 }
 
+impl Default for Forge {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
 impl Forge {
     /// Creates a new Forge instance.
     pub fn new() -> Forge {
diff --git a/cli/src/foundry/mod.rs b/cli/src/foundry/mod.rs
index ec6f529d7..c1a25dfb2 100644
--- a/cli/src/foundry/mod.rs
+++ b/cli/src/foundry/mod.rs
@@ -30,6 +30,12 @@ impl CommandInstalled for Command {
     }
 }
 
+impl Default for FoundryToolchain {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
 impl FoundryToolchain {
     /// Creates a new FoundryToolchain instance.
     pub fn new() -> Self {
diff --git a/cli/src/lib.rs b/cli/src/lib.rs
new file mode 100644
index 000000000..185b3fe9f
--- /dev/null
+++ b/cli/src/lib.rs
@@ -0,0 +1,4 @@
+pub mod create;
+pub mod deploy;
+pub mod foundry;
+pub mod signer;
diff --git a/cli/src/main.rs b/cli/src/main.rs
index 60500fefd..344870c08 100644
--- a/cli/src/main.rs
+++ b/cli/src/main.rs
@@ -1,12 +1,8 @@
 use std::path::PathBuf;
 
+use cargo_gadget::{create, deploy};
 use clap::{Parser, Subcommand};
 
-mod create;
-mod deploy;
-mod foundry;
-mod signer;
-
 /// Tangle CLI tool
 #[derive(Parser, Debug)]
 #[clap(
@@ -83,10 +79,12 @@ async fn main() -> color_eyre::Result<()> {
                     .manifest
                     .manifest_path
                     .unwrap_or_else(|| PathBuf::from("Cargo.toml"));
-                deploy::deploy_to_tangle(deploy::Opts {
+                let _ = deploy::deploy_to_tangle(deploy::Opts {
                     rpc_url,
                     manifest_path,
                     pkg_name: package,
+                    signer: None,
+                    signer_evm: None,
                 })
                 .await?;
             }
diff --git a/environments/tangle/src/gadget.rs b/environments/tangle/src/gadget.rs
index 55ac472f1..d9c63bca7 100644
--- a/environments/tangle/src/gadget.rs
+++ b/environments/tangle/src/gadget.rs
@@ -1,6 +1,6 @@
+use crate::runtime::TangleConfig;
 use crate::work_manager::TangleWorkManager;
 use gadget_common::tangle_subxt::subxt::events::Events;
-use gadget_common::tangle_subxt::subxt::PolkadotConfig;
 use gadget_common::WorkManagerInterface;
 
 #[derive(Clone)]
@@ -10,7 +10,7 @@ pub struct TangleEvent {
     /// Finalized block header hash.
     pub hash: [u8; 32],
     /// Events
-    pub events: Events<PolkadotConfig>,
+    pub events: Events<TangleConfig>,
 }
 
 pub struct TangleJobMetadata {
diff --git a/environments/tangle/src/lib.rs b/environments/tangle/src/lib.rs
index ccc52ae9f..1ba40b92b 100644
--- a/environments/tangle/src/lib.rs
+++ b/environments/tangle/src/lib.rs
@@ -1,4 +1,5 @@
 use crate::gadget::TangleEvent;
+use crate::runtime::TangleConfig;
 use crate::work_manager::TangleWorkManager;
 use environment_utils::transaction_manager::tangle::{SubxtPalletSubmitter, TanglePalletSubmitter};
 use gadget::{SubxtConfig, TangleJobMetadata};
@@ -8,7 +9,6 @@ use gadget_common::client::PairSigner;
 use gadget_common::config::DebugLogger;
 use gadget_common::environments::{EventMetadata, GadgetEnvironment};
 use gadget_common::tangle_subxt::subxt::tx::Signer;
-use gadget_common::tangle_subxt::subxt::PolkadotConfig;
 use gadget_common::utils::serialize;
 use gadget_common::WorkManagerInterface;
 use message::TangleProtocolMessage;
@@ -92,7 +92,7 @@ impl GadgetEnvironment for TangleEnvironment {
 
     async fn setup_runtime(&self) -> Result<Self::Client, Self::Error> {
         let subxt_client =
-            gadget_common::tangle_subxt::subxt::OnlineClient::<PolkadotConfig>::from_url(
+            gadget_common::tangle_subxt::subxt::OnlineClient::<TangleConfig>::from_url(
                 &self.subxt_config.endpoint,
             )
             .await
diff --git a/environments/tangle/src/runtime.rs b/environments/tangle/src/runtime.rs
index 5e1c3b93e..6f51eb0f2 100644
--- a/environments/tangle/src/runtime.rs
+++ b/environments/tangle/src/runtime.rs
@@ -4,11 +4,11 @@ use std::time::Duration;
 use crate::gadget::TangleEvent;
 use gadget_common::locks::TokioMutexExt;
 use gadget_common::tangle_subxt::subxt::blocks::{Block, BlockRef};
-use gadget_common::tangle_subxt::subxt::{self, PolkadotConfig};
+use gadget_common::tangle_subxt::subxt::{self, SubstrateConfig};
 use gadget_common::{async_trait, tangle_runtime::*};
 use gadget_core::gadget::general::Client;
 
-pub type TangleConfig = subxt::PolkadotConfig;
+pub type TangleConfig = SubstrateConfig;
 pub type TangleClient = subxt::OnlineClient<TangleConfig>;
 pub type TangleBlock = Block<TangleConfig, TangleClient>;
 pub type TangleBlockStream = subxt::backend::StreamOfResults<TangleBlock>;
@@ -33,7 +33,7 @@ pub mod crypto {
 
 #[derive(Clone)]
 pub struct TangleRuntime {
-    client: subxt::OnlineClient<PolkadotConfig>,
+    client: subxt::OnlineClient<SubstrateConfig>,
     finality_notification_stream:
         Arc<gadget_common::gadget_io::tokio::sync::Mutex<Option<TangleBlockStream>>>,
     latest_finality_notification:
@@ -43,7 +43,7 @@ pub struct TangleRuntime {
 
 impl TangleRuntime {
     /// Create a new TangleRuntime instance.
-    pub fn new(client: subxt::OnlineClient<PolkadotConfig>, account_id: AccountId32) -> Self {
+    pub fn new(client: subxt::OnlineClient<SubstrateConfig>, account_id: AccountId32) -> Self {
         Self {
             client,
             finality_notification_stream: Arc::new(
@@ -56,7 +56,7 @@ impl TangleRuntime {
         }
     }
 
-    pub fn client(&self) -> subxt::OnlineClient<PolkadotConfig> {
+    pub fn client(&self) -> subxt::OnlineClient<SubstrateConfig> {
         self.client.clone()
     }
 
diff --git a/gadget-io/src/lib.rs b/gadget-io/src/lib.rs
index 0669b21e8..1223d61f5 100644
--- a/gadget-io/src/lib.rs
+++ b/gadget-io/src/lib.rs
@@ -7,13 +7,13 @@ pub mod wasm;
 #[cfg(target_family = "wasm")]
 pub use wasm::{
     keystore::{KeystoreConfig, KeystoreContainer, SubstrateKeystore},
-    shell::{Opt, ShellTomlConfig, SupportedChains},
+    shell::{GadgetTomlConfig, Opt, SupportedChains},
 };
 
 #[cfg(not(target_family = "wasm"))]
 pub use standard::{
     keystore::{KeystoreConfig, KeystoreContainer, SubstrateKeystore},
-    shell::{defaults, Opt, ShellTomlConfig, SupportedChains},
+    shell::{defaults, GadgetConfig, Opt, SupportedChains},
 };
 
 #[cfg(target_family = "wasm")]
diff --git a/gadget-io/src/standard/shell.rs b/gadget-io/src/standard/shell.rs
index 62b26e59a..b94c9d3bf 100644
--- a/gadget-io/src/standard/shell.rs
+++ b/gadget-io/src/standard/shell.rs
@@ -1,4 +1,3 @@
-use color_eyre::Result;
 use libp2p::Multiaddr;
 use serde::{Deserialize, Serialize};
 use std::{fmt::Display, net::IpAddr, path::PathBuf, str::FromStr};
@@ -22,7 +21,7 @@ pub struct Opt {
     pub pretty: bool,
     /// The options for the shell
     #[structopt(flatten)]
-    pub options: ShellTomlConfig,
+    pub options: GadgetConfig,
 }
 
 #[derive(Copy, Clone, Default, Debug, StructOpt, Serialize, Deserialize)]
@@ -62,8 +61,8 @@ impl Display for SupportedChains {
 }
 
 #[derive(Debug, StructOpt, Serialize, Deserialize)]
-/// All shells should expect this as CLI input. The Shell Manager will be responsible for passing these values to the shell.
-pub struct ShellTomlConfig {
+/// All shells should expect this as CLI input. The Blueprint Manager will be responsible for passing these values to this gadget binary
+pub struct GadgetConfig {
     /// The IP address to bind to for the libp2p node.
     #[structopt(long = "bind-ip", short = "i", default_value = defaults::BIND_IP)]
     #[serde(default = "defaults::bind_ip")]
@@ -80,10 +79,6 @@ pub struct ShellTomlConfig {
     #[structopt(long = "bootnodes", parse(try_from_str = <Multiaddr as std::str::FromStr>::from_str))]
     #[serde(default)]
     pub bootnodes: Vec<Multiaddr>,
-    /// The node key in hex format. If not provided, a random node key will be generated.
-    #[structopt(long = "node-key", env, parse(try_from_str = parse_node_key))]
-    #[serde(skip_serializing)]
-    pub node_key: Option<String>,
     /// The base path to store the shell-sdk data, and read data from the keystore.
     #[structopt(
         parse(from_os_str),
@@ -133,21 +128,4 @@ pub mod defaults {
     pub fn bind_port() -> u16 {
         BIND_PORT.parse().expect("Default bind port is valid")
     }
-
-    /// Generates a random node key
-    pub fn generate_node_key() -> [u8; 32] {
-        use rand::Rng;
-
-        let mut rng = rand::thread_rng();
-        let mut array = [0u8; 32];
-        rng.fill(&mut array);
-        array
-    }
-}
-
-fn parse_node_key(s: &str) -> Result<String> {
-    let result: [u8; 32] = hex::decode(s.replace("0x", ""))?.try_into().map_err(|_| {
-        color_eyre::eyre::eyre!("Invalid node key length, expect 32 bytes hex string")
-    })?;
-    Ok(hex::encode(result))
 }
diff --git a/gadget-io/src/wasm/shell.rs b/gadget-io/src/wasm/shell.rs
index 24476408f..c76b827b2 100644
--- a/gadget-io/src/wasm/shell.rs
+++ b/gadget-io/src/wasm/shell.rs
@@ -13,8 +13,8 @@ pub struct Opt {
     pub verbose: i32,
     /// Whether to use pretty logging
     pub pretty: bool,
-    /// The options for the shell
-    pub options: ShellTomlConfig,
+    /// The options for the gadget
+    pub options: GadgetTomlConfig,
 }
 
 #[derive(Default, Debug, Serialize, Deserialize, Tsify)]
@@ -30,8 +30,8 @@ pub enum SupportedChains {
 
 #[derive(Debug, Serialize, Deserialize, Tsify)]
 #[tsify(from_wasm_abi)]
-/// All shells should expect this as CLI input. The Shell Manager will be responsible for passing these values to the shell.
-pub struct ShellTomlConfig {
+/// All gadgets should expect this as CLI input. The Blueprint Manager will be responsible for passing these values to the gadget binary.
+pub struct GadgetTomlConfig {
     /// The IP address to bind to for the libp2p node.
     pub bind_ip: IpAddr,
     /// The port to bind to for the libp2p node.
@@ -40,8 +40,6 @@ pub struct ShellTomlConfig {
     pub url: url::Url,
     /// The List of bootnodes to connect to
     pub bootnodes: Vec<Multiaddr>,
-    /// The node key in hex format. If not provided, a random node key will be generated.
-    pub node_key: Option<String>,
     /// The base path to store the shell-sdk data, and read data from the keystore.
     pub base_path: PathBuf,
     /// Keystore Password, if not provided, the password will be read from the environment variable.
diff --git a/macros/blueprint-proc-macro-playground/blueprint.json b/macros/blueprint-proc-macro-playground/blueprint.json
index f17dca79d..154c1691b 100644
--- a/macros/blueprint-proc-macro-playground/blueprint.json
+++ b/macros/blueprint-proc-macro-playground/blueprint.json
@@ -7,7 +7,7 @@
     "code_repository": "https://github.com/webb-tools/gadget",
     "logo": null,
     "website": "https://tangle.tools",
-    "license": "Unlicense"
+    "license": "MIT OR Apache-2.0"
   },
   "jobs": [
     {
diff --git a/macros/blueprint-proc-macro-playground/src/lib.rs b/macros/blueprint-proc-macro-playground/src/lib.rs
index cba9c892a..e16999aed 100644
--- a/macros/blueprint-proc-macro-playground/src/lib.rs
+++ b/macros/blueprint-proc-macro-playground/src/lib.rs
@@ -21,7 +21,6 @@ impl std::fmt::Display for Error {
 }
 
 impl std::error::Error for Error {}
-
 pub struct MyContext;
 
 // ==================
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
index aeba3233b..288fa153b 100644
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,6 +1,5 @@
 [toolchain]
 channel = "stable"
-#channel = "nightly"
 components = ["rustfmt", "clippy", "rust-src"]
 targets = ["wasm32-unknown-unknown"]
 profile = "minimal"
diff --git a/scripts/run-gadget-local.sh b/scripts/run-gadget-local.sh
index 598a7ddb2..ba5c20c16 100755
--- a/scripts/run-gadget-local.sh
+++ b/scripts/run-gadget-local.sh
@@ -3,22 +3,21 @@ set -e
 # ensure we kill all child processes when we exit
 trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
 
-
 echo "*** Start Gadget ***"
 # Stage gadget for Alice
-./target/release/shell-manager \
+./target/release/blueprint-manager \
   --protocols-config ./global_protocols.toml \
-  --shell-config shell-configs/local-testnet-0.toml &
+  --gadget-config gadget-configs/local-testnet-0.toml &
 
 # Stage gadget for Bob
-./target/release/shell-manager \
+./target/release/blueprint-manager \
   --protocols-config ./global_protocols.toml \
-  --shell-config shell-configs/local-testnet-1.toml &
+  --gadget-config gadget-configs/local-testnet-1.toml &
 
 # Stage gadget for Charlie
-./target/release/shell-manager \
+./target/release/blueprint-manager \
   --protocols-config ./global_protocols.toml \
-  --shell-config shell-configs/local-testnet-2.toml \
+  --gadget-config gadget-configs/local-testnet-2.toml \
   -vvv
 
 popd
diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml
index 0d193d99a..4afdadeac 100644
--- a/sdk/Cargo.toml
+++ b/sdk/Cargo.toml
@@ -30,7 +30,7 @@ tokio = { workspace = true, features = ["rt-multi-thread", "parking_lot"] }
 
 # Logging deps
 log = { workspace = true }
-tracing = { workspace = true, default-features = false }
+tracing = { workspace = true }
 tracing-log = { workspace = true, default-features = false }
 tracing-subscriber = { workspace = true, default-features = false }
 tracing-wasm = { workspace = true, optional = true }
diff --git a/sdk/src/network/setup.rs b/sdk/src/network/setup.rs
index edd879dea..90129b884 100644
--- a/sdk/src/network/setup.rs
+++ b/sdk/src/network/setup.rs
@@ -185,7 +185,7 @@ pub fn multiplexed_libp2p_network(config: NetworkConfig) -> NetworkResult {
         .with_behaviour(|key, relay_client| {
             // Set a custom gossipsub configuration
             let gossipsub_config = gossipsub::ConfigBuilder::default()
-                .protocol_id_prefix("/tangle/gadget-shell-sdk/meshsub")
+                .protocol_id_prefix("/tangle/gadget-binary-sdk/meshsub")
                 .max_transmit_size(MAX_MESSAGE_SIZE)
                 .validate_messages()
                 .validation_mode(gossipsub::ValidationMode::Strict) // This sets the kind of message validation. The default is Strict (enforce message signing)
diff --git a/shell-manager/src/gadget/native.rs b/shell-manager/src/gadget/native.rs
deleted file mode 100644
index f61129900..000000000
--- a/shell-manager/src/gadget/native.rs
+++ /dev/null
@@ -1,235 +0,0 @@
-use crate::config::ShellManagerOpts;
-use crate::gadget::ActiveShells;
-use crate::protocols::resolver::NativeGithubMetadata;
-use crate::utils;
-use crate::utils::get_service_str;
-use color_eyre::eyre::OptionExt;
-use gadget_common::prelude::DebugLogger;
-use gadget_io::ShellTomlConfig;
-use std::fmt::Write;
-use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::{
-    Gadget, GadgetBinary, GithubFetcher,
-};
-use tokio::io::AsyncWriteExt;
-
-pub struct FilteredBlueprint {
-    pub blueprint_id: u64,
-    pub services: Vec<u64>,
-    pub gadget: Gadget,
-    pub registration_mode: bool,
-}
-
-#[allow(clippy::too_many_arguments)]
-pub async fn maybe_handle(
-    blueprints: &[FilteredBlueprint],
-    onchain_services: &[NativeGithubMetadata],
-    onchain_gh_fetchers: &[GithubFetcher],
-    shell_config: &ShellTomlConfig,
-    shell_manager_opts: &ShellManagerOpts,
-    active_shells: &mut ActiveShells,
-    logger: &DebugLogger,
-) -> color_eyre::Result<()> {
-    for (gh, fetcher) in onchain_services.iter().zip(onchain_gh_fetchers) {
-        let native_github_metadata = NativeGithubMetadata {
-            git: gh.git.clone(),
-            tag: gh.tag.clone(),
-            owner: gh.owner.clone(),
-            repo: gh.repo.clone(),
-            gadget_binaries: fetcher.binaries.0.clone(),
-            blueprint_id: gh.blueprint_id,
-        };
-
-        if let Err(err) = handle_github_source(
-            blueprints,
-            &native_github_metadata,
-            shell_config,
-            shell_manager_opts,
-            fetcher,
-            active_shells,
-            logger,
-        )
-        .await
-        {
-            logger.warn(err)
-        }
-    }
-
-    Ok(())
-}
-
-#[allow(clippy::too_many_arguments)]
-async fn handle_github_source(
-    blueprints: &[FilteredBlueprint],
-    service: &NativeGithubMetadata,
-    shell_config: &ShellTomlConfig,
-    shell_manager_opts: &ShellManagerOpts,
-    github: &GithubFetcher,
-    active_shells: &mut ActiveShells,
-    logger: &DebugLogger,
-) -> color_eyre::Result<()> {
-    let blueprint_id = service.blueprint_id;
-    let service_str = get_service_str(service);
-    if !active_shells.contains_key(&blueprint_id) {
-        // Maybe add in the protocol to the active shells
-        let relevant_binary =
-            get_gadget_binary(&github.binaries.0).ok_or_eyre("Unable to find matching binary")?;
-        let expected_hash = slice_32_to_sha_hex_string(relevant_binary.sha256);
-
-        let current_dir = std::env::current_dir()?;
-        let mut binary_download_path =
-            format!("{}/protocol-{:?}", current_dir.display(), github.tag);
-
-        if utils::is_windows() {
-            binary_download_path += ".exe"
-        }
-
-        logger.info(format!("Downloading to {binary_download_path}"));
-
-        // Check if the binary exists, if not download it
-        let retrieved_hash =
-            if !utils::valid_file_exists(&binary_download_path, &expected_hash).await {
-                let url = utils::get_download_url(&service.git, &service.tag);
-
-                let download = reqwest::get(&url)
-                    .await
-                    .map_err(|err| utils::msg_to_error(err.to_string()))?
-                    .bytes()
-                    .await
-                    .map_err(|err| utils::msg_to_error(err.to_string()))?;
-                let retrieved_hash = utils::hash_bytes_to_hex(&download);
-
-                // Write the binary to disk
-                let mut file = tokio::fs::File::create(&binary_download_path).await?;
-                file.write_all(&download).await?;
-                file.flush().await?;
-                Some(retrieved_hash)
-            } else {
-                None
-            };
-
-        if let Some(retrieved_hash) = retrieved_hash {
-            if retrieved_hash.trim() != expected_hash.trim() {
-                logger.error(format!(
-                    "Binary hash {} mismatched expected hash of {} for protocol: {}",
-                    retrieved_hash, expected_hash, service_str
-                ));
-                return Ok(());
-            }
-        }
-
-        if !utils::is_windows() {
-            if let Err(err) = utils::chmod_x_file(&binary_download_path).await {
-                logger.warn(format!("Failed to chmod +x the binary: {err}"));
-            }
-        }
-
-        for blueprint in blueprints {
-            if blueprint.blueprint_id == blueprint_id {
-                for service_id in &blueprint.services {
-                    let sub_service_str = format!("{service_str}-{service_id}");
-                    // Each spawned binary will effectively run a single "RoleType" in old parlance
-                    let arguments = utils::generate_process_arguments(
-                        shell_config,
-                        shell_manager_opts,
-                        blueprint_id,
-                        *service_id,
-                    )?;
-
-                    let env_vars = if blueprint.registration_mode {
-                        let mut env_vars = std::env::vars().collect::<Vec<_>>();
-                        // RPC_URL: The remote RPC url for tangle
-                        // KEYSTORE_URI: the keystore file where the keys are stored and could be retrieved.
-                        // DATA_DIR: is an isolated path for where this gadget should store its data (database, secrets, …etc)
-                        // BLUEPRINT_ID: the active blueprint ID for this gadget
-                        // REGISTRATION_MODE_ON set to any value.
-                        env_vars.push(("RPC_URL".to_string(), shell_config.url.to_string()));
-                        env_vars.push((
-                            "KEYSTORE_URI".to_string(),
-                            shell_manager_opts.keystore_uri.clone(),
-                        ));
-                        env_vars.push((
-                            "DATA_DIR".to_string(),
-                            format!("{}", shell_config.base_path.display()),
-                        ));
-                        env_vars.push(("BLUEPRINT_ID".to_string(), format!("{}", blueprint_id)));
-                        env_vars.push(("REGISTRATION_MODE_ON".to_string(), "true".to_string()));
-                        env_vars
-                    } else {
-                        std::env::vars().collect::<Vec<_>>()
-                    };
-
-                    logger.info(format!("Starting protocol: {sub_service_str}"));
-
-                    // Now that the file is loaded, spawn the process
-                    let process_handle = tokio::process::Command::new(&binary_download_path)
-                        .kill_on_drop(true)
-                        .stdout(std::process::Stdio::inherit()) // Inherit the stdout of this process
-                        .stderr(std::process::Stdio::inherit()) // Inherit the stderr of this process
-                        .stdin(std::process::Stdio::null())
-                        .current_dir(&std::env::current_dir()?)
-                        .envs(env_vars)
-                        .args(arguments)
-                        .spawn()?;
-
-                    if blueprint.registration_mode {
-                        // We must wait for the process to exit successfully
-                        let status = process_handle.wait_with_output().await?;
-                        if !status.status.success() {
-                            logger.error(format!(
-                                "Protocol (registration mode) {sub_service_str} failed to execute: {status:?}"
-                            ));
-                        } else {
-                            logger.info(format!(
-                                "***Protocol (registration mode) {sub_service_str} executed successfully***"
-                            ));
-                        }
-                    } else {
-                        // A normal running gadget binary. Store the process handle and let the event loop handle the rest
-                        let (status_handle, abort) = utils::generate_running_process_status_handle(
-                            process_handle,
-                            logger,
-                            &sub_service_str,
-                        );
-
-                        active_shells
-                            .entry(blueprint_id)
-                            .or_default()
-                            .insert(*service_id, (status_handle, Some(abort)));
-                    }
-                }
-            }
-        }
-    }
-
-    Ok(())
-}
-
-fn slice_32_to_sha_hex_string(hash: [u8; 32]) -> String {
-    hash.iter().fold(String::new(), |mut acc, byte| {
-        write!(&mut acc, "{:02x}", byte).expect("Should be able to write");
-        acc
-    })
-}
-
-fn get_gadget_binary(gadget_binaries: &[GadgetBinary]) -> Option<&GadgetBinary> {
-    let os = utils::get_formatted_os_string().to_lowercase();
-    let arch = std::env::consts::ARCH.to_lowercase();
-    for binary in gadget_binaries {
-        let binary_str = format!("{:?}", binary.os).to_lowercase();
-        if binary_str.contains(&os) || os.contains(&binary_str) || binary_str == os {
-            let mut arch_str = format!("{:?}", binary.arch).to_lowercase();
-
-            if arch_str == "amd" {
-                arch_str = "x86".to_string()
-            } else if arch_str == "amd64" {
-                arch_str = "x86_64".to_string()
-            }
-
-            if arch_str == arch {
-                return Some(binary);
-            }
-        }
-    }
-
-    None
-}
diff --git a/shell-manager/src/main.rs b/shell-manager/src/main.rs
deleted file mode 100644
index 7c5531e81..000000000
--- a/shell-manager/src/main.rs
+++ /dev/null
@@ -1,406 +0,0 @@
-use crate::gadget::native::FilteredBlueprint;
-use crate::gadget::ActiveShells;
-use crate::utils::github_fetcher_to_native_github_metadata;
-use color_eyre::eyre::OptionExt;
-use color_eyre::Report;
-use config::ShellManagerOpts;
-use gadget_common::gadget_io;
-use gadget_common::prelude::GadgetEnvironment;
-use gadget_common::tangle_runtime::api::services::events::PreRegistration;
-use gadget_io::ShellTomlConfig;
-use shell_sdk::entry::keystore_from_base_path;
-use shell_sdk::keystore::load_keys_from_keystore;
-use shell_sdk::{entry, Client, DebugLogger};
-use sp_core::crypto::Pair;
-use std::collections::HashMap;
-use std::sync::atomic::Ordering;
-use structopt::StructOpt;
-use tangle_environment::api::{RpcServicesWithBlueprint, ServicesClient};
-use tangle_environment::gadget::{SubxtConfig, TangleEvent};
-use tangle_environment::TangleEnvironment;
-use tangle_subxt::subxt::utils::AccountId32;
-use tangle_subxt::subxt::PolkadotConfig;
-use tangle_subxt::tangle_testnet_runtime::api::runtime_types;
-use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::GadgetSourceFetcher;
-use tangle_subxt::tangle_testnet_runtime::api::services::events::{
-    JobCalled, JobResultSubmitted, Registered, ServiceInitiated, Unregistered,
-};
-
-pub mod config;
-pub mod error;
-pub mod gadget;
-pub mod protocols;
-pub mod utils;
-
-#[tokio::main]
-async fn main() -> color_eyre::Result<()> {
-    //color_eyre::install()?;
-    let opt = &ShellManagerOpts::from_args();
-    entry::setup_shell_logger(opt.verbose, opt.pretty, "gadget")?;
-    let shell_config_contents = std::fs::read_to_string(opt.shell_config.clone())?;
-    let shell_config: ShellTomlConfig = toml::from_str(&shell_config_contents)
-        .map_err(|err| utils::msg_to_error(err.to_string()))?;
-    let logger = &DebugLogger {
-        id: "Gadget Shell Manager".into(),
-    };
-
-    let keystore = keystore_from_base_path(
-        &shell_config.base_path,
-        shell_config.chain,
-        shell_config.keystore_password.clone(),
-    );
-
-    let (_role_key, acco_key) = load_keys_from_keystore(&keystore)?;
-    let sub_account_id = AccountId32(acco_key.public().0);
-    let subxt_config = SubxtConfig {
-        endpoint: shell_config.url.clone(),
-    };
-
-    let tangle_environment = TangleEnvironment::new(subxt_config, acco_key, logger.clone());
-
-    let tangle_runtime = tangle_environment.setup_runtime().await?;
-    let runtime = ServicesClient::new(logger.clone(), tangle_runtime.client());
-    let mut active_shells = HashMap::new();
-
-    // With the basics setup, we must now implement the main logic
-    /*
-       * Query to get Vec<RpcServicesWithBlueprint>
-       * For each RpcServicesWithBlueprint, fetch the associated gadget binary (fetch/download)
-        -> If the services field is empty, just emit and log inside the executed binary "that states a new service instance got created by one of these blueprints"
-        -> If the services field is not empty, for each service in RpcServicesWithBlueprint.services, spawn the gadget binary, using params to set the job type to listen to (in terms of our old language, each spawned service represents a single "RoleType")
-    */
-
-    let (mut blueprints, init_event) = if let Some(event) = tangle_runtime.next_event().await {
-        (
-            utils::get_blueprints(&runtime, event.hash, sub_account_id.clone()).await?,
-            event,
-        )
-    } else {
-        return Err(Report::msg("Failed to get initial block hash"));
-    };
-
-    logger.info(format!("Received {} blueprints", blueprints.len()));
-
-    let poll_result =
-        handle_tangle_block(&init_event, logger, &mut active_shells, &sub_account_id).await;
-    handle_tangle_event(
-        &init_event,
-        &blueprints,
-        logger,
-        &shell_config,
-        opt,
-        &mut active_shells,
-        poll_result,
-        &runtime,
-    )
-    .await?;
-
-    let manager_task = async move {
-        // Step 2: Listen to FinalityNotifications and poll for new/deleted services that correspond to the blueprints above
-        while let Some(event) = tangle_runtime.next_event().await {
-            let result =
-                handle_tangle_block(&event, logger, &mut active_shells, &sub_account_id).await;
-
-            if result.needs_update {
-                blueprints =
-                    utils::get_blueprints(&runtime, event.hash, sub_account_id.clone()).await?;
-            }
-
-            handle_tangle_event(
-                &event,
-                &blueprints,
-                logger,
-                &shell_config,
-                opt,
-                &mut active_shells,
-                result,
-                &runtime,
-            )
-            .await?;
-        }
-
-        Err::<(), _>(utils::msg_to_error("Finality Notification stream died"))
-    };
-
-    let ctrlc_task = gadget_io::tokio::signal::ctrl_c();
-
-    gadget_io::tokio::select! {
-        res0 = manager_task => {
-            Err(color_eyre::Report::msg(format!("Gadget Manager Closed Unexpectedly: {res0:?}")))
-        },
-
-        _ = ctrlc_task => {
-            logger.info("CTRL-C detected, closing application");
-            Ok(())
-        }
-    }
-}
-
-#[derive(Default, Debug)]
-struct EventPollResult {
-    needs_update: bool,
-    // A vec of blueprints we have not yet become registered to
-    blueprint_registrations: Vec<u64>,
-}
-
-async fn handle_tangle_block(
-    event: &TangleEvent,
-    logger: &DebugLogger,
-    active_shells: &mut ActiveShells,
-    account_id: &AccountId32,
-) -> EventPollResult {
-    let pre_registation_events = event.events.find::<PreRegistration>();
-    let registered_events = event.events.find::<Registered>();
-    let unregistered_events = event.events.find::<Unregistered>();
-    let service_initiated_events = event.events.find::<ServiceInitiated>();
-    let job_called_events = event.events.find::<JobCalled>();
-    let job_result_submitted_events = event.events.find::<JobResultSubmitted>();
-
-    let mut result = EventPollResult::default();
-
-    for evt in pre_registation_events {
-        match evt {
-            Ok(evt) => {
-                if &evt.operator == account_id {
-                    result.blueprint_registrations.push(evt.blueprint_id);
-                    logger.info(format!("Pre-registered event: {evt:?}"));
-                }
-            }
-            Err(err) => {
-                logger.warn(format!("Error handling pre-registered event: {err:?}"));
-            }
-        }
-    }
-
-    // Handle registered events
-    for evt in registered_events {
-        match evt {
-            Ok(evt) => {
-                logger.info(format!("Registered event: {evt:?}"));
-                result.needs_update = true;
-            }
-            Err(err) => {
-                logger.warn(format!("Error handling registered event: {err:?}"));
-            }
-        }
-    }
-
-    // Handle unregistered events
-    for evt in unregistered_events {
-        match evt {
-            Ok(evt) => {
-                logger.info(format!("Unregistered event: {evt:?}"));
-                if &evt.operator == account_id && active_shells.remove(&evt.blueprint_id).is_some()
-                {
-                    logger.info(format!(
-                        "Removed services for blueprint_id: {}",
-                        evt.blueprint_id,
-                    ));
-
-                    result.needs_update = true;
-                }
-            }
-            Err(err) => {
-                logger.warn(format!("Error handling unregistered event: {err:?}"));
-            }
-        }
-    }
-
-    // Handle service initiated events
-    for evt in service_initiated_events {
-        match evt {
-            Ok(evt) => {
-                logger.info(format!("Service initiated event: {evt:?}"));
-            }
-            Err(err) => {
-                logger.warn(format!("Error handling service initiated event: {err:?}"));
-            }
-        }
-    }
-
-    // Handle job called events
-    for evt in job_called_events {
-        match evt {
-            Ok(evt) => {
-                logger.info(format!("Job called event: {evt:?}"));
-            }
-            Err(err) => {
-                logger.warn(format!("Error handling job called event: {err:?}"));
-            }
-        }
-    }
-
-    // Handle job result submitted events
-    for evt in job_result_submitted_events {
-        match evt {
-            Ok(evt) => {
-                logger.info(format!("Job result submitted event: {evt:?}"));
-            }
-            Err(err) => {
-                logger.warn(format!(
-                    "Error handling job result submitted event: {err:?}"
-                ));
-            }
-        }
-    }
-
-    result
-}
-
-#[allow(clippy::too_many_arguments)]
-async fn handle_tangle_event(
-    event: &TangleEvent,
-    blueprints: &[RpcServicesWithBlueprint],
-    logger: &DebugLogger,
-    shell_config: &ShellTomlConfig,
-    shell_manager_opts: &ShellManagerOpts,
-    active_shells: &mut ActiveShells,
-    poll_result: EventPollResult,
-    client: &ServicesClient<PolkadotConfig>,
-) -> color_eyre::Result<()> {
-    logger.info(format!("Received notification {}", event.number));
-
-    let mut registration_blueprints = vec![];
-    // First, check to see if we need to register any new services invoked by the PreRegistration event
-    if !poll_result.blueprint_registrations.is_empty() {
-        for blueprint_id in &poll_result.blueprint_registrations {
-            let blueprint = client
-                .get_blueprint_by_id(event.hash, *blueprint_id)
-                .await?
-                .ok_or_eyre("Unable to retrieve blueprint for registration mode")?;
-
-            let general_blueprint = FilteredBlueprint {
-                blueprint_id: *blueprint_id,
-                services: vec![0], // Add a dummy service id for now, since it does not matter for registration mode
-                gadget: blueprint.gadget,
-                registration_mode: true,
-            };
-
-            registration_blueprints.push(general_blueprint);
-        }
-    }
-
-    // TODO: Refactor into Vec<SourceMetadata<T>> where T: NativeGithubMetadata + [...]
-    let mut onchain_services = vec![];
-    let mut fetchers = vec![];
-    let mut service_ids = vec![];
-    let mut valid_blueprint_ids = vec![];
-
-    let mut blueprints_filtered = vec![];
-
-    for blueprint in blueprints
-        .iter()
-        .map(|r| {
-            let r = FilteredBlueprint {
-                blueprint_id: r.blueprint_id,
-                services: r.services.iter().map(|r| r.id).collect(),
-                gadget: r.blueprint.gadget.clone(),
-                registration_mode: false,
-            };
-
-            r
-        })
-        .chain(registration_blueprints)
-    {
-        let mut services_for_this_blueprint = vec![];
-        if let runtime_types::tangle_primitives::services::Gadget::Native(gadget) =
-            &blueprint.gadget
-        {
-            let gadget_source = &gadget.soruces.0[0];
-            if let GadgetSourceFetcher::Github(gh) = &gadget_source.fetcher {
-                let metadata = github_fetcher_to_native_github_metadata(gh, blueprint.blueprint_id);
-                onchain_services.push(metadata);
-                fetchers.push(gh.clone());
-                valid_blueprint_ids.push(blueprint.blueprint_id);
-
-                for service in &blueprint.services {
-                    services_for_this_blueprint.push(*service);
-                }
-
-                blueprints_filtered.push(blueprint);
-            } else {
-                logger.warn(
-                    "Blueprint does not contain a Github fetcher and thus currently unsupported",
-                );
-            }
-
-            service_ids.push(services_for_this_blueprint);
-        } else {
-            logger
-                .warn("Blueprint does not contain a native gadget and thus currently unsupported");
-        }
-    }
-
-    logger.trace(format!(
-        "OnChain services: {:?}",
-        onchain_services
-            .iter()
-            .map(|r| r.git.clone())
-            .collect::<Vec<_>>()
-    ));
-
-    // Step 3: Check to see if we need to start any new services
-    gadget::native::maybe_handle(
-        &blueprints_filtered,
-        &onchain_services,
-        &fetchers,
-        shell_config,
-        shell_manager_opts,
-        active_shells,
-        logger,
-    )
-    .await?;
-
-    // Check to see if local is running services that are not on-chain
-    let mut to_remove: Vec<(u64, u64)> = vec![];
-
-    // Loop through every (blueprint_id, service_id) running. See if the service is still on-chain. If not, kill it and add it to to_remove
-    for (blueprint_id, process_handles) in &mut *active_shells {
-        for (service_id, process_handle) in process_handles {
-            if !onchain_services
-                .iter()
-                .zip(&service_ids)
-                .all(|(r, s)| r.blueprint_id != *blueprint_id && !s.contains(service_id))
-            {
-                logger.warn(format!(
-                    "Killing service that is no longer on-chain: bid={blueprint_id}//sid={service_id}",
-                ));
-                if let Some(abort_handle) = process_handle.1.take() {
-                    if abort_handle.send(()).is_err() {
-                        logger.error(format!(
-                            "Failed to send abort signal to service: bid={blueprint_id}//sid={service_id}",
-                        ));
-                    }
-                }
-
-                to_remove.push((*blueprint_id, *service_id));
-            }
-        }
-    }
-
-    // Check to see if any process handles have died
-    for (blueprint_id, process_handles) in &mut *active_shells {
-        for (service_id, process_handle) in process_handles {
-            if !process_handle.0.load(Ordering::Relaxed) {
-                // By removing any killed processes, we will auto-restart them on the next finality notification if required
-                to_remove.push((*blueprint_id, *service_id));
-            }
-        }
-    }
-
-    for (blueprint_id, service_id) in to_remove {
-        let mut should_delete_blueprint = false;
-        if let Some(shells) = active_shells.get_mut(&blueprint_id) {
-            shells.remove(&service_id);
-            if shells.is_empty() {
-                should_delete_blueprint = true;
-            }
-        }
-
-        if should_delete_blueprint {
-            active_shells.remove(&blueprint_id);
-        }
-    }
-
-    Ok(())
-}
diff --git a/shell-sdk/DEMO.md b/shell-sdk/DEMO.md
index 28b7d9743..a056289fc 100644
--- a/shell-sdk/DEMO.md
+++ b/shell-sdk/DEMO.md
@@ -21,16 +21,16 @@ cargo build --release
 bash ./scripts/run-standalone-local.sh --clean
 ```
 
-### Setup Gadget Shell
+### Setup the Gadget Binary
 
-1. Compile the Gadget Shell in release mode
+1. Compile the Gadget Binary in release mode
 ```bash
 cargo build --release
 ```
-2. Run the `target/release/gadget-shell` binary using the following command:
+2. Run the `target/release/gadget-binary` binary using the following command:
 
 ```bash
-./target/release/gadget-shell-sdk --config shell-sdk-configs/local-testnet-0.toml -vvv
+./target/release/gadget-binary-sdk --config gadget-sdk-configs/local-testnet-0.toml -vvv
 ```
 
 > Note: The `--config` flag is used to specify the configuration file to use. In this case, we are using the `local-testnet-0.toml` configuration file.
@@ -38,11 +38,11 @@ cargo build --release
 3. Run different shells with different configurations to simulate a network of shells.
 
 ```bash
-./target/release/gadget-shell-sdk --config shell-sdk-configs/local-testnet-1.toml -vvv
+./target/release/gadget-binary-sdk --config gadget-sdk-configs/local-testnet-1.toml -vvv
 ```
 
 ```bash
-./target/release/gadget-shell-sdk --config shell-sdk-configs/local-testnet-2.toml -vvv
+./target/release/gadget-binary-sdk --config gadget-sdk-configs/local-testnet-2.toml -vvv
 ```
 
 > Note: You will notice they automatically connect to each other and start exchanging messages.
diff --git a/shell-sdk/src/config.rs b/shell-sdk/src/config.rs
index 1ca6cb377..67a3e885b 100644
--- a/shell-sdk/src/config.rs
+++ b/shell-sdk/src/config.rs
@@ -6,14 +6,13 @@ use std::path::PathBuf;
 use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::ServiceBlueprint;
 
 #[derive(Debug)]
-pub struct ShellConfig<KBE: Clone, Env: GadgetEnvironment> {
+pub struct SingleGadgetConfig<KBE: Clone, Env: GadgetEnvironment> {
     pub base_path: PathBuf,
     pub keystore: KeystoreConfig,
     pub environment: Env,
     pub bind_ip: IpAddr,
     pub bind_port: u16,
     pub bootnodes: Vec<Multiaddr>,
-    pub node_key: [u8; 32],
     pub services: Vec<ServiceBlueprint>,
     pub n_protocols: usize,
     pub keystore_backend: KBE,
diff --git a/shell-sdk/src/entry.rs b/shell-sdk/src/entry.rs
index 167d59d2f..2f8dd0bac 100644
--- a/shell-sdk/src/entry.rs
+++ b/shell-sdk/src/entry.rs
@@ -1,8 +1,8 @@
-use crate::shell::ShellNodeInput;
+use crate::setup::SingleGadgetInput;
 use gadget_common::environments::GadgetEnvironment;
 use gadget_common::prelude::{DebugLogger, KeystoreBackend};
 use gadget_core::job_manager::SendFuture;
-use gadget_io::{defaults, ShellTomlConfig, SupportedChains};
+use gadget_io::{GadgetConfig, SupportedChains};
 use structopt::StructOpt;
 use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::ServiceBlueprint;
 use tracing_subscriber::fmt::SubscriberBuilder;
@@ -23,11 +23,11 @@ pub fn keystore_from_base_path(
     }
 }
 
-/// Runs a shell for a given protocol.
-pub async fn run_shell_for_protocol<
+/// Runs a gadget for a given protocol.
+pub async fn run_gadget_for_protocol<
     Env: GadgetEnvironment,
     KBE: KeystoreBackend,
-    T: FnOnce(ShellNodeInput<KBE, Env>) -> F,
+    T: FnOnce(SingleGadgetInput<KBE, Env>) -> F,
     F,
     T2: FnOnce() -> F2,
     F2: SendFuture<'static, KBE>,
@@ -43,26 +43,26 @@ where
 {
     let args = std::env::args();
     println!("Args: {args:?}");
-    let config = ShellTomlConfig::from_iter_safe(args);
+    let config = GadgetConfig::from_iter_safe(args);
 
     if config.is_err() {
         return Err(color_eyre::Report::msg(format!(
-            "Failed to parse shell config: {config:?}"
+            "Failed to parse gadget config: {config:?}"
         )));
     }
 
     let config = config.unwrap();
     let keystore_backend = keystore_backend().await;
-    setup_shell_logger(config.verbose, config.pretty, "gadget")?;
     let keystore =
         keystore_from_base_path(&config.base_path, config.chain, config.keystore_password);
 
     let logger = DebugLogger {
         id: "test".to_string(),
     };
-    logger.info("Starting shell with config: {config:?}");
 
-    let (node_input, network_handle) = crate::generate_node_input(crate::ShellConfig {
+    logger.info("Starting gadget with config: {config:?}");
+
+    let (node_input, network_handle) = crate::generate_node_input(crate::SingleGadgetConfig {
         keystore_backend,
         services,
         keystore,
@@ -71,21 +71,11 @@ where
         bind_ip: config.bind_ip,
         bind_port: config.bind_port,
         bootnodes: config.bootnodes,
-        node_key: hex::decode(
-            config
-                .node_key
-                .unwrap_or_else(|| hex::encode(defaults::generate_node_key())),
-        )?
-        .try_into()
-        .map_err(|_| {
-            color_eyre::eyre::eyre!("Invalid node key length, expect 32 bytes hex string")
-        })?,
         n_protocols,
     })
     .await?;
 
     let protocol_future = gadget_io::tokio::task::spawn(executor(node_input));
-    let ctrlc_future = gadget_io::tokio::signal::ctrl_c();
 
     gadget_io::tokio::select! {
         res0 = protocol_future => {
@@ -95,15 +85,15 @@ where
         res1 = network_handle => {
             Err(color_eyre::Report::msg(format!("Networking future unexpectedly finished: {res1:?}")))
         },
-
-        _ = ctrlc_future => {
-            Ok(())
-        }
     }
 }
 
 /// Sets up the logger for the shell-sdk, based on the verbosity level passed in.
-pub fn setup_shell_logger(verbose: i32, pretty: bool, filter: &str) -> color_eyre::Result<()> {
+pub fn setup_blueprint_manager_logger(
+    verbose: i32,
+    pretty: bool,
+    filter: &str,
+) -> color_eyre::Result<()> {
     use tracing::Level;
     let log_level = match verbose {
         0 => Level::ERROR,
diff --git a/shell-sdk/src/lib.rs b/shell-sdk/src/lib.rs
index 31283662b..b0faa1d71 100644
--- a/shell-sdk/src/lib.rs
+++ b/shell-sdk/src/lib.rs
@@ -1,14 +1,14 @@
 pub mod config;
 pub mod entry;
 pub mod keystore;
-pub mod shell;
+pub mod setup;
 
 pub use config::*;
-pub use entry::run_shell_for_protocol;
+pub use entry::run_gadget_for_protocol;
 pub use gadget_common::prelude::*;
 pub use gadget_core::gadget::general::Client;
 pub use gadget_sdk::network::setup::{start_p2p_network, NetworkConfig};
-pub use shell::generate_node_input;
+pub use setup::generate_node_input;
 
 pub mod prelude {
     pub use crate::async_trait;
@@ -38,15 +38,3 @@ pub mod prelude {
     pub use tangle_primitives;
     pub use tangle_subxt;
 }
-
-/// Should be put inside the lib file of the protocol repository
-#[macro_export]
-macro_rules! generate_shell_binary {
-    ($entry_point:path, $keystore:path, $n_protocols:expr, $($role_type:expr),*) => {
-        #[gadget_io::tokio::main]
-        async fn main() -> color_eyre::Result<()> {
-            $crate::run_shell_for_protocol(vec![$($role_type),*], $n_protocols, $keystore, $entry_point).await?;
-            Ok(())
-        }
-    };
-}
diff --git a/shell-sdk/src/shell.rs b/shell-sdk/src/setup.rs
similarity index 86%
rename from shell-sdk/src/shell.rs
rename to shell-sdk/src/setup.rs
index 1ef964648..dc10e6e71 100644
--- a/shell-sdk/src/shell.rs
+++ b/shell-sdk/src/setup.rs
@@ -11,28 +11,32 @@ use gadget_common::{
     keystore::ECDSAKeyStore,
 };
 use gadget_io::tokio::task::JoinHandle;
-use sp_core::{ed25519, keccak_256, sr25519, Pair};
+use sp_core::{keccak_256, sr25519, Pair};
 
-use crate::config::ShellConfig;
+use crate::config::SingleGadgetConfig;
 pub use gadget_io::KeystoreContainer;
 use gadget_sdk::network::gossip::GossipHandle;
 use gadget_sdk::network::setup::NetworkConfig;
 use itertools::Itertools;
 use libp2p::Multiaddr;
 
-pub type ShellNodeInput<KBE, Env> = NodeInput<Env, GossipHandle, KBE, ()>;
+pub type SingleGadgetInput<KBE, Env> = NodeInput<Env, GossipHandle, KBE, ()>;
 
 /// Generates the NodeInput and handle to the networking layer for the given config.
 #[tracing::instrument(skip(config))]
 pub async fn generate_node_input<KBE: KeystoreBackend, Env: GadgetEnvironment>(
-    config: ShellConfig<KBE, Env>,
-) -> color_eyre::Result<(ShellNodeInput<KBE, Env>, JoinHandle<()>)> {
+    config: SingleGadgetConfig<KBE, Env>,
+) -> color_eyre::Result<(SingleGadgetInput<KBE, Env>, JoinHandle<()>)> {
     let (role_key, acco_key) = load_keys_from_keystore(&config.keystore)?;
-    let network_key = ed25519::Pair::from_seed(&config.node_key);
+    //let network_key = ed25519::Pair::from_seed(&config.node_key).to_raw_vec();
     let logger = DebugLogger::default();
     let wrapped_keystore = ECDSAKeyStore::new(config.keystore_backend.clone(), role_key.clone());
 
-    let libp2p_key = libp2p::identity::Keypair::ed25519_from_bytes(network_key.to_raw_vec())
+    // Use the first 32 bytes of the sr25519 account key as the network key. We discard the 32 remaining nonce seed bytes
+    // thus ensuring that the network key, when used, will actually have slightly different properties than the original
+    // key since the nonces will not be the same.
+    let network_key = &mut acco_key.as_ref().to_half_ed25519_bytes()[..32];
+    let libp2p_key = libp2p::identity::Keypair::ed25519_from_bytes(network_key)
         .map_err(|e| color_eyre::eyre::eyre!("Failed to create libp2p keypair: {e}"))?;
 
     // Create a network for each subprotocol in the protocol (e.g., keygen, signing, refresh, rotate, = 4 total subprotocols = n_protocols)
@@ -81,7 +85,7 @@ pub fn generate_node_input_for_role_group<KBE, Env: GadgetEnvironment>(
     logger: DebugLogger,
     tx_manager: Env::TransactionManager,
     keystore: ECDSAKeyStore<KBE>,
-) -> color_eyre::Result<ShellNodeInput<KBE, Env>>
+) -> color_eyre::Result<SingleGadgetInput<KBE, Env>>
 where
     KBE: KeystoreBackend,
 {
@@ -113,7 +117,7 @@ pub async fn generate_node_input_for_required_protocols<KBE, Env: GadgetEnvironm
     acco_key: sr25519::Pair,
     logger: DebugLogger,
     keystore: ECDSAKeyStore<KBE>,
-) -> color_eyre::Result<ShellNodeInput<KBE, Env>>
+) -> color_eyre::Result<SingleGadgetInput<KBE, Env>>
 where
     KBE: KeystoreBackend,
 {


Test patch:
diff --git a/tangle-test-utils/Cargo.toml b/blueprint-test-utils/Cargo.toml
similarity index 87%
rename from tangle-test-utils/Cargo.toml
rename to blueprint-test-utils/Cargo.toml
index 78bc6e689..2c573bf5a 100644
--- a/tangle-test-utils/Cargo.toml
+++ b/blueprint-test-utils/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "tangle-test-utils"
+name = "blueprint-test-utils"
 version = "0.1.0"
 edition = "2021"
 
@@ -37,11 +37,14 @@ wasm = [
 ]
 
 [dependencies]
+blueprint-manager = { workspace = true }
 getrandom = { workspace = true, optional = true, default-features = false }
 environment-utils = { workspace = true }
 parking_lot = { workspace = true }
+tokio = { workspace = true, features = ["full"] }
 futures = { workspace = true }
 sp-io = { workspace = true, default-features = false }
+libp2p = { workspace = true, default-features = false }
 log = { workspace = true }
 async-trait = { workspace = true }
 frame-support = { workspace = true, default-features = false }
@@ -75,3 +78,10 @@ pallet-session = { workspace = true }
 pallet-staking = { workspace = true }
 pallet-evm = { workspace = true }
 sp-staking = { workspace = true }
+url = { workspace = true }
+
+gadget-blueprint-proc-macro-core = { workspace = true }
+cargo-tangle = { workspace = true, default-features = true }
+
+[dev-dependencies]
+cargo_metadata = { workspace = true }
\ No newline at end of file
diff --git a/blueprint-test-utils/src/lib.rs b/blueprint-test-utils/src/lib.rs
new file mode 100644
index 000000000..0d64db067
--- /dev/null
+++ b/blueprint-test-utils/src/lib.rs
@@ -0,0 +1,362 @@
+use blueprint_manager::config::BlueprintManagerConfig;
+use blueprint_manager::executor::BlueprintManagerHandle;
+use cargo_tangle::deploy::Opts;
+use gadget_common::subxt_signer::sr25519;
+use gadget_common::tangle_runtime::api::services::calls::types::call::{Args, Job};
+use gadget_common::tangle_runtime::api::services::calls::types::create_blueprint::Blueprint;
+use gadget_common::tangle_runtime::api::services::calls::types::register::{
+    Preferences, RegistrationArgs,
+};
+use gadget_common::tangle_runtime::api::services::storage::types::job_results::JobResults;
+use gadget_common::tangle_runtime::{api, AccountId32};
+use gadget_common::tangle_subxt::subxt::OnlineClient;
+pub use gadget_core::job_manager::SendFuture;
+use gadget_io::{GadgetConfig, SupportedChains};
+use libp2p::Multiaddr;
+pub use log;
+use std::error::Error;
+use std::net::IpAddr;
+use std::path::PathBuf;
+use std::time::Duration;
+use tangle_environment::runtime::TangleConfig;
+use tracing_subscriber::filter::EnvFilter;
+use tracing_subscriber::fmt::SubscriberBuilder;
+use tracing_subscriber::util::SubscriberInitExt;
+use url::Url;
+
+pub type InputValue = api::runtime_types::tangle_primitives::services::field::Field<AccountId32>;
+pub type OutputValue = api::runtime_types::tangle_primitives::services::field::Field<AccountId32>;
+
+pub mod sync;
+pub mod test_ext;
+
+pub type TestClient = OnlineClient<TangleConfig>;
+
+pub struct PerTestNodeInput<T> {
+    instance_id: u64,
+    bind_ip: IpAddr,
+    bind_port: u16,
+    bootnodes: Vec<Multiaddr>,
+    // Should be in the form: ../tangle/tmp/alice
+    base_path: String,
+    verbose: i32,
+    pretty: bool,
+    #[allow(dead_code)]
+    extra_input: T,
+    local_tangle_node: Url,
+}
+
+/// Runs a test node using a top-down approach and invoking the blueprint manager to auto manage
+/// execution of blueprints and their associated services for the test node.
+pub async fn run_test_blueprint_manager<T: Send + Clone + 'static>(
+    input: PerTestNodeInput<T>,
+) -> BlueprintManagerHandle {
+    let blueprint_manager_config = BlueprintManagerConfig {
+        gadget_config: None,
+        keystore_uri: "./target/keystore".to_string(),
+        verbose: input.verbose,
+        pretty: input.pretty,
+        instance_id: Some(format!("Test Node {}", input.instance_id)),
+    };
+
+    let gadget_config = GadgetConfig {
+        bind_ip: input.bind_ip,
+        bind_port: input.bind_port,
+        url: input.local_tangle_node,
+        bootnodes: input.bootnodes,
+        base_path: PathBuf::from(input.base_path),
+        keystore_password: None,
+        chain: SupportedChains::LocalTestnet,
+        verbose: input.verbose,
+        pretty: input.pretty,
+    };
+
+    let shutdown_signal = futures::future::pending();
+
+    blueprint_manager::run_blueprint_manager(
+        blueprint_manager_config,
+        gadget_config,
+        shutdown_signal,
+    )
+    .await
+    .expect("Failed to run blueprint manager")
+}
+
+/// Sets up the default logging as well as setting a panic hook for tests
+pub fn setup_log() {
+    let _ = SubscriberBuilder::default()
+        .with_env_filter(EnvFilter::from_default_env())
+        .finish()
+        .try_init();
+
+    std::panic::set_hook(Box::new(|info| {
+        log::error!(target: "gadget", "Panic occurred: {info:?}");
+        std::process::exit(1);
+    }));
+}
+
+pub async fn create_blueprint(
+    client: &TestClient,
+    account_id: &sr25519::Keypair,
+    blueprint: Blueprint,
+) -> Result<(), Box<dyn Error>> {
+    let call = api::tx().services().create_blueprint(blueprint);
+    let res = client
+        .tx()
+        .sign_and_submit_then_watch_default(&call, account_id)
+        .await?;
+    res.wait_for_finalized_success().await?;
+    Ok(())
+}
+
+pub async fn register_blueprint(
+    client: &TestClient,
+    account_id: &sr25519::Keypair,
+    blueprint_id: u64,
+    preferences: Preferences,
+    registration_args: RegistrationArgs,
+) -> Result<(), Box<dyn Error>> {
+    let call = api::tx()
+        .services()
+        .register(blueprint_id, preferences, registration_args);
+    let res = client
+        .tx()
+        .sign_and_submit_then_watch_default(&call, account_id)
+        .await?;
+    res.wait_for_finalized_success().await?;
+    Ok(())
+}
+
+pub async fn submit_job(
+    client: &TestClient,
+    user: &sr25519::Keypair,
+    service_id: u64,
+    job_type: Job,
+    job_params: Args,
+) -> Result<(), Box<dyn Error>> {
+    let call = api::tx().services().call(service_id, job_type, job_params);
+    let res = client
+        .tx()
+        .sign_and_submit_then_watch_default(&call, user)
+        .await?;
+    let _res = res.wait_for_finalized_success().await?;
+    Ok(())
+}
+
+pub async fn wait_for_completion_of_job(
+    client: &TestClient,
+    service_id: u64,
+    call_id: u64,
+) -> Result<JobResults, Box<dyn Error>> {
+    loop {
+        gadget_io::tokio::time::sleep(Duration::from_millis(100)).await;
+        let call = api::storage().services().job_results(service_id, call_id);
+        let res = client.storage().at_latest().await?.fetch(&call).await?;
+
+        if let Some(ret) = res {
+            return Ok(ret);
+        }
+    }
+}
+
+pub async fn get_next_blueprint_id(client: &TestClient) -> Result<u64, Box<dyn Error>> {
+    let call = api::storage().services().next_blueprint_id();
+    let res = client.storage().at_latest().await?.fetch(&call).await?;
+    if let Some(ret) = res {
+        Ok(ret)
+    } else {
+        Err("Failed to get next blueprint id".into())
+    }
+}
+
+pub async fn get_next_service_id(client: &TestClient) -> Result<u64, Box<dyn Error>> {
+    let call = api::storage().services().next_instance_id();
+    let res = client.storage().at_latest().await?.fetch(&call).await?;
+    if let Some(ret) = res {
+        Ok(ret)
+    } else {
+        Err("Failed to get next service id".into())
+    }
+}
+
+pub async fn get_next_call_id(client: &TestClient) -> Result<u64, Box<dyn Error>> {
+    let call = api::storage().services().next_job_call_id();
+    let res = client.storage().at_latest().await?.fetch(&call).await?;
+    if let Some(ret) = res {
+        Ok(ret)
+    } else {
+        Err("Failed to get next job id".into())
+    }
+}
+
+#[macro_export]
+macro_rules! test_blueprint {
+    (
+        $blueprint_path:expr,
+        $blueprint_name:expr,
+        [$($input:expr),+],
+        [$($expected_output:expr),+]
+    ) => {
+        use $crate::{
+            get_next_call_id, get_next_service_id, run_test_blueprint_manager,
+            setup_log, submit_job, wait_for_completion_of_job, Opts,
+        };
+
+        use $crate::test_ext::new_test_ext_blueprint_manager;
+
+        #[tokio::test(flavor = "multi_thread")]
+        async fn test_externalities_standard() {
+            setup_log();
+
+            let mut manifest_path = std::env::current_dir().expect("Failed to get current directory");
+            manifest_path.push($blueprint_path);
+            manifest_path.canonicalize().expect("File could not be normalized");
+
+            let ws_addr = "ws://127.0.0.1:9944";
+
+            let opts = Opts {
+                pkg_name: Some($blueprint_name.to_string()),
+                rpc_url: ws_addr.to_string(),
+                manifest_path,
+                signer: None,
+                signer_evm: None,
+            };
+
+            new_test_ext_blueprint_manager::<1, 1, (), _, _>(
+                (),
+                opts,
+                run_test_blueprint_manager,
+            )
+            .await
+            .execute_with_async(move |client, handles| async move {
+                let keypair = handles[0].sr25519_id().clone();
+                let service_id = get_next_service_id(client)
+                    .await
+                    .expect("Failed to get next service id");
+                let call_id = get_next_call_id(client)
+                    .await
+                    .expect("Failed to get next job id");
+
+                let mut job_args = Args::new();
+                for input in [$($input),+] {
+                    job_args.push(input);
+                }
+
+                submit_job(
+                    client,
+                    &keypair,
+                    service_id,
+                    Job::from(call_id as u8),
+                    job_args,
+                )
+                .await
+                .expect("Failed to submit job");
+
+                let job_results = wait_for_completion_of_job(client, service_id, call_id)
+                    .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![$($expected_output),+];
+                assert_eq!(job_results.result.0.len(), expected_outputs.len(), "Number of outputs doesn't match expected");
+
+                for (result, expected) in job_results.result.0.into_iter().zip(expected_outputs.into_iter()) {
+                    assert_eq!(result, expected);
+                }
+            })
+            .await
+        }
+    };
+}
+
+#[cfg(test)]
+mod test_macros {
+    use super::*;
+
+    test_blueprint!(
+        "./blueprints/incredible-squaring/Cargo.toml", // Path to the blueprint's toml
+        "incredible-squaring-blueprint",               // Name of the package
+        [InputValue::Uint64(5)],
+        [OutputValue::Uint64(25)] // Expected output: each input squared
+    );
+}
+
+#[cfg(test)]
+mod tests_standard {
+    use super::*;
+    use crate::test_ext::new_test_ext_blueprint_manager;
+    use cargo_tangle::deploy::Opts;
+
+    // This test requires that `yarn install` has been executed inside the `./blueprints/incredible-squaring/` directory
+    // The other requirement is that there is a locally-running tangle node
+    #[tokio::test(flavor = "multi_thread")]
+    async fn test_externalities_gadget_starts() {
+        setup_log();
+
+        let mut manifest_path = std::env::current_dir().expect("Failed to get current directory");
+        manifest_path.push("../blueprints/incredible-squaring/Cargo.toml");
+        manifest_path
+            .canonicalize()
+            .expect("File could not be normalized");
+
+        let opts = Opts {
+            pkg_name: Some("incredible-squaring-blueprint".to_string()),
+            rpc_url: "ws://127.0.0.1:9944".to_string(),
+            manifest_path,
+            signer: None,
+            signer_evm: None,
+        };
+
+        const INPUT: u64 = 10;
+        const OUTPUT: u64 = INPUT.pow(2);
+
+        new_test_ext_blueprint_manager::<1, 1, (), _, _>((), opts, run_test_blueprint_manager)
+            .await
+            .execute_with_async(move |client, handles| async move {
+                // At this point, init_blueprint has been deployed, and, every node has registered
+                // as an operator to the init_blueprint provided
+
+                // What's left: Submit a job, wait for the job to finish, then assert the job results
+                let keypair = handles[0].sr25519_id().clone();
+                // Important! The tests can only run serially, not in parallel, in order to not cause a race condition in IDs
+                let service_id = get_next_service_id(client)
+                    .await
+                    .expect("Failed to get next service id");
+                let call_id = get_next_call_id(client)
+                    .await
+                    .expect("Failed to get next job id");
+
+                // Pass the argument
+                let mut job_args = Args::new();
+                let input =
+                    api::runtime_types::tangle_primitives::services::field::Field::Uint64(INPUT);
+                job_args.push(input);
+
+                // Next step: submit a job under that service/job id
+                submit_job(
+                    client,
+                    &keypair,
+                    service_id,
+                    Job::from(call_id as u8),
+                    job_args,
+                )
+                .await
+                .expect("Failed to submit job");
+
+                // Step 2: wait for the job to complete
+                let job_results = wait_for_completion_of_job(client, service_id, call_id)
+                    .await
+                    .expect("Failed to wait for job completion");
+
+                // Step 3: Get the job results, compare to expected value(s)
+                let expected_result =
+                    api::runtime_types::tangle_primitives::services::field::Field::Uint64(OUTPUT);
+                assert_eq!(job_results.service_id, service_id);
+                assert_eq!(job_results.call_id, call_id);
+                assert_eq!(job_results.result.0[0], expected_result);
+            })
+            .await
+    }
+}
diff --git a/tangle-test-utils/src/sync/mod.rs b/blueprint-test-utils/src/sync/mod.rs
similarity index 100%
rename from tangle-test-utils/src/sync/mod.rs
rename to blueprint-test-utils/src/sync/mod.rs
diff --git a/tangle-test-utils/src/sync/substrate_test_channel.rs b/blueprint-test-utils/src/sync/substrate_test_channel.rs
similarity index 100%
rename from tangle-test-utils/src/sync/substrate_test_channel.rs
rename to blueprint-test-utils/src/sync/substrate_test_channel.rs
diff --git a/tangle-test-utils/src/sync/tracked_callback_channel.rs b/blueprint-test-utils/src/sync/tracked_callback_channel.rs
similarity index 100%
rename from tangle-test-utils/src/sync/tracked_callback_channel.rs
rename to blueprint-test-utils/src/sync/tracked_callback_channel.rs
diff --git a/blueprint-test-utils/src/test_ext.rs b/blueprint-test-utils/src/test_ext.rs
new file mode 100644
index 000000000..27a0bf61d
--- /dev/null
+++ b/blueprint-test-utils/src/test_ext.rs
@@ -0,0 +1,429 @@
+// This file is part of Tangle.
+// Copyright (C) 2022-2023 Webb Technologies Inc.
+//
+// Tangle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Tangle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Tangle.  If not, see <http://www.gnu.org/licenses/>.
+
+use crate::PerTestNodeInput;
+use async_trait::async_trait;
+use blueprint_manager::executor::BlueprintManagerHandle;
+use cargo_tangle::deploy::{Opts, PrivateKeySigner};
+use environment_utils::transaction_manager::tangle::SubxtPalletSubmitter;
+use gadget_common::config::Network;
+use gadget_common::locks::TokioMutexExt;
+use gadget_common::prelude::{
+    DebugLogger, ECDSAKeyStore, GadgetEnvironment, InMemoryBackend, NodeInput, PairSigner,
+    PrometheusConfig, UnboundedReceiver, UnboundedSender,
+};
+use gadget_common::tangle_runtime::api;
+use gadget_common::tangle_runtime::api::runtime_types::tangle_primitives::services::ApprovalPrefrence;
+use gadget_common::tangle_runtime::api::services::calls::types::register::{
+    Preferences, RegistrationArgs,
+};
+use gadget_common::tangle_subxt::subxt::ext::subxt_core::utils;
+use gadget_common::tangle_subxt::subxt::{OnlineClient, SubstrateConfig};
+use gadget_common::Error;
+use gadget_core::job_manager::{ProtocolMessageMetadata, SendFuture, WorkManagerInterface};
+use libp2p::Multiaddr;
+use sp_application_crypto::ecdsa;
+use sp_core::{sr25519, Pair};
+use std::collections::HashMap;
+use std::future::Future;
+use std::net::IpAddr;
+use std::str::FromStr;
+use std::sync::Arc;
+use std::time::Duration;
+use tangle_environment::runtime::{TangleConfig, TangleRuntime};
+use tangle_environment::TangleEnvironment;
+use url::Url;
+
+pub fn id_to_ecdsa_pair(id: u8) -> ecdsa::Pair {
+    ecdsa::Pair::from_string(&format!("//Alice///{id}"), None).expect("static values are valid")
+}
+
+pub fn id_to_sr25519_pair(id: u8) -> sr25519::Pair {
+    sr25519::Pair::from_string(&format!("//Alice///{id}"), None).expect("static values are valid")
+}
+
+pub fn id_to_public(id: u8) -> ecdsa::Public {
+    id_to_ecdsa_pair(id).public()
+}
+
+pub fn id_to_sr25519_public(id: u8) -> sr25519::Public {
+    id_to_sr25519_pair(id).public()
+}
+
+type PeersRx<Env> =
+    Arc<HashMap<ecdsa::Public, gadget_io::tokio::sync::Mutex<UnboundedReceiver<Env>>>>;
+
+pub struct MockNetwork<Env: GadgetEnvironment> {
+    peers_tx: Arc<
+        HashMap<
+            ecdsa::Public,
+            UnboundedSender<<Env::WorkManager as WorkManagerInterface>::ProtocolMessage>,
+        >,
+    >,
+    peers_rx: PeersRx<<Env::WorkManager as WorkManagerInterface>::ProtocolMessage>,
+    my_id: ecdsa::Public,
+}
+
+impl<Env: GadgetEnvironment> Clone for MockNetwork<Env> {
+    fn clone(&self) -> Self {
+        Self {
+            peers_tx: self.peers_tx.clone(),
+            peers_rx: self.peers_rx.clone(),
+            my_id: self.my_id,
+        }
+    }
+}
+
+impl<Env: GadgetEnvironment> MockNetwork<Env> {
+    pub fn setup(ids: &Vec<ecdsa::Public>) -> Vec<Self> {
+        let mut peers_tx = HashMap::new();
+        let mut peers_rx = HashMap::new();
+        let mut networks = Vec::new();
+
+        for id in ids {
+            let (tx, rx) = gadget_io::tokio::sync::mpsc::unbounded_channel();
+            peers_tx.insert(*id, tx);
+            peers_rx.insert(*id, gadget_io::tokio::sync::Mutex::new(rx));
+        }
+
+        let peers_tx = Arc::new(peers_tx);
+        let peers_rx = Arc::new(peers_rx);
+
+        for id in ids {
+            let network = Self {
+                peers_tx: peers_tx.clone(),
+                peers_rx: peers_rx.clone(),
+                my_id: *id,
+            };
+            networks.push(network);
+        }
+
+        networks
+    }
+}
+
+#[async_trait]
+impl<Env: GadgetEnvironment> Network<Env> for MockNetwork<Env>
+where
+    Env::ProtocolMessage: Clone,
+{
+    async fn next_message(
+        &self,
+    ) -> Option<<Env::WorkManager as WorkManagerInterface>::ProtocolMessage> {
+        self.peers_rx
+            .get(&self.my_id)?
+            .lock_timeout(Duration::from_millis(500))
+            .await
+            .recv()
+            .await
+    }
+
+    async fn send_message(
+        &self,
+        message: <Env::WorkManager as WorkManagerInterface>::ProtocolMessage,
+    ) -> Result<(), Error> {
+        let _check_message_has_ids = message.sender_network_id().ok_or(Error::MissingNetworkId)?;
+        if let Some(peer_id) = message.recipient_network_id() {
+            let tx = self
+                .peers_tx
+                .get(&peer_id)
+                .ok_or(Error::PeerNotFound { id: peer_id })?;
+            tx.send(message).map_err(|err| Error::NetworkError {
+                err: err.to_string(),
+            })?;
+        } else {
+            // Broadcast to everyone except ourself
+            for (peer_id, tx) in self.peers_tx.iter() {
+                if peer_id != &self.my_id {
+                    tx.send(message.clone())
+                        .map_err(|err| Error::NetworkError {
+                            err: err.to_string(),
+                        })?;
+                }
+            }
+        }
+        Ok(())
+    }
+}
+
+const LOCAL_BIND_ADDR: &str = "127.0.0.1";
+const LOCAL_TANGLE_NODE: &str = "ws://127.0.0.1:9944";
+
+/// N: number of nodes
+/// K: Number of networks accessible per node (should be equal to the number of services in a given blueprint)
+/// D: Any data that you want to pass to pass with NodeInput.
+/// F: A function that generates a service's execution via a series of shells. Each shell executes a subset of the service,
+/// as each service may have a set of operations that are executed in parallel, sequentially, or concurrently.
+pub async fn new_test_ext_blueprint_manager<
+    const N: usize,
+    const K: usize,
+    D: Send + Clone + 'static,
+    F: Fn(PerTestNodeInput<D>) -> Fut,
+    Fut: SendFuture<'static, BlueprintManagerHandle>,
+>(
+    additional_params: D,
+    mut opts: Opts,
+    f: F,
+) -> LocalhostTestExt {
+    const NAME_IDS: [&str; 3] = ["alice", "bob", "charlie"];
+
+    assert!(N < 4, "Only up to 3 nodes are supported");
+
+    let bind_addrs = (0..N)
+        .map(|_| find_open_tcp_bind_port())
+        .map(|port| {
+            (
+                Multiaddr::from_str(&format!("/ip4/{LOCAL_BIND_ADDR}/tcp/{port}"))
+                    .expect("Should parse MultiAddr"),
+                port,
+            )
+        })
+        .collect::<Vec<_>>();
+
+    let multi_addrs = bind_addrs
+        .iter()
+        .map(|(addr, _)| addr.clone())
+        .collect::<Vec<_>>();
+
+    let mut handles = vec![];
+
+    for (node_index, (my_addr, my_port)) in bind_addrs.iter().enumerate() {
+        let my_alias = NAME_IDS[node_index];
+
+        let test_input = PerTestNodeInput {
+            instance_id: node_index as _,
+            bind_ip: IpAddr::from_str(LOCAL_BIND_ADDR).expect("Should be a valid IP"),
+            bind_port: *my_port,
+            bootnodes: multi_addrs
+                .iter()
+                .filter(|addr| *addr != my_addr)
+                .cloned()
+                .collect(),
+            // Assumes that that tangle has initialized a dir with a keystore at ../../tangle/tmp/
+            base_path: format!("../../tangle/tmp/{my_alias}"),
+            verbose: 4,
+            pretty: false,
+            extra_input: additional_params.clone(),
+            local_tangle_node: Url::parse(&opts.rpc_url).expect("Should parse URL"),
+        };
+
+        let handle = f(test_input).await;
+
+        if node_index == 0 {
+            // Replace the None signer and signer_evm values inside opts with alice's keys
+            opts.signer = Some(handle.sr25519_id().clone());
+            let k256_ecdsa_secret_key = handle.ecdsa_id().seed();
+            opts.signer_evm = Some(
+                PrivateKeySigner::from_slice(&k256_ecdsa_secret_key)
+                    .expect("Should create a private key signer"),
+            );
+        }
+
+        handles.push(handle);
+    }
+
+    let client = OnlineClient::<SubstrateConfig>::from_url(LOCAL_TANGLE_NODE)
+        .await
+        .expect("Failed to create primary localhost client");
+
+    // Step 1: Create the blueprint using alice's identity
+    let blueprint_id = cargo_tangle::deploy::deploy_to_tangle(opts)
+        .await
+        .expect("Failed to deploy Blueprint to Tangle");
+
+    // Step 2: Have each identity register to a blueprint
+    let registration_args = RegistrationArgs::new();
+
+    for handle in handles.iter() {
+        let keypair = handle.sr25519_id().clone();
+        let key = api::runtime_types::sp_core::ecdsa::Public(handle.ecdsa_id().public().0);
+
+        let preferences = Preferences {
+            key,
+            approval: ApprovalPrefrence::None,
+        };
+
+        super::register_blueprint(
+            &client,
+            &keypair,
+            blueprint_id,
+            preferences,
+            registration_args.clone(),
+        )
+        .await
+        .expect("Failed to register to blueprint");
+    }
+
+    // Now, start every blueprint manager. With the blueprint submitted and every operator registered
+    // to the blueprint, we can now start the blueprint manager, expecting that the blueprint manager
+    // will start the services associated with the blueprint as gadgets.
+    for handle in handles.iter_mut() {
+        handle.start().expect("Failed to start blueprint manager");
+    }
+
+    LocalhostTestExt { client, handles }
+}
+
+fn find_open_tcp_bind_port() -> u16 {
+    let listener = std::net::TcpListener::bind(format!("{LOCAL_BIND_ADDR}:0"))
+        .expect("Should bind to localhost");
+    listener
+        .local_addr()
+        .expect("Should have a local address")
+        .port()
+}
+
+/// N: number of nodes
+/// K: Number of networks accessible per node (should be equal to the number of services in a given blueprint)
+/// D: Any data that you want to pass with NodeInput.
+/// F: A function that generates a service's execution via a series of shells. Each shell executes a subset of the service,
+/// as each service may have a set of operations that are executed in parallel, sequentially, or concurrently.
+pub async fn new_test_ext<
+    const N: usize,
+    const K: usize,
+    D: Send + Clone + 'static,
+    F: Fn(NodeInput<TangleEnvironment, MockNetwork<TangleEnvironment>, InMemoryBackend, D>) -> Fut,
+    Fut: SendFuture<'static, BlueprintManagerHandle>,
+>(
+    additional_params: D,
+    f: F,
+) -> LocalhostTestExt {
+    let role_pairs = (0..N)
+        .map(|i| id_to_ecdsa_pair(i as u8))
+        .collect::<Vec<_>>();
+    let roles_identities = role_pairs
+        .iter()
+        .map(|pair| pair.public())
+        .collect::<Vec<_>>();
+
+    let pairs = (0..N)
+        .map(|i| id_to_sr25519_pair(i as u8))
+        .collect::<Vec<_>>();
+
+    /*
+    let account_ids = pairs
+        .iter()
+        .map(|pair| pair.public().into())
+        .collect::<Vec<AccountId>>();
+
+
+    let balances = account_ids
+        .iter()
+        .map(|public| (public.clone(), 100u128))
+        .collect::<Vec<_>>();*/
+
+    let networks = (0..K)
+        .map(|_| MockNetwork::setup(&roles_identities))
+        .collect::<Vec<_>>();
+
+    // Transpose networks
+    let networks = (0..N)
+        .map(|i| {
+            networks
+                .iter()
+                .map(|network| network[i].clone())
+                .collect::<Vec<_>>()
+        })
+        .collect::<Vec<_>>();
+
+    // Each client connects to ws://127.0.0.1:9944. This client is for the test environment
+    let client = OnlineClient::<SubstrateConfig>::from_url(LOCAL_TANGLE_NODE)
+        .await
+        .expect("Failed to create primary localhost client");
+
+    let mut handles = vec![];
+
+    for (node_index, ((role_pair, pair), networks)) in
+        role_pairs.into_iter().zip(pairs).zip(networks).enumerate()
+    {
+        let account_id: utils::AccountId32 = pair.public().0.into();
+        let mut localhost_clients = Vec::new();
+
+        for _ in 0..K {
+            // Each client connects to ws://127.0.0.1:9944
+            let client = OnlineClient::<SubstrateConfig>::from_url(LOCAL_TANGLE_NODE)
+                .await
+                .expect("Failed to create localhost client");
+
+            let client = TangleRuntime::new(client, account_id.clone());
+            localhost_clients.push(client);
+        }
+
+        let logger = DebugLogger {
+            id: format!("Peer {node_index}"),
+        };
+
+        let pair_signer = PairSigner::<TangleConfig>::new(pair);
+
+        let tx_manager = Arc::new(
+            SubxtPalletSubmitter::new(pair_signer, logger.clone())
+                .await
+                .expect("Failed to create tx manager"),
+        );
+
+        let keystore = ECDSAKeyStore::in_memory(role_pair);
+        let prometheus_config = PrometheusConfig::Disabled;
+
+        let input = NodeInput {
+            clients: localhost_clients,
+            networks,
+            account_id: sr25519::Public(account_id.0),
+            logger,
+            tx_manager: tx_manager as _,
+            keystore,
+            node_index,
+            additional_params: additional_params.clone(),
+            prometheus_config,
+        };
+
+        let handle = f(input).await;
+        handles.push(handle);
+    }
+
+    LocalhostTestExt { handles, client }
+}
+
+pub struct LocalhostTestExt {
+    client: OnlineClient<SubstrateConfig>,
+    handles: Vec<BlueprintManagerHandle>,
+}
+
+impl LocalhostTestExt {
+    /// An identity function (For future reverse-compatible changes)
+    pub fn execute_with<
+        T: FnOnce(&OnlineClient<SubstrateConfig>, &Vec<BlueprintManagerHandle>) -> R + Send + 'static,
+        R: Send + 'static,
+    >(
+        &self,
+        function: T,
+    ) -> R {
+        function(&self.client, &self.handles)
+    }
+
+    /// An identity function (For future reverse-compatible changes)
+    pub async fn execute_with_async<
+        'a,
+        'b: 'a,
+        T: FnOnce(&'a OnlineClient<SubstrateConfig>, &'a Vec<BlueprintManagerHandle>) -> R + Send + 'a,
+        R: Future<Output = Out> + Send + 'a,
+        Out: Send + 'b,
+    >(
+        &'a self,
+        function: T,
+    ) -> Out {
+        function(&self.client, &self.handles).await
+    }
+}
diff --git a/shell-configs/local-testnet-0.toml b/gadget-configs/local-testnet-0.toml
similarity index 50%
rename from shell-configs/local-testnet-0.toml
rename to gadget-configs/local-testnet-0.toml
index 4ea59d4b4..fdea77dd0 100644
--- a/shell-configs/local-testnet-0.toml
+++ b/gadget-configs/local-testnet-0.toml
@@ -1,8 +1,6 @@
 bind_ip = "0.0.0.0"
 bind_port = 30555
 bootnodes = []
-# 12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN
-node_key = "0000000000000000000000000000000000000000000000000000000000000000"
 chain = "local_testnet"
 base_path = "../tangle/tmp/alice"
 verbose=1
diff --git a/gadget-configs/local-testnet-1.toml b/gadget-configs/local-testnet-1.toml
new file mode 100644
index 000000000..25ee07c19
--- /dev/null
+++ b/gadget-configs/local-testnet-1.toml
@@ -0,0 +1,7 @@
+bind_ip = "0.0.0.0"
+bind_port = 30556
+bootnodes = []
+chain = "local_testnet"
+base_path = "../tangle/tmp/bob"
+verbose=1
+pretty=true
\ No newline at end of file
diff --git a/shell-configs/local-testnet-2.toml b/gadget-configs/local-testnet-2.toml
similarity index 50%
rename from shell-configs/local-testnet-2.toml
rename to gadget-configs/local-testnet-2.toml
index de48d5848..8ab019bf6 100644
--- a/shell-configs/local-testnet-2.toml
+++ b/gadget-configs/local-testnet-2.toml
@@ -1,8 +1,6 @@
 bind_ip = "0.0.0.0"
 bind_port = 30557
 bootnodes = []
-# 12D3KooWHdiAxVd8uMQR1hGWXccidmfCwLqcMpGwR6QcTP6QRMuD
-node_key = "0000000000000000000000000000000000000000000000000000000000000002"
 chain = "local_testnet"
 base_path = "../tangle/tmp/charlie"
 verbose=1
diff --git a/shell-configs/local-testnet-1.toml b/shell-configs/local-testnet-1.toml
deleted file mode 100644
index d575c59a9..000000000
--- a/shell-configs/local-testnet-1.toml
+++ /dev/null
@@ -1,9 +0,0 @@
-bind_ip = "0.0.0.0"
-bind_port = 30556
-bootnodes = []
-# 12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp
-node_key = "0000000000000000000000000000000000000000000000000000000000000001"
-chain = "local_testnet"
-base_path = "../tangle/tmp/bob"
-verbose=1
-pretty=true
\ No newline at end of file
diff --git a/tangle-test-utils/src/lib.rs b/tangle-test-utils/src/lib.rs
deleted file mode 100644
index adddc5ddf..000000000
--- a/tangle-test-utils/src/lib.rs
+++ /dev/null
@@ -1,190 +0,0 @@
-use crate::mock::{Jobs, Runtime};
-use crate::sync::substrate_test_channel::MultiThreadedTestExternalities;
-pub use gadget_core::job_manager::SendFuture;
-pub use log;
-use std::time::Duration;
-use tangle_primitives::jobs::{JobId, PhaseResult};
-use tangle_primitives::roles::RoleType;
-use tangle_primitives::AccountId;
-use tracing_subscriber::filter::EnvFilter;
-use tracing_subscriber::fmt::SubscriberBuilder;
-use tracing_subscriber::util::SubscriberInitExt;
-
-pub mod mock;
-pub mod sync;
-
-/// Sets up the default logging as well as setting a panic hook for tests
-pub fn setup_log() {
-    let _ = SubscriberBuilder::default()
-        .with_env_filter(EnvFilter::from_default_env())
-        .finish()
-        .try_init();
-
-    std::panic::set_hook(Box::new(|info| {
-        log::error!(target: "gadget", "Panic occurred: {info:?}");
-        std::process::exit(1);
-    }));
-}
-
-pub async fn wait_for_job_completion(
-    ext: &MultiThreadedTestExternalities,
-    role_type: RoleType,
-    job_id: u64,
-) -> PhaseResult<
-    AccountId,
-    mock::BlockNumber,
-    mock::MaxParticipants,
-    mock::MaxKeyLen,
-    mock::MaxDataLen,
-    mock::MaxSignatureLen,
-    mock::MaxSubmissionLen,
-    mock::MaxProofLen,
-    mock::MaxAdditionalParamsLen,
-> {
-    loop {
-        gadget_io::tokio::time::sleep(Duration::from_millis(100)).await;
-        if let Some(ret) = ext
-            .execute_with_async(move || Jobs::known_results(role_type, job_id))
-            .await
-        {
-            return ret;
-        }
-    }
-}
-
-pub fn remove_job(role_type: RoleType, job_id: JobId) {
-    SubmittedJobs::<Runtime>::remove(role_type, job_id);
-    SubmittedJobsRole::<Runtime>::remove(job_id);
-}
-
-#[macro_export]
-/// When using this macro, the `generate_setup_and_run_command` macro should be used to generate the `setup_node` function.
-/// If not, you must manually create a function named `setup_node` in your crate's lib.rs that accepts a NodeInput and
-/// returns a future that runs the protocols.
-#[allow(clippy::crate_in_macro_def)]
-macro_rules! generate_signing_and_keygen_tss_tests {
-    ($t:expr, $n:expr, $proto_count:expr, $threshold_sig_ty:expr) => {
-        #[cfg(test)]
-        mod tests {
-            use std::sync::Arc;
-            use tangle_primitives::jobs::{
-                DKGTSSPhaseOneJobType, DKGTSSPhaseTwoJobType, JobId, JobSubmission, JobType,
-            };
-            use tangle_primitives::roles::{RoleType, ThresholdSignatureRoleType};
-            use tangle_primitives::AccountId;
-            use gadget_common::full_protocol::FullProtocolConfig;
-            use test_utils::mock::{id_to_public, id_to_sr25519_public, Jobs, MockBackend, RuntimeOrigin};
-            use test_utils::sync::substrate_test_channel::MultiThreadedTestExternalities;
-            use gadget_io::tokio;
-
-            #[gadget_io::tokio::test(flavor = "multi_thread")]
-            async fn test_externalities_gadget_starts() {
-                test_utils::setup_log();
-                new_test_ext::<1>()
-                    .await
-                    .execute_with_async(|| {
-                        assert_eq!(1, 1);
-                    })
-                    .await
-            }
-
-            #[gadget_io::tokio::test(flavor = "multi_thread")]
-            async fn test_externalities_keygen() {
-                test_utils::setup_log();
-                const N: usize = $n;
-                const T: usize = $t;
-
-                let ext = new_test_ext::<N>().await;
-                assert_eq!(wait_for_keygen::<N, T>(&ext).await, 0);
-            }
-
-            #[gadget_io::tokio::test(flavor = "multi_thread")]
-            async fn test_externalities_signing() {
-                test_utils::setup_log();
-                const N: usize = $n;
-                const T: usize = $t;
-
-                let ext = new_test_ext::<N>().await;
-                let keygen_job_id = wait_for_keygen::<N, T>(&ext).await;
-                assert_eq!(wait_for_signing::<N>(&ext, keygen_job_id).await, 1);
-            }
-
-            async fn wait_for_keygen<const N: usize, const T: usize>(
-                ext: &MultiThreadedTestExternalities,
-            ) -> JobId {
-                let job_id = ext
-                    .execute_with_async(|| {
-                        let job_id = Jobs::next_job_id();
-                        let identities = (0..N).map(|i| id_to_sr25519_public(i as u8)).map(AccountId::from).collect::<Vec<_>>();
-
-                        let submission = JobSubmission {
-                            fallback: tangle_primitives::jobs::FallbackOptions::Destroy,
-                            expiry: 100,
-                            ttl: 100,
-                            job_type: JobType::DKGTSSPhaseOne(DKGTSSPhaseOneJobType {
-                                participants: identities.clone().try_into().unwrap(),
-                                threshold: T as _,
-                                permitted_caller: None,
-                                hd_wallet: false,
-                                role_type: $threshold_sig_ty,
-                            }),
-                        };
-
-                        assert!(Jobs::submit_job(RuntimeOrigin::signed(identities[0].clone()), submission).is_ok());
-
-                        $crate::log::info!(target: "gadget", "******* Submitted Keygen Job {job_id}");
-                        job_id
-                    })
-                    .await;
-
-                test_utils::wait_for_job_completion(
-                    ext,
-                    RoleType::Tss($threshold_sig_ty),
-                    job_id,
-                )
-                .await;
-                job_id
-            }
-
-            async fn wait_for_signing<const N: usize>(
-                ext: &MultiThreadedTestExternalities,
-                keygen_job_id: JobId,
-            ) -> JobId {
-                let job_id = ext
-                    .execute_with_async(move || {
-                        let job_id = Jobs::next_job_id();
-                        let identities = (0..N).map(|i| id_to_sr25519_public(i as u8)).map(AccountId::from).collect::<Vec<_>>();
-                        let submission = JobSubmission {
-                            fallback: tangle_primitives::jobs::FallbackOptions::Destroy,
-                            expiry: 100,
-                            ttl: 100,
-                            job_type: JobType::DKGTSSPhaseTwo(DKGTSSPhaseTwoJobType {
-                                phase_one_id: keygen_job_id,
-                                derivation_path: None,
-                                submission: Vec::from("Hello, world!").try_into().unwrap(),
-                                role_type: $threshold_sig_ty,
-                            }),
-                        };
-
-                        assert!(Jobs::submit_job(RuntimeOrigin::signed(identities[0].clone()), submission).is_ok());
-
-                        $crate::log::info!(target: "gadget", "******* Submitted Signing Job {job_id}");
-                        job_id
-                    })
-                    .await;
-
-                test_utils::wait_for_job_completion(
-                    ext,
-                    RoleType::Tss($threshold_sig_ty),
-                    job_id,
-                )
-                .await;
-                job_id
-            }
-
-            async fn new_test_ext<const N: usize>() -> MultiThreadedTestExternalities {
-                test_utils::mock::new_test_ext::<N, $proto_count, (), _, _>((), crate::setup_node).await
-            }
-        }
-    };
-}
diff --git a/tangle-test-utils/src/mock.rs b/tangle-test-utils/src/mock.rs
deleted file mode 100644
index 08c0e6cf8..000000000
--- a/tangle-test-utils/src/mock.rs
+++ /dev/null
@@ -1,981 +0,0 @@
-// This file is part of Tangle.
-// Copyright (C) 2022-2023 Webb Technologies Inc.
-//
-// Tangle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Tangle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Tangle.  If not, see <http://www.gnu.org/licenses/>.
-
-use crate::mock::mock_wrapper_client::{MockClient, TestExternalitiesPalletSubmitter};
-use crate::sync::substrate_test_channel::MultiThreadedTestExternalities;
-use async_trait::async_trait;
-use environment_utils::transaction_manager::tangle::TanglePalletSubmitter;
-use frame_support::pallet_prelude::*;
-use frame_support::traits::{Contains, Hooks, OneSessionHandler};
-use frame_support::{
-    construct_runtime, parameter_types,
-    traits::{ConstU128, ConstU32, ConstU64, Everything},
-    PalletId,
-};
-use frame_system::EnsureSigned;
-use gadget_common::config::Network;
-use gadget_common::locks::TokioMutexExt;
-use gadget_common::prelude::{
-    DebugLogger, ECDSAKeyStore, EventMetadata, GadgetEnvironment, InMemoryBackend, NodeInput,
-    PrometheusConfig, UnboundedReceiver, UnboundedSender,
-};
-use gadget_common::tangle_subxt::subxt::OfflineClient;
-use gadget_common::utils::serialize;
-use gadget_common::Error;
-use gadget_core::job_manager::{ProtocolMessageMetadata, SendFuture, WorkManagerInterface};
-use pallet_services::EvmGasWeightMapping;
-use sc_client_api::{FinalityNotification, FinalizeSummary};
-use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender};
-use serde::{Deserialize, Serialize};
-use sp_api::{ApiRef, ProvideRuntimeApi};
-use sp_application_crypto::ecdsa;
-use sp_core::{sr25519, ByteArray, Pair, H256};
-use sp_keystore::testing::MemoryKeystore;
-use sp_keystore::{KeystoreExt, KeystorePtr};
-use sp_runtime::testing::UintAuthorityId;
-use sp_runtime::traits::ConvertInto;
-use sp_runtime::RuntimeDebug;
-use sp_runtime::{traits::Block as BlockT, traits::IdentityLookup, BuildStorage, DispatchResult};
-use std::collections::HashMap;
-use std::sync::Arc;
-use std::time::Duration;
-use tangle_environment::gadget::{TangleEvent, TangleJobMetadata};
-use tangle_environment::message::{TangleProtocolMessage, UserID};
-use tangle_environment::work_manager::TangleWorkManager;
-use tangle_primitives::AccountId;
-
-pub type Balance = u128;
-pub type BlockNumber = u64;
-
-impl frame_system::Config for Runtime {
-    type RuntimeOrigin = RuntimeOrigin;
-    type Nonce = u64;
-    type RuntimeCall = RuntimeCall;
-    type Hash = H256;
-    type Hashing = sp_runtime::traits::BlakeTwo256;
-    type AccountId = AccountId;
-    type Block = Block;
-    type Lookup = IdentityLookup<Self::AccountId>;
-    type RuntimeEvent = RuntimeEvent;
-    type BlockHashCount = ConstU64<250>;
-    type BlockWeights = ();
-    type BlockLength = ();
-    type Version = ();
-    type PalletInfo = PalletInfo;
-    type AccountData = pallet_balances::AccountData<Balance>;
-    type OnNewAccount = ();
-    type OnKilledAccount = ();
-    type DbWeight = ();
-    type BaseCallFilter = Everything;
-    type SystemWeightInfo = ();
-    type SS58Prefix = ();
-    type OnSetCode = ();
-    type RuntimeTask = ();
-    type MaxConsumers = ConstU32<16>;
-}
-
-impl pallet_balances::Config for Runtime {
-    type Balance = Balance;
-    type DustRemoval = ();
-    type RuntimeEvent = RuntimeEvent;
-    type ExistentialDeposit = ConstU128<1>;
-    type AccountStore = System;
-    type MaxLocks = ();
-    type MaxReserves = ConstU32<50>;
-    type ReserveIdentifier = ();
-    type WeightInfo = ();
-    type RuntimeHoldReason = RuntimeHoldReason;
-    type RuntimeFreezeReason = ();
-    type FreezeIdentifier = ();
-    type MaxFreezes = ();
-}
-
-parameter_types! {
-    pub ElectionBoundsOnChain: ElectionBounds = ElectionBoundsBuilder::default()
-        .voters_count(5_000.into()).targets_count(1_250.into()).build();
-    pub ElectionBoundsMultiPhase: ElectionBounds = ElectionBoundsBuilder::default()
-        .voters_count(10_000.into()).targets_count(1_500.into()).build();
-}
-
-impl pallet_session::historical::Config for Runtime {
-    type FullIdentification = AccountId;
-    type FullIdentificationOf = ConvertInto;
-}
-
-pub struct BaseFilter;
-impl Contains<RuntimeCall> for BaseFilter {
-    fn contains(call: &RuntimeCall) -> bool {
-        let is_stake_unbond_call = matches!(
-            call,
-            RuntimeCall::Staking(pallet_staking::Call::unbond { .. })
-        );
-
-        if is_stake_unbond_call {
-            // no unbond call
-            return false;
-        }
-
-        // no chill call
-        if matches!(
-            call,
-            RuntimeCall::Staking(pallet_staking::Call::chill { .. })
-        ) {
-            return false;
-        }
-
-        // no withdraw_unbonded call
-        let is_stake_withdraw_call = matches!(
-            call,
-            RuntimeCall::Staking(pallet_staking::Call::withdraw_unbonded { .. })
-        );
-
-        if is_stake_withdraw_call {
-            return false;
-        }
-
-        true
-    }
-}
-
-sp_runtime::impl_opaque_keys! {
-    pub struct MockSessionKeys {
-        pub other: MockSessionHandler,
-    }
-}
-
-pub struct MockSessionHandler;
-impl OneSessionHandler<AccountId> for MockSessionHandler {
-    type Key = UintAuthorityId;
-
-    fn on_genesis_session<'a, I: 'a>(_: I)
-    where
-        I: Iterator<Item = (&'a AccountId, Self::Key)>,
-        AccountId: 'a,
-    {
-    }
-
-    fn on_new_session<'a, I: 'a>(_: bool, _: I, _: I)
-    where
-        I: Iterator<Item = (&'a AccountId, Self::Key)>,
-        AccountId: 'a,
-    {
-    }
-
-    fn on_disabled(_validator_index: u32) {}
-}
-
-impl sp_runtime::BoundToRuntimeAppPublic for MockSessionHandler {
-    type Public = UintAuthorityId;
-}
-
-pub struct MockSessionManager;
-
-impl pallet_session::SessionManager<AccountId> for MockSessionManager {
-    fn end_session(_: sp_staking::SessionIndex) {}
-    fn start_session(_: sp_staking::SessionIndex) {}
-    fn new_session(idx: sp_staking::SessionIndex) -> Option<Vec<AccountId>> {
-        if idx == 0 || idx == 1 || idx == 2 {
-            Some(vec![
-                mock_pub_key(1),
-                mock_pub_key(2),
-                mock_pub_key(3),
-                mock_pub_key(4),
-            ])
-        } else {
-            None
-        }
-    }
-}
-
-parameter_types! {
-    pub const Period: u64 = 1;
-    pub const Offset: u64 = 0;
-}
-
-impl pallet_session::Config for Runtime {
-    type SessionManager = MockSessionManager;
-    type Keys = MockSessionKeys;
-    type ShouldEndSession = pallet_session::PeriodicSessions<Period, Offset>;
-    type NextSessionRotation = pallet_session::PeriodicSessions<Period, Offset>;
-    type SessionHandler = (MockSessionHandler,);
-    type RuntimeEvent = RuntimeEvent;
-    type ValidatorId = AccountId;
-    type ValidatorIdOf = pallet_staking::StashOf<Runtime>;
-    type WeightInfo = ();
-}
-
-pub struct OnChainSeqPhragmen;
-impl onchain::Config for OnChainSeqPhragmen {
-    type System = Runtime;
-    type Solver = SequentialPhragmen<AccountId, Perbill>;
-    type DataProvider = Staking;
-    type WeightInfo = ();
-    type MaxWinners = ConstU32<100>;
-    type Bounds = ElectionBoundsOnChain;
-}
-
-/// Upper limit on the number of NPOS nominations.
-const MAX_QUOTA_NOMINATIONS: u32 = 16;
-
-impl pallet_staking::Config for Runtime {
-    type Currency = Balances;
-    type CurrencyBalance = <Self as pallet_balances::Config>::Balance;
-    type UnixTime = pallet_timestamp::Pallet<Self>;
-    type CurrencyToVote = ();
-    type RewardRemainder = ();
-    type RuntimeEvent = RuntimeEvent;
-    type Slash = ();
-    type Reward = ();
-    type SessionsPerEra = ();
-    type SlashDeferDuration = ();
-    type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>;
-    type BondingDuration = ();
-    type SessionInterface = ();
-    type EraPayout = ();
-    type NextNewSession = Session;
-    type MaxExposurePageSize = ConstU32<64>;
-    type MaxControllersInDeprecationBatch = ConstU32<100>;
-    type OffendingValidatorsThreshold = ();
-    type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
-    type GenesisElectionProvider = Self::ElectionProvider;
-    type VoterList = pallet_staking::UseNominatorsAndValidatorsMap<Self>;
-    type TargetList = pallet_staking::UseValidatorsMap<Self>;
-    type MaxUnlockingChunks = ConstU32<32>;
-    type HistoryDepth = ConstU32<84>;
-    type EventListeners = ();
-    type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
-    type NominationsQuota = pallet_staking::FixedNominationsQuota<MAX_QUOTA_NOMINATIONS>;
-    type WeightInfo = ();
-}
-
-parameter_types! {
-    pub const ServicesPalletId: PalletId = PalletId(*b"py/servs");
-}
-
-pub struct PalletEVMGasWeightMapping;
-
-impl EvmGasWeightMapping for PalletEVMGasWeightMapping {
-    fn gas_to_weight(gas: u64, without_base_weight: bool) -> Weight {
-        pallet_evm::FixedGasWeightMapping::<Runtime>::gas_to_weight(gas, without_base_weight)
-    }
-
-    fn weight_to_gas(weight: Weight) -> u64 {
-        pallet_evm::FixedGasWeightMapping::<Runtime>::weight_to_gas(weight)
-    }
-}
-
-parameter_types! {
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxFields: u32 = 256;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxFieldsSize: u32 = 1024;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxMetadataLength: u32 = 1024;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxJobsPerService: u32 = 1024;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxOperatorsPerService: u32 = 1024;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxPermittedCallers: u32 = 256;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxServicesPerOperator: u32 = 1024;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxBlueprintsPerOperator: u32 = 1024;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxServicesPerUser: u32 = 1024;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxBinariesPerGadget: u32 = 64;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxSourcesPerGadget: u32 = 64;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxGitOwnerLength: u32 = 1024;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxGitRepoLength: u32 = 1024;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxGitTagLength: u32 = 1024;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxBinaryNameLength: u32 = 1024;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxIpfsHashLength: u32 = 46;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxContainerRegistryLength: u32 = 1024;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxContainerImageNameLength: u32 = 1024;
-
-    #[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
-    #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
-    pub const MaxContainerImageTagLength: u32 = 1024;
-}
-
-impl pallet_services::Config for Runtime {
-    type RuntimeEvent = RuntimeEvent;
-    type ForceOrigin = frame_system::EnsureRoot<AccountId>;
-    type Currency = Balances;
-    type PalletId = ServicesPalletId;
-    type EvmRunner = MockedEvmRunner;
-    type EvmGasWeightMapping = PalletEVMGasWeightMapping;
-    type MaxFields = MaxFields;
-    type MaxFieldsSize = MaxFieldsSize;
-    type MaxMetadataLength = MaxMetadataLength;
-    type MaxJobsPerService = MaxJobsPerService;
-    type MaxOperatorsPerService = MaxOperatorsPerService;
-    type MaxPermittedCallers = MaxPermittedCallers;
-    type MaxServicesPerOperator = MaxServicesPerOperator;
-    type MaxBlueprintsPerOperator = MaxBlueprintsPerOperator;
-    type MaxServicesPerUser = MaxServicesPerUser;
-    type MaxBinariesPerGadget = MaxBinariesPerGadget;
-    type MaxSourcesPerGadget = MaxSourcesPerGadget;
-    type MaxGitOwnerLength = MaxGitOwnerLength;
-    type MaxGitRepoLength = MaxGitRepoLength;
-    type MaxGitTagLength = MaxGitTagLength;
-    type MaxBinaryNameLength = MaxBinaryNameLength;
-    type MaxIpfsHashLength = MaxIpfsHashLength;
-    type MaxContainerRegistryLength = MaxContainerRegistryLength;
-    type MaxContainerImageNameLength = MaxContainerImageNameLength;
-    type MaxContainerImageTagLength = MaxContainerImageTagLength;
-    type Constraints = pallet_services::types::ConstraintsOf<Self>;
-    type WeightInfo = ();
-}
-
-type Block = frame_system::mocking::MockBlock<Runtime>;
-
-#[allow(non_camel_case_types)]
-construct_runtime!(
-    pub enum Runtime
-    {
-        System: frame_system,
-        Timestamp: pallet_timestamp,
-        Balances: pallet_balances,
-        Services: pallet_services,
-        Session: pallet_session,
-        Staking: pallet_staking,
-    }
-);
-
-pub struct ExtBuilder;
-
-impl Default for ExtBuilder {
-    fn default() -> Self {
-        ExtBuilder
-    }
-}
-
-impl ProvideRuntimeApi<Block> for Runtime {
-    type Api = Self;
-    fn runtime_api(&self) -> ApiRef<Self::Api> {
-        ApiRef::from(*self)
-    }
-}
-
-pub fn id_to_ecdsa_pair(id: u8) -> ecdsa::Pair {
-    ecdsa::Pair::from_string(&format!("//Alice///{id}"), None).expect("static values are valid")
-}
-
-pub fn id_to_sr25519_pair(id: u8) -> sr25519::Pair {
-    sr25519::Pair::from_string(&format!("//Alice///{id}"), None).expect("static values are valid")
-}
-
-pub fn id_to_public(id: u8) -> ecdsa::Public {
-    id_to_ecdsa_pair(id).public()
-}
-
-pub fn id_to_sr25519_public(id: u8) -> sr25519::Public {
-    id_to_sr25519_pair(id).public()
-}
-
-sp_externalities::decl_extension! {
-    pub struct TracingUnboundedReceiverExt(TracingUnboundedReceiver<<Block as BlockT>::Hash>);
-}
-
-type PeersRx<Env> =
-    Arc<HashMap<ecdsa::Public, gadget_io::tokio::sync::Mutex<UnboundedReceiver<Env>>>>;
-
-pub struct MockNetwork<Env: GadgetEnvironment> {
-    peers_tx: Arc<
-        HashMap<
-            ecdsa::Public,
-            UnboundedSender<<Env::WorkManager as WorkManagerInterface>::ProtocolMessage>,
-        >,
-    >,
-    peers_rx: PeersRx<<Env::WorkManager as WorkManagerInterface>::ProtocolMessage>,
-    my_id: ecdsa::Public,
-}
-
-impl<Env: GadgetEnvironment> Clone for MockNetwork<Env> {
-    fn clone(&self) -> Self {
-        Self {
-            peers_tx: self.peers_tx.clone(),
-            peers_rx: self.peers_rx.clone(),
-            my_id: self.my_id,
-        }
-    }
-}
-
-impl<Env: GadgetEnvironment> MockNetwork<Env> {
-    pub fn setup(ids: &Vec<ecdsa::Public>) -> Vec<Self> {
-        let mut peers_tx = HashMap::new();
-        let mut peers_rx = HashMap::new();
-        let mut networks = Vec::new();
-
-        for id in ids {
-            let (tx, rx) = gadget_io::tokio::sync::mpsc::unbounded_channel();
-            peers_tx.insert(*id, tx);
-            peers_rx.insert(*id, gadget_io::tokio::sync::Mutex::new(rx));
-        }
-
-        let peers_tx = Arc::new(peers_tx);
-        let peers_rx = Arc::new(peers_rx);
-
-        for id in ids {
-            let network = Self {
-                peers_tx: peers_tx.clone(),
-                peers_rx: peers_rx.clone(),
-                my_id: *id,
-            };
-            networks.push(network);
-        }
-
-        networks
-    }
-}
-
-#[async_trait]
-impl<Env: GadgetEnvironment> Network<Env> for MockNetwork<Env>
-where
-    Env::ProtocolMessage: Clone,
-{
-    async fn next_message(
-        &self,
-    ) -> Option<<Env::WorkManager as WorkManagerInterface>::ProtocolMessage> {
-        self.peers_rx
-            .get(&self.my_id)?
-            .lock_timeout(Duration::from_millis(500))
-            .await
-            .recv()
-            .await
-    }
-
-    async fn send_message(
-        &self,
-        message: <Env::WorkManager as WorkManagerInterface>::ProtocolMessage,
-    ) -> Result<(), Error> {
-        let _check_message_has_ids = message.sender_network_id().ok_or(Error::MissingNetworkId)?;
-        if let Some(peer_id) = message.recipient_network_id() {
-            let tx = self
-                .peers_tx
-                .get(&peer_id)
-                .ok_or(Error::PeerNotFound { id: peer_id })?;
-            tx.send(message).map_err(|err| Error::NetworkError {
-                err: err.to_string(),
-            })?;
-        } else {
-            // Broadcast to everyone except ourself
-            for (peer_id, tx) in self.peers_tx.iter() {
-                if peer_id != &self.my_id {
-                    tx.send(message.clone())
-                        .map_err(|err| Error::NetworkError {
-                            err: err.to_string(),
-                        })?;
-                }
-            }
-        }
-        Ok(())
-    }
-}
-
-pub type MockBackend = sc_client_api::in_mem::Backend<Block>;
-
-static TEST_EXTERNALITIES: parking_lot::Mutex<Option<MultiThreadedTestExternalities>> =
-    parking_lot::Mutex::new(None);
-
-pub fn advance_to_block(block_number: u64) {
-    while System::block_number() < block_number {
-        System::on_finalize(System::block_number());
-        Services::on_finalize(System::block_number());
-        Balances::on_finalize(System::block_number());
-        System::set_block_number(System::block_number() + 1);
-        System::on_initialize(System::block_number());
-        Services::on_initialize(System::block_number());
-        Balances::on_initialize(System::block_number());
-    }
-}
-
-// Checks events against the latest. A contiguous set of events must be
-// provided. They must include the most recent RuntimeEvent, but do not have to include
-// every past RuntimeEvent.
-pub fn assert_events(mut expected: Vec<RuntimeEvent>) {
-    let mut actual: Vec<RuntimeEvent> = System::events().iter().map(|e| e.event.clone()).collect();
-
-    expected.reverse();
-    for evt in expected {
-        let next = actual.pop().expect("RuntimeEvent expected");
-        assert_eq!(next, evt, "Events don't match (actual,expected)");
-    }
-}
-
-/// This function basically just builds a genesis storage key/value store according to
-/// our desired mockup.
-/// N: number of nodes
-/// K: Number of networks accessible per node
-/// D: Any data that you want to pass to pass with NodeInput.
-/// F: A function that generates a singular full node (all possible protocols) by returning a future representing the node's execution
-pub async fn new_test_ext<
-    const N: usize,
-    const K: usize,
-    D: Send + Clone + 'static,
-    F: Fn(
-        NodeInput<TangleExtEnvironment, MockNetwork<TangleExtEnvironment>, InMemoryBackend, D>,
-    ) -> Fut,
-    Fut: SendFuture<'static, ()>,
->(
-    additional_params: D,
-    f: F,
-) -> MultiThreadedTestExternalities {
-    let mut t = frame_system::GenesisConfig::<Runtime>::default()
-        .build_storage()
-        .unwrap();
-
-    let role_pairs = (0..N)
-        .map(|i| id_to_ecdsa_pair(i as u8))
-        .collect::<Vec<_>>();
-    let roles_identities = role_pairs
-        .iter()
-        .map(|pair| pair.public())
-        .collect::<Vec<_>>();
-
-    let pairs = (0..N)
-        .map(|i| id_to_sr25519_pair(i as u8))
-        .collect::<Vec<_>>();
-    let account_ids = pairs
-        .iter()
-        .map(|pair| pair.public().into())
-        .collect::<Vec<AccountId>>();
-
-    let balances = account_ids
-        .iter()
-        .map(|public| (public.clone(), 100u128))
-        .collect::<Vec<_>>();
-
-    let networks = (0..K)
-        .map(|_| MockNetwork::setup(&roles_identities))
-        .collect::<Vec<_>>();
-
-    // Transpose networks
-    let networks = (0..N)
-        .map(|i| {
-            networks
-                .iter()
-                .map(|network| network[i].clone())
-                .collect::<Vec<_>>()
-        })
-        .collect::<Vec<_>>();
-
-    pallet_balances::GenesisConfig::<Runtime> { balances }
-        .assimilate_storage(&mut t)
-        .unwrap();
-
-    let mut ext = sp_io::TestExternalities::new(t);
-    ext.register_extension(KeystoreExt(Arc::new(MemoryKeystore::new()) as KeystorePtr));
-
-    let ext = MultiThreadedTestExternalities::new(ext);
-    assert!(TEST_EXTERNALITIES.lock().replace(ext.clone()).is_none(), "Make sure to run tests serially with -- --test-threads=1 or with nextest to ensure separate program spaces per test");
-
-    let finality_notification_txs = Arc::new(parking_lot::Mutex::new(Vec::new()));
-    let sinks = finality_notification_txs.clone();
-    let externalities = ext.clone();
-
-    // Spawn a thread that sends a finality notification whenever it detects a change in block number
-    gadget_io::tokio::task::spawn(async move {
-        let mut prev: Option<u64> = None;
-        loop {
-            let number = externalities
-                .execute_with_async(move || {
-                    let number = System::block_number();
-                    System::finalize();
-                    advance_to_block(number + 1);
-                    number + 1
-                })
-                .await;
-            // log::info!(target: "gadget", "Current block number: {number}");
-            if prev.is_none() || prev.unwrap() != number {
-                prev = Some(number);
-                log::info!(target: "gadget", "Creating finality notification {number}");
-
-                let lock = sinks.lock();
-                for sink in lock.iter() {
-                    let notification = generate_finality_notification_at(number);
-                    if sink.unbounded_send(notification).is_err() {
-                        log::warn!(target: "gadget", "Will not deliver FinalityNotification because the receiver is gone");
-                    }
-                }
-            }
-
-            gadget_io::tokio::time::sleep(Duration::from_millis(12000)).await;
-        }
-    });
-
-    for (node_index, ((role_pair, pair), networks)) in
-        role_pairs.into_iter().zip(pairs).zip(networks).enumerate()
-    {
-        let mut mock_clients = Vec::new();
-
-        for _ in 0..K {
-            mock_clients.push(MockClient::new(Runtime, finality_notification_txs.clone()).await);
-        }
-
-        let account_id: AccountId = pair.public().into();
-
-        let logger = DebugLogger {
-            id: format!("Peer {node_index}"),
-        };
-
-        let genesis_hash = generate_finality_notification_at(0).hash;
-        let tx_manager = Arc::new(TestExternalitiesPalletSubmitter {
-            id: account_id.clone(),
-            ext: ext.clone(),
-            subxt_client: OfflineClient::new(genesis_hash, "1.0.0".into(), []),
-        });
-
-        let keystore = ECDSAKeyStore::in_memory(role_pair);
-        let prometheus_config = PrometheusConfig::Disabled;
-
-        let input = NodeInput {
-            clients: mock_clients,
-            networks,
-            account_id: sr25519::Public(account_id.into()),
-            logger,
-            tx_manager: tx_manager as _,
-            keystore,
-            node_index,
-            additional_params: additional_params.clone(),
-            prometheus_config,
-        };
-
-        let task = f(input);
-        gadget_io::tokio::task::spawn(task);
-    }
-
-    ext
-}
-
-fn generate_finality_notification_at(number: u64) -> FinalityNotification<Block> {
-    let (faux_sink, faux_stream) = tracing_unbounded("faux_sink", 1024);
-    std::mem::forget(faux_stream);
-    let header = <Block as BlockT>::Header::new_from_number(number);
-    let summary = FinalizeSummary::<Block> {
-        finalized: vec![header.hash()],
-        header,
-        stale_heads: vec![],
-    };
-
-    FinalityNotification::from_summary(summary, faux_sink)
-}
-
-pub mod mock_wrapper_client {
-    use crate::mock::{Runtime, TangleExtEnvironment};
-    use crate::sync::substrate_test_channel::MultiThreadedTestExternalities;
-    use async_trait::async_trait;
-    use environment_utils::transaction_manager::tangle::TanglePalletSubmitter;
-    use futures::StreamExt;
-    use gadget_common::client::exec_client_function;
-    use gadget_common::locks::TokioMutexExt;
-    use gadget_common::tangle_runtime::*;
-    use gadget_common::tangle_subxt;
-    use gadget_common::tangle_subxt::subxt::utils::AccountId32;
-    use gadget_common::tangle_subxt::subxt::{Config, OfflineClient, OnlineClient};
-    use gadget_core::gadget::general::Client;
-    use gadget_core::gadget::substrate;
-    use gadget_io::tokio;
-    use parity_scale_codec::{Decode, Encode};
-    use sc_client_api::{
-        BlockchainEvents, FinalityNotification, FinalityNotifications, ImportNotifications,
-        StorageEventStream, StorageKey,
-    };
-    use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender};
-    use sp_api::{ApiRef, ProvideRuntimeApi};
-    use sp_runtime::traits::Block;
-    use sp_runtime::traits::Header;
-    use std::sync::Arc;
-    use std::time::Duration;
-    use tangle_environment::gadget::TangleEvent;
-    use tangle_primitives::AccountId;
-
-    #[derive(Clone)]
-    pub struct MockClient<R, B: Block> {
-        runtime: Arc<R>,
-        finality_notification_stream:
-            Arc<gadget_io::tokio::sync::Mutex<Option<FinalityNotifications<B>>>>,
-        latest_finality_notification: Arc<gadget_io::tokio::sync::Mutex<Option<TangleEvent>>>,
-        finality_notification_txs:
-            Arc<parking_lot::Mutex<Vec<TracingUnboundedSender<TangleEvent>>>>,
-    }
-
-    impl<R, B: Block> MockClient<R, B> {
-        pub async fn new(
-            runtime: R,
-            finality_notification_txs: Arc<
-                parking_lot::Mutex<Vec<TracingUnboundedSender<TangleEvent>>>,
-            >,
-        ) -> Self {
-            let runtime = Arc::new(runtime);
-            let finality_notification_stream = Arc::new(gadget_io::tokio::sync::Mutex::new(None));
-
-            let this = Self {
-                runtime,
-                finality_notification_stream,
-                latest_finality_notification: gadget_io::tokio::sync::Mutex::new(None).into(),
-                finality_notification_txs,
-            };
-
-            *this
-                .finality_notification_stream
-                .lock_timeout(Duration::from_millis(500))
-                .await = Some(this.finality_notification_stream());
-            this
-        }
-    }
-
-    #[async_trait]
-    impl<R: Send + Sync + Clone, B: Block> Client<TangleEvent> for MockClient<R, B> {
-        async fn next_event(&self) -> Option<TangleEvent> {
-            let mut lock = self
-                .finality_notification_stream
-                .lock_timeout(Duration::from_millis(500))
-                .await;
-            let next = lock.as_mut().expect("Should exist").next().await;
-            log::trace!(target: "gadget", "Latest Finality Notification: {:?}", next.as_ref().map(|r| r.header.number()));
-            self.latest_finality_notification
-                .lock_timeout(Duration::from_millis(500))
-                .await
-                .clone_from(&next);
-
-            next.map(|n| {
-                let mut hash = [0u8; 32];
-                hash.copy_from_slice(n.header.hash().as_ref());
-                let number =
-                    Decode::decode(&mut Encode::encode(&n.header.number()).as_slice()).unwrap();
-                TangleEvent {
-                    hash,
-                    number,
-                    events: n,
-                }
-            })
-        }
-
-        async fn latest_event(&self) -> Option<TangleEvent> {
-            let lock = self
-                .latest_finality_notification
-                .lock_timeout(Duration::from_millis(500))
-                .await;
-            if let Some(n) = lock.clone() {
-                let mut hash = [0u8; 32];
-                hash.copy_from_slice(&n.hash);
-                let number = Decode::decode(&mut Encode::encode(&n.number).as_slice()).unwrap();
-                Some(TangleEvent {
-                    hash,
-                    number,
-                    events: n,
-                })
-            } else {
-                drop(lock);
-                self.next_event().await
-            }
-        }
-    }
-
-    impl<R: ProvideRuntimeApi<B>, B: Block> ProvideRuntimeApi<B> for MockClient<R, B> {
-        type Api = R::Api;
-        fn runtime_api(&self) -> ApiRef<Self::Api> {
-            self.runtime.runtime_api()
-        }
-    }
-
-    impl<R, B: Block> BlockchainEvents<B> for MockClient<R, B> {
-        fn import_notification_stream(&self) -> ImportNotifications<B> {
-            let (sink, stream) = tracing_unbounded("import_notification_stream", 1024);
-            // We are not interested in block import notifications for tests
-            std::mem::forget(sink);
-            stream
-        }
-
-        fn every_import_notification_stream(&self) -> ImportNotifications<B> {
-            unimplemented!()
-        }
-
-        fn finality_notification_stream(&self) -> FinalityNotifications<B> {
-            let (sink, stream) =
-                tracing_unbounded::<FinalityNotification<B>>("finality_notification_stream", 1024);
-            self.finality_notification_txs.lock().push(sink);
-            stream
-        }
-
-        fn storage_changes_notification_stream(
-            &self,
-            _filter_keys: Option<&[StorageKey]>,
-            _child_filter_keys: Option<&[(StorageKey, Option<Vec<StorageKey>>)]>,
-        ) -> sc_client_api::blockchain::Result<StorageEventStream<<B as Block>::Hash>> {
-            unimplemented!()
-        }
-    }
-
-    pub struct TestExternalitiesPalletSubmitter<C: Config> {
-        pub ext: MultiThreadedTestExternalities,
-        pub id: AccountId,
-        pub subxt_client: OfflineClient<C>,
-    }
-
-    impl<C: Config> std::fmt::Debug for TestExternalitiesPalletSubmitter<C> {
-        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-            f.debug_struct("TestExternalitiesPalletSubmitter")
-                .field("id", &self.id.to_string())
-                .finish()
-        }
-    }
-
-    #[async_trait]
-    impl<C: Config> TanglePalletSubmitter for TestExternalitiesPalletSubmitter<C> {
-        async fn submit_job_call_result(
-            &self,
-            service_id: u64,
-            call_id: u64,
-            result: api::services::calls::types::submit_result::Result,
-        ) -> Result<(), gadget_common::Error> {
-            let id = self.id.clone();
-            let client = self.subxt_client.clone();
-            let function = async move {
-                let call = api::tx()
-                    .services()
-                    .submit_result(service_id, call_id, result);
-                let _res = client
-                    .tx()
-                    .sign_and_submit_then_watch_default(&call, &id)
-                    .await?
-                    .wait_for_finalized_success()
-                    .await?
-                    .block_hash();
-
-                Ok(())
-            };
-
-            function.await
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct TangleExtEnvironment {
-    finality_notification_txs:
-        Arc<parking_lot::Mutex<Vec<TracingUnboundedSender<FinalityNotification<Block>>>>>,
-    #[allow(dead_code)]
-    tx_manager: Arc<parking_lot::Mutex<Option<Arc<dyn TanglePalletSubmitter>>>>,
-}
-
-#[async_trait]
-impl GadgetEnvironment for TangleExtEnvironment {
-    type Event = TangleEvent;
-    type ProtocolMessage = TangleProtocolMessage;
-    type Client = MockClient<Runtime, Block>;
-    type WorkManager = TangleWorkManager;
-    type Error = gadget_common::Error;
-    type Clock = <Self::WorkManager as WorkManagerInterface>::Clock;
-    type RetryID = <Self::WorkManager as WorkManagerInterface>::RetryID;
-    type TaskID = <Self::WorkManager as WorkManagerInterface>::TaskID;
-    type SessionID = <Self::WorkManager as WorkManagerInterface>::SessionID;
-    type TransactionManager = Arc<dyn TanglePalletSubmitter>;
-    type JobInitMetadata = TangleJobMetadata;
-
-    fn build_protocol_message<Payload: Serialize>(
-        associated_block_id: <Self::WorkManager as WorkManagerInterface>::Clock,
-        associated_session_id: <Self::WorkManager as WorkManagerInterface>::SessionID,
-        associated_retry_id: <Self::WorkManager as WorkManagerInterface>::RetryID,
-        associated_task_id: <Self::WorkManager as WorkManagerInterface>::TaskID,
-        from: UserID,
-        to: Option<UserID>,
-        payload: &Payload,
-        from_account_id: Option<ecdsa::Public>,
-        to_network_id: Option<ecdsa::Public>,
-    ) -> Self::ProtocolMessage {
-        TangleProtocolMessage {
-            associated_block_id,
-            associated_session_id,
-            associated_retry_id,
-            task_hash: associated_task_id,
-            from,
-            to,
-            payload: serialize(payload).expect("Failed to serialize message"),
-            from_network_id: from_account_id,
-            to_network_id,
-        }
-    }
-
-    async fn setup_runtime(&self) -> Result<Self::Client, Self::Error> {
-        let finality_notification_txs = self.finality_notification_txs.clone();
-        Ok(MockClient::<Runtime, Block>::new(Runtime, finality_notification_txs).await)
-    }
-
-    fn transaction_manager(&self) -> Self::TransactionManager {
-        if let Some(tx_manager) = &*self.tx_manager.lock() {
-            tx_manager.clone()
-        } else {
-            panic!("Transaction manager not set")
-        }
-    }
-}
-
-impl EventMetadata<TangleExtEnvironment> for TangleEvent {
-    fn number(&self) -> <TangleExtEnvironment as GadgetEnvironment>::Clock {
-        self.number
-    }
-}
-
-pub fn mock_pub_key(id: u8) -> AccountId {
-    sr25519::Public::from_raw([id; 32]).into()
-}