diff --git a/Cargo.toml b/Cargo.toml
index c0eb9b04..cfe42d92 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -39,6 +39,7 @@ rustdoc-args = ["--cfg", "docsrs"]
maili-protocol = { version = "0.1.0", path = "crates/protocol", default-features = false }
maili-provider = { version = "0.1.0", path = "crates/provider", default-features = false }
maili-registry = { version = "0.1.0", path = "crates/registry", default-features = false }
+maili-rpc-jsonrpsee = { version = "0.1.0", path = "crates/rpc-jsonrpsee", default-features = false }
maili-rpc-types-engine = { version = "0.1.0", path = "crates/rpc-types-engine", default-features = false }
# OP-Alloy
@@ -46,7 +47,6 @@ op-alloy-genesis = { version = "0.9.1", default-features = false }
op-alloy-consensus = { version = "0.9.1", default-features = false }
op-alloy-network = { version = "0.9.1", default-features = false }
op-alloy-rpc-types = { version = "0.9.1", default-features = false }
-op-alloy-rpc-jsonrpsee = { version = "0.9.1", default-features = false }
op-alloy-rpc-types-engine = { version = "0.9.1", default-features = false }
# Alloy
diff --git a/crates/rpc-jsonrpsee/Cargo.toml b/crates/rpc-jsonrpsee/Cargo.toml
new file mode 100644
index 00000000..fa213134
--- /dev/null
+++ b/crates/rpc-jsonrpsee/Cargo.toml
@@ -0,0 +1,44 @@
+[package]
+name = "maili-rpc-jsonrpsee"
+description = "Optimism RPC Client"
+
+version.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+homepage.workspace = true
+authors.workspace = true
+repository.workspace = true
+exclude.workspace = true
+
+[lints]
+workspace = true
+
+[dependencies]
+# Workspace
+maili-protocol = { workspace = true, default-features = false, features = ["serde"] }
+maili-rpc-types-engine = { workspace = true, default-features = false, features = ["serde"] }
+
+# OP-Alloy
+op-alloy-rpc-types.workspace = true
+
+# Alloy
+alloy-eips.workspace = true
+alloy-primitives.workspace = true
+
+# rpc
+jsonrpsee.workspace = true
+
+[features]
+default = ["std"]
+std = [
+ "maili-protocol/std",
+ "maili-rpc-types-engine/std",
+ "op-alloy-rpc-types/std",
+ "alloy-eips/std",
+ "alloy-primitives/std",
+]
+client = [
+ "jsonrpsee/client",
+ "jsonrpsee/async-client",
+]
diff --git a/crates/rpc-jsonrpsee/README.md b/crates/rpc-jsonrpsee/README.md
new file mode 100644
index 00000000..e66295a8
--- /dev/null
+++ b/crates/rpc-jsonrpsee/README.md
@@ -0,0 +1,10 @@
+## `maili-rpc-jsonrpsee`
+
+
+
+
+
+
+
+
+Low-level Optimism JSON-RPC server and client implementations.
diff --git a/crates/rpc-jsonrpsee/src/lib.rs b/crates/rpc-jsonrpsee/src/lib.rs
new file mode 100644
index 00000000..dd08fe38
--- /dev/null
+++ b/crates/rpc-jsonrpsee/src/lib.rs
@@ -0,0 +1,12 @@
+#![doc = include_str!("../README.md")]
+#![doc(
+ html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg",
+ html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico"
+)]
+#![cfg_attr(not(test), warn(unused_crate_dependencies))]
+#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
+#![cfg_attr(not(feature = "std"), no_std)]
+
+extern crate alloc;
+
+pub mod traits;
diff --git a/crates/rpc-jsonrpsee/src/traits.rs b/crates/rpc-jsonrpsee/src/traits.rs
new file mode 100644
index 00000000..7eacba13
--- /dev/null
+++ b/crates/rpc-jsonrpsee/src/traits.rs
@@ -0,0 +1,162 @@
+#![allow(missing_docs)]
+
+//! Rollup Node
+
+use alloc::{boxed::Box, string::String, vec::Vec};
+use core::net::IpAddr;
+
+use alloy_eips::BlockNumberOrTag;
+use alloy_primitives::{B256, U64};
+use jsonrpsee::{core::RpcResult, proc_macros::rpc};
+use maili_protocol::{ExecutingMessage, SafetyLevel};
+use maili_rpc_types_engine::{ProtocolVersion, SuperchainSignal};
+use op_alloy_rpc_types::{
+ OutputResponse, PeerDump, PeerInfo, PeerStats, RollupConfig, SafeHeadResponse, SyncStatus,
+};
+
+/// Optimism specified rpc interface.
+///
+/// https://docs.optimism.io/builders/node-operators/json-rpc
+/// https://github.com/ethereum-optimism/optimism/blob/8dd17a7b114a7c25505cd2e15ce4e3d0f7e3f7c1/op-node/node/api.go#L114
+#[cfg_attr(not(feature = "client"), rpc(server, namespace = "optimism"))]
+#[cfg_attr(feature = "client", rpc(server, client, namespace = "optimism"))]
+pub trait RollupNode {
+ /// Get the output root at a specific block.
+ #[method(name = "outputAtBlock")]
+ async fn op_output_at_block(&self, block_number: BlockNumberOrTag)
+ -> RpcResult;
+
+ /// Gets the safe head at an L1 block height.
+ #[method(name = "safeHeadAtL1Block")]
+ async fn op_safe_head_at_l1_block(
+ &self,
+ block_number: BlockNumberOrTag,
+ ) -> RpcResult;
+
+ /// Get the synchronization status.
+ #[method(name = "syncStatus")]
+ async fn op_sync_status(&self) -> RpcResult;
+
+ /// Get the rollup configuration parameters.
+ #[method(name = "rollupConfig")]
+ async fn op_rollup_config(&self) -> RpcResult;
+
+ /// Get the software version.
+ #[method(name = "version")]
+ async fn op_version(&self) -> RpcResult;
+}
+
+/// The opp2p namespace handles peer interactions.
+#[cfg_attr(not(feature = "client"), rpc(server, namespace = "opp2p"))]
+#[cfg_attr(feature = "client", rpc(server, client, namespace = "opp2p"))]
+pub trait OpP2PApi {
+ /// Returns information of node
+ #[method(name = "self")]
+ async fn opp2p_self(&self) -> RpcResult;
+
+ #[method(name = "peers")]
+ async fn opp2p_peers(&self) -> RpcResult;
+
+ #[method(name = "peerStats")]
+ async fn opp2p_peer_stats(&self) -> RpcResult;
+
+ #[method(name = "discoveryTable")]
+ async fn opp2p_discovery_table(&self) -> RpcResult>;
+
+ #[method(name = "blockPeer")]
+ async fn opp2p_block_peer(&self, peer: String) -> RpcResult<()>;
+
+ #[method(name = "listBlockedPeers")]
+ async fn opp2p_list_blocked_peers(&self) -> RpcResult>;
+
+ #[method(name = "blocAddr")]
+ async fn opp2p_block_addr(&self, ip: IpAddr) -> RpcResult<()>;
+
+ #[method(name = "unblockAddr")]
+ async fn opp2p_unblock_addr(&self, ip: IpAddr) -> RpcResult<()>;
+
+ #[method(name = "listBlockedAddrs")]
+ async fn opp2p_list_blocked_addrs(&self) -> RpcResult>;
+
+ /// todo: should be IPNet?
+ #[method(name = "blockSubnet")]
+ async fn opp2p_block_subnet(&self, subnet: String) -> RpcResult<()>;
+
+ /// todo: should be IPNet?
+ #[method(name = "unblockSubnet")]
+ async fn opp2p_unblock_subnet(&self, subnet: String) -> RpcResult<()>;
+
+ /// todo: should be IPNet?
+ #[method(name = "listBlockedSubnets")]
+ async fn opp2p_list_blocked_subnets(&self) -> RpcResult>;
+
+ #[method(name = "protectPeer")]
+ async fn opp2p_protect_peer(&self, peer: String) -> RpcResult<()>;
+
+ #[method(name = "unprotectPeer")]
+ async fn opp2p_unprotect_peer(&self, peer: String) -> RpcResult<()>;
+
+ #[method(name = "connectPeer")]
+ async fn opp2p_connect_peer(&self, peer: String) -> RpcResult<()>;
+
+ #[method(name = "disconnectPeer")]
+ async fn opp2p_disconnect_peer(&self, peer: String) -> RpcResult<()>;
+}
+
+/// The admin namespace endpoints
+/// https://github.com/ethereum-optimism/optimism/blob/c7ad0ebae5dca3bf8aa6f219367a95c15a15ae41/op-node/node/api.go#L28-L36
+#[cfg_attr(not(feature = "client"), rpc(server, namespace = "admin"))]
+#[cfg_attr(feature = "client", rpc(server, client, namespace = "admin"))]
+pub trait OpAdminApi {
+ #[method(name = "resetDerivationPipeline")]
+ async fn admin_reset_derivation_pipeline(&self) -> RpcResult<()>;
+
+ #[method(name = "startSequencer")]
+ async fn admin_start_sequencer(&self, block_hash: B256) -> RpcResult<()>;
+
+ #[method(name = "stopSequencer")]
+ async fn admin_stop_sequencer(&self) -> RpcResult;
+
+ #[method(name = "sequencerActive")]
+ async fn admin_sequencer_active(&self) -> RpcResult;
+}
+
+/// Engine API extension for Optimism superchain signaling
+#[cfg_attr(not(feature = "client"), rpc(server, namespace = "engine"))]
+#[cfg_attr(feature = "client", rpc(server, client, namespace = "engine"))]
+pub trait EngineApiExt {
+ /// Signal superchain v1 message
+ ///
+ /// The execution engine SHOULD warn when the recommended version is newer than the current
+ /// version. The execution engine SHOULD take safety precautions if it does not meet
+ /// the required version.
+ ///
+ /// # Returns
+ /// The latest supported OP-Stack protocol version of the execution engine.
+ ///
+ /// See:
+ #[method(name = "signalSuperchainV1")]
+ async fn signal_superchain_v1(&self, signal: SuperchainSignal) -> RpcResult;
+}
+
+/// Op API extension for controlling the miner.
+#[cfg_attr(not(feature = "client"), rpc(server, namespace = "miner"))]
+#[cfg_attr(feature = "client", rpc(server, client, namespace = "miner"))]
+pub trait MinerApiExt {
+ /// Sets the maximum data availability size of any tx allowed in a block, and the total max l1
+ /// data size of the block. 0 means no maximum.
+ #[method(name = "setMaxDASize")]
+ async fn set_max_da_size(&self, max_tx_size: U64, max_block_size: U64) -> RpcResult;
+}
+
+/// Supervisor API for interop.
+#[rpc(client, namespace = "supervisor")]
+pub trait SupervisorApi {
+ /// Checks if the given messages meet the given minimum safety level.
+ #[method(name = "checkMessages")]
+ async fn check_messages(
+ &self,
+ messages: Vec,
+ min_safety: SafetyLevel,
+ ) -> RpcResult<()>;
+}