diff --git a/Cargo.toml b/Cargo.toml
index f873155c44d93df91b9dcc9811c6b7e151a5088b..5df2f40067df8d564599aa70333c8ff69af1e02d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -45,6 +45,7 @@ libp2p = { version = "0.52" }
 subxt = "0.32.0"
 subxt-signer = { version = "0.32.0", features = ["subxt"]}
 tracing = "0.1.35"
+pjs-rs = "0.1.2"
 
 # Zombienet workspace crates:
 support = { package = "zombienet-support", version = "0.1.0-alpha.0", path = "crates/support" }
diff --git a/crates/examples/Cargo.toml b/crates/examples/Cargo.toml
index 0dd7f343b211fc1cae31666735c50c75406f156c..9c4d0c1fd8c69d10cb12a7e1311a3956d5705b13 100644
--- a/crates/examples/Cargo.toml
+++ b/crates/examples/Cargo.toml
@@ -11,3 +11,4 @@ tokio = { workspace = true }
 futures = { workspace = true }
 subxt = { workspace = true }
 tracing-subscriber = "0.3"
+serde_json = { workspace = true }
diff --git a/crates/examples/examples/pjs.rs b/crates/examples/examples/pjs.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1fd7f671c4b3bf3baea66907d1ff029a3eac6f76
--- /dev/null
+++ b/crates/examples/examples/pjs.rs
@@ -0,0 +1,53 @@
+use futures::stream::StreamExt;
+use serde_json::json;
+use zombienet_sdk::{NetworkConfigBuilder, NetworkConfigExt};
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    tracing_subscriber::fmt::init();
+    let network = NetworkConfigBuilder::new()
+        .with_relaychain(|r| {
+            r.with_chain("rococo-local")
+                .with_default_command("polkadot")
+                .with_node(|node| node.with_name("alice"))
+                .with_node(|node| node.with_name("bob"))
+        })
+        .with_parachain(|p| {
+            p.with_id(100)
+                .cumulus_based(true)
+                .with_collator(|n| n.with_name("collator").with_command("polkadot-parachain"))
+        })
+        .build()
+        .unwrap()
+        .spawn_native()
+        .await?;
+
+    println!("🚀🚀🚀🚀 network deployed");
+
+    let alice = network.get_node("alice")?;
+    let client = alice.client::<subxt::PolkadotConfig>().await?;
+
+    // wait 2 blocks
+    let mut blocks = client.blocks().subscribe_finalized().await?.take(2);
+
+    while let Some(block) = blocks.next().await {
+        println!("Block #{}", block?.header().number);
+    }
+
+    // run pjs with code
+    let query_paras = r#"
+    const parachains: number[] = (await api.query.paras.parachains()) || [];
+    return parachains.toJSON()
+    "#;
+
+    let paras = alice.pjs(query_paras, vec![]).await??;
+
+    println!("parachains registered: {:?}", paras);
+
+    // run pjs with file
+    let _ = alice
+        .pjs_file("./examples/pjs_transfer.js", vec![json!("//Alice")])
+        .await?;
+
+    Ok(())
+}
diff --git a/crates/examples/examples/pjs_transfer.js b/crates/examples/examples/pjs_transfer.js
new file mode 100644
index 0000000000000000000000000000000000000000..ecc85f948ccc296780f7920ecdbde9fd949e659d
--- /dev/null
+++ b/crates/examples/examples/pjs_transfer.js
@@ -0,0 +1,32 @@
+const seed = arguments[0];
+
+await utilCrypto.cryptoWaitReady();
+const k = new keyring.Keyring({ type: "sr25519" });
+const signer = k.addFromUri(seed);
+
+// Make a transfer from Alice to Bob and listen to system events.
+// You need to be connected to a development chain for this example to work.
+const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
+const BOB = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty';
+
+// Get a random number between 1 and 100000
+const randomAmount = Math.floor((Math.random() * 100000) + 1);
+
+// Create a extrinsic, transferring randomAmount units to Bob.
+const transferAllowDeath = api.tx.balances.transferAllowDeath(BOB, randomAmount);
+
+return new Promise(async (resolve, _reject) => {
+    // Sign and Send the transaction
+    const unsub = await transferAllowDeath.signAndSend(signer, ({ events = [], status }) => {
+        if (status.isInBlock) {
+            console.log('Successful transfer of ' + randomAmount + ' with hash ' + status.asInBlock.toHex());
+            return resolve();
+        } else {
+            console.log('Status of transfer: ' + status.type);
+        }
+
+        events.forEach(({ phase, event: { data, method, section } }) => {
+            console.log(phase.toString() + ' : ' + section + '.' + method + ' ' + data.toString());
+        });
+    });
+});
\ No newline at end of file
diff --git a/crates/orchestrator/Cargo.toml b/crates/orchestrator/Cargo.toml
index f8aff9d8aff967c61ad4b433941e77e3a08f5fc6..f6c28fac8f823506d99d8091f128597d513ca384 100644
--- a/crates/orchestrator/Cargo.toml
+++ b/crates/orchestrator/Cargo.toml
@@ -28,6 +28,7 @@ subxt = { workspace = true }
 subxt-signer = { workspace = true }
 reqwest = { workspace = true }
 tracing = { workspace = true }
+pjs-rs = { workspace = true }
 
 # Zombienet deps
 configuration = { workspace = true }
diff --git a/crates/orchestrator/src/lib.rs b/crates/orchestrator/src/lib.rs
index b2d42c3b3dacd0580607fadb2ffa68a552ab4ee9..26f748f271d6cd75c2166e8a5111c92ab2e45d50 100644
--- a/crates/orchestrator/src/lib.rs
+++ b/crates/orchestrator/src/lib.rs
@@ -459,3 +459,4 @@ pub enum ZombieRole {
 
 // re-export
 pub use network::{AddCollatorOptions, AddNodeOptions};
+pub use shared::types::PjsResult;
diff --git a/crates/orchestrator/src/network/node.rs b/crates/orchestrator/src/network/node.rs
index 63ffaf36f80f67e7d941e59e5baddccec2043f02..a45439c3d24aadf2c51e8055aea0b653d7a70eeb 100644
--- a/crates/orchestrator/src/network/node.rs
+++ b/crates/orchestrator/src/network/node.rs
@@ -1,12 +1,14 @@
-use std::{sync::Arc, time::Duration};
+use std::{path::Path, sync::Arc, thread, time::Duration};
 
 use anyhow::anyhow;
+use pjs_rs::ReturnValue;
 use prom_metrics_parser::MetricMap;
 use provider::DynNode;
+use serde_json::json;
 use subxt::{backend::rpc::RpcClient, OnlineClient};
 use tokio::sync::RwLock;
 
-use crate::network_spec::node::NodeSpec;
+use crate::{network_spec::node::NodeSpec, shared::types::PjsResult};
 
 #[derive(Clone)]
 pub struct NetworkNode {
@@ -66,6 +68,51 @@ impl NetworkNode {
         OnlineClient::from_url(&self.ws_uri).await
     }
 
+    /// Execute js/ts code inside [pjs_rs] custom runtime.
+    ///
+    /// The code will be run in a wrapper similat to the `javascript` developer tab
+    /// of polkadot.js apps. The returning value is represented as [PjsResult] enum, to allow
+    /// to communicate that the execution was succeful but the returning value can be deserialized as [serde_json::Value].
+    pub async fn pjs(
+        &self,
+        code: impl AsRef<str>,
+        args: Vec<serde_json::Value>,
+    ) -> Result<PjsResult, anyhow::Error> {
+        let code = pjs_build_template(self.ws_uri(), code.as_ref(), args);
+        let value = match thread::spawn(|| pjs_inner(code))
+            .join()
+            .map_err(|_| anyhow!("[pjs] Thread panicked"))??
+        {
+            ReturnValue::Deserialized(val) => Ok(val),
+            ReturnValue::CantDeserialize(msg) => Err(msg),
+        };
+
+        Ok(value)
+    }
+
+    /// Execute js/ts file  inside [pjs_rs] custom runtime.
+    ///
+    /// The content of the file will be run in a wrapper similat to the `javascript` developer tab
+    /// of polkadot.js apps. The returning value is represented as [PjsResult] enum, to allow
+    /// to communicate that the execution was succeful but the returning value can be deserialized as [serde_json::Value].
+    pub async fn pjs_file(
+        &self,
+        file: impl AsRef<Path>,
+        args: Vec<serde_json::Value>,
+    ) -> Result<PjsResult, anyhow::Error> {
+        let content = std::fs::read_to_string(file)?;
+        let code = pjs_build_template(self.ws_uri(), content.as_ref(), args);
+        let value = match thread::spawn(|| pjs_inner(code))
+            .join()
+            .map_err(|_| anyhow!("[pjs] Thread panicked"))??
+        {
+            ReturnValue::Deserialized(val) => Ok(val),
+            ReturnValue::CantDeserialize(msg) => Err(msg),
+        };
+
+        Ok(value)
+    }
+
     /// Resume the node, this is implemented by resuming the
     /// actual process (e.g polkadot) with sendig `SIGCONT` signal
     pub async fn resume(&self) -> Result<(), anyhow::Error> {
@@ -152,3 +199,31 @@ impl std::fmt::Debug for NetworkNode {
             .finish()
     }
 }
+
+// Helper methods
+
+fn pjs_build_template(ws_uri: &str, content: &str, args: Vec<serde_json::Value>) -> String {
+    format!(
+        r#"
+    const {{ util, utilCrypto, keyring, types }} = pjs;
+    ( async () => {{
+        const api = await pjs.api.ApiPromise.create({{ provider: new pjs.api.WsProvider('{}') }});
+        const _run = async (api, hashing, keyring, types, util, arguments) => {{
+            {}
+        }};
+        return await _run(api, utilCrypto, keyring, types, util, {});
+    }})()
+    "#,
+        ws_uri,
+        content,
+        json!(args)
+    )
+}
+
+// Since pjs-rs run a custom javascript runtime (using deno_core) we need to
+// execute in an isolated thread.
+#[tokio::main(flavor = "current_thread")]
+async fn pjs_inner(code: String) -> Result<ReturnValue, anyhow::Error> {
+    // Arguments are already encoded in the code built from the template.
+    pjs_rs::run_ts_code(code, None).await
+}
diff --git a/crates/orchestrator/src/shared/types.rs b/crates/orchestrator/src/shared/types.rs
index a0bc2b367175a05fa4b7bd1a7b3c57b6ebf24d93..015170f90b4584b4686d13b8d38f725002a6b24f 100644
--- a/crates/orchestrator/src/shared/types.rs
+++ b/crates/orchestrator/src/shared/types.rs
@@ -75,3 +75,12 @@ pub struct ParachainGenesisArgs {
     pub validation_code: String,
     pub parachain: bool,
 }
+
+/// pjs-rs success [Result] type
+///
+/// Represent the possible states returned from a succefully call to pjs-rs
+///
+/// Ok(value) -> Deserialized return value into a [serde_json::Value]
+/// Err(msg) -> Execution of the script finish Ok, but the returned value
+/// can't be deserialize into a [serde_json::Value]
+pub type PjsResult = Result<serde_json::Value, String>;
diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs
index 867e483f84195ee2d80085c429573ab154ec0ae1..828a00ed761c6eea61b6ac467a9b8a8940dd78cd 100644
--- a/crates/sdk/src/lib.rs
+++ b/crates/sdk/src/lib.rs
@@ -2,6 +2,7 @@ use async_trait::async_trait;
 pub use configuration::{NetworkConfig, NetworkConfigBuilder, RegistrationStrategy};
 pub use orchestrator::{
     errors::OrchestratorError, network::Network, AddCollatorOptions, AddNodeOptions, Orchestrator,
+    PjsResult,
 };
 use provider::NativeProvider;
 use support::{fs::local::LocalFileSystem, process::os::OsProcessManager};