diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock
index 907c917d723a7ad03cfd4a8e296b112a8c3c15f7..928be6b18fc153cf53bf8b1526038079eed27d3f 100644
--- a/substrate/Cargo.lock
+++ b/substrate/Cargo.lock
@@ -70,7 +70,7 @@ checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
 dependencies = [
  "getrandom 0.2.3",
  "once_cell",
- "version_check 0.9.2",
+ "version_check",
 ]
 
 [[package]]
@@ -88,7 +88,7 @@ version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
 dependencies = [
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -225,21 +225,21 @@ dependencies = [
  "concurrent-queue",
  "futures-lite",
  "libc",
- "log 0.4.16",
+ "log",
  "once_cell",
  "parking",
  "polling",
  "slab",
  "socket2 0.4.4",
  "waker-fn",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
 name = "async-lock"
-version = "2.3.0"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1996609732bde4a9988bc42125f55f2af5f3c36370e27c778d5191a4a1b63bfb"
+checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
 dependencies = [
  "event-listener",
 ]
@@ -255,9 +255,9 @@ dependencies = [
 
 [[package]]
 name = "async-process"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83137067e3a2a6a06d67168e49e68a0957d215410473a740cea95a2425c0b7c6"
+checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c"
 dependencies = [
  "async-io",
  "blocking",
@@ -267,7 +267,7 @@ dependencies = [
  "libc",
  "once_cell",
  "signal-hook",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -282,14 +282,14 @@ dependencies = [
  "async-io",
  "async-lock",
  "async-process",
- "crossbeam-utils 0.8.5",
+ "crossbeam-utils",
  "futures-channel",
  "futures-core",
  "futures-io",
  "futures-lite",
  "gloo-timers",
  "kv-log-macro",
- "log 0.4.16",
+ "log",
  "memchr",
  "num_cpus",
  "once_cell",
@@ -358,7 +358,7 @@ version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f0de5164e5edbf51c45fb8c2d9664ae1c095cce1b265ecf7569093c0d66ef690"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "futures-sink",
  "futures-util",
  "memchr",
@@ -388,7 +388,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
 dependencies = [
  "hermit-abi",
  "libc",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -436,25 +436,6 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581"
 
-[[package]]
-name = "base64"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
-dependencies = [
- "byteorder",
- "safemem",
-]
-
-[[package]]
-name = "base64"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
-dependencies = [
- "byteorder",
-]
-
 [[package]]
 name = "base64"
 version = "0.13.0"
@@ -476,10 +457,10 @@ version = "4.0.0-dev"
 dependencies = [
  "beefy-primitives",
  "fnv",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "hex",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "parking_lot 0.12.0",
  "sc-chain-spec",
@@ -519,12 +500,9 @@ version = "4.0.0-dev"
 dependencies = [
  "beefy-gadget",
  "beefy-primitives",
- "futures 0.3.21",
- "jsonrpc-core",
- "jsonrpc-core-client",
- "jsonrpc-derive",
- "jsonrpc-pubsub",
- "log 0.4.16",
+ "futures",
+ "jsonrpsee",
+ "log",
  "parity-scale-codec",
  "parking_lot 0.12.0",
  "sc-rpc",
@@ -535,6 +513,7 @@ dependencies = [
  "sp-runtime",
  "substrate-test-runtime-client",
  "thiserror",
+ "tokio",
 ]
 
 [[package]]
@@ -544,7 +523,7 @@ dependencies = [
  "env_logger 0.9.0",
  "hex",
  "hex-literal",
- "log 0.4.16",
+ "log",
  "tiny-keccak",
 ]
 
@@ -814,16 +793,6 @@ version = "1.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
 
-[[package]]
-name = "bytes"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
-dependencies = [
- "byteorder",
- "iovec",
-]
-
 [[package]]
 name = "bytes"
 version = "1.1.0"
@@ -966,7 +935,7 @@ dependencies = [
  "num-integer",
  "num-traits",
  "time",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -1185,9 +1154,9 @@ dependencies = [
  "cranelift-codegen-shared 0.76.0",
  "cranelift-entity 0.76.0",
  "gimli 0.25.0",
- "log 0.4.16",
+ "log",
  "regalloc 0.0.31",
- "smallvec 1.8.0",
+ "smallvec",
  "target-lexicon",
 ]
 
@@ -1202,9 +1171,9 @@ dependencies = [
  "cranelift-codegen-shared 0.82.3",
  "cranelift-entity 0.82.3",
  "gimli 0.26.1",
- "log 0.4.16",
+ "log",
  "regalloc 0.0.34",
- "smallvec 1.8.0",
+ "smallvec",
  "target-lexicon",
 ]
 
@@ -1261,8 +1230,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "279afcc0d3e651b773f94837c3d581177b348c8d69e928104b2e9fccb226f921"
 dependencies = [
  "cranelift-codegen 0.76.0",
- "log 0.4.16",
- "smallvec 1.8.0",
+ "log",
+ "smallvec",
  "target-lexicon",
 ]
 
@@ -1273,8 +1242,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a006e3e32d80ce0e4ba7f1f9ddf66066d052a8c884a110b91d05404d6ce26dce"
 dependencies = [
  "cranelift-codegen 0.82.3",
- "log 0.4.16",
- "smallvec 1.8.0",
+ "log",
+ "smallvec",
  "target-lexicon",
 ]
 
@@ -1299,8 +1268,8 @@ dependencies = [
  "cranelift-entity 0.82.3",
  "cranelift-frontend 0.82.3",
  "itertools",
- "log 0.4.16",
- "smallvec 1.8.0",
+ "log",
+ "smallvec",
  "wasmparser 0.83.0",
  "wasmtime-types",
 ]
@@ -1325,7 +1294,7 @@ dependencies = [
  "clap 2.34.0",
  "criterion-plot",
  "csv",
- "futures 0.3.21",
+ "futures",
  "itertools",
  "lazy_static",
  "num-traits",
@@ -1359,7 +1328,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
 dependencies = [
  "cfg-if 1.0.0",
- "crossbeam-utils 0.8.5",
+ "crossbeam-utils",
 ]
 
 [[package]]
@@ -1370,7 +1339,7 @@ checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
 dependencies = [
  "cfg-if 1.0.0",
  "crossbeam-epoch",
- "crossbeam-utils 0.8.5",
+ "crossbeam-utils",
 ]
 
 [[package]]
@@ -1380,23 +1349,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
 dependencies = [
  "cfg-if 1.0.0",
- "crossbeam-utils 0.8.5",
+ "crossbeam-utils",
  "lazy_static",
  "memoffset",
  "scopeguard",
 ]
 
-[[package]]
-name = "crossbeam-utils"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
-dependencies = [
- "autocfg 1.0.1",
- "cfg-if 0.1.10",
- "lazy_static",
-]
-
 [[package]]
 name = "crossbeam-utils"
 version = "0.8.5"
@@ -1706,7 +1664,7 @@ checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
 dependencies = [
  "libc",
  "redox_users 0.3.5",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -1717,7 +1675,7 @@ checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
 dependencies = [
  "libc",
  "redox_users 0.4.0",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -1728,7 +1686,7 @@ checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
 dependencies = [
  "libc",
  "redox_users 0.4.0",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -1964,7 +1922,7 @@ checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
 dependencies = [
  "atty",
  "humantime 1.3.0",
- "log 0.4.16",
+ "log",
  "regex",
  "termcolor",
 ]
@@ -1975,7 +1933,7 @@ version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
 dependencies = [
- "log 0.4.16",
+ "log",
  "regex",
 ]
 
@@ -1987,7 +1945,7 @@ checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
 dependencies = [
  "atty",
  "humantime 2.1.0",
- "log 0.4.16",
+ "log",
  "regex",
  "termcolor",
 ]
@@ -2006,7 +1964,7 @@ checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
 dependencies = [
  "errno-dragonfly",
  "libc",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -2031,7 +1989,7 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5"
 dependencies = [
- "futures 0.3.21",
+ "futures",
 ]
 
 [[package]]
@@ -2081,7 +2039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4fdbe0d94371f9ce939b555dd342d0686cc4c0cadbcd4b61d70af5ff97eb4126"
 dependencies = [
  "env_logger 0.7.1",
- "log 0.4.16",
+ "log",
 ]
 
 [[package]]
@@ -2091,9 +2049,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d9def033d8505edf199f6a5d07aa7e6d2d6185b164293b77f0efd108f4f3e11d"
 dependencies = [
  "either",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
- "log 0.4.16",
+ "log",
  "num-traits",
  "parity-scale-codec",
  "parking_lot 0.11.2",
@@ -2138,21 +2096,6 @@ version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
-[[package]]
-name = "foreign-types"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
-dependencies = [
- "foreign-types-shared",
-]
-
-[[package]]
-name = "foreign-types-shared"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
-
 [[package]]
 name = "fork-tree"
 version = "3.0.0"
@@ -2167,7 +2110,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
 dependencies = [
  "matches",
- "percent-encoding 2.1.0",
+ "percent-encoding",
 ]
 
 [[package]]
@@ -2178,7 +2121,7 @@ dependencies = [
  "frame-system",
  "hex-literal",
  "linregress",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "paste 1.0.6",
  "scale-info",
@@ -2210,7 +2153,7 @@ dependencies = [
  "kvdb",
  "lazy_static",
  "linked-hash-map",
- "log 0.4.16",
+ "log",
  "memory-db",
  "parity-scale-codec",
  "prettytable-rs",
@@ -2249,7 +2192,7 @@ dependencies = [
  "frame-election-provider-support",
  "frame-support",
  "parity-scale-codec",
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate",
  "proc-macro2",
  "quote",
  "scale-info",
@@ -2336,7 +2279,7 @@ dependencies = [
  "frame-system",
  "impl-trait-for-tuples",
  "k256",
- "log 0.4.16",
+ "log",
  "once_cell",
  "parity-scale-codec",
  "parity-util-mem",
@@ -2344,7 +2287,7 @@ dependencies = [
  "pretty_assertions",
  "scale-info",
  "serde",
- "smallvec 1.8.0",
+ "smallvec",
  "sp-arithmetic",
  "sp-core",
  "sp-core-hashing-proc-macro",
@@ -2374,7 +2317,7 @@ name = "frame-support-procedural-tools"
 version = "4.0.0-dev"
 dependencies = [
  "frame-support-procedural-tools-derive",
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate",
  "proc-macro2",
  "quote",
  "syn",
@@ -2440,7 +2383,7 @@ version = "4.0.0-dev"
 dependencies = [
  "criterion",
  "frame-support",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "scale-info",
  "serde",
@@ -2495,7 +2438,7 @@ dependencies = [
  "lazy_static",
  "libc",
  "libloading 0.5.2",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -2505,7 +2448,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
 dependencies = [
  "libc",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -2520,34 +2463,12 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
 
-[[package]]
-name = "fuchsia-zircon"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
-dependencies = [
- "bitflags",
- "fuchsia-zircon-sys",
-]
-
-[[package]]
-name = "fuchsia-zircon-sys"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
-
 [[package]]
 name = "funty"
 version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
 
-[[package]]
-name = "futures"
-version = "0.1.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
-
 [[package]]
 name = "futures"
 version = "0.3.21"
@@ -2658,7 +2579,6 @@ version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
 dependencies = [
- "futures 0.1.31",
  "futures-channel",
  "futures-core",
  "futures-io",
@@ -2707,7 +2627,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
 dependencies = [
  "typenum",
- "version_check 0.9.2",
+ "version_check",
 ]
 
 [[package]]
@@ -2775,8 +2695,8 @@ dependencies = [
  "bitflags",
  "libc",
  "libgit2-sys",
- "log 0.4.16",
- "url 2.2.1",
+ "log",
+ "url",
 ]
 
 [[package]]
@@ -2794,7 +2714,7 @@ dependencies = [
  "aho-corasick",
  "bstr",
  "fnv",
- "log 0.4.16",
+ "log",
  "regex",
 ]
 
@@ -2828,7 +2748,7 @@ version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f072413d126e57991455e0a922b31e4c8ba7c2ffbebf6b78b4f8521397d65cd"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "fnv",
  "futures-core",
  "futures-sink",
@@ -2853,7 +2773,7 @@ version = "4.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "99d6a30320f094710245150395bc763ad23128d6a1ebbad7594dc4164b62c56b"
 dependencies = [
- "log 0.4.16",
+ "log",
  "pest",
  "pest_derive",
  "quick-error 2.0.0",
@@ -2986,7 +2906,7 @@ checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
 dependencies = [
  "libc",
  "match_cfg",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -2995,7 +2915,7 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "fnv",
  "itoa 0.4.8",
 ]
@@ -3006,7 +2926,7 @@ version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "60daa14be0e0786db0f03a9e57cb404c9d756eed2b6c62b9ea98ec5743ec75a9"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "http",
  "pin-project-lite 0.2.6",
 ]
@@ -3038,32 +2958,13 @@ version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
-[[package]]
-name = "hyper"
-version = "0.10.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273"
-dependencies = [
- "base64 0.9.3",
- "httparse",
- "language-tags",
- "log 0.3.9",
- "mime",
- "num_cpus",
- "time",
- "traitobject",
- "typeable",
- "unicase 1.4.2",
- "url 1.7.2",
-]
-
 [[package]]
 name = "hyper"
 version = "0.14.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "futures-channel",
  "futures-core",
  "futures-util",
@@ -3089,8 +2990,8 @@ checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64"
 dependencies = [
  "ct-logs",
  "futures-util",
- "hyper 0.14.16",
- "log 0.4.16",
+ "hyper",
+ "log",
  "rustls 0.19.1",
  "rustls-native-certs 0.5.0",
  "tokio",
@@ -3098,36 +2999,12 @@ dependencies = [
  "webpki 0.21.4",
 ]
 
-[[package]]
-name = "hyper-tls"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
-dependencies = [
- "bytes 1.1.0",
- "hyper 0.14.16",
- "native-tls",
- "tokio",
- "tokio-native-tls",
-]
-
 [[package]]
 name = "ident_case"
 version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
 
-[[package]]
-name = "idna"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
-dependencies = [
- "matches",
- "unicode-bidi",
- "unicode-normalization",
-]
-
 [[package]]
 name = "idna"
 version = "0.2.3"
@@ -3146,7 +3023,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9"
 dependencies = [
  "libc",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -3158,10 +3035,10 @@ dependencies = [
  "async-io",
  "core-foundation",
  "fnv",
- "futures 0.3.21",
+ "futures",
  "if-addrs",
  "ipnet",
- "log 0.4.16",
+ "log",
  "rtnetlink",
  "system-configuration",
  "windows",
@@ -3231,15 +3108,6 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec58677acfea8a15352d42fc87d11d63596ade9239e0a7c9352914417515dbe6"
 
-[[package]]
-name = "iovec"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
-dependencies = [
- "libc",
-]
-
 [[package]]
 name = "ip_network"
 version = "0.4.1"
@@ -3254,7 +3122,7 @@ checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98"
 dependencies = [
  "socket2 0.4.4",
  "widestring",
- "winapi 0.3.9",
+ "winapi",
  "winreg",
 ]
 
@@ -3303,161 +3171,28 @@ dependencies = [
  "wasm-bindgen",
 ]
 
-[[package]]
-name = "jsonrpc-client-transports"
-version = "18.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2b99d4207e2a04fb4581746903c2bb7eb376f88de9c699d0f3e10feeac0cd3a"
-dependencies = [
- "derive_more",
- "futures 0.3.21",
- "hyper 0.14.16",
- "hyper-tls",
- "jsonrpc-core",
- "jsonrpc-pubsub",
- "log 0.4.16",
- "serde",
- "serde_json",
- "tokio",
- "url 1.7.2",
- "websocket",
-]
-
-[[package]]
-name = "jsonrpc-core"
-version = "18.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb"
-dependencies = [
- "futures 0.3.21",
- "futures-executor",
- "futures-util",
- "log 0.4.16",
- "serde",
- "serde_derive",
- "serde_json",
-]
-
-[[package]]
-name = "jsonrpc-core-client"
-version = "18.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b51da17abecbdab3e3d4f26b01c5ec075e88d3abe3ab3b05dc9aa69392764ec0"
-dependencies = [
- "futures 0.3.21",
- "jsonrpc-client-transports",
-]
-
-[[package]]
-name = "jsonrpc-derive"
-version = "18.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2"
-dependencies = [
- "proc-macro-crate 0.1.5",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "jsonrpc-http-server"
-version = "18.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff"
-dependencies = [
- "futures 0.3.21",
- "hyper 0.14.16",
- "jsonrpc-core",
- "jsonrpc-server-utils",
- "log 0.4.16",
- "net2",
- "parking_lot 0.11.2",
- "unicase 2.6.0",
-]
-
-[[package]]
-name = "jsonrpc-ipc-server"
-version = "18.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "382bb0206323ca7cda3dcd7e245cea86d37d02457a02a975e3378fb149a48845"
-dependencies = [
- "futures 0.3.21",
- "jsonrpc-core",
- "jsonrpc-server-utils",
- "log 0.4.16",
- "parity-tokio-ipc",
- "parking_lot 0.11.2",
- "tower-service",
-]
-
-[[package]]
-name = "jsonrpc-pubsub"
-version = "18.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240f87695e6c6f62fb37f05c02c04953cf68d6408b8c1c89de85c7a0125b1011"
-dependencies = [
- "futures 0.3.21",
- "jsonrpc-core",
- "lazy_static",
- "log 0.4.16",
- "parking_lot 0.11.2",
- "rand 0.7.3",
- "serde",
-]
-
-[[package]]
-name = "jsonrpc-server-utils"
-version = "18.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4"
-dependencies = [
- "bytes 1.1.0",
- "futures 0.3.21",
- "globset",
- "jsonrpc-core",
- "lazy_static",
- "log 0.4.16",
- "tokio",
- "tokio-stream",
- "tokio-util 0.6.7",
- "unicase 2.6.0",
-]
-
-[[package]]
-name = "jsonrpc-ws-server"
-version = "18.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f892c7d766369475ab7b0669f417906302d7c0fb521285c0a0c92e52e7c8e946"
-dependencies = [
- "futures 0.3.21",
- "jsonrpc-core",
- "jsonrpc-server-utils",
- "log 0.4.16",
- "parity-ws",
- "parking_lot 0.11.2",
- "slab",
-]
-
 [[package]]
 name = "jsonrpsee"
-version = "0.10.1"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91dc760c341fa81173f9a434931aaf32baad5552b0230cc6c93e8fb7eaad4c19"
+checksum = "ad6f9ff3481f3069c92474b697c104502f7e9191d29b34bfa38ae9a19415f1cd"
 dependencies = [
  "jsonrpsee-core",
+ "jsonrpsee-http-server",
  "jsonrpsee-proc-macros",
  "jsonrpsee-types",
  "jsonrpsee-ws-client",
+ "jsonrpsee-ws-server",
+ "tracing",
 ]
 
 [[package]]
 name = "jsonrpsee-client-transport"
-version = "0.10.1"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "765f7a36d5087f74e3b3b47805c2188fef8eb54afcb587b078d9f8ebfe9c7220"
+checksum = "4358e100faf43b2f3b7b0ecf0ad4ce3e6275fe12fda8428dedda2979751dd184"
 dependencies = [
- "futures 0.3.21",
+ "futures-util",
  "http",
  "jsonrpsee-core",
  "jsonrpsee-types",
@@ -3474,18 +3209,22 @@ dependencies = [
 
 [[package]]
 name = "jsonrpsee-core"
-version = "0.10.1"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82ef77ecd20c2254d54f5da8c0738eacca61e6b6511268a8f2753e3148c6c706"
+checksum = "8e1d26ab3868749d6f716345a5fbd3334a100c0709fe464bd9189ee9d78adcde"
 dependencies = [
  "anyhow",
  "arrayvec 0.7.1",
+ "async-lock",
  "async-trait",
  "beef",
  "futures-channel",
+ "futures-timer",
  "futures-util",
- "hyper 0.14.16",
+ "hyper",
  "jsonrpsee-types",
+ "parking_lot 0.12.0",
+ "rand 0.8.4",
  "rustc-hash",
  "serde",
  "serde_json",
@@ -3495,13 +3234,32 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "jsonrpsee-http-server"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee87f19a7a01a55248fc4b4861d822331c4fd60151d99e7ac9c6771999132671"
+dependencies = [
+ "futures-channel",
+ "futures-util",
+ "globset",
+ "hyper",
+ "jsonrpsee-core",
+ "jsonrpsee-types",
+ "lazy_static",
+ "serde_json",
+ "tokio",
+ "tracing",
+ "unicase",
+]
+
 [[package]]
 name = "jsonrpsee-proc-macros"
-version = "0.10.1"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7291c72805bc7d413b457e50d8ef3e87aa554da65ecbbc278abb7dfc283e7f0"
+checksum = "b75da57d54817577801c2f7a1b638610819dfd86f0470c21a2af81b06eb41ba6"
 dependencies = [
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate",
  "proc-macro2",
  "quote",
  "syn",
@@ -3509,9 +3267,9 @@ dependencies = [
 
 [[package]]
 name = "jsonrpsee-types"
-version = "0.10.1"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38b6aa52f322cbf20c762407629b8300f39bcc0cf0619840d9252a2f65fd2dd9"
+checksum = "f5fe5a629443d17a30ff564881ba68881a710fd7eb02a538087b0bc51cb4962c"
 dependencies = [
  "anyhow",
  "beef",
@@ -3523,15 +3281,32 @@ dependencies = [
 
 [[package]]
 name = "jsonrpsee-ws-client"
-version = "0.10.1"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd66d18bab78d956df24dd0d2e41e4c00afbb818fda94a98264bdd12ce8506ac"
+checksum = "ba31eb2b9a4b73d8833f53fe55e579516289f8b31adb6104b3dbc629755acf7d"
 dependencies = [
  "jsonrpsee-client-transport",
  "jsonrpsee-core",
  "jsonrpsee-types",
 ]
 
+[[package]]
+name = "jsonrpsee-ws-server"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "179fe584af5c0145f922c581770d073c661a514ae6cdfa5b1a0bce41fdfdf646"
+dependencies = [
+ "futures-channel",
+ "futures-util",
+ "jsonrpsee-core",
+ "jsonrpsee-types",
+ "serde_json",
+ "soketto",
+ "tokio",
+ "tokio-util 0.7.1",
+ "tracing",
+]
+
 [[package]]
 name = "k256"
 version = "0.10.4"
@@ -3561,23 +3336,13 @@ dependencies = [
  "tiny-keccak",
 ]
 
-[[package]]
-name = "kernel32-sys"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-dependencies = [
- "winapi 0.2.8",
- "winapi-build",
-]
-
 [[package]]
 name = "kv-log-macro"
 version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
 dependencies = [
- "log 0.4.16",
+ "log",
 ]
 
 [[package]]
@@ -3587,7 +3352,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a301d8ecb7989d4a6e2c57a49baca77d353bdbf879909debe3f375fe25d61f86"
 dependencies = [
  "parity-util-mem",
- "smallvec 1.8.0",
+ "smallvec",
 ]
 
 [[package]]
@@ -3609,22 +3374,16 @@ checksum = "ca7fbdfd71cd663dceb0faf3367a99f8cf724514933e9867cec4995b6027cbc1"
 dependencies = [
  "fs-swap",
  "kvdb",
- "log 0.4.16",
+ "log",
  "num_cpus",
  "owning_ref",
  "parity-util-mem",
  "parking_lot 0.12.0",
  "regex",
  "rocksdb",
- "smallvec 1.8.0",
+ "smallvec",
 ]
 
-[[package]]
-name = "language-tags"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
-
 [[package]]
 name = "lazy_static"
 version = "1.4.0"
@@ -3651,9 +3410,9 @@ checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
 
 [[package]]
 name = "libgit2-sys"
-version = "0.13.2+1.4.2"
+version = "0.13.3+1.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a42de9a51a5c12e00fc0e4ca6bc2ea43582fc6418488e8f615e905d886f258b"
+checksum = "c24d36c3ac9b9996a2418d6bf428cc0bc5d1a814a84303fc60986088c5ed60de"
 dependencies = [
  "cc",
  "libc",
@@ -3668,7 +3427,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753"
 dependencies = [
  "cc",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -3678,7 +3437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
 dependencies = [
  "cfg-if 1.0.0",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -3694,8 +3453,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "475ce2ac4a9727e53a519f6ee05b38abfcba8f0d39c4d24f103d184e36fd5b0f"
 dependencies = [
  "atomic",
- "bytes 1.1.0",
- "futures 0.3.21",
+ "bytes",
+ "futures",
  "futures-timer",
  "getrandom 0.2.3",
  "instant",
@@ -3729,7 +3488,7 @@ dependencies = [
  "parking_lot 0.12.0",
  "pin-project 1.0.10",
  "rand 0.7.3",
- "smallvec 1.8.0",
+ "smallvec",
 ]
 
 [[package]]
@@ -3739,13 +3498,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a13b690e65046af6a09c0b27bd9508fa1cab0efce889de74b0b643b9d2a98f9a"
 dependencies = [
  "async-trait",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "instant",
  "libp2p-core",
  "libp2p-request-response",
  "libp2p-swarm",
- "log 0.4.16",
+ "log",
  "prost",
  "prost-build",
  "rand 0.8.4",
@@ -3762,12 +3521,12 @@ dependencies = [
  "ed25519-dalek",
  "either",
  "fnv",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "instant",
  "lazy_static",
  "libsecp256k1",
- "log 0.4.16",
+ "log",
  "multiaddr",
  "multihash",
  "multistream-select",
@@ -3779,7 +3538,7 @@ dependencies = [
  "ring",
  "rw-stream-sink",
  "sha2 0.10.2",
- "smallvec 1.8.0",
+ "smallvec",
  "thiserror",
  "unsigned-varint",
  "void",
@@ -3793,7 +3552,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6b1d37f042f748e224f04785d0e987ae09a2aa518d6401d82d412dad83e360ed"
 dependencies = [
  "flate2",
- "futures 0.3.21",
+ "futures",
  "libp2p-core",
 ]
 
@@ -3804,10 +3563,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "066e33e854e10b5c93fc650458bf2179c7e0d143db260b0963e44a94859817f1"
 dependencies = [
  "async-std-resolver",
- "futures 0.3.21",
+ "futures",
  "libp2p-core",
- "log 0.4.16",
- "smallvec 1.8.0",
+ "log",
+ "smallvec",
  "trust-dns-resolver",
 ]
 
@@ -3819,14 +3578,14 @@ checksum = "733d3ea6ebe7a7a85df2bc86678b93f24b015fae5fe3b3acc4c400e795a55d2d"
 dependencies = [
  "cuckoofilter",
  "fnv",
- "futures 0.3.21",
+ "futures",
  "libp2p-core",
  "libp2p-swarm",
- "log 0.4.16",
+ "log",
  "prost",
  "prost-build",
  "rand 0.7.3",
- "smallvec 1.8.0",
+ "smallvec",
 ]
 
 [[package]]
@@ -3836,23 +3595,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a90c989a7c0969c2ab63e898da9bc735e3be53fb4f376e9c045ce516bcc9f928"
 dependencies = [
  "asynchronous-codec",
- "base64 0.13.0",
+ "base64",
  "byteorder",
- "bytes 1.1.0",
+ "bytes",
  "fnv",
- "futures 0.3.21",
+ "futures",
  "hex_fmt",
  "instant",
  "libp2p-core",
  "libp2p-swarm",
- "log 0.4.16",
+ "log",
  "prometheus-client",
  "prost",
  "prost-build",
  "rand 0.7.3",
  "regex",
  "sha2 0.10.2",
- "smallvec 1.8.0",
+ "smallvec",
  "unsigned-varint",
  "wasm-timer",
 ]
@@ -3863,15 +3622,15 @@ version = "0.35.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c5ef5a5b57904c7c33d6713ef918d239dc6b7553458f3475d87f8a18e9c651c8"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "libp2p-core",
  "libp2p-swarm",
- "log 0.4.16",
+ "log",
  "lru",
  "prost",
  "prost-build",
- "smallvec 1.8.0",
+ "smallvec",
 ]
 
 [[package]]
@@ -3882,20 +3641,20 @@ checksum = "564e6bd64d177446399ed835b9451a8825b07929d6daa6a94e6405592974725e"
 dependencies = [
  "arrayvec 0.5.2",
  "asynchronous-codec",
- "bytes 1.1.0",
+ "bytes",
  "either",
  "fnv",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "instant",
  "libp2p-core",
  "libp2p-swarm",
- "log 0.4.16",
+ "log",
  "prost",
  "prost-build",
  "rand 0.7.3",
  "sha2 0.10.2",
- "smallvec 1.8.0",
+ "smallvec",
  "thiserror",
  "uint",
  "unsigned-varint",
@@ -3911,14 +3670,14 @@ dependencies = [
  "async-io",
  "data-encoding",
  "dns-parser",
- "futures 0.3.21",
+ "futures",
  "if-watch",
  "lazy_static",
  "libp2p-core",
  "libp2p-swarm",
- "log 0.4.16",
+ "log",
  "rand 0.8.4",
- "smallvec 1.8.0",
+ "smallvec",
  "socket2 0.4.4",
  "void",
 ]
@@ -3946,14 +3705,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "442eb0c9fff0bf22a34f015724b4143ce01877e079ed0963c722d94c07c72160"
 dependencies = [
  "asynchronous-codec",
- "bytes 1.1.0",
- "futures 0.3.21",
+ "bytes",
+ "futures",
  "libp2p-core",
- "log 0.4.16",
+ "log",
  "nohash-hasher",
  "parking_lot 0.12.0",
  "rand 0.7.3",
- "smallvec 1.8.0",
+ "smallvec",
  "unsigned-varint",
 ]
 
@@ -3963,12 +3722,12 @@ version = "0.35.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9dd7e0c94051cda67123be68cf6b65211ba3dde7277be9068412de3e7ffd63ef"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "curve25519-dalek 3.0.2",
- "futures 0.3.21",
+ "futures",
  "lazy_static",
  "libp2p-core",
- "log 0.4.16",
+ "log",
  "prost",
  "prost-build",
  "rand 0.8.4",
@@ -3985,12 +3744,12 @@ version = "0.35.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bf57a3c2e821331dda9fe612d4654d676ab6e33d18d9434a18cced72630df6ad"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "instant",
  "libp2p-core",
  "libp2p-swarm",
- "log 0.4.16",
+ "log",
  "rand 0.7.3",
  "void",
 ]
@@ -4002,10 +3761,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "962c0fb0e7212fb96a69b87f2d09bcefd317935239bdc79cda900e7a8897a3fe"
 dependencies = [
  "asynchronous-codec",
- "bytes 1.1.0",
- "futures 0.3.21",
+ "bytes",
+ "futures",
  "libp2p-core",
- "log 0.4.16",
+ "log",
  "prost",
  "prost-build",
  "unsigned-varint",
@@ -4018,8 +3777,8 @@ version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0f1a458bbda880107b5b36fcb9b5a1ef0c329685da0e203ed692a8ebe64cc92c"
 dependencies = [
- "futures 0.3.21",
- "log 0.4.16",
+ "futures",
+ "log",
  "pin-project 1.0.10",
  "rand 0.7.3",
  "salsa20",
@@ -4033,19 +3792,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3aa754cb7bccef51ebc3c458c6bbcef89d83b578a9925438389be841527d408f"
 dependencies = [
  "asynchronous-codec",
- "bytes 1.1.0",
+ "bytes",
  "either",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "instant",
  "libp2p-core",
  "libp2p-swarm",
- "log 0.4.16",
+ "log",
  "pin-project 1.0.10",
  "prost",
  "prost-build",
  "rand 0.8.4",
- "smallvec 1.8.0",
+ "smallvec",
  "static_assertions",
  "thiserror",
  "unsigned-varint",
@@ -4060,12 +3819,12 @@ checksum = "bbd0baab894c5b84da510b915d53264d566c3c35889f09931fe9edbd2a773bee"
 dependencies = [
  "asynchronous-codec",
  "bimap",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "instant",
  "libp2p-core",
  "libp2p-swarm",
- "log 0.4.16",
+ "log",
  "prost",
  "prost-build",
  "rand 0.8.4",
@@ -4082,14 +3841,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b5e6a6fc6c9ad95661f46989473b34bd2993d14a4de497ff3b2668a910d4b869"
 dependencies = [
  "async-trait",
- "bytes 1.1.0",
- "futures 0.3.21",
+ "bytes",
+ "futures",
  "instant",
  "libp2p-core",
  "libp2p-swarm",
- "log 0.4.16",
+ "log",
  "rand 0.7.3",
- "smallvec 1.8.0",
+ "smallvec",
  "unsigned-varint",
 ]
 
@@ -4101,14 +3860,14 @@ checksum = "8f0c69ad9e8f7c5fc50ad5ad9c7c8b57f33716532a2b623197f69f93e374d14c"
 dependencies = [
  "either",
  "fnv",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "instant",
  "libp2p-core",
- "log 0.4.16",
+ "log",
  "pin-project 1.0.10",
  "rand 0.7.3",
- "smallvec 1.8.0",
+ "smallvec",
  "thiserror",
  "void",
 ]
@@ -4130,13 +3889,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "193447aa729c85aac2376828df76d171c1a589c9e6b58fcc7f9d9a020734122c"
 dependencies = [
  "async-io",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "if-watch",
  "ipnet",
  "libc",
  "libp2p-core",
- "log 0.4.16",
+ "log",
  "socket2 0.4.4",
 ]
 
@@ -4147,9 +3906,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24bdab114f7f2701757d6541266e1131b429bbae382008f207f2114ee4222dcb"
 dependencies = [
  "async-std",
- "futures 0.3.21",
+ "futures",
  "libp2p-core",
- "log 0.4.16",
+ "log",
 ]
 
 [[package]]
@@ -4158,7 +3917,7 @@ version = "0.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4f6ea0f84a967ef59a16083f222c18115ae2e91db69809dce275df62e101b279"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "js-sys",
  "libp2p-core",
  "parity-send-wrapper",
@@ -4173,14 +3932,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c932834c3754501c368d1bf3d0fb458487a642b90fc25df082a3a2f3d3b32e37"
 dependencies = [
  "either",
- "futures 0.3.21",
+ "futures",
  "futures-rustls",
  "libp2p-core",
- "log 0.4.16",
+ "log",
  "quicksink",
  "rw-stream-sink",
  "soketto",
- "url 2.2.1",
+ "url",
  "webpki-roots",
 ]
 
@@ -4190,7 +3949,7 @@ version = "0.36.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "be902ebd89193cd020e89e89107726a38cfc0d16d18f613f4a37d046e92c7517"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "libp2p-core",
  "parking_lot 0.12.0",
  "thiserror",
@@ -4219,7 +3978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b0452aac8bab02242429380e9b2f94ea20cea2b37e2c1777a1358799bbe97f37"
 dependencies = [
  "arrayref",
- "base64 0.13.0",
+ "base64",
  "digest 0.9.0",
  "hmac-drbg",
  "libsecp256k1-core",
@@ -4321,15 +4080,6 @@ dependencies = [
  "paste 0.1.18",
 ]
 
-[[package]]
-name = "lock_api"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
-dependencies = [
- "scopeguard",
-]
-
 [[package]]
 name = "lock_api"
 version = "0.4.6"
@@ -4339,15 +4089,6 @@ dependencies = [
  "scopeguard",
 ]
 
-[[package]]
-name = "log"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
-dependencies = [
- "log 0.4.16",
-]
-
 [[package]]
 name = "log"
 version = "0.4.16"
@@ -4462,12 +4203,6 @@ dependencies = [
  "rawpointer",
 ]
 
-[[package]]
-name = "maybe-uninit"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
-
 [[package]]
 name = "memchr"
 version = "2.4.1"
@@ -4481,7 +4216,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
 dependencies = [
  "libc",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -4540,15 +4275,6 @@ dependencies = [
  "zeroize",
 ]
 
-[[package]]
-name = "mime"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
-dependencies = [
- "log 0.3.9",
-]
-
 [[package]]
 name = "minimal-lexical"
 version = "0.2.1"
@@ -4565,25 +4291,6 @@ dependencies = [
  "autocfg 1.0.1",
 ]
 
-[[package]]
-name = "mio"
-version = "0.6.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
-dependencies = [
- "cfg-if 0.1.10",
- "fuchsia-zircon",
- "fuchsia-zircon-sys",
- "iovec",
- "kernel32-sys",
- "libc",
- "log 0.4.16",
- "miow 0.2.2",
- "net2",
- "slab",
- "winapi 0.2.8",
-]
-
 [[package]]
 name = "mio"
 version = "0.8.0"
@@ -4591,34 +4298,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2"
 dependencies = [
  "libc",
- "log 0.4.16",
- "miow 0.3.6",
+ "log",
+ "miow",
  "ntapi",
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "mio-extras"
-version = "2.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
-dependencies = [
- "lazycell",
- "log 0.4.16",
- "mio 0.6.23",
- "slab",
-]
-
-[[package]]
-name = "miow"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
-dependencies = [
- "kernel32-sys",
- "net2",
- "winapi 0.2.8",
- "ws2_32-sys",
+ "winapi",
 ]
 
 [[package]]
@@ -4628,7 +4311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
 dependencies = [
  "socket2 0.3.19",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -4648,11 +4331,11 @@ dependencies = [
  "byteorder",
  "data-encoding",
  "multihash",
- "percent-encoding 2.1.0",
+ "percent-encoding",
  "serde",
  "static_assertions",
  "unsigned-varint",
- "url 2.2.1",
+ "url",
 ]
 
 [[package]]
@@ -4689,7 +4372,7 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd"
 dependencies = [
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate",
  "proc-macro-error",
  "proc-macro2",
  "quote",
@@ -4709,11 +4392,11 @@ version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "363a84be6453a70e63513660f4894ef815daf88e3356bffcda9ca27d810ce83b"
 dependencies = [
- "bytes 1.1.0",
- "futures 0.3.21",
- "log 0.4.16",
+ "bytes",
+ "futures",
+ "log",
  "pin-project 1.0.10",
- "smallvec 1.8.0",
+ "smallvec",
  "unsigned-varint",
 ]
 
@@ -4755,35 +4438,6 @@ dependencies = [
  "rand 0.8.4",
 ]
 
-[[package]]
-name = "native-tls"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d"
-dependencies = [
- "lazy_static",
- "libc",
- "log 0.4.16",
- "openssl",
- "openssl-probe",
- "openssl-sys",
- "schannel",
- "security-framework",
- "security-framework-sys",
- "tempfile",
-]
-
-[[package]]
-name = "net2"
-version = "0.2.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
-dependencies = [
- "cfg-if 0.1.10",
- "libc",
- "winapi 0.3.9",
-]
-
 [[package]]
 name = "netlink-packet-core"
 version = "0.4.2"
@@ -4828,9 +4482,9 @@ version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ef8785b8141e8432aa45fceb922a7e876d7da3fad37fa7e7ec702ace3aa0826b"
 dependencies = [
- "bytes 1.1.0",
- "futures 0.3.21",
- "log 0.4.16",
+ "bytes",
+ "futures",
+ "log",
  "netlink-packet-core",
  "netlink-sys",
  "tokio",
@@ -4843,10 +4497,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3e4c9f9547a08241bee7b6558b9b98e1f290d187de8b7cfca2bbb4937bcaa8f8"
 dependencies = [
  "async-io",
- "bytes 1.1.0",
- "futures 0.3.21",
+ "bytes",
+ "futures",
  "libc",
- "log 0.4.16",
+ "log",
 ]
 
 [[package]]
@@ -4882,13 +4536,13 @@ dependencies = [
  "clap 3.1.6",
  "derive_more",
  "fs_extra",
- "futures 0.3.21",
+ "futures",
  "hash-db",
  "hex",
  "kvdb",
  "kvdb-rocksdb",
  "lazy_static",
- "log 0.4.16",
+ "log",
  "node-primitives",
  "node-runtime",
  "node-testing",
@@ -4924,9 +4578,10 @@ dependencies = [
  "frame-benchmarking-cli",
  "frame-system",
  "frame-system-rpc-runtime-api",
- "futures 0.3.21",
+ "futures",
  "hex-literal",
- "log 0.4.16",
+ "jsonrpsee",
+ "log",
  "nix 0.23.1",
  "node-executor",
  "node-inspect",
@@ -5003,7 +4658,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "futures 0.3.21",
+ "futures",
  "node-primitives",
  "node-runtime",
  "node-testing",
@@ -5060,7 +4715,7 @@ dependencies = [
 name = "node-rpc"
 version = "3.0.0-dev"
 dependencies = [
- "jsonrpc-core",
+ "jsonrpsee",
  "node-primitives",
  "pallet-contracts-rpc",
  "pallet-mmr-rpc",
@@ -5100,7 +4755,7 @@ dependencies = [
  "frame-system-rpc-runtime-api",
  "frame-try-runtime",
  "hex-literal",
- "log 0.4.16",
+ "log",
  "node-primitives",
  "pallet-asset-tx-payment",
  "pallet-assets",
@@ -5195,7 +4850,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-benchmarking-cli",
  "frame-system",
- "jsonrpc-core",
+ "jsonrpsee",
  "node-template-runtime",
  "pallet-transaction-payment",
  "pallet-transaction-payment-rpc",
@@ -5272,8 +4927,8 @@ version = "3.0.0-dev"
 dependencies = [
  "frame-system",
  "fs_extra",
- "futures 0.3.21",
- "log 0.4.16",
+ "futures",
+ "log",
  "node-executor",
  "node-primitives",
  "node-runtime",
@@ -5320,7 +4975,7 @@ checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
 dependencies = [
  "memchr",
  "minimal-lexical",
- "version_check 0.9.2",
+ "version_check",
 ]
 
 [[package]]
@@ -5329,7 +4984,7 @@ version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
 dependencies = [
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -5462,39 +5117,12 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
 
-[[package]]
-name = "openssl"
-version = "0.10.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "549430950c79ae24e6d02e0b7404534ecf311d94cc9f861e9e4020187d13d885"
-dependencies = [
- "bitflags",
- "cfg-if 1.0.0",
- "foreign-types",
- "libc",
- "once_cell",
- "openssl-sys",
-]
-
 [[package]]
 name = "openssl-probe"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
 
-[[package]]
-name = "openssl-sys"
-version = "0.9.65"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a7907e3bfa08bb85105209cdfcb6c63d109f8f6c1ed6ca318fff5c1853fbc1d"
-dependencies = [
- "autocfg 1.0.1",
- "cc",
- "libc",
- "pkg-config",
- "vcpkg",
-]
-
 [[package]]
 name = "os_str_bytes"
 version = "6.0.0"
@@ -5510,7 +5138,7 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
 dependencies = [
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -5536,7 +5164,7 @@ dependencies = [
  "scale-info",
  "serde",
  "serde_json",
- "smallvec 1.8.0",
+ "smallvec",
  "sp-core",
  "sp-io",
  "sp-runtime",
@@ -5633,7 +5261,7 @@ dependencies = [
  "frame-election-provider-support",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-authorship",
  "pallet-balances",
  "pallet-offences",
@@ -5662,7 +5290,7 @@ dependencies = [
  "frame-election-provider-support",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-balances",
  "parity-scale-codec",
  "scale-info",
@@ -5690,7 +5318,7 @@ dependencies = [
  "frame-election-provider-support",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-bags-list",
  "pallet-staking",
  "remote-externalities",
@@ -5699,7 +5327,6 @@ dependencies = [
  "sp-std",
  "sp-storage",
  "sp-tracing",
- "tokio",
 ]
 
 [[package]]
@@ -5709,7 +5336,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-transaction-payment",
  "parity-scale-codec",
  "scale-info",
@@ -5747,7 +5374,7 @@ dependencies = [
  "frame-system",
  "hex",
  "hex-literal",
- "log 0.4.16",
+ "log",
  "pallet-beefy",
  "pallet-mmr",
  "pallet-session",
@@ -5768,7 +5395,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-balances",
  "pallet-treasury",
  "parity-scale-codec",
@@ -5786,7 +5413,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-balances",
  "pallet-bounties",
  "pallet-treasury",
@@ -5805,7 +5432,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "scale-info",
  "sp-core",
@@ -5825,7 +5452,7 @@ dependencies = [
  "frame-support",
  "frame-system",
  "hex-literal",
- "log 0.4.16",
+ "log",
  "pallet-balances",
  "pallet-contracts-primitives",
  "pallet-contracts-proc-macro",
@@ -5838,7 +5465,7 @@ dependencies = [
  "rand_pcg 0.3.1",
  "scale-info",
  "serde",
- "smallvec 1.8.0",
+ "smallvec",
  "sp-core",
  "sp-io",
  "sp-keystore",
@@ -5877,9 +5504,7 @@ dependencies = [
 name = "pallet-contracts-rpc"
 version = "4.0.0-dev"
 dependencies = [
- "jsonrpc-core",
- "jsonrpc-core-client",
- "jsonrpc-derive",
+ "jsonrpsee",
  "pallet-contracts-primitives",
  "pallet-contracts-rpc-runtime-api",
  "parity-scale-codec",
@@ -5949,7 +5574,7 @@ dependencies = [
  "frame-election-provider-support",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-balances",
  "pallet-election-provider-support-benchmarking",
  "parity-scale-codec",
@@ -5986,7 +5611,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-balances",
  "parity-scale-codec",
  "scale-info",
@@ -6005,7 +5630,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-balances",
  "parity-scale-codec",
  "scale-info",
@@ -6022,7 +5647,7 @@ dependencies = [
  "frame-support",
  "frame-system",
  "lite-json",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "scale-info",
  "sp-core",
@@ -6073,7 +5698,7 @@ dependencies = [
  "frame-election-provider-support",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-authorship",
  "pallet-balances",
  "pallet-offences",
@@ -6118,7 +5743,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-authorship",
  "pallet-session",
  "parity-scale-codec",
@@ -6172,7 +5797,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "scale-info",
  "sp-core",
@@ -6204,9 +5829,7 @@ dependencies = [
 name = "pallet-mmr-rpc"
 version = "3.0.0"
 dependencies = [
- "jsonrpc-core",
- "jsonrpc-core-client",
- "jsonrpc-derive",
+ "jsonrpsee",
  "parity-scale-codec",
  "serde",
  "serde_json",
@@ -6254,7 +5877,7 @@ version = "4.0.0-dev"
 dependencies = [
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "scale-info",
  "sp-core",
@@ -6309,7 +5932,7 @@ version = "4.0.0-dev"
 dependencies = [
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-balances",
  "parity-scale-codec",
  "scale-info",
@@ -6454,7 +6077,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-preimage",
  "parity-scale-codec",
  "scale-info",
@@ -6487,7 +6110,7 @@ dependencies = [
  "frame-support",
  "frame-system",
  "impl-trait-for-tuples",
- "log 0.4.16",
+ "log",
  "pallet-timestamp",
  "parity-scale-codec",
  "scale-info",
@@ -6548,7 +6171,7 @@ dependencies = [
  "frame-election-provider-support",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-authorship",
  "pallet-bags-list",
  "pallet-balances",
@@ -6574,7 +6197,7 @@ dependencies = [
 name = "pallet-staking-reward-curve"
 version = "4.0.0-dev"
 dependencies = [
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate",
  "proc-macro2",
  "quote",
  "sp-runtime",
@@ -6585,7 +6208,7 @@ dependencies = [
 name = "pallet-staking-reward-fn"
 version = "4.0.0-dev"
 dependencies = [
- "log 0.4.16",
+ "log",
  "sp-arithmetic",
 ]
 
@@ -6596,7 +6219,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-balances",
  "parity-scale-codec",
  "parking_lot 0.12.0",
@@ -6649,7 +6272,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "scale-info",
  "sp-core",
@@ -6667,7 +6290,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-balances",
  "pallet-treasury",
  "parity-scale-codec",
@@ -6691,7 +6314,7 @@ dependencies = [
  "scale-info",
  "serde",
  "serde_json",
- "smallvec 1.8.0",
+ "smallvec",
  "sp-core",
  "sp-io",
  "sp-runtime",
@@ -6702,9 +6325,7 @@ dependencies = [
 name = "pallet-transaction-payment-rpc"
 version = "4.0.0-dev"
 dependencies = [
- "jsonrpc-core",
- "jsonrpc-core-client",
- "jsonrpc-derive",
+ "jsonrpsee",
  "pallet-transaction-payment-rpc-runtime-api",
  "parity-scale-codec",
  "sp-api",
@@ -6769,7 +6390,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-balances",
  "parity-scale-codec",
  "scale-info",
@@ -6802,7 +6423,7 @@ dependencies = [
  "frame-benchmarking",
  "frame-support",
  "frame-system",
- "log 0.4.16",
+ "log",
  "pallet-balances",
  "parity-scale-codec",
  "scale-info",
@@ -6841,7 +6462,7 @@ dependencies = [
  "fs2",
  "hex",
  "libc",
- "log 0.4.16",
+ "log",
  "lz4",
  "memmap2 0.2.1",
  "parking_lot 0.11.2",
@@ -6869,7 +6490,7 @@ version = "3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2c6e626dc84025ff56bf1476ed0e30d10c84d7f89a475ef46ebabee1095a8fba"
 dependencies = [
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate",
  "proc-macro2",
  "quote",
  "syn",
@@ -6881,20 +6502,6 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f"
 
-[[package]]
-name = "parity-tokio-ipc"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6"
-dependencies = [
- "futures 0.3.21",
- "libc",
- "log 0.4.16",
- "rand 0.7.3",
- "tokio",
- "winapi 0.3.9",
-]
-
 [[package]]
 name = "parity-util-mem"
 version = "0.11.0"
@@ -6907,8 +6514,8 @@ dependencies = [
  "parity-util-mem-derive",
  "parking_lot 0.12.0",
  "primitive-types",
- "smallvec 1.8.0",
- "winapi 0.3.9",
+ "smallvec",
+ "winapi",
 ]
 
 [[package]]
@@ -6937,41 +6544,12 @@ version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92"
 
-[[package]]
-name = "parity-ws"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0ab8a461779bd022964cae2b4989fa9c99deb270bec162da2125ec03c09fcaa"
-dependencies = [
- "byteorder",
- "bytes 0.4.12",
- "httparse",
- "log 0.4.16",
- "mio 0.6.23",
- "mio-extras",
- "rand 0.7.3",
- "sha-1 0.8.2",
- "slab",
- "url 2.2.1",
-]
-
 [[package]]
 name = "parking"
 version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
 
-[[package]]
-name = "parking_lot"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
-dependencies = [
- "lock_api 0.3.4",
- "parking_lot_core 0.6.2",
- "rustc_version 0.2.3",
-]
-
 [[package]]
 name = "parking_lot"
 version = "0.11.2"
@@ -6979,7 +6557,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
 dependencies = [
  "instant",
- "lock_api 0.4.6",
+ "lock_api",
  "parking_lot_core 0.8.5",
 ]
 
@@ -6989,25 +6567,10 @@ version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
 dependencies = [
- "lock_api 0.4.6",
+ "lock_api",
  "parking_lot_core 0.9.1",
 ]
 
-[[package]]
-name = "parking_lot_core"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
-dependencies = [
- "cfg-if 0.1.10",
- "cloudabi",
- "libc",
- "redox_syscall 0.1.57",
- "rustc_version 0.2.3",
- "smallvec 0.6.14",
- "winapi 0.3.9",
-]
-
 [[package]]
 name = "parking_lot_core"
 version = "0.8.5"
@@ -7018,8 +6581,8 @@ dependencies = [
  "instant",
  "libc",
  "redox_syscall 0.2.10",
- "smallvec 1.8.0",
- "winapi 0.3.9",
+ "smallvec",
+ "winapi",
 ]
 
 [[package]]
@@ -7031,7 +6594,7 @@ dependencies = [
  "cfg-if 1.0.0",
  "libc",
  "redox_syscall 0.2.10",
- "smallvec 1.8.0",
+ "smallvec",
  "windows-sys",
 ]
 
@@ -7084,12 +6647,6 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
 
-[[package]]
-name = "percent-encoding"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
-
 [[package]]
 name = "percent-encoding"
 version = "2.1.0"
@@ -7255,9 +6812,9 @@ checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4"
 dependencies = [
  "cfg-if 0.1.10",
  "libc",
- "log 0.4.16",
+ "log",
  "wepoll-sys",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -7355,15 +6912,6 @@ dependencies = [
  "uint",
 ]
 
-[[package]]
-name = "proc-macro-crate"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
-dependencies = [
- "toml",
-]
-
 [[package]]
 name = "proc-macro-crate"
 version = "1.1.3"
@@ -7384,7 +6932,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "syn",
- "version_check 0.9.2",
+ "version_check",
 ]
 
 [[package]]
@@ -7395,7 +6943,7 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
 dependencies = [
  "proc-macro2",
  "quote",
- "version_check 0.9.2",
+ "version_check",
 ]
 
 [[package]]
@@ -7406,9 +6954,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.37"
+version = "1.0.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
+checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa"
 dependencies = [
  "unicode-xid",
 ]
@@ -7456,7 +7004,7 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "prost-derive",
 ]
 
@@ -7466,11 +7014,11 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "heck 0.3.2",
  "itertools",
  "lazy_static",
- "log 0.4.16",
+ "log",
  "multimap",
  "petgraph",
  "prost",
@@ -7499,7 +7047,7 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "prost",
 ]
 
@@ -7551,7 +7099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
 dependencies = [
  "env_logger 0.8.4",
- "log 0.4.16",
+ "log",
  "rand 0.8.4",
 ]
 
@@ -7597,7 +7145,7 @@ dependencies = [
  "rand_os",
  "rand_pcg 0.1.2",
  "rand_xorshift",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -7743,7 +7291,7 @@ checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
 dependencies = [
  "libc",
  "rand_core 0.4.2",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -7757,7 +7305,7 @@ dependencies = [
  "libc",
  "rand_core 0.4.2",
  "rdrand",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -7823,7 +7371,7 @@ checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
 dependencies = [
  "crossbeam-channel",
  "crossbeam-deque",
- "crossbeam-utils 0.8.5",
+ "crossbeam-utils",
  "lazy_static",
  "num_cpus",
 ]
@@ -7899,9 +7447,9 @@ version = "0.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5"
 dependencies = [
- "log 0.4.16",
+ "log",
  "rustc-hash",
- "smallvec 1.8.0",
+ "smallvec",
 ]
 
 [[package]]
@@ -7910,9 +7458,9 @@ version = "0.0.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "62446b1d3ebf980bdc68837700af1d77b37bc430e524bf95319c6eada2a4cc02"
 dependencies = [
- "log 0.4.16",
+ "log",
  "rustc-hash",
- "smallvec 1.8.0",
+ "smallvec",
 ]
 
 [[package]]
@@ -7951,7 +7499,7 @@ dependencies = [
  "bitflags",
  "libc",
  "mach",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -7963,7 +7511,7 @@ dependencies = [
  "bitflags",
  "libc",
  "mach",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -7973,7 +7521,7 @@ dependencies = [
  "env_logger 0.9.0",
  "frame-support",
  "jsonrpsee",
- "log 0.4.16",
+ "log",
  "pallet-elections-phragmen",
  "parity-scale-codec",
  "serde",
@@ -7991,7 +7539,7 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
 dependencies = [
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -8042,7 +7590,7 @@ dependencies = [
  "spin",
  "untrusted",
  "web-sys",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -8087,7 +7635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb"
 dependencies = [
  "libc",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -8097,8 +7645,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6f54290e54521dac3de4149d83ddf9f62a359b3cc93bcb494a794a41e6f4744b"
 dependencies = [
  "async-global-executor",
- "futures 0.3.21",
- "log 0.4.16",
+ "futures",
+ "log",
  "netlink-packet-route",
  "netlink-proto",
  "nix 0.22.3",
@@ -8111,10 +7659,10 @@ version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
 dependencies = [
- "base64 0.13.0",
+ "base64",
  "blake2b_simd 0.5.11",
  "constant_time_eq",
- "crossbeam-utils 0.8.5",
+ "crossbeam-utils",
 ]
 
 [[package]]
@@ -8173,7 +7721,7 @@ dependencies = [
  "io-lifetimes",
  "libc",
  "linux-raw-sys",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -8182,8 +7730,8 @@ version = "0.19.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
 dependencies = [
- "base64 0.13.0",
- "log 0.4.16",
+ "base64",
+ "log",
  "ring",
  "sct 0.6.0",
  "webpki 0.21.4",
@@ -8195,7 +7743,7 @@ version = "0.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84"
 dependencies = [
- "log 0.4.16",
+ "log",
  "ring",
  "sct 0.7.0",
  "webpki 0.22.0",
@@ -8231,7 +7779,7 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9"
 dependencies = [
- "base64 0.13.0",
+ "base64",
 ]
 
 [[package]]
@@ -8246,7 +7794,7 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4da5fcb054c46f5a5dff833b129285a93d3f0179531735e6c866e8cc307d2020"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "pin-project 0.4.27",
  "static_assertions",
 ]
@@ -8266,12 +7814,6 @@ dependencies = [
  "rustc_version 0.2.3",
 ]
 
-[[package]]
-name = "safemem"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
-
 [[package]]
 name = "salsa20"
 version = "0.9.0"
@@ -8294,7 +7836,7 @@ dependencies = [
 name = "sc-allocator"
 version = "4.1.0-dev"
 dependencies = [
- "log 0.4.16",
+ "log",
  "sp-core",
  "sp-wasm-interface",
  "thiserror",
@@ -8305,11 +7847,11 @@ name = "sc-authority-discovery"
 version = "0.10.0-dev"
 dependencies = [
  "async-trait",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "ip_network",
  "libp2p",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "prost",
  "prost-build",
@@ -8333,9 +7875,9 @@ dependencies = [
 name = "sc-basic-authorship"
 version = "0.10.0-dev"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "futures-timer",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "parking_lot 0.12.0",
  "sc-block-builder",
@@ -8390,7 +7932,7 @@ dependencies = [
 name = "sc-chain-spec-derive"
 version = "4.0.0-dev"
 dependencies = [
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate",
  "proc-macro2",
  "quote",
  "syn",
@@ -8403,10 +7945,10 @@ dependencies = [
  "chrono",
  "clap 3.1.6",
  "fdlimit",
- "futures 0.3.21",
+ "futures",
  "hex",
  "libp2p",
- "log 0.4.16",
+ "log",
  "names",
  "parity-scale-codec",
  "rand 0.7.3",
@@ -8440,9 +7982,9 @@ name = "sc-client-api"
 version = "4.0.0-dev"
 dependencies = [
  "fnv",
- "futures 0.3.21",
+ "futures",
  "hash-db",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "parking_lot 0.12.0",
  "sc-executor",
@@ -8474,7 +8016,7 @@ dependencies = [
  "kvdb-memorydb",
  "kvdb-rocksdb",
  "linked-hash-map",
- "log 0.4.16",
+ "log",
  "parity-db",
  "parity-scale-codec",
  "parking_lot 0.12.0",
@@ -8498,10 +8040,10 @@ name = "sc-consensus"
 version = "0.10.0-dev"
 dependencies = [
  "async-trait",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "libp2p",
- "log 0.4.16",
+ "log",
  "parking_lot 0.12.0",
  "sc-client-api",
  "sc-utils",
@@ -8522,8 +8064,8 @@ name = "sc-consensus-aura"
 version = "0.10.0-dev"
 dependencies = [
  "async-trait",
- "futures 0.3.21",
- "log 0.4.16",
+ "futures",
+ "log",
  "parity-scale-codec",
  "parking_lot 0.12.0",
  "sc-block-builder",
@@ -8560,8 +8102,8 @@ version = "0.10.0-dev"
 dependencies = [
  "async-trait",
  "fork-tree",
- "futures 0.3.21",
- "log 0.4.16",
+ "futures",
+ "log",
  "merlin",
  "num-bigint",
  "num-rational 0.2.4",
@@ -8608,10 +8150,8 @@ dependencies = [
 name = "sc-consensus-babe-rpc"
 version = "0.10.0-dev"
 dependencies = [
- "futures 0.3.21",
- "jsonrpc-core",
- "jsonrpc-core-client",
- "jsonrpc-derive",
+ "futures",
+ "jsonrpsee",
  "sc-consensus",
  "sc-consensus-babe",
  "sc-consensus-epochs",
@@ -8631,6 +8171,7 @@ dependencies = [
  "substrate-test-runtime-client",
  "tempfile",
  "thiserror",
+ "tokio",
 ]
 
 [[package]]
@@ -8651,11 +8192,9 @@ version = "0.10.0-dev"
 dependencies = [
  "assert_matches",
  "async-trait",
- "futures 0.3.21",
- "jsonrpc-core",
- "jsonrpc-core-client",
- "jsonrpc-derive",
- "log 0.4.16",
+ "futures",
+ "jsonrpsee",
+ "log",
  "parity-scale-codec",
  "sc-basic-authorship",
  "sc-client-api",
@@ -8689,9 +8228,9 @@ name = "sc-consensus-pow"
 version = "0.10.0-dev"
 dependencies = [
  "async-trait",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "parking_lot 0.12.0",
  "sc-client-api",
@@ -8713,9 +8252,9 @@ name = "sc-consensus-slots"
 version = "0.10.0-dev"
 dependencies = [
  "async-trait",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "sc-client-api",
  "sc-consensus",
@@ -8803,7 +8342,7 @@ dependencies = [
 name = "sc-executor-wasmi"
 version = "0.10.0-dev"
 dependencies = [
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "sc-allocator",
  "sc-executor-common",
@@ -8819,7 +8358,7 @@ version = "0.10.0-dev"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "parity-wasm 0.42.2",
  "sc-allocator",
@@ -8843,10 +8382,10 @@ dependencies = [
  "dyn-clone",
  "finality-grandpa",
  "fork-tree",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "hex",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "parking_lot 0.12.0",
  "rand 0.8.4",
@@ -8885,12 +8424,9 @@ name = "sc-finality-grandpa-rpc"
 version = "0.10.0-dev"
 dependencies = [
  "finality-grandpa",
- "futures 0.3.21",
- "jsonrpc-core",
- "jsonrpc-core-client",
- "jsonrpc-derive",
- "jsonrpc-pubsub",
- "log 0.4.16",
+ "futures",
+ "jsonrpsee",
+ "log",
  "parity-scale-codec",
  "sc-block-builder",
  "sc-client-api",
@@ -8905,6 +8441,7 @@ dependencies = [
  "sp-runtime",
  "substrate-test-runtime-client",
  "thiserror",
+ "tokio",
 ]
 
 [[package]]
@@ -8912,9 +8449,9 @@ name = "sc-informant"
 version = "0.10.0-dev"
 dependencies = [
  "ansi_term",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
- "log 0.4.16",
+ "log",
  "parity-util-mem",
  "sc-client-api",
  "sc-network",
@@ -8947,19 +8484,19 @@ dependencies = [
  "async-trait",
  "asynchronous-codec",
  "bitflags",
- "bytes 1.1.0",
+ "bytes",
  "cid",
  "either",
  "fnv",
  "fork-tree",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "hex",
  "ip_network",
  "libp2p",
  "linked-hash-map",
  "linked_hash_set",
- "log 0.4.16",
+ "log",
  "lru",
  "parity-scale-codec",
  "parking_lot 0.12.0",
@@ -8977,7 +8514,7 @@ dependencies = [
  "sc-utils",
  "serde",
  "serde_json",
- "smallvec 1.8.0",
+ "smallvec",
  "sp-arithmetic",
  "sp-blockchain",
  "sp-consensus",
@@ -9000,12 +8537,12 @@ dependencies = [
 name = "sc-network-common"
 version = "0.10.0-dev"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "libp2p",
  "parity-scale-codec",
  "prost-build",
  "sc-peerset",
- "smallvec 1.8.0",
+ "smallvec",
 ]
 
 [[package]]
@@ -9014,10 +8551,10 @@ version = "0.10.0-dev"
 dependencies = [
  "ahash",
  "async-std",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "libp2p",
- "log 0.4.16",
+ "log",
  "lru",
  "quickcheck",
  "sc-network",
@@ -9034,9 +8571,9 @@ dependencies = [
  "bitflags",
  "either",
  "fork-tree",
- "futures 0.3.21",
+ "futures",
  "libp2p",
- "log 0.4.16",
+ "log",
  "lru",
  "parity-scale-codec",
  "prost",
@@ -9047,7 +8584,7 @@ dependencies = [
  "sc-consensus",
  "sc-network-common",
  "sc-peerset",
- "smallvec 1.8.0",
+ "smallvec",
  "sp-arithmetic",
  "sp-blockchain",
  "sp-consensus",
@@ -9066,10 +8603,10 @@ version = "0.8.0"
 dependencies = [
  "async-std",
  "async-trait",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "libp2p",
- "log 0.4.16",
+ "log",
  "parking_lot 0.12.0",
  "rand 0.7.3",
  "sc-block-builder",
@@ -9092,12 +8629,12 @@ dependencies = [
 name = "sc-offchain"
 version = "4.0.0-dev"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "fnv",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "hex",
- "hyper 0.14.16",
+ "hyper",
  "hyper-rustls",
  "lazy_static",
  "num_cpus",
@@ -9128,9 +8665,9 @@ dependencies = [
 name = "sc-peerset"
 version = "4.0.0-dev"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "libp2p",
- "log 0.4.16",
+ "log",
  "rand 0.7.3",
  "sc-utils",
  "serde_json",
@@ -9141,7 +8678,7 @@ dependencies = [
 name = "sc-proposer-metrics"
 version = "0.10.0-dev"
 dependencies = [
- "log 0.4.16",
+ "log",
  "substrate-prometheus-endpoint",
 ]
 
@@ -9150,12 +8687,12 @@ name = "sc-rpc"
 version = "4.0.0-dev"
 dependencies = [
  "assert_matches",
- "futures 0.3.21",
+ "env_logger 0.9.0",
+ "futures",
  "hash-db",
- "jsonrpc-core",
- "jsonrpc-pubsub",
+ "jsonrpsee",
  "lazy_static",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "parking_lot 0.12.0",
  "sc-block-builder",
@@ -9180,18 +8717,16 @@ dependencies = [
  "sp-session",
  "sp-version",
  "substrate-test-runtime-client",
+ "tokio",
 ]
 
 [[package]]
 name = "sc-rpc-api"
 version = "0.10.0-dev"
 dependencies = [
- "futures 0.3.21",
- "jsonrpc-core",
- "jsonrpc-core-client",
- "jsonrpc-derive",
- "jsonrpc-pubsub",
- "log 0.4.16",
+ "futures",
+ "jsonrpsee",
+ "log",
  "parity-scale-codec",
  "parking_lot 0.12.0",
  "sc-chain-spec",
@@ -9211,13 +8746,9 @@ dependencies = [
 name = "sc-rpc-server"
 version = "4.0.0-dev"
 dependencies = [
- "futures 0.3.21",
- "jsonrpc-core",
- "jsonrpc-http-server",
- "jsonrpc-ipc-server",
- "jsonrpc-pubsub",
- "jsonrpc-ws-server",
- "log 0.4.16",
+ "futures",
+ "jsonrpsee",
+ "log",
  "serde_json",
  "substrate-prometheus-endpoint",
  "tokio",
@@ -9245,12 +8776,11 @@ dependencies = [
  "async-trait",
  "directories",
  "exit-future",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "hash-db",
- "jsonrpc-core",
- "jsonrpc-pubsub",
- "log 0.4.16",
+ "jsonrpsee",
+ "log",
  "parity-scale-codec",
  "parity-util-mem",
  "parking_lot 0.12.0",
@@ -9310,10 +8840,10 @@ name = "sc-service-test"
 version = "2.0.0"
 dependencies = [
  "fdlimit",
- "futures 0.3.21",
+ "futures",
  "hex",
  "hex-literal",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "parking_lot 0.12.0",
  "sc-block-builder",
@@ -9345,7 +8875,7 @@ dependencies = [
 name = "sc-state-db"
 version = "0.10.0-dev"
 dependencies = [
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "parity-util-mem",
  "parity-util-mem-derive",
@@ -9358,9 +8888,7 @@ dependencies = [
 name = "sc-sync-state-rpc"
 version = "0.10.0-dev"
 dependencies = [
- "jsonrpc-core",
- "jsonrpc-core-client",
- "jsonrpc-derive",
+ "jsonrpsee",
  "parity-scale-codec",
  "sc-chain-spec",
  "sc-client-api",
@@ -9378,9 +8906,9 @@ dependencies = [
 name = "sc-sysinfo"
 version = "6.0.0-dev"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "libc",
- "log 0.4.16",
+ "log",
  "rand 0.7.3",
  "rand_pcg 0.2.1",
  "regex",
@@ -9397,9 +8925,9 @@ name = "sc-telemetry"
 version = "4.0.0-dev"
 dependencies = [
  "chrono",
- "futures 0.3.21",
+ "futures",
  "libp2p",
- "log 0.4.16",
+ "log",
  "parking_lot 0.12.0",
  "pin-project 1.0.10",
  "rand 0.7.3",
@@ -9419,7 +8947,7 @@ dependencies = [
  "criterion",
  "lazy_static",
  "libc",
- "log 0.4.16",
+ "log",
  "once_cell",
  "parking_lot 0.12.0",
  "regex",
@@ -9444,7 +8972,7 @@ dependencies = [
 name = "sc-tracing-proc-macro"
 version = "4.0.0-dev"
 dependencies = [
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate",
  "proc-macro2",
  "quote",
  "syn",
@@ -9456,11 +8984,11 @@ version = "4.0.0-dev"
 dependencies = [
  "assert_matches",
  "criterion",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "hex",
  "linked-hash-map",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "parity-util-mem",
  "parking_lot 0.12.0",
@@ -9488,8 +9016,8 @@ dependencies = [
 name = "sc-transaction-pool-api"
 version = "4.0.0-dev"
 dependencies = [
- "futures 0.3.21",
- "log 0.4.16",
+ "futures",
+ "log",
  "serde",
  "sp-blockchain",
  "sp-runtime",
@@ -9500,10 +9028,10 @@ dependencies = [
 name = "sc-utils"
 version = "4.0.0-dev"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "futures-timer",
  "lazy_static",
- "log 0.4.16",
+ "log",
  "parking_lot 0.12.0",
  "prometheus",
  "tokio-test",
@@ -9529,7 +9057,7 @@ version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b7805950c36512db9e3251c970bb7ac425f326716941862205d612ab3b5e46e2"
 dependencies = [
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate",
  "proc-macro2",
  "quote",
  "syn",
@@ -9542,7 +9070,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
 dependencies = [
  "lazy_static",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -9793,12 +9321,6 @@ dependencies = [
  "opaque-debug 0.3.0",
 ]
 
-[[package]]
-name = "sha1"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
-
 [[package]]
 name = "sha2"
 version = "0.8.2"
@@ -9919,15 +9441,6 @@ version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
 
-[[package]]
-name = "smallvec"
-version = "0.6.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
-dependencies = [
- "maybe-uninit",
-]
-
 [[package]]
 name = "smallvec"
 version = "1.8.0"
@@ -9965,7 +9478,7 @@ checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -9975,7 +9488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
 dependencies = [
  "libc",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -9984,12 +9497,12 @@ version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2"
 dependencies = [
- "base64 0.13.0",
- "bytes 1.1.0",
+ "base64",
+ "bytes",
  "flate2",
- "futures 0.3.21",
+ "futures",
  "httparse",
- "log 0.4.16",
+ "log",
  "rand 0.8.4",
  "sha-1 0.9.4",
 ]
@@ -9999,7 +9512,7 @@ name = "sp-api"
 version = "4.0.0-dev"
 dependencies = [
  "hash-db",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "sp-api-proc-macro",
  "sp-core",
@@ -10016,7 +9529,7 @@ name = "sp-api-proc-macro"
 version = "4.0.0-dev"
 dependencies = [
  "blake2",
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate",
  "proc-macro2",
  "quote",
  "syn",
@@ -10027,8 +9540,8 @@ name = "sp-api-test"
 version = "2.0.1"
 dependencies = [
  "criterion",
- "futures 0.3.21",
- "log 0.4.16",
+ "futures",
+ "log",
  "parity-scale-codec",
  "rustversion",
  "sc-block-builder",
@@ -10132,8 +9645,8 @@ dependencies = [
 name = "sp-blockchain"
 version = "4.0.0-dev"
 dependencies = [
- "futures 0.3.21",
- "log 0.4.16",
+ "futures",
+ "log",
  "lru",
  "parity-scale-codec",
  "parking_lot 0.12.0",
@@ -10150,9 +9663,9 @@ name = "sp-consensus"
 version = "0.10.0-dev"
 dependencies = [
  "async-trait",
- "futures 0.3.21",
+ "futures",
  "futures-timer",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "sp-core",
  "sp-inherents",
@@ -10250,7 +9763,7 @@ dependencies = [
  "criterion",
  "dyn-clonable",
  "ed25519-dalek",
- "futures 0.3.21",
+ "futures",
  "hash-db",
  "hash256-std-hasher",
  "hex",
@@ -10258,7 +9771,7 @@ dependencies = [
  "impl-serde",
  "lazy_static",
  "libsecp256k1",
- "log 0.4.16",
+ "log",
  "merlin",
  "num-traits",
  "parity-scale-codec",
@@ -10344,7 +9857,7 @@ name = "sp-finality-grandpa"
 version = "4.0.0-dev"
 dependencies = [
  "finality-grandpa",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "scale-info",
  "serde",
@@ -10361,7 +9874,7 @@ name = "sp-inherents"
 version = "4.0.0-dev"
 dependencies = [
  "async-trait",
- "futures 0.3.21",
+ "futures",
  "impl-trait-for-tuples",
  "parity-scale-codec",
  "sp-core",
@@ -10374,10 +9887,10 @@ dependencies = [
 name = "sp-io"
 version = "6.0.0"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "hash-db",
  "libsecp256k1",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "parking_lot 0.12.0",
  "secp256k1",
@@ -10409,7 +9922,7 @@ name = "sp-keystore"
 version = "0.12.0"
 dependencies = [
  "async-trait",
- "futures 0.3.21",
+ "futures",
  "merlin",
  "parity-scale-codec",
  "parking_lot 0.12.0",
@@ -10435,7 +9948,7 @@ name = "sp-mmr-primitives"
 version = "4.0.0-dev"
 dependencies = [
  "hex-literal",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "serde",
  "sp-api",
@@ -10508,7 +10021,7 @@ dependencies = [
  "either",
  "hash256-std-hasher",
  "impl-trait-for-tuples",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "parity-util-mem",
  "paste 1.0.6",
@@ -10555,7 +10068,7 @@ name = "sp-runtime-interface-proc-macro"
 version = "5.0.0"
 dependencies = [
  "Inflector",
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate",
  "proc-macro2",
  "quote",
  "syn",
@@ -10604,7 +10117,7 @@ name = "sp-sandbox"
 version = "0.10.0-dev"
 dependencies = [
  "assert_matches",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "sp-core",
  "sp-io",
@@ -10651,13 +10164,13 @@ version = "0.12.0"
 dependencies = [
  "hash-db",
  "hex-literal",
- "log 0.4.16",
+ "log",
  "num-traits",
  "parity-scale-codec",
  "parking_lot 0.12.0",
  "pretty_assertions",
  "rand 0.7.3",
- "smallvec 1.8.0",
+ "smallvec",
  "sp-core",
  "sp-externalities",
  "sp-panic-handler",
@@ -10689,7 +10202,7 @@ dependencies = [
 name = "sp-tasks"
 version = "4.0.0-dev"
 dependencies = [
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "sp-core",
  "sp-externalities",
@@ -10716,7 +10229,7 @@ version = "4.0.0-dev"
 dependencies = [
  "async-trait",
  "futures-timer",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "sp-api",
  "sp-inherents",
@@ -10749,7 +10262,7 @@ name = "sp-transaction-storage-proof"
 version = "4.0.0-dev"
 dependencies = [
  "async-trait",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "scale-info",
  "sp-core",
@@ -10811,7 +10324,7 @@ name = "sp-wasm-interface"
 version = "6.0.0"
 dependencies = [
  "impl-trait-for-tuples",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "sp-std",
  "wasmi",
@@ -10938,8 +10451,8 @@ version = "3.0.0"
 dependencies = [
  "frame-support",
  "frame-system",
- "futures 0.3.21",
- "jsonrpc-client-transports",
+ "futures",
+ "jsonrpsee",
  "parity-scale-codec",
  "sc-rpc-api",
  "scale-info",
@@ -10952,17 +10465,17 @@ dependencies = [
 name = "substrate-frame-rpc-system"
 version = "4.0.0-dev"
 dependencies = [
+ "assert_matches",
  "frame-system-rpc-runtime-api",
- "futures 0.3.21",
- "jsonrpc-core",
- "jsonrpc-core-client",
- "jsonrpc-derive",
- "log 0.4.16",
+ "futures",
+ "jsonrpsee",
+ "log",
  "parity-scale-codec",
  "sc-client-api",
  "sc-rpc-api",
  "sc-transaction-pool",
  "sc-transaction-pool-api",
+ "serde_json",
  "sp-api",
  "sp-block-builder",
  "sp-blockchain",
@@ -10970,6 +10483,7 @@ dependencies = [
  "sp-runtime",
  "sp-tracing",
  "substrate-test-runtime-client",
+ "tokio",
 ]
 
 [[package]]
@@ -10977,8 +10491,8 @@ name = "substrate-prometheus-endpoint"
 version = "0.10.0-dev"
 dependencies = [
  "futures-util",
- "hyper 0.14.16",
- "log 0.4.16",
+ "hyper",
+ "log",
  "prometheus",
  "thiserror",
  "tokio",
@@ -10988,10 +10502,8 @@ dependencies = [
 name = "substrate-state-trie-migration-rpc"
 version = "4.0.0-dev"
 dependencies = [
- "jsonrpc-core",
- "jsonrpc-core-client",
- "jsonrpc-derive",
- "log 0.4.16",
+ "jsonrpsee",
+ "log",
  "parity-scale-codec",
  "sc-client-api",
  "sc-rpc-api",
@@ -11012,7 +10524,7 @@ name = "substrate-test-client"
 version = "2.0.1"
 dependencies = [
  "async-trait",
- "futures 0.3.21",
+ "futures",
  "hex",
  "parity-scale-codec",
  "sc-client-api",
@@ -11041,8 +10553,8 @@ dependencies = [
  "frame-support",
  "frame-system",
  "frame-system-rpc-runtime-api",
- "futures 0.3.21",
- "log 0.4.16",
+ "futures",
+ "log",
  "memory-db",
  "pallet-babe",
  "pallet-timestamp",
@@ -11083,7 +10595,7 @@ dependencies = [
 name = "substrate-test-runtime-client"
 version = "2.0.0"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "parity-scale-codec",
  "sc-block-builder",
  "sc-client-api",
@@ -11101,7 +10613,7 @@ dependencies = [
 name = "substrate-test-runtime-transaction-pool"
 version = "2.0.0"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "parity-scale-codec",
  "parking_lot 0.12.0",
  "sc-transaction-pool",
@@ -11116,7 +10628,7 @@ dependencies = [
 name = "substrate-test-utils"
 version = "4.0.0-dev"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "sc-service",
  "substrate-test-utils-derive",
  "tokio",
@@ -11127,7 +10639,7 @@ dependencies = [
 name = "substrate-test-utils-derive"
 version = "0.10.0-dev"
 dependencies = [
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate",
  "proc-macro2",
  "quote",
  "syn",
@@ -11230,7 +10742,7 @@ dependencies = [
  "libc",
  "redox_syscall 0.2.10",
  "remove_dir_all",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -11241,7 +10753,7 @@ checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
 dependencies = [
  "byteorder",
  "dirs",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -11331,7 +10843,7 @@ checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
 dependencies = [
  "libc",
  "wasi 0.10.0+wasi-snapshot-preview1",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -11393,10 +10905,10 @@ version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "libc",
  "memchr",
- "mio 0.8.0",
+ "mio",
  "num_cpus",
  "once_cell",
  "parking_lot 0.12.0",
@@ -11404,39 +10916,7 @@ dependencies = [
  "signal-hook-registry",
  "socket2 0.4.4",
  "tokio-macros",
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "tokio-codec"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b"
-dependencies = [
- "bytes 0.4.12",
- "futures 0.1.31",
- "tokio-io",
-]
-
-[[package]]
-name = "tokio-executor"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671"
-dependencies = [
- "crossbeam-utils 0.7.2",
- "futures 0.1.31",
-]
-
-[[package]]
-name = "tokio-io"
-version = "0.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674"
-dependencies = [
- "bytes 0.4.12",
- "futures 0.1.31",
- "log 0.4.16",
+ "winapi",
 ]
 
 [[package]]
@@ -11450,35 +10930,6 @@ dependencies = [
  "syn",
 ]
 
-[[package]]
-name = "tokio-native-tls"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
-dependencies = [
- "native-tls",
- "tokio",
-]
-
-[[package]]
-name = "tokio-reactor"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351"
-dependencies = [
- "crossbeam-utils 0.7.2",
- "futures 0.1.31",
- "lazy_static",
- "log 0.4.16",
- "mio 0.6.23",
- "num_cpus",
- "parking_lot 0.9.0",
- "slab",
- "tokio-executor",
- "tokio-io",
- "tokio-sync",
-]
-
 [[package]]
 name = "tokio-rustls"
 version = "0.22.0"
@@ -11512,30 +10963,6 @@ dependencies = [
  "tokio",
 ]
 
-[[package]]
-name = "tokio-sync"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee"
-dependencies = [
- "fnv",
- "futures 0.1.31",
-]
-
-[[package]]
-name = "tokio-tcp"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72"
-dependencies = [
- "bytes 0.4.12",
- "futures 0.1.31",
- "iovec",
- "mio 0.6.23",
- "tokio-io",
- "tokio-reactor",
-]
-
 [[package]]
 name = "tokio-test"
 version = "0.4.2"
@@ -11543,33 +10970,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "53474327ae5e166530d17f2d956afcb4f8a004de581b3cae10f12006bc8163e3"
 dependencies = [
  "async-stream",
- "bytes 1.1.0",
+ "bytes",
  "futures-core",
  "tokio",
  "tokio-stream",
 ]
 
-[[package]]
-name = "tokio-tls"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c"
-dependencies = [
- "futures 0.1.31",
- "native-tls",
- "tokio-io",
-]
-
 [[package]]
 name = "tokio-util"
 version = "0.6.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "futures-core",
  "futures-sink",
- "log 0.4.16",
+ "log",
  "pin-project-lite 0.2.6",
  "tokio",
 ]
@@ -11580,7 +10996,7 @@ version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764"
 dependencies = [
- "bytes 1.1.0",
+ "bytes",
  "futures-core",
  "futures-io",
  "futures-sink",
@@ -11610,7 +11026,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
 dependencies = [
  "cfg-if 1.0.0",
- "log 0.4.16",
+ "log",
  "pin-project-lite 0.2.6",
  "tracing-attributes",
  "tracing-core",
@@ -11655,7 +11071,7 @@ checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
 dependencies = [
  "ahash",
  "lazy_static",
- "log 0.4.16",
+ "log",
  "lru",
  "tracing-core",
 ]
@@ -11685,7 +11101,7 @@ dependencies = [
  "serde",
  "serde_json",
  "sharded-slab",
- "smallvec 1.8.0",
+ "smallvec",
  "thread_local",
  "tracing",
  "tracing-core",
@@ -11693,12 +11109,6 @@ dependencies = [
  "tracing-serde",
 ]
 
-[[package]]
-name = "traitobject"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
-
 [[package]]
 name = "treeline"
 version = "0.1.0"
@@ -11729,9 +11139,9 @@ checksum = "d32d034c0d3db64b43c31de38e945f15b40cd4ca6d2dcfc26d4798ce8de4ab83"
 dependencies = [
  "hash-db",
  "hashbrown 0.12.0",
- "log 0.4.16",
+ "log",
  "rustc-hex",
- "smallvec 1.8.0",
+ "smallvec",
 ]
 
 [[package]]
@@ -11766,15 +11176,15 @@ dependencies = [
  "futures-channel",
  "futures-io",
  "futures-util",
- "idna 0.2.3",
+ "idna",
  "ipnet",
  "lazy_static",
- "log 0.4.16",
+ "log",
  "rand 0.8.4",
- "smallvec 1.8.0",
+ "smallvec",
  "thiserror",
  "tinyvec",
- "url 2.2.1",
+ "url",
 ]
 
 [[package]]
@@ -11787,11 +11197,11 @@ dependencies = [
  "futures-util",
  "ipconfig",
  "lazy_static",
- "log 0.4.16",
+ "log",
  "lru-cache",
  "parking_lot 0.12.0",
  "resolv-conf",
- "smallvec 1.8.0",
+ "smallvec",
  "thiserror",
  "trust-dns-proto",
 ]
@@ -11808,7 +11218,7 @@ version = "0.10.0-dev"
 dependencies = [
  "clap 3.1.6",
  "jsonrpsee",
- "log 0.4.16",
+ "log",
  "parity-scale-codec",
  "remote-externalities",
  "sc-chain-spec",
@@ -11860,12 +11270,6 @@ dependencies = [
  "static_assertions",
 ]
 
-[[package]]
-name = "typeable"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
-
 [[package]]
 name = "typenum"
 version = "1.15.0"
@@ -11890,22 +11294,13 @@ dependencies = [
  "static_assertions",
 ]
 
-[[package]]
-name = "unicase"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
-dependencies = [
- "version_check 0.1.5",
-]
-
 [[package]]
 name = "unicase"
 version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
 dependencies = [
- "version_check 0.9.2",
+ "version_check",
 ]
 
 [[package]]
@@ -11940,9 +11335,9 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
 
 [[package]]
 name = "unicode-xid"
-version = "0.2.2"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
 
 [[package]]
 name = "universal-hash"
@@ -11961,7 +11356,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836"
 dependencies = [
  "asynchronous-codec",
- "bytes 1.1.0",
+ "bytes",
  "futures-io",
  "futures-util",
 ]
@@ -11972,17 +11367,6 @@ version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
 
-[[package]]
-name = "url"
-version = "1.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
-dependencies = [
- "idna 0.1.5",
- "matches",
- "percent-encoding 1.0.1",
-]
-
 [[package]]
 name = "url"
 version = "2.2.1"
@@ -11990,9 +11374,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b"
 dependencies = [
  "form_urlencoded",
- "idna 0.2.3",
+ "idna",
  "matches",
- "percent-encoding 2.1.0",
+ "percent-encoding",
 ]
 
 [[package]]
@@ -12008,7 +11392,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f"
 dependencies = [
  "ctor",
- "version_check 0.9.2",
+ "version_check",
 ]
 
 [[package]]
@@ -12023,12 +11407,6 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d"
 
-[[package]]
-name = "version_check"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
-
 [[package]]
 name = "version_check"
 version = "0.9.2"
@@ -12063,7 +11441,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
 dependencies = [
  "same-file",
- "winapi 0.3.9",
+ "winapi",
  "winapi-util",
 ]
 
@@ -12073,7 +11451,7 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
 dependencies = [
- "log 0.4.16",
+ "log",
  "try-lock",
 ]
 
@@ -12107,7 +11485,7 @@ checksum = "f34c405b4f0658583dba0c1c7c9b694f3cac32655db463b56c254a1c75269523"
 dependencies = [
  "bumpalo",
  "lazy_static",
- "log 0.4.16",
+ "log",
  "proc-macro2",
  "quote",
  "syn",
@@ -12161,7 +11539,7 @@ version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d0c32691b6c7e6c14e7f8fd55361a9088b507aa49620fcd06c09b3a1082186b9"
 dependencies = [
- "log 0.4.16",
+ "log",
  "parity-wasm 0.32.0",
  "rustc-demangle",
 ]
@@ -12181,7 +11559,7 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f"
 dependencies = [
- "futures 0.3.21",
+ "futures",
  "js-sys",
  "parking_lot 0.11.2",
  "pin-utils",
@@ -12214,7 +11592,7 @@ dependencies = [
  "wasmer-types",
  "wasmer-vm",
  "wat",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -12228,7 +11606,7 @@ dependencies = [
  "rkyv",
  "serde",
  "serde_bytes",
- "smallvec 1.8.0",
+ "smallvec",
  "target-lexicon",
  "thiserror",
  "wasmer-types",
@@ -12249,7 +11627,7 @@ dependencies = [
  "loupe",
  "more-asserts",
  "rayon",
- "smallvec 1.8.0",
+ "smallvec",
  "target-lexicon",
  "tracing",
  "wasmer-compiler",
@@ -12270,7 +11648,7 @@ dependencies = [
  "loupe",
  "more-asserts",
  "rayon",
- "smallvec 1.8.0",
+ "smallvec",
  "wasmer-compiler",
  "wasmer-types",
  "wasmer-vm",
@@ -12352,7 +11730,7 @@ dependencies = [
  "wasmer-engine",
  "wasmer-types",
  "wasmer-vm",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -12400,7 +11778,7 @@ dependencies = [
  "serde",
  "thiserror",
  "wasmer-types",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -12454,7 +11832,7 @@ dependencies = [
  "indexmap",
  "lazy_static",
  "libc",
- "log 0.4.16",
+ "log",
  "object 0.27.1",
  "once_cell",
  "paste 1.0.6",
@@ -12469,7 +11847,7 @@ dependencies = [
  "wasmtime-environ",
  "wasmtime-jit",
  "wasmtime-runtime",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -12479,16 +11857,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "85c6ab24291fa7cb3a181f5669f6c72599b7ef781669759b45c7828c5999d0c0"
 dependencies = [
  "anyhow",
- "base64 0.13.0",
+ "base64",
  "bincode",
  "directories-next",
  "file-per-thread-logger",
- "log 0.4.16",
+ "log",
  "rustix",
  "serde",
  "sha2 0.9.8",
  "toml",
- "winapi 0.3.9",
+ "winapi",
  "zstd",
 ]
 
@@ -12505,7 +11883,7 @@ dependencies = [
  "cranelift-native",
  "cranelift-wasm",
  "gimli 0.26.1",
- "log 0.4.16",
+ "log",
  "more-asserts",
  "object 0.27.1",
  "target-lexicon",
@@ -12524,7 +11902,7 @@ dependencies = [
  "cranelift-entity 0.82.3",
  "gimli 0.26.1",
  "indexmap",
- "log 0.4.16",
+ "log",
  "more-asserts",
  "object 0.27.1",
  "serde",
@@ -12546,7 +11924,7 @@ dependencies = [
  "cfg-if 1.0.0",
  "cpp_demangle",
  "gimli 0.26.1",
- "log 0.4.16",
+ "log",
  "object 0.27.1",
  "region 2.2.0",
  "rustc-demangle",
@@ -12557,7 +11935,7 @@ dependencies = [
  "wasmtime-environ",
  "wasmtime-jit-debug",
  "wasmtime-runtime",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -12583,7 +11961,7 @@ dependencies = [
  "cfg-if 1.0.0",
  "indexmap",
  "libc",
- "log 0.4.16",
+ "log",
  "mach",
  "memoffset",
  "more-asserts",
@@ -12593,7 +11971,7 @@ dependencies = [
  "thiserror",
  "wasmtime-environ",
  "wasmtime-jit-debug",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -12665,47 +12043,6 @@ dependencies = [
  "webpki 0.22.0",
 ]
 
-[[package]]
-name = "websocket"
-version = "0.24.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "413b37840b9e27b340ce91b319ede10731de8c72f5bc4cb0206ec1ca4ce581d0"
-dependencies = [
- "bytes 0.4.12",
- "futures 0.1.31",
- "hyper 0.10.16",
- "native-tls",
- "rand 0.6.5",
- "tokio-codec",
- "tokio-io",
- "tokio-reactor",
- "tokio-tcp",
- "tokio-tls",
- "unicase 1.4.2",
- "url 1.7.2",
- "websocket-base",
-]
-
-[[package]]
-name = "websocket-base"
-version = "0.24.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e3810f0d00c4dccb54c30a4eee815e703232819dec7b007db115791c42aa374"
-dependencies = [
- "base64 0.10.1",
- "bitflags",
- "byteorder",
- "bytes 0.4.12",
- "futures 0.1.31",
- "native-tls",
- "rand 0.6.5",
- "sha1",
- "tokio-codec",
- "tokio-io",
- "tokio-tcp",
- "tokio-tls",
-]
-
 [[package]]
 name = "wepoll-sys"
 version = "3.0.1"
@@ -12731,12 +12068,6 @@ version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983"
 
-[[package]]
-name = "winapi"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
-
 [[package]]
 name = "winapi"
 version = "0.3.9"
@@ -12747,12 +12078,6 @@ dependencies = [
  "winapi-x86_64-pc-windows-gnu",
 ]
 
-[[package]]
-name = "winapi-build"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
-
 [[package]]
 name = "winapi-i686-pc-windows-gnu"
 version = "0.4.0"
@@ -12765,7 +12090,7 @@ version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
 dependencies = [
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -12866,17 +12191,7 @@ version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
 dependencies = [
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "ws2_32-sys"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
-dependencies = [
- "winapi 0.2.8",
- "winapi-build",
+ "winapi",
 ]
 
 [[package]]
@@ -12905,8 +12220,8 @@ version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0c0608f53c1dc0bad505d03a34bbd49fbf2ad7b51eb036123e896365532745a1"
 dependencies = [
- "futures 0.3.21",
- "log 0.4.16",
+ "futures",
+ "log",
  "nohash-hasher",
  "parking_lot 0.12.0",
  "rand 0.8.4",
diff --git a/substrate/bin/node-template/node/Cargo.toml b/substrate/bin/node-template/node/Cargo.toml
index e642ce3c0411ea2c98ef9b079b73b4ed6f1d218d..ab91dc799038019580b40cca9092ed22e59407cd 100644
--- a/substrate/bin/node-template/node/Cargo.toml
+++ b/substrate/bin/node-template/node/Cargo.toml
@@ -42,7 +42,7 @@ frame-system = { version = "4.0.0-dev", path = "../../../frame/system" }
 pallet-transaction-payment = { version = "4.0.0-dev", default-features = false, path = "../../../frame/transaction-payment" }
 
 # These dependencies are used for the node template's RPCs
-jsonrpc-core = "18.0.0"
+jsonrpsee = { version = "0.12.0", features = ["server"] }
 sc-rpc = { version = "4.0.0-dev", path = "../../../client/rpc" }
 sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" }
 sc-rpc-api = { version = "0.10.0-dev", path = "../../../client/rpc-api" }
diff --git a/substrate/bin/node-template/node/src/rpc.rs b/substrate/bin/node-template/node/src/rpc.rs
index 7f3701b5ab74f17bcba9e9fa182173ce1ef44217..7edae4d81474fe6030c60ecdd5c20b7a5731749a 100644
--- a/substrate/bin/node-template/node/src/rpc.rs
+++ b/substrate/bin/node-template/node/src/rpc.rs
@@ -7,13 +7,15 @@
 
 use std::sync::Arc;
 
+use jsonrpsee::RpcModule;
 use node_template_runtime::{opaque::Block, AccountId, Balance, Index};
-pub use sc_rpc_api::DenyUnsafe;
 use sc_transaction_pool_api::TransactionPool;
 use sp_api::ProvideRuntimeApi;
 use sp_block_builder::BlockBuilder;
 use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
 
+pub use sc_rpc_api::DenyUnsafe;
+
 /// Full client dependencies.
 pub struct FullDeps<C, P> {
 	/// The client instance to use.
@@ -25,7 +27,9 @@ pub struct FullDeps<C, P> {
 }
 
 /// Instantiate all full RPC extensions.
-pub fn create_full<C, P>(deps: FullDeps<C, P>) -> jsonrpc_core::IoHandler<sc_rpc::Metadata>
+pub fn create_full<C, P>(
+	deps: FullDeps<C, P>,
+) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
 where
 	C: ProvideRuntimeApi<Block>,
 	C: HeaderBackend<Block> + HeaderMetadata<Block, Error = BlockChainError> + 'static,
@@ -35,20 +39,19 @@ where
 	C::Api: BlockBuilder<Block>,
 	P: TransactionPool + 'static,
 {
-	use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi};
-	use substrate_frame_rpc_system::{FullSystem, SystemApi};
+	use pallet_transaction_payment_rpc::{TransactionPaymentApiServer, TransactionPaymentRpc};
+	use substrate_frame_rpc_system::{SystemApiServer, SystemRpc};
 
-	let mut io = jsonrpc_core::IoHandler::default();
+	let mut module = RpcModule::new(());
 	let FullDeps { client, pool, deny_unsafe } = deps;
 
-	io.extend_with(SystemApi::to_delegate(FullSystem::new(client.clone(), pool, deny_unsafe)));
-
-	io.extend_with(TransactionPaymentApi::to_delegate(TransactionPayment::new(client)));
+	module.merge(SystemRpc::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?;
+	module.merge(TransactionPaymentRpc::new(client).into_rpc())?;
 
 	// Extend this RPC with a custom API by using the following syntax.
 	// `YourRpcStruct` should have a reference to a client, which is needed
 	// to call into the runtime.
-	// `io.extend_with(YourRpcTrait::to_delegate(YourRpcStruct::new(ReferenceToClient, ...)));`
+	// `module.merge(YourRpcTrait::into_rpc(YourRpcStruct::new(ReferenceToClient, ...)))?;`
 
-	io
+	Ok(module)
 }
diff --git a/substrate/bin/node-template/node/src/service.rs b/substrate/bin/node-template/node/src/service.rs
index 5f46a16a9668f86b14a9c0410d902e1c6a75bb1a..f45f914d94f441e936f9f42b1e38b971cf754954 100644
--- a/substrate/bin/node-template/node/src/service.rs
+++ b/substrate/bin/node-template/node/src/service.rs
@@ -228,8 +228,7 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError>
 		Box::new(move |deny_unsafe, _| {
 			let deps =
 				crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe };
-
-			Ok(crate::rpc::create_full(deps))
+			crate::rpc::create_full(deps).map_err(Into::into)
 		})
 	};
 
@@ -239,7 +238,7 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError>
 		keystore: keystore_container.sync_keystore(),
 		task_manager: &mut task_manager,
 		transaction_pool: transaction_pool.clone(),
-		rpc_extensions_builder,
+		rpc_builder: rpc_extensions_builder,
 		backend,
 		system_rpc_tx,
 		config,
diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml
index 6bb36b9f9ab94b9d42fef8b5cdbac8e3fdd00765..c18f2f5d1a1082bbe18ba6a079b22198b55d33f9 100644
--- a/substrate/bin/node/cli/Cargo.toml
+++ b/substrate/bin/node/cli/Cargo.toml
@@ -37,6 +37,7 @@ crate-type = ["cdylib", "rlib"]
 clap = { version = "3.1.6", features = ["derive"], optional = true }
 codec = { package = "parity-scale-codec", version = "3.0.0" }
 serde = { version = "1.0.136", features = ["derive"] }
+jsonrpsee = { version = "0.12.0", features = ["server"] }
 futures = "0.3.21"
 hex-literal = "0.3.4"
 log = "0.4.16"
diff --git a/substrate/bin/node/cli/benches/block_production.rs b/substrate/bin/node/cli/benches/block_production.rs
index 6eab08c39e5a236b89cbee6c8cc7e3c6d4d7fe26..376241d8157bf5cffb10fbb2e2f9298acc7ec413 100644
--- a/substrate/bin/node/cli/benches/block_production.rs
+++ b/substrate/bin/node/cli/benches/block_production.rs
@@ -92,6 +92,10 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase {
 		rpc_cors: None,
 		rpc_methods: Default::default(),
 		rpc_max_payload: None,
+		rpc_max_request_size: None,
+		rpc_max_response_size: None,
+		rpc_id_provider: None,
+		rpc_max_subs_per_conn: None,
 		ws_max_out_buffer_capacity: None,
 		prometheus_config: None,
 		telemetry_endpoints: None,
diff --git a/substrate/bin/node/cli/benches/transaction_pool.rs b/substrate/bin/node/cli/benches/transaction_pool.rs
index eb0e24d2fdd379ea4b0a3a9ad6782249d090bc4e..f1fce16d8c1b37d03ee46314a655ae34f5cd6eb5 100644
--- a/substrate/bin/node/cli/benches/transaction_pool.rs
+++ b/substrate/bin/node/cli/benches/transaction_pool.rs
@@ -84,6 +84,10 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase {
 		rpc_cors: None,
 		rpc_methods: Default::default(),
 		rpc_max_payload: None,
+		rpc_max_request_size: None,
+		rpc_max_response_size: None,
+		rpc_id_provider: None,
+		rpc_max_subs_per_conn: None,
 		ws_max_out_buffer_capacity: None,
 		prometheus_config: None,
 		telemetry_endpoints: None,
diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs
index 01c7eb9abe1b7462912322080ab08fa612e47ac3..bff4be88002fb79466753544a1ac1013410d0e46 100644
--- a/substrate/bin/node/cli/src/service.rs
+++ b/substrate/bin/node/cli/src/service.rs
@@ -134,7 +134,7 @@ pub fn new_partial(
 			impl Fn(
 				node_rpc::DenyUnsafe,
 				sc_rpc::SubscriptionTaskExecutor,
-			) -> Result<node_rpc::IoHandler, sc_service::Error>,
+			) -> Result<jsonrpsee::RpcModule<()>, sc_service::Error>,
 			(
 				sc_consensus_babe::BabeBlockImport<Block, FullClient, FullGrandpaBlockImport>,
 				grandpa::LinkHalf<Block, FullClient, FullSelectChain>,
@@ -236,7 +236,7 @@ pub fn new_partial(
 		let justification_stream = grandpa_link.justification_stream();
 		let shared_authority_set = grandpa_link.shared_authority_set().clone();
 		let shared_voter_state = grandpa::SharedVoterState::empty();
-		let rpc_setup = shared_voter_state.clone();
+		let shared_voter_state2 = shared_voter_state.clone();
 
 		let finality_proof_provider = grandpa::FinalityProofProvider::new_for_service(
 			backend.clone(),
@@ -277,7 +277,7 @@ pub fn new_partial(
 			node_rpc::create_full(deps, rpc_backend.clone()).map_err(Into::into)
 		};
 
-		(rpc_extensions_builder, rpc_setup)
+		(rpc_extensions_builder, shared_voter_state2)
 	};
 
 	Ok(sc_service::PartialComponents {
@@ -332,7 +332,7 @@ pub fn new_full_base(
 		keystore_container,
 		select_chain,
 		transaction_pool,
-		other: (rpc_extensions_builder, import_setup, rpc_setup, mut telemetry),
+		other: (rpc_builder, import_setup, rpc_setup, mut telemetry),
 	} = new_partial(&config)?;
 
 	let shared_voter_state = rpc_setup;
@@ -386,7 +386,7 @@ pub fn new_full_base(
 		client: client.clone(),
 		keystore: keystore_container.sync_keystore(),
 		network: network.clone(),
-		rpc_extensions_builder: Box::new(rpc_extensions_builder),
+		rpc_builder: Box::new(rpc_builder),
 		transaction_pool: transaction_pool.clone(),
 		task_manager: &mut task_manager,
 		system_rpc_tx,
diff --git a/substrate/bin/node/cli/tests/common.rs b/substrate/bin/node/cli/tests/common.rs
index c17cabfa1d38a45e1ab972b653db26ec4d22ac7d..9c739c2cf2d28b55413ee5fe07d07526149f7b82 100644
--- a/substrate/bin/node/cli/tests/common.rs
+++ b/substrate/bin/node/cli/tests/common.rs
@@ -26,15 +26,14 @@ use nix::{
 use node_primitives::Block;
 use remote_externalities::rpc_api;
 use std::{
+	io::{BufRead, BufReader, Read},
 	ops::{Deref, DerefMut},
 	path::Path,
-	process::{Child, Command, ExitStatus},
+	process::{self, Child, Command, ExitStatus},
 	time::Duration,
 };
 use tokio::time::timeout;
 
-static LOCALHOST_WS: &str = "ws://127.0.0.1:9944/";
-
 /// Wait for the given `child` the given number of `secs`.
 ///
 /// Returns the `Some(exit status)` or `None` if the process did not finish in the given time.
@@ -63,8 +62,9 @@ pub fn wait_for(child: &mut Child, secs: u64) -> Result<ExitStatus, ()> {
 pub async fn wait_n_finalized_blocks(
 	n: usize,
 	timeout_secs: u64,
+	url: &str,
 ) -> Result<(), tokio::time::error::Elapsed> {
-	timeout(Duration::from_secs(timeout_secs), wait_n_finalized_blocks_from(n, LOCALHOST_WS)).await
+	timeout(Duration::from_secs(timeout_secs), wait_n_finalized_blocks_from(n, url)).await
 }
 
 /// Wait for at least n blocks to be finalized from a specified node
@@ -85,12 +85,23 @@ pub async fn wait_n_finalized_blocks_from(n: usize, url: &str) {
 
 /// Run the node for a while (3 blocks)
 pub async fn run_node_for_a_while(base_path: &Path, args: &[&str]) {
-	let mut cmd = Command::new(cargo_bin("substrate"));
+	let mut cmd = Command::new(cargo_bin("substrate"))
+		.stdout(process::Stdio::piped())
+		.stderr(process::Stdio::piped())
+		.args(args)
+		.arg("-d")
+		.arg(base_path)
+		.spawn()
+		.unwrap();
 
-	let mut child = KillChildOnDrop(cmd.args(args).arg("-d").arg(base_path).spawn().unwrap());
+	let stderr = cmd.stderr.take().unwrap();
+
+	let mut child = KillChildOnDrop(cmd);
+
+	let (ws_url, _) = find_ws_url_from_output(stderr);
 
 	// Let it produce some blocks.
-	let _ = wait_n_finalized_blocks(3, 30).await;
+	let _ = wait_n_finalized_blocks(3, 30, &ws_url).await;
 
 	assert!(child.try_wait().unwrap().is_none(), "the process should still be running");
 
@@ -134,3 +145,30 @@ impl DerefMut for KillChildOnDrop {
 		&mut self.0
 	}
 }
+
+/// Read the WS address from the output.
+///
+/// This is hack to get the actual binded sockaddr because
+/// substrate assigns a random port if the specified port was already binded.
+pub fn find_ws_url_from_output(read: impl Read + Send) -> (String, String) {
+	let mut data = String::new();
+
+	let ws_url = BufReader::new(read)
+		.lines()
+		.find_map(|line| {
+			let line =
+				line.expect("failed to obtain next line from stdout for WS address discovery");
+			data.push_str(&line);
+
+			// does the line contain our port (we expect this specific output from substrate).
+			let sock_addr = match line.split_once("Running JSON-RPC WS server: addr=") {
+				None => return None,
+				Some((_, after)) => after.split_once(",").unwrap().0,
+			};
+
+			Some(format!("ws://{}", sock_addr))
+		})
+		.expect("We should get a WebSocket address");
+
+	(ws_url, data)
+}
diff --git a/substrate/bin/node/cli/tests/running_the_node_and_interrupt.rs b/substrate/bin/node/cli/tests/running_the_node_and_interrupt.rs
index 35f0fc106613c20b339937bb3e36cbe2dddf7616..6d4a4b40425c4e60fc514e3769946618fbbd1816 100644
--- a/substrate/bin/node/cli/tests/running_the_node_and_interrupt.rs
+++ b/substrate/bin/node/cli/tests/running_the_node_and_interrupt.rs
@@ -25,7 +25,7 @@ use nix::{
 	},
 	unistd::Pid,
 };
-use std::process::{Child, Command};
+use std::process::{self, Child, Command};
 use tempfile::tempdir;
 
 pub mod common;
@@ -36,6 +36,8 @@ async fn running_the_node_works_and_can_be_interrupted() {
 		let base_path = tempdir().expect("could not create a temp dir");
 		let mut cmd = common::KillChildOnDrop(
 			Command::new(cargo_bin("substrate"))
+				.stdout(process::Stdio::piped())
+				.stderr(process::Stdio::piped())
 				.args(&["--dev", "-d"])
 				.arg(base_path.path())
 				.arg("--db=paritydb")
@@ -44,7 +46,13 @@ async fn running_the_node_works_and_can_be_interrupted() {
 				.unwrap(),
 		);
 
-		common::wait_n_finalized_blocks(3, 30).await.unwrap();
+		let stderr = cmd.stderr.take().unwrap();
+
+		let (ws_url, _) = common::find_ws_url_from_output(stderr);
+
+		common::wait_n_finalized_blocks(3, 30, &ws_url)
+			.await
+			.expect("Blocks are produced in time");
 		assert!(cmd.try_wait().unwrap().is_none(), "the process should still be running");
 		kill(Pid::from_raw(cmd.id().try_into().unwrap()), signal).unwrap();
 		assert_eq!(
@@ -69,6 +77,8 @@ async fn running_the_node_works_and_can_be_interrupted() {
 async fn running_two_nodes_with_the_same_ws_port_should_work() {
 	fn start_node() -> Child {
 		Command::new(cargo_bin("substrate"))
+			.stdout(process::Stdio::piped())
+			.stderr(process::Stdio::piped())
 			.args(&["--dev", "--tmp", "--ws-port=45789", "--no-hardware-benchmarks"])
 			.spawn()
 			.unwrap()
@@ -77,7 +87,10 @@ async fn running_two_nodes_with_the_same_ws_port_should_work() {
 	let mut first_node = common::KillChildOnDrop(start_node());
 	let mut second_node = common::KillChildOnDrop(start_node());
 
-	let _ = common::wait_n_finalized_blocks(3, 30).await;
+	let stderr = first_node.stderr.take().unwrap();
+	let (ws_url, _) = common::find_ws_url_from_output(stderr);
+
+	common::wait_n_finalized_blocks(3, 30, &ws_url).await.unwrap();
 
 	assert!(first_node.try_wait().unwrap().is_none(), "The first node should still be running");
 	assert!(second_node.try_wait().unwrap().is_none(), "The second node should still be running");
diff --git a/substrate/bin/node/cli/tests/temp_base_path_works.rs b/substrate/bin/node/cli/tests/temp_base_path_works.rs
index df293161e3234b9d328e5d376edb606e559cec48..98422a21f5308a1f4e6d6e4a6359616c9d1127ca 100644
--- a/substrate/bin/node/cli/tests/temp_base_path_works.rs
+++ b/substrate/bin/node/cli/tests/temp_base_path_works.rs
@@ -43,8 +43,11 @@ async fn temp_base_path_works() {
 			.unwrap(),
 	);
 
+	let mut stderr = child.stderr.take().unwrap();
+	let (ws_url, mut data) = common::find_ws_url_from_output(&mut stderr);
+
 	// Let it produce some blocks.
-	common::wait_n_finalized_blocks(3, 30).await.unwrap();
+	common::wait_n_finalized_blocks(3, 30, &ws_url).await.unwrap();
 	assert!(child.try_wait().unwrap().is_none(), "the process should still be running");
 
 	// Stop the process
@@ -52,10 +55,9 @@ async fn temp_base_path_works() {
 	assert!(common::wait_for(&mut child, 40).map(|x| x.success()).unwrap_or_default());
 
 	// Ensure the database has been deleted
-	let mut stderr = String::new();
-	child.stderr.as_mut().unwrap().read_to_string(&mut stderr).unwrap();
+	stderr.read_to_string(&mut data).unwrap();
 	let re = Regex::new(r"Database: .+ at (\S+)").unwrap();
-	let db_path = PathBuf::from(re.captures(stderr.as_str()).unwrap().get(1).unwrap().as_str());
+	let db_path = PathBuf::from(re.captures(data.as_str()).unwrap().get(1).unwrap().as_str());
 
 	assert!(!db_path.exists());
 }
diff --git a/substrate/bin/node/rpc/Cargo.toml b/substrate/bin/node/rpc/Cargo.toml
index f0ae8b42e6398111e6d8614ec66c1070584b6321..9520c621d3165a2600677ab3ed518ddad2561681 100644
--- a/substrate/bin/node/rpc/Cargo.toml
+++ b/substrate/bin/node/rpc/Cargo.toml
@@ -11,7 +11,7 @@ repository = "https://github.com/paritytech/substrate/"
 targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
-jsonrpc-core = "18.0.0"
+jsonrpsee = { version = "0.12.0", features = ["server"] }
 node-primitives = { version = "2.0.0", path = "../primitives" }
 pallet-contracts-rpc = { version = "4.0.0-dev", path = "../../../frame/contracts/rpc/" }
 pallet-mmr-rpc = { version = "3.0.0", path = "../../../frame/merkle-mountain-range/rpc/" }
diff --git a/substrate/bin/node/rpc/src/lib.rs b/substrate/bin/node/rpc/src/lib.rs
index b8349e26cd1dafb93265f709fbcfe6c152e1b917..05aa973e102b1c2da84f6cb252af60738de58ddc 100644
--- a/substrate/bin/node/rpc/src/lib.rs
+++ b/substrate/bin/node/rpc/src/lib.rs
@@ -29,18 +29,20 @@
 //! be placed here or imported from corresponding FRAME RPC definitions.
 
 #![warn(missing_docs)]
+#![warn(unused_crate_dependencies)]
 
 use std::sync::Arc;
 
+use jsonrpsee::RpcModule;
 use node_primitives::{AccountId, Balance, Block, BlockNumber, Hash, Index};
 use sc_client_api::AuxStore;
 use sc_consensus_babe::{Config, Epoch};
-use sc_consensus_babe_rpc::BabeRpcHandler;
+use sc_consensus_babe_rpc::BabeRpc;
 use sc_consensus_epochs::SharedEpochChanges;
 use sc_finality_grandpa::{
 	FinalityProofProvider, GrandpaJustificationStream, SharedAuthoritySet, SharedVoterState,
 };
-use sc_finality_grandpa_rpc::GrandpaRpcHandler;
+use sc_finality_grandpa_rpc::GrandpaRpc;
 use sc_rpc::SubscriptionTaskExecutor;
 pub use sc_rpc_api::DenyUnsafe;
 use sc_transaction_pool_api::TransactionPool;
@@ -93,14 +95,11 @@ pub struct FullDeps<C, P, SC, B> {
 	pub grandpa: GrandpaDeps<B>,
 }
 
-/// A IO handler that uses all Full RPC extensions.
-pub type IoHandler = jsonrpc_core::IoHandler<sc_rpc::Metadata>;
-
 /// Instantiate all Full RPC extensions.
 pub fn create_full<C, P, SC, B>(
 	deps: FullDeps<C, P, SC, B>,
 	backend: Arc<B>,
-) -> Result<jsonrpc_core::IoHandler<sc_rpc_api::Metadata>, Box<dyn std::error::Error + Send + Sync>>
+) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
 where
 	C: ProvideRuntimeApi<Block>
 		+ sc_client_api::BlockBackend<Block>
@@ -121,13 +120,17 @@ where
 	B: sc_client_api::Backend<Block> + Send + Sync + 'static,
 	B::State: sc_client_api::backend::StateBackend<sp_runtime::traits::HashFor<Block>>,
 {
-	use pallet_contracts_rpc::{Contracts, ContractsApi};
-	use pallet_mmr_rpc::{Mmr, MmrApi};
-	use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi};
-	use sc_rpc::dev::{Dev, DevApi};
-	use substrate_frame_rpc_system::{FullSystem, SystemApi};
+	use pallet_contracts_rpc::{ContractsApiServer, ContractsRpc};
+	use pallet_mmr_rpc::{MmrApiServer, MmrRpc};
+	use pallet_transaction_payment_rpc::{TransactionPaymentApiServer, TransactionPaymentRpc};
+	use sc_consensus_babe_rpc::BabeApiServer;
+	use sc_finality_grandpa_rpc::GrandpaApiServer;
+	use sc_rpc::dev::{Dev, DevApiServer};
+	use sc_sync_state_rpc::{SyncStateRpc, SyncStateRpcApiServer};
+	use substrate_frame_rpc_system::{SystemApiServer, SystemRpc};
+	use substrate_state_trie_migration_rpc::StateMigrationApiServer;
 
-	let mut io = jsonrpc_core::IoHandler::default();
+	let mut io = RpcModule::new(());
 	let FullDeps { client, pool, select_chain, chain_spec, deny_unsafe, babe, grandpa } = deps;
 
 	let BabeDeps { keystore, babe_config, shared_epoch_changes } = babe;
@@ -139,40 +142,45 @@ where
 		finality_provider,
 	} = grandpa;
 
-	io.extend_with(SystemApi::to_delegate(FullSystem::new(client.clone(), pool, deny_unsafe)));
+	io.merge(SystemRpc::new(client.clone(), pool, deny_unsafe).into_rpc())?;
 	// Making synchronous calls in light client freezes the browser currently,
 	// more context: https://github.com/paritytech/substrate/pull/3480
 	// These RPCs should use an asynchronous caller instead.
-	io.extend_with(ContractsApi::to_delegate(Contracts::new(client.clone())));
-	io.extend_with(MmrApi::to_delegate(Mmr::new(client.clone())));
-	io.extend_with(TransactionPaymentApi::to_delegate(TransactionPayment::new(client.clone())));
-	io.extend_with(sc_consensus_babe_rpc::BabeApi::to_delegate(BabeRpcHandler::new(
-		client.clone(),
-		shared_epoch_changes.clone(),
-		keystore,
-		babe_config,
-		select_chain,
-		deny_unsafe,
-	)));
-	io.extend_with(sc_finality_grandpa_rpc::GrandpaApi::to_delegate(GrandpaRpcHandler::new(
-		shared_authority_set.clone(),
-		shared_voter_state,
-		justification_stream,
-		subscription_executor,
-		finality_provider,
-	)));
-	io.extend_with(substrate_state_trie_migration_rpc::StateMigrationApi::to_delegate(
-		substrate_state_trie_migration_rpc::MigrationRpc::new(client.clone(), backend, deny_unsafe),
-	));
-	io.extend_with(sc_sync_state_rpc::SyncStateRpcApi::to_delegate(
-		sc_sync_state_rpc::SyncStateRpcHandler::new(
-			chain_spec,
+	io.merge(ContractsRpc::new(client.clone()).into_rpc())?;
+	io.merge(MmrRpc::new(client.clone()).into_rpc())?;
+	io.merge(TransactionPaymentRpc::new(client.clone()).into_rpc())?;
+	io.merge(
+		BabeRpc::new(
 			client.clone(),
-			shared_authority_set,
-			shared_epoch_changes,
-		)?,
-	));
-	io.extend_with(DevApi::to_delegate(Dev::new(client, deny_unsafe)));
+			shared_epoch_changes.clone(),
+			keystore,
+			babe_config,
+			select_chain,
+			deny_unsafe,
+		)
+		.into_rpc(),
+	)?;
+	io.merge(
+		GrandpaRpc::new(
+			subscription_executor,
+			shared_authority_set.clone(),
+			shared_voter_state,
+			justification_stream,
+			finality_provider,
+		)
+		.into_rpc(),
+	)?;
+
+	io.merge(
+		SyncStateRpc::new(chain_spec, client.clone(), shared_authority_set, shared_epoch_changes)?
+			.into_rpc(),
+	)?;
+
+	io.merge(
+		substrate_state_trie_migration_rpc::MigrationRpc::new(client.clone(), backend, deny_unsafe)
+			.into_rpc(),
+	)?;
+	io.merge(Dev::new(client, deny_unsafe).into_rpc())?;
 
 	Ok(io)
 }
diff --git a/substrate/client/beefy/Cargo.toml b/substrate/client/beefy/Cargo.toml
index 4b6496e52c2f28c54eaa0b3d049070cbcc9ea120..bd25496f2dfea3f3eaf07eddf2325b2c27f1fb55 100644
--- a/substrate/client/beefy/Cargo.toml
+++ b/substrate/client/beefy/Cargo.toml
@@ -41,7 +41,7 @@ sp-runtime = { version = "6.0.0", path = "../../primitives/runtime" }
 serde = "1.0.136"
 strum = { version = "0.23", features = ["derive"] }
 tempfile = "3.1.0"
-tokio = "1.15"
+tokio = "1.17.0"
 sc-consensus = { version = "0.10.0-dev", path = "../consensus/common" }
 sc-network-test = { version = "0.8.0", path = "../network/test" }
 sp-finality-grandpa = { version = "4.0.0-dev", path = "../../primitives/finality-grandpa" }
diff --git a/substrate/client/beefy/rpc/Cargo.toml b/substrate/client/beefy/rpc/Cargo.toml
index adf29f1cef7321b874a5382b7c673cd873e315dd..f8ca6470f267a6ab7189fcd08c9e45d38ec948b9 100644
--- a/substrate/client/beefy/rpc/Cargo.toml
+++ b/substrate/client/beefy/rpc/Cargo.toml
@@ -11,10 +11,7 @@ homepage = "https://substrate.io"
 [dependencies]
 codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] }
 futures = "0.3.21"
-jsonrpc-core = "18.0.0"
-jsonrpc-core-client = "18.0.0"
-jsonrpc-derive = "18.0.0"
-jsonrpc-pubsub = "18.0.0"
+jsonrpsee = { version = "0.12.0", features = ["server", "macros"] }
 log = "0.4"
 parking_lot = "0.12.0"
 serde = { version = "1.0.136", features = ["derive"] }
@@ -32,3 +29,4 @@ sc-rpc = { version = "4.0.0-dev", features = [
 	"test-helpers",
 ], path = "../../rpc" }
 substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" }
+tokio = { version = "1.17.0", features = ["macros"] }
diff --git a/substrate/client/beefy/rpc/src/lib.rs b/substrate/client/beefy/rpc/src/lib.rs
index e49af3352ae4ed81cb9c766add412ad380881bc8..e4c8c76419ccbcecf031e45956c4c3411ec0687a 100644
--- a/substrate/client/beefy/rpc/src/lib.rs
+++ b/substrate/client/beefy/rpc/src/lib.rs
@@ -23,19 +23,22 @@
 use parking_lot::RwLock;
 use std::sync::Arc;
 
+use sc_rpc::SubscriptionTaskExecutor;
 use sp_runtime::traits::Block as BlockT;
 
-use futures::{task::SpawnError, FutureExt, SinkExt, StreamExt, TryFutureExt};
-use jsonrpc_derive::rpc;
-use jsonrpc_pubsub::{manager::SubscriptionManager, typed::Subscriber, SubscriptionId};
+use futures::{task::SpawnError, FutureExt, StreamExt};
+use jsonrpsee::{
+	core::{async_trait, Error as JsonRpseeError, RpcResult},
+	proc_macros::rpc,
+	types::{error::CallError, ErrorObject},
+	PendingSubscription,
+};
 use log::warn;
 
 use beefy_gadget::notification::{BeefyBestBlockStream, BeefySignedCommitmentStream};
 
 mod notification;
 
-type FutureResult<T> = jsonrpc_core::BoxFuture<Result<T, jsonrpc_core::Error>>;
-
 #[derive(Debug, thiserror::Error)]
 /// Top-level error type for the RPC handler
 pub enum Error {
@@ -64,195 +67,149 @@ impl From<Error> for ErrorCode {
 	}
 }
 
-impl From<Error> for jsonrpc_core::Error {
+impl From<Error> for JsonRpseeError {
 	fn from(error: Error) -> Self {
-		let message = format!("{}", error);
+		let message = error.to_string();
 		let code = ErrorCode::from(error);
-		jsonrpc_core::Error {
+		JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
+			code as i32,
 			message,
-			code: jsonrpc_core::ErrorCode::ServerError(code as i64),
-			data: None,
-		}
+			None::<()>,
+		)))
 	}
 }
 
-/// Provides RPC methods for interacting with BEEFY.
-#[rpc]
+// Provides RPC methods for interacting with BEEFY.
+#[rpc(client, server)]
 pub trait BeefyApi<Notification, Hash> {
-	/// RPC Metadata
-	type Metadata;
-
 	/// Returns the block most recently finalized by BEEFY, alongside side its justification.
-	#[pubsub(
-		subscription = "beefy_justifications",
-		subscribe,
-		name = "beefy_subscribeJustifications"
-	)]
-	fn subscribe_justifications(
-		&self,
-		metadata: Self::Metadata,
-		subscriber: Subscriber<Notification>,
-	);
-
-	/// Unsubscribe from receiving notifications about recently finalized blocks.
-	#[pubsub(
-		subscription = "beefy_justifications",
-		unsubscribe,
-		name = "beefy_unsubscribeJustifications"
+	#[subscription(
+		name = "beefy_subscribeJustifications" => "beefy_justifications",
+		unsubscribe = "beefy_unsubscribeJustifications",
+		item = Notification,
 	)]
-	fn unsubscribe_justifications(
-		&self,
-		metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> jsonrpc_core::Result<bool>;
+	fn subscribe_justifications(&self);
 
 	/// Returns hash of the latest BEEFY finalized block as seen by this client.
 	///
 	/// The latest BEEFY block might not be available if the BEEFY gadget is not running
 	/// in the network or if the client is still initializing or syncing with the network.
 	/// In such case an error would be returned.
-	#[rpc(name = "beefy_getFinalizedHead")]
-	fn latest_finalized(&self) -> FutureResult<Hash>;
+	#[method(name = "beefy_getFinalizedHead")]
+	async fn latest_finalized(&self) -> RpcResult<Hash>;
 }
 
 /// Implements the BeefyApi RPC trait for interacting with BEEFY.
 pub struct BeefyRpcHandler<Block: BlockT> {
 	signed_commitment_stream: BeefySignedCommitmentStream<Block>,
 	beefy_best_block: Arc<RwLock<Option<Block::Hash>>>,
-	manager: SubscriptionManager,
+	executor: SubscriptionTaskExecutor,
 }
 
-impl<Block: BlockT> BeefyRpcHandler<Block> {
+impl<Block> BeefyRpcHandler<Block>
+where
+	Block: BlockT,
+{
 	/// Creates a new BeefyRpcHandler instance.
-	pub fn new<E>(
+	pub fn new(
 		signed_commitment_stream: BeefySignedCommitmentStream<Block>,
 		best_block_stream: BeefyBestBlockStream<Block>,
-		executor: E,
-	) -> Result<Self, Error>
-	where
-		E: futures::task::Spawn + Send + Sync + 'static,
-	{
+		executor: SubscriptionTaskExecutor,
+	) -> Result<Self, Error> {
 		let beefy_best_block = Arc::new(RwLock::new(None));
 
 		let stream = best_block_stream.subscribe();
 		let closure_clone = beefy_best_block.clone();
 		let future = stream.for_each(move |best_beefy| {
 			let async_clone = closure_clone.clone();
-			async move {
-				*async_clone.write() = Some(best_beefy);
-			}
+			async move { *async_clone.write() = Some(best_beefy) }
 		});
 
-		executor
-			.spawn_obj(futures::task::FutureObj::new(Box::pin(future)))
-			.map_err(|e| {
-				log::error!("Failed to spawn BEEFY RPC background task; err: {}", e);
-				e
-			})?;
-
-		let manager = SubscriptionManager::new(Arc::new(executor));
-		Ok(Self { signed_commitment_stream, beefy_best_block, manager })
+		executor.spawn("substrate-rpc-subscription", Some("rpc"), future.map(drop).boxed());
+		Ok(Self { signed_commitment_stream, beefy_best_block, executor })
 	}
 }
 
-impl<Block> BeefyApi<notification::EncodedSignedCommitment, Block::Hash> for BeefyRpcHandler<Block>
+#[async_trait]
+impl<Block> BeefyApiServer<notification::EncodedSignedCommitment, Block::Hash>
+	for BeefyRpcHandler<Block>
 where
 	Block: BlockT,
 {
-	type Metadata = sc_rpc::Metadata;
-
-	fn subscribe_justifications(
-		&self,
-		_metadata: Self::Metadata,
-		subscriber: Subscriber<notification::EncodedSignedCommitment>,
-	) {
+	fn subscribe_justifications(&self, pending: PendingSubscription) {
 		let stream = self
 			.signed_commitment_stream
 			.subscribe()
-			.map(|x| Ok::<_, ()>(Ok(notification::EncodedSignedCommitment::new::<Block>(x))));
+			.map(|sc| notification::EncodedSignedCommitment::new::<Block>(sc));
 
-		self.manager.add(subscriber, |sink| {
-			stream
-				.forward(sink.sink_map_err(|e| warn!("Error sending notifications: {:?}", e)))
-				.map(|_| ())
-		});
-	}
+		let fut = async move {
+			if let Some(mut sink) = pending.accept() {
+				sink.pipe_from_stream(stream).await;
+			}
+		}
+		.boxed();
 
-	fn unsubscribe_justifications(
-		&self,
-		_metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> jsonrpc_core::Result<bool> {
-		Ok(self.manager.cancel(id))
+		self.executor
+			.spawn("substrate-rpc-subscription", Some("rpc"), fut.map(drop).boxed());
 	}
 
-	fn latest_finalized(&self) -> FutureResult<Block::Hash> {
-		let result: Result<Block::Hash, jsonrpc_core::Error> = self
-			.beefy_best_block
+	async fn latest_finalized(&self) -> RpcResult<Block::Hash> {
+		self.beefy_best_block
 			.read()
 			.as_ref()
 			.cloned()
-			.ok_or_else(|| Error::EndpointNotReady.into());
-		let future = async move { result }.boxed();
-		future.map_err(jsonrpc_core::Error::from).boxed()
+			.ok_or(Error::EndpointNotReady)
+			.map_err(Into::into)
 	}
 }
 
 #[cfg(test)]
 mod tests {
 	use super::*;
-	use jsonrpc_core::{types::Params, Notification, Output};
 
-	use beefy_gadget::notification::{BeefySignedCommitment, BeefySignedCommitmentSender};
+	use beefy_gadget::notification::{
+		BeefyBestBlockStream, BeefySignedCommitment, BeefySignedCommitmentSender,
+	};
 	use beefy_primitives::{known_payload_ids, Payload};
 	use codec::{Decode, Encode};
+	use jsonrpsee::{types::EmptyParams, RpcModule};
 	use sp_runtime::traits::{BlakeTwo256, Hash};
 	use substrate_test_runtime_client::runtime::Block;
 
-	fn setup_io_handler(
-	) -> (jsonrpc_core::MetaIoHandler<sc_rpc::Metadata>, BeefySignedCommitmentSender<Block>) {
+	fn setup_io_handler() -> (RpcModule<BeefyRpcHandler<Block>>, BeefySignedCommitmentSender<Block>)
+	{
 		let (_, stream) = BeefyBestBlockStream::<Block>::channel();
 		setup_io_handler_with_best_block_stream(stream)
 	}
 
 	fn setup_io_handler_with_best_block_stream(
 		best_block_stream: BeefyBestBlockStream<Block>,
-	) -> (jsonrpc_core::MetaIoHandler<sc_rpc::Metadata>, BeefySignedCommitmentSender<Block>) {
+	) -> (RpcModule<BeefyRpcHandler<Block>>, BeefySignedCommitmentSender<Block>) {
 		let (commitment_sender, commitment_stream) =
 			BeefySignedCommitmentStream::<Block>::channel();
 
-		let handler: BeefyRpcHandler<Block> = BeefyRpcHandler::new(
+		let handler = BeefyRpcHandler::new(
 			commitment_stream,
 			best_block_stream,
-			sc_rpc::testing::TaskExecutor,
+			sc_rpc::testing::test_executor(),
 		)
-		.unwrap();
+		.expect("Setting up the BEEFY RPC handler works");
 
-		let mut io = jsonrpc_core::MetaIoHandler::default();
-		io.extend_with(BeefyApi::to_delegate(handler));
-
-		(io, commitment_sender)
-	}
-
-	fn setup_session() -> (sc_rpc::Metadata, futures::channel::mpsc::UnboundedReceiver<String>) {
-		let (tx, rx) = futures::channel::mpsc::unbounded();
-		let meta = sc_rpc::Metadata::new(tx);
-		(meta, rx)
+		(handler.into_rpc(), commitment_sender)
 	}
 
-	#[test]
-	fn uninitialized_rpc_handler() {
-		let (io, _) = setup_io_handler();
-
+	#[tokio::test]
+	async fn uninitialized_rpc_handler() {
+		let (rpc, _) = setup_io_handler();
 		let request = r#"{"jsonrpc":"2.0","method":"beefy_getFinalizedHead","params":[],"id":1}"#;
-		let response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"BEEFY RPC endpoint not ready"},"id":1}"#;
+		let expected_response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"BEEFY RPC endpoint not ready"},"id":1}"#.to_string();
+		let (result, _) = rpc.raw_json_request(&request).await.unwrap();
 
-		let meta = sc_rpc::Metadata::default();
-		assert_eq!(Some(response.into()), io.handle_request_sync(request, meta));
+		assert_eq!(expected_response, result,);
 	}
 
-	#[test]
-	fn latest_finalized_rpc() {
+	#[tokio::test]
+	async fn latest_finalized_rpc() {
 		let (sender, stream) = BeefyBestBlockStream::<Block>::channel();
 		let (io, _) = setup_io_handler_with_best_block_stream(stream);
 
@@ -266,83 +223,78 @@ mod tests {
 			\"jsonrpc\":\"2.0\",\
 			\"result\":\"0x2f0039e93a27221fcf657fb877a1d4f60307106113e885096cb44a461cd0afbf\",\
 			\"id\":1\
-		}";
+		}"
+		.to_string();
 		let not_ready = "{\
 			\"jsonrpc\":\"2.0\",\
 			\"error\":{\"code\":1,\"message\":\"BEEFY RPC endpoint not ready\"},\
 			\"id\":1\
-		}";
+		}"
+		.to_string();
 
 		let deadline = std::time::Instant::now() + std::time::Duration::from_secs(2);
 		while std::time::Instant::now() < deadline {
-			let meta = sc_rpc::Metadata::default();
-			let response = io.handle_request_sync(request, meta);
-			// Retry "not ready" responses.
-			if response != Some(not_ready.into()) {
-				assert_eq!(response, Some(expected.into()));
+			let (response, _) = io.raw_json_request(request).await.expect("RPC requests work");
+			if response != not_ready {
+				assert_eq!(response, expected);
 				// Success
 				return
 			}
-			std::thread::sleep(std::time::Duration::from_millis(50));
+			std::thread::sleep(std::time::Duration::from_millis(50))
 		}
+
 		panic!(
 			"Deadline reached while waiting for best BEEFY block to update. Perhaps the background task is broken?"
 		);
 	}
 
-	#[test]
-	fn subscribe_and_unsubscribe_to_justifications() {
-		let (io, _) = setup_io_handler();
-		let (meta, _) = setup_session();
+	#[tokio::test]
+	async fn subscribe_and_unsubscribe_to_justifications() {
+		let (rpc, _) = setup_io_handler();
 
-		// Subscribe
-		let sub_request =
-			r#"{"jsonrpc":"2.0","method":"beefy_subscribeJustifications","params":[],"id":1}"#;
-		let resp = io.handle_request_sync(sub_request, meta.clone());
-		let resp: Output = serde_json::from_str(&resp.unwrap()).unwrap();
+		// Subscribe call.
+		let sub = rpc
+			.subscribe("beefy_subscribeJustifications", EmptyParams::new())
+			.await
+			.unwrap();
 
-		let sub_id = match resp {
-			Output::Success(success) => success.result,
-			_ => panic!(),
-		};
+		let ser_id = serde_json::to_string(sub.subscription_id()).unwrap();
 
 		// Unsubscribe
 		let unsub_req = format!(
-			r#"{{"jsonrpc":"2.0","method":"beefy_unsubscribeJustifications","params":[{}],"id":1}}"#,
-			sub_id
-		);
-		assert_eq!(
-			io.handle_request_sync(&unsub_req, meta.clone()),
-			Some(r#"{"jsonrpc":"2.0","result":true,"id":1}"#.into()),
+			"{{\"jsonrpc\":\"2.0\",\"method\":\"beefy_unsubscribeJustifications\",\"params\":[{}],\"id\":1}}",
+			ser_id
 		);
+		let (response, _) = rpc.raw_json_request(&unsub_req).await.unwrap();
+
+		assert_eq!(response, r#"{"jsonrpc":"2.0","result":true,"id":1}"#);
 
 		// Unsubscribe again and fail
-		assert_eq!(
-			io.handle_request_sync(&unsub_req, meta),
-			Some(r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid subscription id."},"id":1}"#.into()),
-		);
-	}
+		let (response, _) = rpc.raw_json_request(&unsub_req).await.unwrap();
+		let expected = r#"{"jsonrpc":"2.0","result":false,"id":1}"#;
 
-	#[test]
-	fn subscribe_and_unsubscribe_with_wrong_id() {
-		let (io, _) = setup_io_handler();
-		let (meta, _) = setup_session();
+		assert_eq!(response, expected);
+	}
 
-		// Subscribe
-		let sub_request =
-			r#"{"jsonrpc":"2.0","method":"beefy_subscribeJustifications","params":[],"id":1}"#;
-		let resp = io.handle_request_sync(sub_request, meta.clone());
-		let resp: Output = serde_json::from_str(&resp.unwrap()).unwrap();
-		assert!(matches!(resp, Output::Success(_)));
+	#[tokio::test]
+	async fn subscribe_and_unsubscribe_with_wrong_id() {
+		let (rpc, _) = setup_io_handler();
+		// Subscribe call.
+		let _sub = rpc
+			.subscribe("beefy_subscribeJustifications", EmptyParams::new())
+			.await
+			.unwrap();
 
 		// Unsubscribe with wrong ID
-		assert_eq!(
-			io.handle_request_sync(
+		let (response, _) = rpc
+			.raw_json_request(
 				r#"{"jsonrpc":"2.0","method":"beefy_unsubscribeJustifications","params":["FOO"],"id":1}"#,
-				meta.clone()
-			),
-			Some(r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid subscription id."},"id":1}"#.into())
-		);
+			)
+			.await
+			.unwrap();
+		let expected = r#"{"jsonrpc":"2.0","result":false,"id":1}"#;
+
+		assert_eq!(response, expected);
 	}
 
 	fn create_commitment() -> BeefySignedCommitment<Block> {
@@ -357,18 +309,15 @@ mod tests {
 		}
 	}
 
-	#[test]
-	fn subscribe_and_listen_to_one_justification() {
-		let (io, commitment_sender) = setup_io_handler();
-		let (meta, receiver) = setup_session();
+	#[tokio::test]
+	async fn subscribe_and_listen_to_one_justification() {
+		let (rpc, commitment_sender) = setup_io_handler();
 
 		// Subscribe
-		let sub_request =
-			r#"{"jsonrpc":"2.0","method":"beefy_subscribeJustifications","params":[],"id":1}"#;
-
-		let resp = io.handle_request_sync(sub_request, meta.clone());
-		let mut resp: serde_json::Value = serde_json::from_str(&resp.unwrap()).unwrap();
-		let sub_id: String = serde_json::from_value(resp["result"].take()).unwrap();
+		let mut sub = rpc
+			.subscribe("beefy_subscribeJustifications", EmptyParams::new())
+			.await
+			.unwrap();
 
 		// Notify with commitment
 		let commitment = create_commitment();
@@ -376,21 +325,10 @@ mod tests {
 		r.unwrap();
 
 		// Inspect what we received
-		let recv = futures::executor::block_on(receiver.take(1).collect::<Vec<_>>());
-		let recv: Notification = serde_json::from_str(&recv[0]).unwrap();
-		let mut json_map = match recv.params {
-			Params::Map(json_map) => json_map,
-			_ => panic!(),
-		};
-
-		let recv_sub_id: String = serde_json::from_value(json_map["subscription"].take()).unwrap();
-		let recv_commitment: sp_core::Bytes =
-			serde_json::from_value(json_map["result"].take()).unwrap();
+		let (bytes, recv_sub_id) = sub.next::<sp_core::Bytes>().await.unwrap().unwrap();
 		let recv_commitment: BeefySignedCommitment<Block> =
-			Decode::decode(&mut &recv_commitment[..]).unwrap();
-
-		assert_eq!(recv.method, "beefy_justifications");
-		assert_eq!(recv_sub_id, sub_id);
+			Decode::decode(&mut &bytes[..]).unwrap();
+		assert_eq!(&recv_sub_id, sub.subscription_id());
 		assert_eq!(recv_commitment, commitment);
 	}
 }
diff --git a/substrate/client/cli/src/commands/run_cmd.rs b/substrate/client/cli/src/commands/run_cmd.rs
index dea1e2a6dae5b33da7c1cfeec180ab91725839d5..6cb0de0ebd04cff09b0347e49b49b107a3d924f9 100644
--- a/substrate/client/cli/src/commands/run_cmd.rs
+++ b/substrate/client/cli/src/commands/run_cmd.rs
@@ -100,18 +100,28 @@ pub struct RunCmd {
 	#[clap(long)]
 	pub unsafe_ws_external: bool,
 
-	/// Set the the maximum RPC payload size for both requests and responses (both http and ws), in
-	/// megabytes. Default is 15MiB.
+	/// DEPRECATED, this has no affect anymore. Use `rpc_max_request_size` or
+	/// `rpc_max_response_size` instead.
 	#[clap(long)]
 	pub rpc_max_payload: Option<usize>,
 
+	/// Set the the maximum RPC request payload size for both HTTP and WS in megabytes.
+	/// Default is 15MiB.
+	#[clap(long)]
+	pub rpc_max_request_size: Option<usize>,
+
+	/// Set the the maximum RPC response payload size for both HTTP and WS in megabytes.
+	/// Default is 15MiB.
+	#[clap(long)]
+	pub rpc_max_response_size: Option<usize>,
+
 	/// Expose Prometheus exporter on all interfaces.
 	///
 	/// Default is local.
 	#[clap(long)]
 	pub prometheus_external: bool,
 
-	/// Specify IPC RPC server path
+	/// DEPRECATED, IPC support has been removed.
 	#[clap(long, value_name = "PATH")]
 	pub ipc_path: Option<String>,
 
@@ -127,7 +137,7 @@ pub struct RunCmd {
 	#[clap(long, value_name = "COUNT")]
 	pub ws_max_connections: Option<usize>,
 
-	/// Set the the maximum WebSocket output buffer size in MiB. Default is 16.
+	/// DEPRECATED, this has no affect anymore. Use `rpc_max_response_size` instead.
 	#[clap(long)]
 	pub ws_max_out_buffer_capacity: Option<usize>,
 
diff --git a/substrate/client/cli/src/config.rs b/substrate/client/cli/src/config.rs
index aef1da8193757a182f1a7e7ad89f18ac5b565cea..e38d34b92c74d3fc93a6e7d0ae88f902b687c9fb 100644
--- a/substrate/client/cli/src/config.rs
+++ b/substrate/client/cli/src/config.rs
@@ -359,6 +359,16 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
 		Ok(None)
 	}
 
+	/// Get maximum RPC request payload size.
+	fn rpc_max_request_size(&self) -> Result<Option<usize>> {
+		Ok(None)
+	}
+
+	/// Get maximum RPC response payload size.
+	fn rpc_max_response_size(&self) -> Result<Option<usize>> {
+		Ok(None)
+	}
+
 	/// Get maximum WS output buffer capacity.
 	fn ws_max_out_buffer_capacity(&self) -> Result<Option<usize>> {
 		Ok(None)
@@ -526,6 +536,10 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
 			rpc_ws_max_connections: self.rpc_ws_max_connections()?,
 			rpc_cors: self.rpc_cors(is_dev)?,
 			rpc_max_payload: self.rpc_max_payload()?,
+			rpc_max_request_size: self.rpc_max_request_size()?,
+			rpc_max_response_size: self.rpc_max_response_size()?,
+			rpc_id_provider: None,
+			rpc_max_subs_per_conn: None,
 			ws_max_out_buffer_capacity: self.ws_max_out_buffer_capacity()?,
 			prometheus_config: self
 				.prometheus_config(DCV::prometheus_listen_port(), &chain_spec)?,
diff --git a/substrate/client/consensus/babe/rpc/Cargo.toml b/substrate/client/consensus/babe/rpc/Cargo.toml
index 0e7141c77f8b206bc645ac854ae4c0548927e2d2..4be5d1f8bba9081de06590d9dc00724f334430e1 100644
--- a/substrate/client/consensus/babe/rpc/Cargo.toml
+++ b/substrate/client/consensus/babe/rpc/Cargo.toml
@@ -13,10 +13,8 @@ readme = "README.md"
 targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
+jsonrpsee = { version = "0.12.0", features = ["server", "macros"] }
 futures = "0.3.21"
-jsonrpc-core = "18.0.0"
-jsonrpc-core-client = "18.0.0"
-jsonrpc-derive = "18.0.0"
 serde = { version = "1.0.136", features = ["derive"] }
 thiserror = "1.0"
 sc-consensus-babe = { version = "0.10.0-dev", path = "../" }
@@ -34,6 +32,7 @@ sp-runtime = { version = "6.0.0", path = "../../../../primitives/runtime" }
 [dev-dependencies]
 serde_json = "1.0.79"
 tempfile = "3.1.0"
+tokio = "1.17.0"
 sc-consensus = { version = "0.10.0-dev", path = "../../../consensus/common" }
 sc-keystore = { version = "4.0.0-dev", path = "../../../keystore" }
 sp-keyring = { version = "6.0.0", path = "../../../../primitives/keyring" }
diff --git a/substrate/client/consensus/babe/rpc/src/lib.rs b/substrate/client/consensus/babe/rpc/src/lib.rs
index 2d0c81afc777502b02d824fa71910b383ab51f3f..d5f21606c62ed915755e02b1630f23d47b544f80 100644
--- a/substrate/client/consensus/babe/rpc/src/lib.rs
+++ b/substrate/client/consensus/babe/rpc/src/lib.rs
@@ -18,9 +18,13 @@
 
 //! RPC api for babe.
 
-use futures::{FutureExt, TryFutureExt};
-use jsonrpc_core::Error as RpcError;
-use jsonrpc_derive::rpc;
+use futures::TryFutureExt;
+use jsonrpsee::{
+	core::{async_trait, Error as JsonRpseeError, RpcResult},
+	proc_macros::rpc,
+	types::{error::CallError, ErrorObject},
+};
+
 use sc_consensus_babe::{authorship, Config, Epoch};
 use sc_consensus_epochs::{descendent_query, Epoch as EpochT, SharedEpochChanges};
 use sc_rpc_api::DenyUnsafe;
@@ -35,19 +39,17 @@ use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
 use sp_runtime::traits::{Block as BlockT, Header as _};
 use std::{collections::HashMap, sync::Arc};
 
-type FutureResult<T> = jsonrpc_core::BoxFuture<Result<T, RpcError>>;
-
 /// Provides rpc methods for interacting with Babe.
-#[rpc]
+#[rpc(client, server)]
 pub trait BabeApi {
 	/// Returns data about which slots (primary or secondary) can be claimed in the current epoch
 	/// with the keys in the keystore.
-	#[rpc(name = "babe_epochAuthorship")]
-	fn epoch_authorship(&self) -> FutureResult<HashMap<AuthorityId, EpochAuthorship>>;
+	#[method(name = "babe_epochAuthorship")]
+	async fn epoch_authorship(&self) -> RpcResult<HashMap<AuthorityId, EpochAuthorship>>;
 }
 
-/// Implements the BabeRpc trait for interacting with Babe.
-pub struct BabeRpcHandler<B: BlockT, C, SC> {
+/// Provides RPC methods for interacting with Babe.
+pub struct BabeRpc<B: BlockT, C, SC> {
 	/// shared reference to the client.
 	client: Arc<C>,
 	/// shared reference to EpochChanges
@@ -62,7 +64,7 @@ pub struct BabeRpcHandler<B: BlockT, C, SC> {
 	deny_unsafe: DenyUnsafe,
 }
 
-impl<B: BlockT, C, SC> BabeRpcHandler<B, C, SC> {
+impl<B: BlockT, C, SC> BabeRpc<B, C, SC> {
 	/// Creates a new instance of the BabeRpc handler.
 	pub fn new(
 		client: Arc<C>,
@@ -76,7 +78,8 @@ impl<B: BlockT, C, SC> BabeRpcHandler<B, C, SC> {
 	}
 }
 
-impl<B, C, SC> BabeApi for BabeRpcHandler<B, C, SC>
+#[async_trait]
+impl<B: BlockT, C, SC> BabeApiServer for BabeRpc<B, C, SC>
 where
 	B: BlockT,
 	C: ProvideRuntimeApi<B>
@@ -86,71 +89,63 @@ where
 	C::Api: BabeRuntimeApi<B>,
 	SC: SelectChain<B> + Clone + 'static,
 {
-	fn epoch_authorship(&self) -> FutureResult<HashMap<AuthorityId, EpochAuthorship>> {
-		if let Err(err) = self.deny_unsafe.check_if_safe() {
-			return async move { Err(err.into()) }.boxed()
-		}
-
-		let (babe_config, keystore, shared_epoch, client, select_chain) = (
-			self.babe_config.clone(),
-			self.keystore.clone(),
-			self.shared_epoch_changes.clone(),
-			self.client.clone(),
-			self.select_chain.clone(),
-		);
-
-		async move {
-			let header = select_chain.best_chain().map_err(Error::Consensus).await?;
-			let epoch_start = client
-				.runtime_api()
-				.current_epoch_start(&BlockId::Hash(header.hash()))
-				.map_err(|err| Error::StringError(err.to_string()))?;
-			let epoch =
-				epoch_data(&shared_epoch, &client, &babe_config, *epoch_start, &select_chain)
-					.await?;
-			let (epoch_start, epoch_end) = (epoch.start_slot(), epoch.end_slot());
-
-			let mut claims: HashMap<AuthorityId, EpochAuthorship> = HashMap::new();
-
-			let keys = {
-				epoch
-					.authorities
-					.iter()
-					.enumerate()
-					.filter_map(|(i, a)| {
-						if SyncCryptoStore::has_keys(
-							&*keystore,
-							&[(a.0.to_raw_vec(), AuthorityId::ID)],
-						) {
-							Some((a.0.clone(), i))
-						} else {
-							None
-						}
-					})
-					.collect::<Vec<_>>()
-			};
-
-			for slot in *epoch_start..*epoch_end {
-				if let Some((claim, key)) =
-					authorship::claim_slot_using_keys(slot.into(), &epoch, &keystore, &keys)
-				{
-					match claim {
-						PreDigest::Primary { .. } => {
-							claims.entry(key).or_default().primary.push(slot);
-						},
-						PreDigest::SecondaryPlain { .. } => {
-							claims.entry(key).or_default().secondary.push(slot);
-						},
-						PreDigest::SecondaryVRF { .. } => {
-							claims.entry(key).or_default().secondary_vrf.push(slot);
-						},
-					};
-				}
+	async fn epoch_authorship(&self) -> RpcResult<HashMap<AuthorityId, EpochAuthorship>> {
+		self.deny_unsafe.check_if_safe()?;
+		let header = self.select_chain.best_chain().map_err(Error::Consensus).await?;
+		let epoch_start = self
+			.client
+			.runtime_api()
+			.current_epoch_start(&BlockId::Hash(header.hash()))
+			.map_err(|err| Error::StringError(format!("{:?}", err)))?;
+
+		let epoch = epoch_data(
+			&self.shared_epoch_changes,
+			&self.client,
+			&self.babe_config,
+			*epoch_start,
+			&self.select_chain,
+		)
+		.await?;
+		let (epoch_start, epoch_end) = (epoch.start_slot(), epoch.end_slot());
+		let mut claims: HashMap<AuthorityId, EpochAuthorship> = HashMap::new();
+
+		let keys = {
+			epoch
+				.authorities
+				.iter()
+				.enumerate()
+				.filter_map(|(i, a)| {
+					if SyncCryptoStore::has_keys(
+						&*self.keystore,
+						&[(a.0.to_raw_vec(), AuthorityId::ID)],
+					) {
+						Some((a.0.clone(), i))
+					} else {
+						None
+					}
+				})
+				.collect::<Vec<_>>()
+		};
+
+		for slot in *epoch_start..*epoch_end {
+			if let Some((claim, key)) =
+				authorship::claim_slot_using_keys(slot.into(), &epoch, &self.keystore, &keys)
+			{
+				match claim {
+					PreDigest::Primary { .. } => {
+						claims.entry(key).or_default().primary.push(slot);
+					},
+					PreDigest::SecondaryPlain { .. } => {
+						claims.entry(key).or_default().secondary.push(slot);
+					},
+					PreDigest::SecondaryVRF { .. } => {
+						claims.entry(key).or_default().secondary_vrf.push(slot.into());
+					},
+				};
 			}
-
-			Ok(claims)
 		}
-		.boxed()
+
+		Ok(claims)
 	}
 }
 
@@ -176,13 +171,13 @@ pub enum Error {
 	StringError(String),
 }
 
-impl From<Error> for jsonrpc_core::Error {
+impl From<Error> for JsonRpseeError {
 	fn from(error: Error) -> Self {
-		jsonrpc_core::Error {
-			message: format!("{}", error),
-			code: jsonrpc_core::ErrorCode::ServerError(1234),
-			data: None,
-		}
+		JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
+			1234,
+			error.to_string(),
+			None::<()>,
+		)))
 	}
 }
 
@@ -226,7 +221,6 @@ mod tests {
 		TestClientBuilderExt,
 	};
 
-	use jsonrpc_core::IoHandler;
 	use sc_consensus_babe::{block_import, AuthorityPair, Config};
 	use std::sync::Arc;
 
@@ -243,9 +237,9 @@ mod tests {
 		(keystore, keystore_path)
 	}
 
-	fn test_babe_rpc_handler(
+	fn test_babe_rpc_module(
 		deny_unsafe: DenyUnsafe,
-	) -> BabeRpcHandler<Block, TestClient, sc_consensus::LongestChain<Backend, Block>> {
+	) -> BabeRpc<Block, TestClient, sc_consensus::LongestChain<Backend, Block>> {
 		let builder = TestClientBuilder::new();
 		let (client, longest_chain) = builder.build_with_longest_chain();
 		let client = Arc::new(client);
@@ -256,40 +250,30 @@ mod tests {
 		let epoch_changes = link.epoch_changes().clone();
 		let keystore = create_temp_keystore::<AuthorityPair>(Sr25519Keyring::Alice).0;
 
-		BabeRpcHandler::new(
-			client.clone(),
-			epoch_changes,
-			keystore,
-			config,
-			longest_chain,
-			deny_unsafe,
-		)
+		BabeRpc::new(client.clone(), epoch_changes, keystore, config, longest_chain, deny_unsafe)
 	}
 
-	#[test]
-	fn epoch_authorship_works() {
-		let handler = test_babe_rpc_handler(DenyUnsafe::No);
-		let mut io = IoHandler::new();
+	#[tokio::test]
+	async fn epoch_authorship_works() {
+		let babe_rpc = test_babe_rpc_module(DenyUnsafe::No);
+		let api = babe_rpc.into_rpc();
 
-		io.extend_with(BabeApi::to_delegate(handler));
 		let request = r#"{"jsonrpc":"2.0","method":"babe_epochAuthorship","params": [],"id":1}"#;
-		let response = r#"{"jsonrpc":"2.0","result":{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY":{"primary":[0],"secondary":[1,2,4],"secondary_vrf":[]}},"id":1}"#;
+		let (response, _) = api.raw_json_request(request).await.unwrap();
+		let expected = r#"{"jsonrpc":"2.0","result":{"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY":{"primary":[0],"secondary":[1,2,4],"secondary_vrf":[]}},"id":1}"#;
 
-		assert_eq!(Some(response.into()), io.handle_request_sync(request));
+		assert_eq!(&response, expected);
 	}
 
-	#[test]
-	fn epoch_authorship_is_unsafe() {
-		let handler = test_babe_rpc_handler(DenyUnsafe::Yes);
-		let mut io = IoHandler::new();
-
-		io.extend_with(BabeApi::to_delegate(handler));
-		let request = r#"{"jsonrpc":"2.0","method":"babe_epochAuthorship","params": [],"id":1}"#;
+	#[tokio::test]
+	async fn epoch_authorship_is_unsafe() {
+		let babe_rpc = test_babe_rpc_module(DenyUnsafe::Yes);
+		let api = babe_rpc.into_rpc();
 
-		let response = io.handle_request_sync(request).unwrap();
-		let mut response: serde_json::Value = serde_json::from_str(&response).unwrap();
-		let error: RpcError = serde_json::from_value(response["error"].take()).unwrap();
+		let request = r#"{"jsonrpc":"2.0","method":"babe_epochAuthorship","params":[],"id":1}"#;
+		let (response, _) = api.raw_json_request(request).await.unwrap();
+		let expected = r#"{"jsonrpc":"2.0","error":{"code":-32601,"message":"RPC call is unsafe to be called externally"},"id":1}"#;
 
-		assert_eq!(error, sc_rpc_api::UnsafeRpcError.into())
+		assert_eq!(&response, expected);
 	}
 }
diff --git a/substrate/client/consensus/manual-seal/Cargo.toml b/substrate/client/consensus/manual-seal/Cargo.toml
index 9452d2b1afd0826239627266ac357cf5c1faf250..e8f4e20ab0e553afb5f69d17d746d7fdededb885 100644
--- a/substrate/client/consensus/manual-seal/Cargo.toml
+++ b/substrate/client/consensus/manual-seal/Cargo.toml
@@ -13,13 +13,11 @@ readme = "README.md"
 targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
+jsonrpsee = { version = "0.12.0", features = ["server", "macros"] }
 assert_matches = "1.3.0"
 async-trait = "0.1.50"
 codec = { package = "parity-scale-codec", version = "3.0.0" }
 futures = "0.3.21"
-jsonrpc-core = "18.0.0"
-jsonrpc-core-client = "18.0.0"
-jsonrpc-derive = "18.0.0"
 log = "0.4.16"
 serde = { version = "1.0", features = ["derive"] }
 thiserror = "1.0"
diff --git a/substrate/client/consensus/manual-seal/src/error.rs b/substrate/client/consensus/manual-seal/src/error.rs
index 7c3211203bf546210adcf8706ea5e0d077a8c947..a056c541c3cef1d8b2961fb66c65d7761487386b 100644
--- a/substrate/client/consensus/manual-seal/src/error.rs
+++ b/substrate/client/consensus/manual-seal/src/error.rs
@@ -20,6 +20,10 @@
 //! This is suitable for a testing environment.
 
 use futures::channel::{mpsc::SendError, oneshot};
+use jsonrpsee::{
+	core::Error as JsonRpseeError,
+	types::error::{CallError, ErrorObject},
+};
 use sc_consensus::ImportResult;
 use sp_blockchain::Error as BlockchainError;
 use sp_consensus::Error as ConsensusError;
@@ -27,14 +31,14 @@ use sp_inherents::Error as InherentsError;
 
 /// Error code for rpc
 mod codes {
-	pub const SERVER_SHUTTING_DOWN: i64 = 10_000;
-	pub const BLOCK_IMPORT_FAILED: i64 = 11_000;
-	pub const EMPTY_TRANSACTION_POOL: i64 = 12_000;
-	pub const BLOCK_NOT_FOUND: i64 = 13_000;
-	pub const CONSENSUS_ERROR: i64 = 14_000;
-	pub const INHERENTS_ERROR: i64 = 15_000;
-	pub const BLOCKCHAIN_ERROR: i64 = 16_000;
-	pub const UNKNOWN_ERROR: i64 = 20_000;
+	pub const SERVER_SHUTTING_DOWN: i32 = 10_000;
+	pub const BLOCK_IMPORT_FAILED: i32 = 11_000;
+	pub const EMPTY_TRANSACTION_POOL: i32 = 12_000;
+	pub const BLOCK_NOT_FOUND: i32 = 13_000;
+	pub const CONSENSUS_ERROR: i32 = 14_000;
+	pub const INHERENTS_ERROR: i32 = 15_000;
+	pub const BLOCKCHAIN_ERROR: i32 = 16_000;
+	pub const UNKNOWN_ERROR: i32 = 20_000;
 }
 
 /// errors encountered by background block authorship task
@@ -71,7 +75,7 @@ pub enum Error {
 	SendError(#[from] SendError),
 	/// Some other error.
 	#[error("Other error: {0}")]
-	Other(#[from] Box<dyn std::error::Error + Send>),
+	Other(Box<dyn std::error::Error + Send + Sync>),
 }
 
 impl From<ImportResult> for Error {
@@ -87,7 +91,7 @@ impl From<String> for Error {
 }
 
 impl Error {
-	fn to_code(&self) -> i64 {
+	fn to_code(&self) -> i32 {
 		use Error::*;
 		match self {
 			BlockImportError(_) => codes::BLOCK_IMPORT_FAILED,
@@ -102,12 +106,8 @@ impl Error {
 	}
 }
 
-impl From<Error> for jsonrpc_core::Error {
-	fn from(error: Error) -> Self {
-		jsonrpc_core::Error {
-			code: jsonrpc_core::ErrorCode::ServerError(error.to_code()),
-			message: format!("{}", error),
-			data: None,
-		}
+impl From<Error> for JsonRpseeError {
+	fn from(err: Error) -> Self {
+		CallError::Custom(ErrorObject::owned(err.to_code(), err.to_string(), None::<()>)).into()
 	}
 }
diff --git a/substrate/client/consensus/manual-seal/src/rpc.rs b/substrate/client/consensus/manual-seal/src/rpc.rs
index 4a8dcbc0cb765ebe849514c9d40c1e5ffd00d91c..b9bb06551f8186a2354522d09514b9ebf5056815 100644
--- a/substrate/client/consensus/manual-seal/src/rpc.rs
+++ b/substrate/client/consensus/manual-seal/src/rpc.rs
@@ -18,21 +18,21 @@
 
 //! RPC interface for the `ManualSeal` Engine.
 
-pub use self::gen_client::Client as ManualSealClient;
+use crate::error::Error;
 use futures::{
 	channel::{mpsc, oneshot},
-	FutureExt, SinkExt, TryFutureExt,
+	SinkExt,
+};
+use jsonrpsee::{
+	core::{async_trait, Error as JsonRpseeError, RpcResult},
+	proc_macros::rpc,
 };
-use jsonrpc_core::Error;
-use jsonrpc_derive::rpc;
 use sc_consensus::ImportedAux;
 use serde::{Deserialize, Serialize};
 use sp_runtime::EncodedJustification;
 
-/// Future's type for jsonrpc
-type FutureResult<T> = jsonrpc_core::BoxFuture<Result<T, Error>>;
-/// sender passed to the authorship task to report errors or successes.
-pub type Sender<T> = Option<oneshot::Sender<std::result::Result<T, crate::Error>>>;
+/// Sender passed to the authorship task to report errors or successes.
+pub type Sender<T> = Option<oneshot::Sender<std::result::Result<T, Error>>>;
 
 /// Message sent to the background authorship task, usually by RPC.
 pub enum EngineCommand<Hash> {
@@ -65,27 +65,27 @@ pub enum EngineCommand<Hash> {
 }
 
 /// RPC trait that provides methods for interacting with the manual-seal authorship task over rpc.
-#[rpc]
+#[rpc(client, server)]
 pub trait ManualSealApi<Hash> {
 	/// Instructs the manual-seal authorship task to create a new block
-	#[rpc(name = "engine_createBlock")]
-	fn create_block(
+	#[method(name = "engine_createBlock")]
+	async fn create_block(
 		&self,
 		create_empty: bool,
 		finalize: bool,
 		parent_hash: Option<Hash>,
-	) -> FutureResult<CreatedBlock<Hash>>;
+	) -> RpcResult<CreatedBlock<Hash>>;
 
 	/// Instructs the manual-seal authorship task to finalize a block
-	#[rpc(name = "engine_finalizeBlock")]
-	fn finalize_block(
+	#[method(name = "engine_finalizeBlock")]
+	async fn finalize_block(
 		&self,
 		hash: Hash,
 		justification: Option<EncodedJustification>,
-	) -> FutureResult<bool>;
+	) -> RpcResult<bool>;
 }
 
-/// A struct that implements the [`ManualSealApi`].
+/// A struct that implements the [`ManualSealApiServer`].
 pub struct ManualSeal<Hash> {
 	import_block_channel: mpsc::Sender<EngineCommand<Hash>>,
 }
@@ -106,44 +106,43 @@ impl<Hash> ManualSeal<Hash> {
 	}
 }
 
-impl<Hash: Send + 'static> ManualSealApi<Hash> for ManualSeal<Hash> {
-	fn create_block(
+#[async_trait]
+impl<Hash: Send + 'static> ManualSealApiServer<Hash> for ManualSeal<Hash> {
+	async fn create_block(
 		&self,
 		create_empty: bool,
 		finalize: bool,
 		parent_hash: Option<Hash>,
-	) -> FutureResult<CreatedBlock<Hash>> {
+	) -> RpcResult<CreatedBlock<Hash>> {
 		let mut sink = self.import_block_channel.clone();
-		async move {
-			let (sender, receiver) = oneshot::channel();
-			let command = EngineCommand::SealNewBlock {
-				create_empty,
-				finalize,
-				parent_hash,
-				sender: Some(sender),
-			};
-			sink.send(command).await?;
-			receiver.await?
+		let (sender, receiver) = oneshot::channel();
+		// NOTE: this sends a Result over the channel.
+		let command = EngineCommand::SealNewBlock {
+			create_empty,
+			finalize,
+			parent_hash,
+			sender: Some(sender),
+		};
+
+		sink.send(command).await?;
+
+		match receiver.await {
+			Ok(Ok(rx)) => Ok(rx),
+			Ok(Err(e)) => Err(e.into()),
+			Err(e) => Err(JsonRpseeError::to_call_error(e)),
 		}
-		.map_err(Error::from)
-		.boxed()
 	}
 
-	fn finalize_block(
+	async fn finalize_block(
 		&self,
 		hash: Hash,
 		justification: Option<EncodedJustification>,
-	) -> FutureResult<bool> {
+	) -> RpcResult<bool> {
 		let mut sink = self.import_block_channel.clone();
-		async move {
-			let (sender, receiver) = oneshot::channel();
-			sink.send(EngineCommand::FinalizeBlock { hash, sender: Some(sender), justification })
-				.await?;
-
-			receiver.await?.map(|_| true)
-		}
-		.map_err(Error::from)
-		.boxed()
+		let (sender, receiver) = oneshot::channel();
+		let command = EngineCommand::FinalizeBlock { hash, sender: Some(sender), justification };
+		sink.send(command).await?;
+		receiver.await.map(|_| true).map_err(|e| JsonRpseeError::to_call_error(e))
 	}
 }
 
diff --git a/substrate/client/finality-grandpa/rpc/Cargo.toml b/substrate/client/finality-grandpa/rpc/Cargo.toml
index 40a4150f8dd981e44dcf020fa0bf74e379c26520..c124712e3fa8437c9e76bdad2b3339fe0d4083e6 100644
--- a/substrate/client/finality-grandpa/rpc/Cargo.toml
+++ b/substrate/client/finality-grandpa/rpc/Cargo.toml
@@ -12,10 +12,7 @@ homepage = "https://substrate.io"
 [dependencies]
 finality-grandpa = { version = "0.15.0", features = ["derive-codec"] }
 futures = "0.3.16"
-jsonrpc-core = "18.0.0"
-jsonrpc-core-client = "18.0.0"
-jsonrpc-derive = "18.0.0"
-jsonrpc-pubsub = "18.0.0"
+jsonrpsee = { version = "0.12.0", features = ["server", "macros"] }
 log = "0.4.8"
 parity-scale-codec = { version = "3.0.0", features = ["derive"] }
 serde = { version = "1.0.105", features = ["derive"] }
@@ -37,3 +34,4 @@ sp-core = { version = "6.0.0", path = "../../../primitives/core" }
 sp-finality-grandpa = { version = "4.0.0-dev", path = "../../../primitives/finality-grandpa" }
 sp-keyring = { version = "6.0.0", path = "../../../primitives/keyring" }
 substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" }
+tokio = { version = "1.17.0", features = ["macros"] }
diff --git a/substrate/client/finality-grandpa/rpc/src/error.rs b/substrate/client/finality-grandpa/rpc/src/error.rs
index 845b4d99dcc1a87935026fc50f60a40d767524c4..197c0b8a721024aaa4328ac457338f1443b00b61 100644
--- a/substrate/client/finality-grandpa/rpc/src/error.rs
+++ b/substrate/client/finality-grandpa/rpc/src/error.rs
@@ -16,6 +16,11 @@
 // You should have received a copy of the GNU General Public License
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
+use jsonrpsee::{
+	core::Error as JsonRpseeError,
+	types::error::{CallError, ErrorObject},
+};
+
 #[derive(Debug, thiserror::Error)]
 /// Top-level error type for the RPC handler
 pub enum Error {
@@ -56,15 +61,15 @@ impl From<Error> for ErrorCode {
 	}
 }
 
-impl From<Error> for jsonrpc_core::Error {
+impl From<Error> for JsonRpseeError {
 	fn from(error: Error) -> Self {
-		let message = format!("{}", error);
+		let message = error.to_string();
 		let code = ErrorCode::from(error);
-		jsonrpc_core::Error {
+		JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
+			code as i32,
 			message,
-			code: jsonrpc_core::ErrorCode::ServerError(code as i64),
-			data: None,
-		}
+			None::<()>,
+		)))
 	}
 }
 
diff --git a/substrate/client/finality-grandpa/rpc/src/lib.rs b/substrate/client/finality-grandpa/rpc/src/lib.rs
index 9c51bc3d226a700cb2ce8cf28286e1dd941fd42f..82962d716d58909a7df244902bc2859ba470ef17 100644
--- a/substrate/client/finality-grandpa/rpc/src/lib.rs
+++ b/substrate/client/finality-grandpa/rpc/src/lib.rs
@@ -19,167 +19,137 @@
 //! RPC API for GRANDPA.
 #![warn(missing_docs)]
 
-use futures::{task::Spawn, FutureExt, SinkExt, StreamExt, TryFutureExt};
-use jsonrpc_derive::rpc;
-use jsonrpc_pubsub::{manager::SubscriptionManager, typed::Subscriber, SubscriptionId};
+use futures::{FutureExt, StreamExt};
 use log::warn;
 use std::sync::Arc;
 
+use jsonrpsee::{
+	core::{async_trait, RpcResult},
+	proc_macros::rpc,
+	PendingSubscription,
+};
+
 mod error;
 mod finality;
 mod notification;
 mod report;
 
 use sc_finality_grandpa::GrandpaJustificationStream;
+use sc_rpc::SubscriptionTaskExecutor;
 use sp_runtime::traits::{Block as BlockT, NumberFor};
 
 use finality::{EncodedFinalityProof, RpcFinalityProofProvider};
 use notification::JustificationNotification;
 use report::{ReportAuthoritySet, ReportVoterState, ReportedRoundStates};
 
-type FutureResult<T> = jsonrpc_core::BoxFuture<Result<T, jsonrpc_core::Error>>;
-
 /// Provides RPC methods for interacting with GRANDPA.
-#[rpc]
+#[rpc(client, server)]
 pub trait GrandpaApi<Notification, Hash, Number> {
-	/// RPC Metadata
-	type Metadata;
-
 	/// Returns the state of the current best round state as well as the
 	/// ongoing background rounds.
-	#[rpc(name = "grandpa_roundState")]
-	fn round_state(&self) -> FutureResult<ReportedRoundStates>;
+	#[method(name = "grandpa_roundState")]
+	async fn round_state(&self) -> RpcResult<ReportedRoundStates>;
 
 	/// Returns the block most recently finalized by Grandpa, alongside
 	/// side its justification.
-	#[pubsub(
-		subscription = "grandpa_justifications",
-		subscribe,
-		name = "grandpa_subscribeJustifications"
-	)]
-	fn subscribe_justifications(
-		&self,
-		metadata: Self::Metadata,
-		subscriber: Subscriber<Notification>,
-	);
-
-	/// Unsubscribe from receiving notifications about recently finalized blocks.
-	#[pubsub(
-		subscription = "grandpa_justifications",
-		unsubscribe,
-		name = "grandpa_unsubscribeJustifications"
+	#[subscription(
+		name = "grandpa_subscribeJustifications" => "grandpa_justifications",
+		unsubscribe = "grandpa_unsubscribeJustifications",
+		item = Notification
 	)]
-	fn unsubscribe_justifications(
-		&self,
-		metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> jsonrpc_core::Result<bool>;
+	fn subscribe_justifications(&self);
 
 	/// Prove finality for the given block number by returning the Justification for the last block
 	/// in the set and all the intermediary headers to link them together.
-	#[rpc(name = "grandpa_proveFinality")]
-	fn prove_finality(&self, block: Number) -> FutureResult<Option<EncodedFinalityProof>>;
+	#[method(name = "grandpa_proveFinality")]
+	async fn prove_finality(&self, block: Number) -> RpcResult<Option<EncodedFinalityProof>>;
 }
 
-/// Implements the GrandpaApi RPC trait for interacting with GRANDPA.
-pub struct GrandpaRpcHandler<AuthoritySet, VoterState, Block: BlockT, ProofProvider> {
+/// Provides RPC methods for interacting with GRANDPA.
+pub struct GrandpaRpc<AuthoritySet, VoterState, Block: BlockT, ProofProvider> {
+	executor: SubscriptionTaskExecutor,
 	authority_set: AuthoritySet,
 	voter_state: VoterState,
 	justification_stream: GrandpaJustificationStream<Block>,
-	manager: SubscriptionManager,
 	finality_proof_provider: Arc<ProofProvider>,
 }
-
 impl<AuthoritySet, VoterState, Block: BlockT, ProofProvider>
-	GrandpaRpcHandler<AuthoritySet, VoterState, Block, ProofProvider>
+	GrandpaRpc<AuthoritySet, VoterState, Block, ProofProvider>
 {
-	/// Creates a new GrandpaRpcHandler instance.
-	pub fn new<E>(
+	/// Prepare a new [`GrandpaRpc`]
+	pub fn new(
+		executor: SubscriptionTaskExecutor,
 		authority_set: AuthoritySet,
 		voter_state: VoterState,
 		justification_stream: GrandpaJustificationStream<Block>,
-		executor: E,
 		finality_proof_provider: Arc<ProofProvider>,
-	) -> Self
-	where
-		E: Spawn + Sync + Send + 'static,
-	{
-		let manager = SubscriptionManager::new(Arc::new(executor));
-		Self { authority_set, voter_state, justification_stream, manager, finality_proof_provider }
+	) -> Self {
+		Self { executor, authority_set, voter_state, justification_stream, finality_proof_provider }
 	}
 }
 
+#[async_trait]
 impl<AuthoritySet, VoterState, Block, ProofProvider>
-	GrandpaApi<JustificationNotification, Block::Hash, NumberFor<Block>>
-	for GrandpaRpcHandler<AuthoritySet, VoterState, Block, ProofProvider>
+	GrandpaApiServer<JustificationNotification, Block::Hash, NumberFor<Block>>
+	for GrandpaRpc<AuthoritySet, VoterState, Block, ProofProvider>
 where
 	VoterState: ReportVoterState + Send + Sync + 'static,
 	AuthoritySet: ReportAuthoritySet + Send + Sync + 'static,
 	Block: BlockT,
 	ProofProvider: RpcFinalityProofProvider<Block> + Send + Sync + 'static,
 {
-	type Metadata = sc_rpc::Metadata;
-
-	fn round_state(&self) -> FutureResult<ReportedRoundStates> {
-		let round_states = ReportedRoundStates::from(&self.authority_set, &self.voter_state);
-		let future = async move { round_states }.boxed();
-		future.map_err(jsonrpc_core::Error::from).boxed()
+	async fn round_state(&self) -> RpcResult<ReportedRoundStates> {
+		ReportedRoundStates::from(&self.authority_set, &self.voter_state).map_err(Into::into)
 	}
 
-	fn subscribe_justifications(
-		&self,
-		_metadata: Self::Metadata,
-		subscriber: Subscriber<JustificationNotification>,
-	) {
-		let stream = self
-			.justification_stream
-			.subscribe()
-			.map(|x| Ok(Ok::<_, jsonrpc_core::Error>(JustificationNotification::from(x))));
-
-		self.manager.add(subscriber, |sink| {
-			stream
-				.forward(sink.sink_map_err(|e| warn!("Error sending notifications: {:?}", e)))
-				.map(|_| ())
-		});
-	}
+	fn subscribe_justifications(&self, pending: PendingSubscription) {
+		let stream = self.justification_stream.subscribe().map(
+			|x: sc_finality_grandpa::GrandpaJustification<Block>| {
+				JustificationNotification::from(x)
+			},
+		);
 
-	fn unsubscribe_justifications(
-		&self,
-		_metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> jsonrpc_core::Result<bool> {
-		Ok(self.manager.cancel(id))
+		let fut = async move {
+			if let Some(mut sink) = pending.accept() {
+				sink.pipe_from_stream(stream).await;
+			}
+		}
+		.boxed();
+
+		self.executor
+			.spawn("substrate-rpc-subscription", Some("rpc"), fut.map(drop).boxed());
 	}
 
-	fn prove_finality(
+	async fn prove_finality(
 		&self,
 		block: NumberFor<Block>,
-	) -> FutureResult<Option<EncodedFinalityProof>> {
-		let result = self.finality_proof_provider.rpc_prove_finality(block);
-		let future = async move { result }.boxed();
-		future
+	) -> RpcResult<Option<EncodedFinalityProof>> {
+		self.finality_proof_provider
+			.rpc_prove_finality(block)
 			.map_err(|e| {
 				warn!("Error proving finality: {}", e);
 				error::Error::ProveFinalityFailed(e)
 			})
-			.map_err(jsonrpc_core::Error::from)
-			.boxed()
+			.map_err(Into::into)
 	}
 }
 
 #[cfg(test)]
 mod tests {
 	use super::*;
-	use jsonrpc_core::{types::Params, Notification, Output};
-	use std::{collections::HashSet, sync::Arc};
+	use std::{collections::HashSet, convert::TryInto, sync::Arc};
 
+	use jsonrpsee::{
+		types::{EmptyParams, SubscriptionId},
+		RpcModule,
+	};
 	use parity_scale_codec::{Decode, Encode};
 	use sc_block_builder::{BlockBuilder, RecordProof};
 	use sc_finality_grandpa::{
 		report, AuthorityId, FinalityProof, GrandpaJustification, GrandpaJustificationSender,
 	};
 	use sp_blockchain::HeaderBackend;
-	use sp_core::crypto::ByteArray;
+	use sp_core::{crypto::ByteArray, testing::TaskExecutor};
 	use sp_keyring::Ed25519Keyring;
 	use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
 	use substrate_test_runtime_client::{
@@ -274,7 +244,10 @@ mod tests {
 
 	fn setup_io_handler<VoterState>(
 		voter_state: VoterState,
-	) -> (jsonrpc_core::MetaIoHandler<sc_rpc::Metadata>, GrandpaJustificationSender<Block>)
+	) -> (
+		RpcModule<GrandpaRpc<TestAuthoritySet, VoterState, Block, TestFinalityProofProvider>>,
+		GrandpaJustificationSender<Block>,
+	)
 	where
 		VoterState: ReportVoterState + Send + Sync + 'static,
 	{
@@ -284,120 +257,107 @@ mod tests {
 	fn setup_io_handler_with_finality_proofs<VoterState>(
 		voter_state: VoterState,
 		finality_proof: Option<FinalityProof<Header>>,
-	) -> (jsonrpc_core::MetaIoHandler<sc_rpc::Metadata>, GrandpaJustificationSender<Block>)
+	) -> (
+		RpcModule<GrandpaRpc<TestAuthoritySet, VoterState, Block, TestFinalityProofProvider>>,
+		GrandpaJustificationSender<Block>,
+	)
 	where
 		VoterState: ReportVoterState + Send + Sync + 'static,
 	{
 		let (justification_sender, justification_stream) = GrandpaJustificationStream::channel();
 		let finality_proof_provider = Arc::new(TestFinalityProofProvider { finality_proof });
+		let executor = Arc::new(TaskExecutor::default());
 
-		let handler = GrandpaRpcHandler::new(
+		let rpc = GrandpaRpc::new(
+			executor,
 			TestAuthoritySet,
 			voter_state,
 			justification_stream,
-			sc_rpc::testing::TaskExecutor,
 			finality_proof_provider,
-		);
-
-		let mut io = jsonrpc_core::MetaIoHandler::default();
-		io.extend_with(GrandpaApi::to_delegate(handler));
+		)
+		.into_rpc();
 
-		(io, justification_sender)
+		(rpc, justification_sender)
 	}
 
-	#[test]
-	fn uninitialized_rpc_handler() {
-		let (io, _) = setup_io_handler(EmptyVoterState);
-
-		let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":1}"#;
-		let response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"GRANDPA RPC endpoint not ready"},"id":1}"#;
+	#[tokio::test]
+	async fn uninitialized_rpc_handler() {
+		let (rpc, _) = setup_io_handler(EmptyVoterState);
+		let expected_response = r#"{"jsonrpc":"2.0","error":{"code":1,"message":"GRANDPA RPC endpoint not ready"},"id":0}"#.to_string();
+		let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#;
+		let (result, _) = rpc.raw_json_request(&request).await.unwrap();
 
-		let meta = sc_rpc::Metadata::default();
-		assert_eq!(Some(response.into()), io.handle_request_sync(request, meta));
+		assert_eq!(expected_response, result,);
 	}
 
-	#[test]
-	fn working_rpc_handler() {
-		let (io, _) = setup_io_handler(TestVoterState);
-
-		let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":1}"#;
-		let response = "{\"jsonrpc\":\"2.0\",\"result\":{\
-			\"background\":[{\
-				\"precommits\":{\"currentWeight\":100,\"missing\":[]},\
-				\"prevotes\":{\"currentWeight\":100,\"missing\":[]},\
-				\"round\":1,\"thresholdWeight\":67,\"totalWeight\":100\
-			}],\
+	#[tokio::test]
+	async fn working_rpc_handler() {
+		let (rpc, _) = setup_io_handler(TestVoterState);
+		let expected_response = "{\"jsonrpc\":\"2.0\",\"result\":{\
+			\"setId\":1,\
 			\"best\":{\
-				\"precommits\":{\"currentWeight\":0,\"missing\":[\"5C62Ck4UrFPiBtoCmeSrgF7x9yv9mn38446dhCpsi2mLHiFT\",\"5C7LYpP2ZH3tpKbvVvwiVe54AapxErdPBbvkYhe6y9ZBkqWt\"]},\
+				\"round\":2,\"totalWeight\":100,\"thresholdWeight\":67,\
 				\"prevotes\":{\"currentWeight\":50,\"missing\":[\"5C7LYpP2ZH3tpKbvVvwiVe54AapxErdPBbvkYhe6y9ZBkqWt\"]},\
-				\"round\":2,\"thresholdWeight\":67,\"totalWeight\":100\
+				\"precommits\":{\"currentWeight\":0,\"missing\":[\"5C62Ck4UrFPiBtoCmeSrgF7x9yv9mn38446dhCpsi2mLHiFT\",\"5C7LYpP2ZH3tpKbvVvwiVe54AapxErdPBbvkYhe6y9ZBkqWt\"]}\
 			},\
-			\"setId\":1\
-		},\"id\":1}";
-
-		let meta = sc_rpc::Metadata::default();
-		assert_eq!(io.handle_request_sync(request, meta), Some(response.into()));
-	}
+				\"background\":[{\
+				\"round\":1,\"totalWeight\":100,\"thresholdWeight\":67,\
+				\"prevotes\":{\"currentWeight\":100,\"missing\":[]},\
+				\"precommits\":{\"currentWeight\":100,\"missing\":[]}\
+			}]\
+		},\"id\":0}".to_string();
 
-	fn setup_session() -> (sc_rpc::Metadata, futures::channel::mpsc::UnboundedReceiver<String>) {
-		let (tx, rx) = futures::channel::mpsc::unbounded();
-		let meta = sc_rpc::Metadata::new(tx);
-		(meta, rx)
+		let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#;
+		let (result, _) = rpc.raw_json_request(&request).await.unwrap();
+		assert_eq!(expected_response, result);
 	}
 
-	#[test]
-	fn subscribe_and_unsubscribe_to_justifications() {
-		let (io, _) = setup_io_handler(TestVoterState);
-		let (meta, _) = setup_session();
+	#[tokio::test]
+	async fn subscribe_and_unsubscribe_to_justifications() {
+		let (rpc, _) = setup_io_handler(TestVoterState);
+		// Subscribe call.
+		let sub = rpc
+			.subscribe("grandpa_subscribeJustifications", EmptyParams::new())
+			.await
+			.unwrap();
 
-		// Subscribe
-		let sub_request =
-			r#"{"jsonrpc":"2.0","method":"grandpa_subscribeJustifications","params":[],"id":1}"#;
-		let resp = io.handle_request_sync(sub_request, meta.clone());
-		let resp: Output = serde_json::from_str(&resp.unwrap()).unwrap();
-
-		let sub_id = match resp {
-			Output::Success(success) => success.result,
-			_ => panic!(),
-		};
+		let ser_id = serde_json::to_string(sub.subscription_id()).unwrap();
 
 		// Unsubscribe
 		let unsub_req = format!(
 			"{{\"jsonrpc\":\"2.0\",\"method\":\"grandpa_unsubscribeJustifications\",\"params\":[{}],\"id\":1}}",
-			sub_id
-		);
-		assert_eq!(
-			io.handle_request_sync(&unsub_req, meta.clone()),
-			Some(r#"{"jsonrpc":"2.0","result":true,"id":1}"#.into()),
+			ser_id
 		);
+		let (response, _) = rpc.raw_json_request(&unsub_req).await.unwrap();
+
+		assert_eq!(response, r#"{"jsonrpc":"2.0","result":true,"id":1}"#);
 
 		// Unsubscribe again and fail
-		assert_eq!(
-			io.handle_request_sync(&unsub_req, meta),
-			Some("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid subscription id.\"},\"id\":1}".into()),
-		);
-	}
+		let (response, _) = rpc.raw_json_request(&unsub_req).await.unwrap();
+		let expected = r#"{"jsonrpc":"2.0","result":false,"id":1}"#;
 
-	#[test]
-	fn subscribe_and_unsubscribe_with_wrong_id() {
-		let (io, _) = setup_io_handler(TestVoterState);
-		let (meta, _) = setup_session();
+		assert_eq!(response, expected);
+	}
 
-		// Subscribe
-		let sub_request =
-			r#"{"jsonrpc":"2.0","method":"grandpa_subscribeJustifications","params":[],"id":1}"#;
-		let resp = io.handle_request_sync(sub_request, meta.clone());
-		let resp: Output = serde_json::from_str(&resp.unwrap()).unwrap();
-		assert!(matches!(resp, Output::Success(_)));
+	#[tokio::test]
+	async fn subscribe_and_unsubscribe_with_wrong_id() {
+		let (rpc, _) = setup_io_handler(TestVoterState);
+		// Subscribe call.
+		let _sub = rpc
+			.subscribe("grandpa_subscribeJustifications", EmptyParams::new())
+			.await
+			.unwrap();
 
 		// Unsubscribe with wrong ID
-		assert_eq!(
-			io.handle_request_sync(
+		let (response, _) = rpc
+			.raw_json_request(
 				r#"{"jsonrpc":"2.0","method":"grandpa_unsubscribeJustifications","params":["FOO"],"id":1}"#,
-				meta.clone()
-			),
-			Some("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid subscription id.\"},\"id\":1}".into())
-		);
+			)
+			.await
+			.unwrap();
+		let expected = r#"{"jsonrpc":"2.0","result":false,"id":1}"#;
+
+		assert_eq!(response, expected);
 	}
 
 	fn create_justification() -> GrandpaJustification<Block> {
@@ -454,60 +414,41 @@ mod tests {
 		justification
 	}
 
-	#[test]
-	fn subscribe_and_listen_to_one_justification() {
-		let (io, justification_sender) = setup_io_handler(TestVoterState);
-		let (meta, receiver) = setup_session();
-
-		// Subscribe
-		let sub_request =
-			r#"{"jsonrpc":"2.0","method":"grandpa_subscribeJustifications","params":[],"id":1}"#;
+	#[tokio::test]
+	async fn subscribe_and_listen_to_one_justification() {
+		let (rpc, justification_sender) = setup_io_handler(TestVoterState);
 
-		let resp = io.handle_request_sync(sub_request, meta.clone());
-		let mut resp: serde_json::Value = serde_json::from_str(&resp.unwrap()).unwrap();
-		let sub_id: String = serde_json::from_value(resp["result"].take()).unwrap();
+		let mut sub = rpc
+			.subscribe("grandpa_subscribeJustifications", EmptyParams::new())
+			.await
+			.unwrap();
 
 		// Notify with a header and justification
 		let justification = create_justification();
 		justification_sender.notify(|| Ok::<_, ()>(justification.clone())).unwrap();
 
 		// Inspect what we received
-		let recv = futures::executor::block_on(receiver.take(1).collect::<Vec<_>>());
-		let recv: Notification = serde_json::from_str(&recv[0]).unwrap();
-		let mut json_map = match recv.params {
-			Params::Map(json_map) => json_map,
-			_ => panic!(),
-		};
-
-		let recv_sub_id: String = serde_json::from_value(json_map["subscription"].take()).unwrap();
-		let recv_justification: sp_core::Bytes =
-			serde_json::from_value(json_map["result"].take()).unwrap();
+		let (recv_justification, recv_sub_id): (sp_core::Bytes, SubscriptionId) =
+			sub.next().await.unwrap().unwrap();
 		let recv_justification: GrandpaJustification<Block> =
 			Decode::decode(&mut &recv_justification[..]).unwrap();
 
-		assert_eq!(recv.method, "grandpa_justifications");
-		assert_eq!(recv_sub_id, sub_id);
+		assert_eq!(&recv_sub_id, sub.subscription_id());
 		assert_eq!(recv_justification, justification);
 	}
 
-	#[test]
-	fn prove_finality_with_test_finality_proof_provider() {
+	#[tokio::test]
+	async fn prove_finality_with_test_finality_proof_provider() {
 		let finality_proof = FinalityProof {
 			block: header(42).hash(),
 			justification: create_justification().encode(),
 			unknown_headers: vec![header(2)],
 		};
-		let (io, _) =
+		let (rpc, _) =
 			setup_io_handler_with_finality_proofs(TestVoterState, Some(finality_proof.clone()));
 
-		let request =
-			"{\"jsonrpc\":\"2.0\",\"method\":\"grandpa_proveFinality\",\"params\":[42],\"id\":1}";
-
-		let meta = sc_rpc::Metadata::default();
-		let resp = io.handle_request_sync(request, meta);
-		let mut resp: serde_json::Value = serde_json::from_str(&resp.unwrap()).unwrap();
-		let result: sp_core::Bytes = serde_json::from_value(resp["result"].take()).unwrap();
-		let finality_proof_rpc: FinalityProof<Header> = Decode::decode(&mut &result[..]).unwrap();
+		let bytes: sp_core::Bytes = rpc.call("grandpa_proveFinality", [42]).await.unwrap();
+		let finality_proof_rpc: FinalityProof<Header> = Decode::decode(&mut &bytes[..]).unwrap();
 		assert_eq!(finality_proof_rpc, finality_proof);
 	}
 }
diff --git a/substrate/client/rpc-api/Cargo.toml b/substrate/client/rpc-api/Cargo.toml
index 0287b0fd30799fc73132b76f28ad6e2e8d4d28c4..f8dfaab2a58a3a862f14eec66fa5a90fae1d9b53 100644
--- a/substrate/client/rpc-api/Cargo.toml
+++ b/substrate/client/rpc-api/Cargo.toml
@@ -15,10 +15,6 @@ targets = ["x86_64-unknown-linux-gnu"]
 [dependencies]
 codec = { package = "parity-scale-codec", version = "3.0.0" }
 futures = "0.3.21"
-jsonrpc-core = "18.0.0"
-jsonrpc-core-client = "18.0.0"
-jsonrpc-derive = "18.0.0"
-jsonrpc-pubsub = "18.0.0"
 log = "0.4.16"
 parking_lot = "0.12.0"
 scale-info = { version = "2.0.1", default-features = false, features = ["derive"] }
@@ -32,3 +28,4 @@ sp-rpc = { version = "6.0.0", path = "../../primitives/rpc" }
 sp-runtime = { version = "6.0.0", path = "../../primitives/runtime" }
 sp-tracing = { version = "5.0.0", path = "../../primitives/tracing" }
 sp-version = { version = "5.0.0", path = "../../primitives/version" }
+jsonrpsee = { version = "0.12.0", features = ["server", "macros"] }
diff --git a/substrate/client/rpc-api/src/author/error.rs b/substrate/client/rpc-api/src/author/error.rs
index 1f8c65c47139807e3f11c81726f1badb1214cde4..57a27d48de3ad0710950bda28dad98dfe0d38be4 100644
--- a/substrate/client/rpc-api/src/author/error.rs
+++ b/substrate/client/rpc-api/src/author/error.rs
@@ -18,28 +18,27 @@
 
 //! Authoring RPC module errors.
 
-use crate::errors;
-use jsonrpc_core as rpc;
+use jsonrpsee::{
+	core::Error as JsonRpseeError,
+	types::error::{CallError, ErrorObject},
+};
 use sp_runtime::transaction_validity::InvalidTransaction;
 
 /// Author RPC Result type.
 pub type Result<T> = std::result::Result<T, Error>;
 
-/// Author RPC future Result type.
-pub type FutureResult<T> = jsonrpc_core::BoxFuture<Result<T>>;
-
 /// Author RPC errors.
 #[derive(Debug, thiserror::Error)]
 pub enum Error {
 	/// Client error.
 	#[error("Client error: {}", .0)]
-	Client(Box<dyn std::error::Error + Send>),
+	Client(Box<dyn std::error::Error + Send + Sync>),
 	/// Transaction pool error,
 	#[error("Transaction pool error: {}", .0)]
 	Pool(#[from] sc_transaction_pool_api::error::Error),
 	/// Verification error
 	#[error("Extrinsic verification error: {}", .0)]
-	Verification(Box<dyn std::error::Error + Send>),
+	Verification(Box<dyn std::error::Error + Send + Sync>),
 	/// Incorrect extrinsic format.
 	#[error("Invalid extrinsic format: {}", .0)]
 	BadFormat(#[from] codec::Error),
@@ -58,98 +57,127 @@ pub enum Error {
 }
 
 /// Base code for all authorship errors.
-const BASE_ERROR: i64 = 1000;
+const BASE_ERROR: i32 = 1000;
 /// Extrinsic has an invalid format.
-const BAD_FORMAT: i64 = BASE_ERROR + 1;
+const BAD_FORMAT: i32 = BASE_ERROR + 1;
 /// Error during transaction verification in runtime.
-const VERIFICATION_ERROR: i64 = BASE_ERROR + 2;
+const VERIFICATION_ERROR: i32 = BASE_ERROR + 2;
 
 /// Pool rejected the transaction as invalid
-const POOL_INVALID_TX: i64 = BASE_ERROR + 10;
+const POOL_INVALID_TX: i32 = BASE_ERROR + 10;
 /// Cannot determine transaction validity.
-const POOL_UNKNOWN_VALIDITY: i64 = POOL_INVALID_TX + 1;
+const POOL_UNKNOWN_VALIDITY: i32 = POOL_INVALID_TX + 1;
 /// The transaction is temporarily banned.
-const POOL_TEMPORARILY_BANNED: i64 = POOL_INVALID_TX + 2;
+const POOL_TEMPORARILY_BANNED: i32 = POOL_INVALID_TX + 2;
 /// The transaction is already in the pool
-const POOL_ALREADY_IMPORTED: i64 = POOL_INVALID_TX + 3;
+const POOL_ALREADY_IMPORTED: i32 = POOL_INVALID_TX + 3;
 /// Transaction has too low priority to replace existing one in the pool.
-const POOL_TOO_LOW_PRIORITY: i64 = POOL_INVALID_TX + 4;
+const POOL_TOO_LOW_PRIORITY: i32 = POOL_INVALID_TX + 4;
 /// Including this transaction would cause a dependency cycle.
-const POOL_CYCLE_DETECTED: i64 = POOL_INVALID_TX + 5;
+const POOL_CYCLE_DETECTED: i32 = POOL_INVALID_TX + 5;
 /// The transaction was not included to the pool because of the limits.
-const POOL_IMMEDIATELY_DROPPED: i64 = POOL_INVALID_TX + 6;
+const POOL_IMMEDIATELY_DROPPED: i32 = POOL_INVALID_TX + 6;
 /// The transaction was not included to the pool since it is unactionable,
 /// it is not propagable and the local node does not author blocks.
-const POOL_UNACTIONABLE: i64 = POOL_INVALID_TX + 8;
+const POOL_UNACTIONABLE: i32 = POOL_INVALID_TX + 8;
+/// Transaction does not provide any tags, so the pool can't identify it.
+const POOL_NO_TAGS: i32 = POOL_INVALID_TX + 9;
+/// Invalid block ID.
+const POOL_INVALID_BLOCK_ID: i32 = POOL_INVALID_TX + 10;
+/// The pool is not accepting future transactions.
+const POOL_FUTURE_TX: i32 = POOL_INVALID_TX + 11;
 
-impl From<Error> for rpc::Error {
+impl From<Error> for JsonRpseeError {
 	fn from(e: Error) -> Self {
 		use sc_transaction_pool_api::error::Error as PoolError;
 
 		match e {
-			Error::BadFormat(e) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(BAD_FORMAT),
-				message: format!("Extrinsic has invalid format: {}", e),
-				data: None,
-			},
-			Error::Verification(e) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(VERIFICATION_ERROR),
-				message: format!("Verification Error: {}", e),
-				data: Some(e.to_string().into()),
-			},
-			Error::Pool(PoolError::InvalidTransaction(InvalidTransaction::Custom(e))) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(POOL_INVALID_TX),
-				message: "Invalid Transaction".into(),
-				data: Some(format!("Custom error: {}", e).into()),
+			Error::BadFormat(e) => CallError::Custom(ErrorObject::owned(
+				BAD_FORMAT,
+				format!("Extrinsic has invalid format: {}", e),
+				None::<()>,
+			)),
+			Error::Verification(e) => CallError::Custom(ErrorObject::owned(
+				VERIFICATION_ERROR,
+				format!("Verification Error: {}", e),
+				Some(format!("{:?}", e)),
+			)),
+			Error::Pool(PoolError::InvalidTransaction(InvalidTransaction::Custom(e))) => {
+				CallError::Custom(ErrorObject::owned(
+					POOL_INVALID_TX,
+					"Invalid Transaction",
+					Some(format!("Custom error: {}", e)),
+				))
 			},
 			Error::Pool(PoolError::InvalidTransaction(e)) => {
 				let msg: &str = e.into();
-				rpc::Error {
-					code: rpc::ErrorCode::ServerError(POOL_INVALID_TX),
-					message: "Invalid Transaction".into(),
-					data: Some(msg.into()),
-				}
-			},
-			Error::Pool(PoolError::UnknownTransaction(e)) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(POOL_UNKNOWN_VALIDITY),
-				message: "Unknown Transaction Validity".into(),
-				data: serde_json::to_value(e).ok(),
-			},
-			Error::Pool(PoolError::TemporarilyBanned) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(POOL_TEMPORARILY_BANNED),
-				message: "Transaction is temporarily banned".into(),
-				data: None,
-			},
-			Error::Pool(PoolError::AlreadyImported(hash)) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(POOL_ALREADY_IMPORTED),
-				message: "Transaction Already Imported".into(),
-				data: Some(format!("{:?}", hash).into()),
-			},
-			Error::Pool(PoolError::TooLowPriority { old, new }) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(POOL_TOO_LOW_PRIORITY),
-				message: format!("Priority is too low: ({} vs {})", old, new),
-				data: Some("The transaction has too low priority to replace another transaction already in the pool.".into()),
-			},
-			Error::Pool(PoolError::CycleDetected) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(POOL_CYCLE_DETECTED),
-				message: "Cycle Detected".into(),
-				data: None,
+				CallError::Custom(ErrorObject::owned(
+					POOL_INVALID_TX,
+					"Invalid Transaction",
+					Some(msg),
+				))
 			},
-			Error::Pool(PoolError::ImmediatelyDropped) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(POOL_IMMEDIATELY_DROPPED),
-				message: "Immediately Dropped".into(),
-				data: Some("The transaction couldn't enter the pool because of the limit".into()),
+			Error::Pool(PoolError::UnknownTransaction(e)) => {
+				CallError::Custom(ErrorObject::owned(
+					POOL_UNKNOWN_VALIDITY,
+					"Unknown Transaction Validity",
+					Some(format!("{:?}", e)),
+				))
 			},
-			Error::Pool(PoolError::Unactionable) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(POOL_UNACTIONABLE),
-				message: "Unactionable".into(),
-				data: Some(
-					"The transaction is unactionable since it is not propagable and \
-					 the local node does not author blocks".into(),
-				),
+			Error::Pool(PoolError::TemporarilyBanned) =>
+				CallError::Custom(ErrorObject::owned(
+				POOL_TEMPORARILY_BANNED,
+				"Transaction is temporarily banned",
+				None::<()>,
+			)),
+			Error::Pool(PoolError::AlreadyImported(hash)) =>
+				CallError::Custom(ErrorObject::owned(
+				POOL_ALREADY_IMPORTED,
+				"Transaction Already Imported",
+				Some(format!("{:?}", hash)),
+			)),
+			Error::Pool(PoolError::TooLowPriority { old, new }) => CallError::Custom(ErrorObject::owned(
+				POOL_TOO_LOW_PRIORITY,
+				format!("Priority is too low: ({} vs {})", old, new),
+				Some("The transaction has too low priority to replace another transaction already in the pool.")
+			)),
+			Error::Pool(PoolError::CycleDetected) =>
+				CallError::Custom(ErrorObject::owned(
+				POOL_CYCLE_DETECTED,
+				"Cycle Detected",
+				None::<()>
+			)),
+			Error::Pool(PoolError::ImmediatelyDropped) => CallError::Custom(ErrorObject::owned(
+				POOL_IMMEDIATELY_DROPPED,
+				"Immediately Dropped",
+				Some("The transaction couldn't enter the pool because of the limit"),
+			)),
+			Error::Pool(PoolError::Unactionable) => CallError::Custom(ErrorObject::owned(
+				POOL_UNACTIONABLE,
+				"Unactionable",
+				Some("The transaction is unactionable since it is not propagable and \
+				the local node does not author blocks")
+			)),
+			Error::Pool(PoolError::NoTagsProvided) => CallError::Custom(ErrorObject::owned(
+				POOL_NO_TAGS,
+				"No tags provided",
+				Some("Transaction does not provide any tags, so the pool can't identify it")
+			)),
+			Error::Pool(PoolError::InvalidBlockId(_)) =>
+				CallError::Custom(ErrorObject::owned(
+				POOL_INVALID_BLOCK_ID,
+				"The provided block ID is not valid",
+				None::<()>
+			)),
+			Error::Pool(PoolError::RejectedFutureTransaction) => {
+				CallError::Custom(ErrorObject::owned(
+					POOL_FUTURE_TX,
+					"The pool is not accepting future transactions",
+					None::<()>,
+				))
 			},
 			Error::UnsafeRpcCalled(e) => e.into(),
-			e => errors::internal(e),
-		}
+			e => CallError::Failed(e.into()),
+		}.into()
 	}
 }
diff --git a/substrate/client/rpc-api/src/author/mod.rs b/substrate/client/rpc-api/src/author/mod.rs
index 84167ee95d1081e8c8a374064e75cecd5703a5ea..feba7640e3b9f18b3ef5374f40e0dd51bc85e3f8 100644
--- a/substrate/client/rpc-api/src/author/mod.rs
+++ b/substrate/client/rpc-api/src/author/mod.rs
@@ -18,85 +18,61 @@
 
 //! Substrate block-author/full-node API.
 
-pub mod error;
-pub mod hash;
-
-use self::error::{FutureResult, Result};
-use jsonrpc_derive::rpc;
-use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId};
+use jsonrpsee::{core::RpcResult, proc_macros::rpc};
 use sc_transaction_pool_api::TransactionStatus;
 use sp_core::Bytes;
 
-pub use self::gen_client::Client as AuthorClient;
+pub mod error;
+pub mod hash;
 
 /// Substrate authoring RPC API
-#[rpc]
+#[rpc(client, server)]
 pub trait AuthorApi<Hash, BlockHash> {
-	/// RPC metadata
-	type Metadata;
-
 	/// Submit hex-encoded extrinsic for inclusion in block.
-	#[rpc(name = "author_submitExtrinsic")]
-	fn submit_extrinsic(&self, extrinsic: Bytes) -> FutureResult<Hash>;
+	#[method(name = "author_submitExtrinsic")]
+	async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult<Hash>;
 
 	/// Insert a key into the keystore.
-	#[rpc(name = "author_insertKey")]
-	fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> Result<()>;
+	#[method(name = "author_insertKey")]
+	fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> RpcResult<()>;
 
 	/// Generate new session keys and returns the corresponding public keys.
-	#[rpc(name = "author_rotateKeys")]
-	fn rotate_keys(&self) -> Result<Bytes>;
+	#[method(name = "author_rotateKeys")]
+	fn rotate_keys(&self) -> RpcResult<Bytes>;
 
 	/// Checks if the keystore has private keys for the given session public keys.
 	///
 	/// `session_keys` is the SCALE encoded session keys object from the runtime.
 	///
 	/// Returns `true` iff all private keys could be found.
-	#[rpc(name = "author_hasSessionKeys")]
-	fn has_session_keys(&self, session_keys: Bytes) -> Result<bool>;
+	#[method(name = "author_hasSessionKeys")]
+	fn has_session_keys(&self, session_keys: Bytes) -> RpcResult<bool>;
 
 	/// Checks if the keystore has private keys for the given public key and key type.
 	///
 	/// Returns `true` if a private key could be found.
-	#[rpc(name = "author_hasKey")]
-	fn has_key(&self, public_key: Bytes, key_type: String) -> Result<bool>;
+	#[method(name = "author_hasKey")]
+	fn has_key(&self, public_key: Bytes, key_type: String) -> RpcResult<bool>;
 
 	/// Returns all pending extrinsics, potentially grouped by sender.
-	#[rpc(name = "author_pendingExtrinsics")]
-	fn pending_extrinsics(&self) -> Result<Vec<Bytes>>;
+	#[method(name = "author_pendingExtrinsics")]
+	fn pending_extrinsics(&self) -> RpcResult<Vec<Bytes>>;
 
 	/// Remove given extrinsic from the pool and temporarily ban it to prevent reimporting.
-	#[rpc(name = "author_removeExtrinsic")]
+	#[method(name = "author_removeExtrinsic")]
 	fn remove_extrinsic(
 		&self,
 		bytes_or_hash: Vec<hash::ExtrinsicOrHash<Hash>>,
-	) -> Result<Vec<Hash>>;
+	) -> RpcResult<Vec<Hash>>;
 
 	/// Submit an extrinsic to watch.
 	///
 	/// See [`TransactionStatus`](sc_transaction_pool_api::TransactionStatus) for details on
 	/// transaction life cycle.
-	#[pubsub(
-		subscription = "author_extrinsicUpdate",
-		subscribe,
-		name = "author_submitAndWatchExtrinsic"
+	#[subscription(
+		name = "author_submitAndWatchExtrinsic" => "author_extrinsicUpdate",
+		unsubscribe = "author_unwatchExtrinsic",
+		item = TransactionStatus<Hash, BlockHash>,
 	)]
-	fn watch_extrinsic(
-		&self,
-		metadata: Self::Metadata,
-		subscriber: Subscriber<TransactionStatus<Hash, BlockHash>>,
-		bytes: Bytes,
-	);
-
-	/// Unsubscribe from extrinsic watching.
-	#[pubsub(
-		subscription = "author_extrinsicUpdate",
-		unsubscribe,
-		name = "author_unwatchExtrinsic"
-	)]
-	fn unwatch_extrinsic(
-		&self,
-		metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> Result<bool>;
+	fn watch_extrinsic(&self, bytes: Bytes);
 }
diff --git a/substrate/client/rpc-api/src/chain/error.rs b/substrate/client/rpc-api/src/chain/error.rs
index a0cacb6739155fea3be7e29fbb6f3b7248b0cc02..670e221cf1cdee27b3eaeabd3b88533b423bbd41 100644
--- a/substrate/client/rpc-api/src/chain/error.rs
+++ b/substrate/client/rpc-api/src/chain/error.rs
@@ -18,38 +18,33 @@
 
 //! Error helpers for Chain RPC module.
 
-use crate::errors;
-use jsonrpc_core as rpc;
-
+use jsonrpsee::{
+	core::Error as JsonRpseeError,
+	types::error::{CallError, ErrorObject},
+};
 /// Chain RPC Result type.
 pub type Result<T> = std::result::Result<T, Error>;
 
-/// Chain RPC future Result type.
-pub type FutureResult<T> = jsonrpc_core::BoxFuture<Result<T>>;
-
 /// Chain RPC errors.
 #[derive(Debug, thiserror::Error)]
 pub enum Error {
 	/// Client error.
 	#[error("Client error: {}", .0)]
-	Client(#[from] Box<dyn std::error::Error + Send>),
+	Client(#[from] Box<dyn std::error::Error + Send + Sync>),
 	/// Other error type.
 	#[error("{0}")]
 	Other(String),
 }
 
 /// Base error code for all chain errors.
-const BASE_ERROR: i64 = 3000;
+const BASE_ERROR: i32 = 3000;
 
-impl From<Error> for rpc::Error {
+impl From<Error> for JsonRpseeError {
 	fn from(e: Error) -> Self {
 		match e {
-			Error::Other(message) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(BASE_ERROR + 1),
-				message,
-				data: None,
-			},
-			e => errors::internal(e),
+			Error::Other(message) =>
+				CallError::Custom(ErrorObject::owned(BASE_ERROR + 1, message, None::<()>)).into(),
+			e => e.into(),
 		}
 	}
 }
diff --git a/substrate/client/rpc-api/src/chain/mod.rs b/substrate/client/rpc-api/src/chain/mod.rs
index d7d598942f1eacaacf92b6405948c042d0b15f78..f5f9524264e345c2100bce09afb07920552255a0 100644
--- a/substrate/client/rpc-api/src/chain/mod.rs
+++ b/substrate/client/rpc-api/src/chain/mod.rs
@@ -18,96 +18,59 @@
 
 //! Substrate blockchain API.
 
-pub mod error;
-
-use self::error::{FutureResult, Result};
-use jsonrpc_core::Result as RpcResult;
-use jsonrpc_derive::rpc;
-use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId};
+use jsonrpsee::{core::RpcResult, proc_macros::rpc};
 use sp_rpc::{list::ListOrValue, number::NumberOrHex};
 
-pub use self::gen_client::Client as ChainClient;
+pub mod error;
 
-/// Substrate blockchain API
-#[rpc]
+#[rpc(client, server)]
 pub trait ChainApi<Number, Hash, Header, SignedBlock> {
-	/// RPC metadata
-	type Metadata;
-
-	/// Get header of a relay chain block.
-	#[rpc(name = "chain_getHeader")]
-	fn header(&self, hash: Option<Hash>) -> FutureResult<Option<Header>>;
+	/// Get header.
+	#[method(name = "chain_getHeader")]
+	async fn header(&self, hash: Option<Hash>) -> RpcResult<Option<Header>>;
 
 	/// Get header and body of a relay chain block.
-	#[rpc(name = "chain_getBlock")]
-	fn block(&self, hash: Option<Hash>) -> FutureResult<Option<SignedBlock>>;
+	#[method(name = "chain_getBlock")]
+	async fn block(&self, hash: Option<Hash>) -> RpcResult<Option<SignedBlock>>;
 
 	/// Get hash of the n-th block in the canon chain.
 	///
 	/// By default returns latest block hash.
-	#[rpc(name = "chain_getBlockHash", alias("chain_getHead"))]
+	#[method(name = "chain_getBlockHash", aliases = ["chain_getHead"])]
 	fn block_hash(
 		&self,
 		hash: Option<ListOrValue<NumberOrHex>>,
-	) -> Result<ListOrValue<Option<Hash>>>;
+	) -> RpcResult<ListOrValue<Option<Hash>>>;
 
 	/// Get hash of the last finalized block in the canon chain.
-	#[rpc(name = "chain_getFinalizedHead", alias("chain_getFinalisedHead"))]
-	fn finalized_head(&self) -> Result<Hash>;
-
-	/// All head subscription
-	#[pubsub(subscription = "chain_allHead", subscribe, name = "chain_subscribeAllHeads")]
-	fn subscribe_all_heads(&self, metadata: Self::Metadata, subscriber: Subscriber<Header>);
-
-	/// Unsubscribe from all head subscription.
-	#[pubsub(subscription = "chain_allHead", unsubscribe, name = "chain_unsubscribeAllHeads")]
-	fn unsubscribe_all_heads(
-		&self,
-		metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool>;
-
-	/// New head subscription
-	#[pubsub(
-		subscription = "chain_newHead",
-		subscribe,
-		name = "chain_subscribeNewHeads",
-		alias("subscribe_newHead", "chain_subscribeNewHead")
+	#[method(name = "chain_getFinalizedHead", aliases = ["chain_getFinalisedHead"])]
+	fn finalized_head(&self) -> RpcResult<Hash>;
+
+	/// All head subscription.
+	#[subscription(
+		name = "chain_subscribeAllHeads" => "chain_allHead",
+		unsubscribe = "chain_unsubscribeAllHeads",
+		item = Header
 	)]
-	fn subscribe_new_heads(&self, metadata: Self::Metadata, subscriber: Subscriber<Header>);
-
-	/// Unsubscribe from new head subscription.
-	#[pubsub(
-		subscription = "chain_newHead",
-		unsubscribe,
-		name = "chain_unsubscribeNewHeads",
-		alias("unsubscribe_newHead", "chain_unsubscribeNewHead")
+	fn subscribe_all_heads(&self);
+
+	/// New head subscription.
+	#[subscription(
+		name = "chain_subscribeNewHeads" => "chain_newHead",
+		aliases = ["subscribe_newHead", "chain_subscribeNewHead"],
+		unsubscribe = "chain_unsubscribeNewHeads",
+		unsubscribe_aliases = ["unsubscribe_newHead", "chain_unsubscribeNewHead"],
+		item = Header
 	)]
-	fn unsubscribe_new_heads(
-		&self,
-		metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool>;
-
-	/// Finalized head subscription
-	#[pubsub(
-		subscription = "chain_finalizedHead",
-		subscribe,
-		name = "chain_subscribeFinalizedHeads",
-		alias("chain_subscribeFinalisedHeads")
+	fn subscribe_new_heads(&self);
+
+	/// Finalized head subscription.
+	#[subscription(
+		name = "chain_subscribeFinalizedHeads" => "chain_finalizedHead",
+		aliases = ["chain_subscribeFinalisedHeads"],
+		unsubscribe = "chain_unsubscribeFinalizedHeads",
+		unsubscribe_aliases = ["chain_unsubscribeFinalisedHeads"],
+		item = Header
 	)]
-	fn subscribe_finalized_heads(&self, metadata: Self::Metadata, subscriber: Subscriber<Header>);
-
-	/// Unsubscribe from finalized head subscription.
-	#[pubsub(
-		subscription = "chain_finalizedHead",
-		unsubscribe,
-		name = "chain_unsubscribeFinalizedHeads",
-		alias("chain_unsubscribeFinalisedHeads")
-	)]
-	fn unsubscribe_finalized_heads(
-		&self,
-		metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool>;
+	fn subscribe_finalized_heads(&self);
 }
diff --git a/substrate/client/rpc-api/src/child_state/mod.rs b/substrate/client/rpc-api/src/child_state/mod.rs
index 6b4cd20f226050bd3d7d735905178a664937ec66..a15b1a0e7ee057f2cef5444d01df79d50eb3fbe6 100644
--- a/substrate/client/rpc-api/src/child_state/mod.rs
+++ b/substrate/client/rpc-api/src/child_state/mod.rs
@@ -16,89 +16,82 @@
 // You should have received a copy of the GNU General Public License
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
-//! Substrate state API.
-
-use crate::state::error::FutureResult;
-use jsonrpc_derive::rpc;
-use sp_core::storage::{PrefixedStorageKey, StorageData, StorageKey};
-
-pub use self::gen_client::Client as ChildStateClient;
+//! Substrate child state API
 use crate::state::ReadProof;
+use jsonrpsee::{core::RpcResult, proc_macros::rpc};
+use sp_core::storage::{PrefixedStorageKey, StorageData, StorageKey};
 
 /// Substrate child state API
 ///
 /// Note that all `PrefixedStorageKey` are deserialized
 /// from json and not guaranteed valid.
-#[rpc]
+#[rpc(client, server)]
 pub trait ChildStateApi<Hash> {
-	/// RPC Metadata
-	type Metadata;
-
-	/// DEPRECATED: Please use `childstate_getKeysPaged` with proper paging support.
 	/// Returns the keys with prefix from a child storage, leave empty to get all the keys
-	#[rpc(name = "childstate_getKeys")]
-	fn storage_keys(
+	#[method(name = "childstate_getKeys")]
+	#[deprecated(since = "2.0.0", note = "Please use `getKeysPaged` with proper paging support")]
+	async fn storage_keys(
 		&self,
 		child_storage_key: PrefixedStorageKey,
 		prefix: StorageKey,
 		hash: Option<Hash>,
-	) -> FutureResult<Vec<StorageKey>>;
+	) -> RpcResult<Vec<StorageKey>>;
 
 	/// Returns the keys with prefix from a child storage with pagination support.
 	/// Up to `count` keys will be returned.
 	/// If `start_key` is passed, return next keys in storage in lexicographic order.
-	#[rpc(name = "childstate_getKeysPaged", alias("childstate_getKeysPagedAt"))]
-	fn storage_keys_paged(
+	#[method(name = "childstate_getKeysPaged", aliases = ["childstate_getKeysPagedAt"])]
+	async fn storage_keys_paged(
 		&self,
 		child_storage_key: PrefixedStorageKey,
 		prefix: Option<StorageKey>,
 		count: u32,
 		start_key: Option<StorageKey>,
 		hash: Option<Hash>,
-	) -> FutureResult<Vec<StorageKey>>;
+	) -> RpcResult<Vec<StorageKey>>;
 
 	/// Returns a child storage entry at a specific block's state.
-	#[rpc(name = "childstate_getStorage")]
-	fn storage(
+	#[method(name = "childstate_getStorage")]
+	async fn storage(
 		&self,
 		child_storage_key: PrefixedStorageKey,
 		key: StorageKey,
 		hash: Option<Hash>,
-	) -> FutureResult<Option<StorageData>>;
+	) -> RpcResult<Option<StorageData>>;
 
 	/// Returns child storage entries for multiple keys at a specific block's state.
-	#[rpc(name = "childstate_getStorageEntries")]
-	fn storage_entries(
+	#[method(name = "childstate_getStorageEntries")]
+	async fn storage_entries(
 		&self,
 		child_storage_key: PrefixedStorageKey,
 		keys: Vec<StorageKey>,
 		hash: Option<Hash>,
-	) -> FutureResult<Vec<Option<StorageData>>>;
+	) -> RpcResult<Vec<Option<StorageData>>>;
 
 	/// Returns the hash of a child storage entry at a block's state.
-	#[rpc(name = "childstate_getStorageHash")]
-	fn storage_hash(
+	#[method(name = "childstate_getStorageHash")]
+	async fn storage_hash(
 		&self,
 		child_storage_key: PrefixedStorageKey,
 		key: StorageKey,
 		hash: Option<Hash>,
-	) -> FutureResult<Option<Hash>>;
+	) -> RpcResult<Option<Hash>>;
 
 	/// Returns the size of a child storage entry at a block's state.
-	#[rpc(name = "childstate_getStorageSize")]
-	fn storage_size(
+	#[method(name = "childstate_getStorageSize")]
+	async fn storage_size(
 		&self,
 		child_storage_key: PrefixedStorageKey,
 		key: StorageKey,
 		hash: Option<Hash>,
-	) -> FutureResult<Option<u64>>;
+	) -> RpcResult<Option<u64>>;
 
 	/// Returns proof of storage for child key entries at a specific block's state.
-	#[rpc(name = "state_getChildReadProof")]
-	fn read_child_proof(
+	#[method(name = "state_getChildReadProof")]
+	async fn read_child_proof(
 		&self,
 		child_storage_key: PrefixedStorageKey,
 		keys: Vec<StorageKey>,
 		hash: Option<Hash>,
-	) -> FutureResult<ReadProof<Hash>>;
+	) -> RpcResult<ReadProof<Hash>>;
 }
diff --git a/substrate/client/rpc-api/src/dev/error.rs b/substrate/client/rpc-api/src/dev/error.rs
index 1a14b0d78994e180a78d6741768c172b15fc83b8..fe74dea256376f2fc5a93c75ec7f21c4e2136b53 100644
--- a/substrate/client/rpc-api/src/dev/error.rs
+++ b/substrate/client/rpc-api/src/dev/error.rs
@@ -18,14 +18,10 @@
 
 //! Error helpers for Dev RPC module.
 
-use crate::errors;
-use jsonrpc_core as rpc;
-
-/// Dev RPC Result type.
-pub type Result<T> = std::result::Result<T, Error>;
-
-/// Dev RPC future Result type.
-pub type FutureResult<T> = jsonrpc_core::BoxFuture<Result<T>>;
+use jsonrpsee::{
+	core::Error as JsonRpseeError,
+	types::error::{CallError, ErrorObject},
+};
 
 /// Dev RPC errors.
 #[derive(Debug, thiserror::Error)]
@@ -45,27 +41,21 @@ pub enum Error {
 }
 
 /// Base error code for all dev errors.
-const BASE_ERROR: i64 = 6000;
+const BASE_ERROR: i32 = 6000;
 
-impl From<Error> for rpc::Error {
+impl From<Error> for JsonRpseeError {
 	fn from(e: Error) -> Self {
+		let msg = e.to_string();
+
 		match e {
-			Error::BlockQueryError(_) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(BASE_ERROR + 1),
-				message: e.to_string(),
-				data: None,
-			},
-			Error::BlockExecutionFailed => rpc::Error {
-				code: rpc::ErrorCode::ServerError(BASE_ERROR + 3),
-				message: e.to_string(),
-				data: None,
-			},
-			Error::WitnessCompactionFailed => rpc::Error {
-				code: rpc::ErrorCode::ServerError(BASE_ERROR + 4),
-				message: e.to_string(),
-				data: None,
-			},
-			e => errors::internal(e),
+			Error::BlockQueryError(_) =>
+				CallError::Custom(ErrorObject::owned(BASE_ERROR + 1, msg, None::<()>)),
+			Error::BlockExecutionFailed =>
+				CallError::Custom(ErrorObject::owned(BASE_ERROR + 3, msg, None::<()>)),
+			Error::WitnessCompactionFailed =>
+				CallError::Custom(ErrorObject::owned(BASE_ERROR + 4, msg, None::<()>)),
+			Error::UnsafeRpcCalled(e) => e.into(),
 		}
+		.into()
 	}
 }
diff --git a/substrate/client/rpc-api/src/dev/mod.rs b/substrate/client/rpc-api/src/dev/mod.rs
index b1ae8934af8a174bc109ea24fbf3111bd49655cc..afd83272a0127977b66738b3305b75d9fe6b8bc5 100644
--- a/substrate/client/rpc-api/src/dev/mod.rs
+++ b/substrate/client/rpc-api/src/dev/mod.rs
@@ -22,9 +22,8 @@
 
 pub mod error;
 
-use self::error::Result;
 use codec::{Decode, Encode};
-use jsonrpc_derive::rpc;
+use jsonrpsee::{core::RpcResult, proc_macros::rpc};
 use scale_info::TypeInfo;
 use serde::{Deserialize, Serialize};
 
@@ -52,13 +51,13 @@ pub struct BlockStats {
 ///
 /// This API contains unstable and unsafe methods only meant for development nodes. They
 /// are all flagged as unsafe for this reason.
-#[rpc]
+#[rpc(client, server)]
 pub trait DevApi<Hash> {
 	/// Reexecute the specified `block_hash` and gather statistics while doing so.
 	///
 	/// This function requires the specified block and its parent to be available
 	/// at the queried node. If either the specified block or the parent is pruned,
 	/// this function will return `None`.
-	#[rpc(name = "dev_getBlockStats")]
-	fn block_stats(&self, block_hash: Hash) -> Result<Option<BlockStats>>;
+	#[method(name = "dev_getBlockStats")]
+	fn block_stats(&self, block_hash: Hash) -> RpcResult<Option<BlockStats>>;
 }
diff --git a/substrate/client/rpc-api/src/errors.rs b/substrate/client/rpc-api/src/errors.rs
deleted file mode 100644
index e59b1b0eda5ce50819d8db54af34f03865fc5580..0000000000000000000000000000000000000000
--- a/substrate/client/rpc-api/src/errors.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd.
-// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
-
-// This program 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.
-
-// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
-
-use log::warn;
-
-pub fn internal<E: std::fmt::Display>(e: E) -> jsonrpc_core::Error {
-	warn!("Unknown error: {}", e);
-	jsonrpc_core::Error {
-		code: jsonrpc_core::ErrorCode::InternalError,
-		message: "Unknown error occurred".into(),
-		data: Some(e.to_string().into()),
-	}
-}
diff --git a/substrate/client/rpc-api/src/helpers.rs b/substrate/client/rpc-api/src/helpers.rs
deleted file mode 100644
index 2fbd2f504046386efade364368012aaac98e1b7d..0000000000000000000000000000000000000000
--- a/substrate/client/rpc-api/src/helpers.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd.
-// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
-
-// This program 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.
-
-// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
-
-use futures::{channel::oneshot, Future};
-use std::pin::Pin;
-
-/// Wraps around `oneshot::Receiver` and adjusts the error type to produce an internal error if the
-/// sender gets dropped.
-pub struct Receiver<T>(pub oneshot::Receiver<T>);
-
-impl<T> Future for Receiver<T> {
-	type Output = Result<T, jsonrpc_core::Error>;
-
-	fn poll(
-		mut self: Pin<&mut Self>,
-		cx: &mut std::task::Context<'_>,
-	) -> std::task::Poll<Self::Output> {
-		Future::poll(Pin::new(&mut self.0), cx).map_err(|_| jsonrpc_core::Error::internal_error())
-	}
-}
-
-impl<T: Send + 'static> jsonrpc_core::WrapFuture<T, jsonrpc_core::Error> for Receiver<T> {
-	fn into_future(self) -> jsonrpc_core::BoxFuture<Result<T, jsonrpc_core::Error>> {
-		Box::pin(async { self.await })
-	}
-}
diff --git a/substrate/client/rpc-api/src/lib.rs b/substrate/client/rpc-api/src/lib.rs
index e06f30bf9cd87dabd49883d20a2f1317fbe1d23b..a0cbbcee80e3ea5df81ccbdfe31775ce43303e2a 100644
--- a/substrate/client/rpc-api/src/lib.rs
+++ b/substrate/client/rpc-api/src/lib.rs
@@ -22,15 +22,9 @@
 
 #![warn(missing_docs)]
 
-mod errors;
-mod helpers;
-mod metadata;
 mod policy;
 
-pub use helpers::Receiver;
-pub use jsonrpc_core::IoHandlerExtension as RpcExtension;
-pub use metadata::Metadata;
-pub use policy::{DenyUnsafe, UnsafeRpcError};
+pub use policy::DenyUnsafe;
 
 pub mod author;
 pub mod chain;
diff --git a/substrate/client/rpc-api/src/metadata.rs b/substrate/client/rpc-api/src/metadata.rs
deleted file mode 100644
index 3c798782062e9f12e12c26e7c4ff6a3ec5e9e9ce..0000000000000000000000000000000000000000
--- a/substrate/client/rpc-api/src/metadata.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.
-// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
-
-// This program 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.
-
-// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
-
-//! RPC Metadata
-use std::sync::Arc;
-
-use futures::channel::mpsc;
-use jsonrpc_pubsub::{PubSubMetadata, Session};
-
-/// RPC Metadata.
-///
-/// Manages persistent session for transports that support it
-/// and may contain some additional info extracted from specific transports
-/// (like remote client IP address, request headers, etc)
-#[derive(Default, Clone)]
-pub struct Metadata {
-	session: Option<Arc<Session>>,
-}
-
-impl jsonrpc_core::Metadata for Metadata {}
-impl PubSubMetadata for Metadata {
-	fn session(&self) -> Option<Arc<Session>> {
-		self.session.clone()
-	}
-}
-
-impl Metadata {
-	/// Create new `Metadata` with session (Pub/Sub) support.
-	pub fn new(transport: mpsc::UnboundedSender<String>) -> Self {
-		Metadata { session: Some(Arc::new(Session::new(transport))) }
-	}
-
-	/// Create new `Metadata` for tests.
-	#[cfg(test)]
-	pub fn new_test() -> (mpsc::UnboundedReceiver<String>, Self) {
-		let (tx, rx) = mpsc::unbounded();
-		(rx, Self::new(tx))
-	}
-}
-
-impl From<mpsc::UnboundedSender<String>> for Metadata {
-	fn from(sender: mpsc::UnboundedSender<String>) -> Self {
-		Self::new(sender)
-	}
-}
diff --git a/substrate/client/rpc-api/src/offchain/error.rs b/substrate/client/rpc-api/src/offchain/error.rs
index 41f1416bfb36790d415f529d2cabf2827863a968..be72e05fc446047367d6b0a6d6dff76c9b7a2876 100644
--- a/substrate/client/rpc-api/src/offchain/error.rs
+++ b/substrate/client/rpc-api/src/offchain/error.rs
@@ -18,7 +18,10 @@
 
 //! Offchain RPC errors.
 
-use jsonrpc_core as rpc;
+use jsonrpsee::{
+	core::Error as JsonRpseeError,
+	types::error::{CallError, ErrorObject},
+};
 
 /// Offchain RPC Result type.
 pub type Result<T> = std::result::Result<T, Error>;
@@ -35,16 +38,17 @@ pub enum Error {
 }
 
 /// Base error code for all offchain errors.
-const BASE_ERROR: i64 = 5000;
+const BASE_ERROR: i32 = 5000;
 
-impl From<Error> for rpc::Error {
+impl From<Error> for JsonRpseeError {
 	fn from(e: Error) -> Self {
 		match e {
-			Error::UnavailableStorageKind => rpc::Error {
-				code: rpc::ErrorCode::ServerError(BASE_ERROR + 1),
-				message: "This storage kind is not available yet".into(),
-				data: None,
-			},
+			Error::UnavailableStorageKind => CallError::Custom(ErrorObject::owned(
+				BASE_ERROR + 1,
+				"This storage kind is not available yet",
+				None::<()>,
+			))
+			.into(),
 			Error::UnsafeRpcCalled(e) => e.into(),
 		}
 	}
diff --git a/substrate/client/rpc-api/src/offchain/mod.rs b/substrate/client/rpc-api/src/offchain/mod.rs
index c76e83011072d1cc80a4cd0a6ac4a5efcb5dd2e6..d9435d9a875fe8905eb536712132520d37a9275e 100644
--- a/substrate/client/rpc-api/src/offchain/mod.rs
+++ b/substrate/client/rpc-api/src/offchain/mod.rs
@@ -18,22 +18,19 @@
 
 //! Substrate offchain API.
 
-pub mod error;
-
-use self::error::Result;
-use jsonrpc_derive::rpc;
+use jsonrpsee::{core::RpcResult, proc_macros::rpc};
 use sp_core::{offchain::StorageKind, Bytes};
 
-pub use self::gen_client::Client as OffchainClient;
+pub mod error;
 
 /// Substrate offchain RPC API
-#[rpc]
+#[rpc(client, server)]
 pub trait OffchainApi {
 	/// Set offchain local storage under given key and prefix.
-	#[rpc(name = "offchain_localStorageSet")]
-	fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<()>;
+	#[method(name = "offchain_localStorageSet")]
+	fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> RpcResult<()>;
 
 	/// Get offchain local storage under given key and prefix.
-	#[rpc(name = "offchain_localStorageGet")]
-	fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result<Option<Bytes>>;
+	#[method(name = "offchain_localStorageGet")]
+	fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> RpcResult<Option<Bytes>>;
 }
diff --git a/substrate/client/rpc-api/src/policy.rs b/substrate/client/rpc-api/src/policy.rs
index dc0753c1b9139f4cea4d6d30360170e47900a8e7..69ca8958520a607e87ee057dc8c81c9d2c97d89a 100644
--- a/substrate/client/rpc-api/src/policy.rs
+++ b/substrate/client/rpc-api/src/policy.rs
@@ -21,7 +21,13 @@
 //! Contains a `DenyUnsafe` type that can be used to deny potentially unsafe
 //! RPC when accessed externally.
 
-use jsonrpc_core as rpc;
+use jsonrpsee::{
+	core::Error as JsonRpseeError,
+	types::{
+		error::{CallError, ErrorCode},
+		ErrorObject,
+	},
+};
 
 /// Signifies whether a potentially unsafe RPC should be denied.
 #[derive(Clone, Copy, Debug)]
@@ -55,8 +61,18 @@ impl std::fmt::Display for UnsafeRpcError {
 
 impl std::error::Error for UnsafeRpcError {}
 
-impl From<UnsafeRpcError> for rpc::Error {
-	fn from(error: UnsafeRpcError) -> rpc::Error {
-		rpc::Error { code: rpc::ErrorCode::MethodNotFound, message: error.to_string(), data: None }
+impl From<UnsafeRpcError> for CallError {
+	fn from(e: UnsafeRpcError) -> CallError {
+		CallError::Custom(ErrorObject::owned(
+			ErrorCode::MethodNotFound.code(),
+			e.to_string(),
+			None::<()>,
+		))
+	}
+}
+
+impl From<UnsafeRpcError> for JsonRpseeError {
+	fn from(e: UnsafeRpcError) -> JsonRpseeError {
+		JsonRpseeError::Call(e.into())
 	}
 }
diff --git a/substrate/client/rpc-api/src/state/error.rs b/substrate/client/rpc-api/src/state/error.rs
index 4414629e2e2945f6f323173f587ae60f4625f74c..b1df64b4789ab82610c8e00c2ece0babce6e2808 100644
--- a/substrate/client/rpc-api/src/state/error.rs
+++ b/substrate/client/rpc-api/src/state/error.rs
@@ -18,21 +18,19 @@
 
 //! State RPC errors.
 
-use crate::errors;
-use jsonrpc_core as rpc;
-
+use jsonrpsee::{
+	core::Error as JsonRpseeError,
+	types::error::{CallError, ErrorObject},
+};
 /// State RPC Result type.
 pub type Result<T> = std::result::Result<T, Error>;
 
-/// State RPC future Result type.
-pub type FutureResult<T> = jsonrpc_core::BoxFuture<Result<T>>;
-
 /// State RPC errors.
 #[derive(Debug, thiserror::Error)]
 pub enum Error {
 	/// Client error.
 	#[error("Client error: {}", .0)]
-	Client(#[from] Box<dyn std::error::Error + Send>),
+	Client(#[from] Box<dyn std::error::Error + Send + Sync>),
 	/// Provided block range couldn't be resolved to a list of blocks.
 	#[error("Cannot resolve a block range ['{:?}' ... '{:?}]. {}", .from, .to, .details)]
 	InvalidBlockRange {
@@ -57,22 +55,18 @@ pub enum Error {
 }
 
 /// Base code for all state errors.
-const BASE_ERROR: i64 = 4000;
+const BASE_ERROR: i32 = 4000;
 
-impl From<Error> for rpc::Error {
+impl From<Error> for JsonRpseeError {
 	fn from(e: Error) -> Self {
 		match e {
-			Error::InvalidBlockRange { .. } => rpc::Error {
-				code: rpc::ErrorCode::ServerError(BASE_ERROR + 1),
-				message: format!("{}", e),
-				data: None,
-			},
-			Error::InvalidCount { .. } => rpc::Error {
-				code: rpc::ErrorCode::ServerError(BASE_ERROR + 2),
-				message: format!("{}", e),
-				data: None,
-			},
-			e => errors::internal(e),
+			Error::InvalidBlockRange { .. } =>
+				CallError::Custom(ErrorObject::owned(BASE_ERROR + 1, e.to_string(), None::<()>))
+					.into(),
+			Error::InvalidCount { .. } =>
+				CallError::Custom(ErrorObject::owned(BASE_ERROR + 2, e.to_string(), None::<()>))
+					.into(),
+			e => Self::to_call_error(e),
 		}
 	}
 }
diff --git a/substrate/client/rpc-api/src/state/mod.rs b/substrate/client/rpc-api/src/state/mod.rs
index 42e927580960c575c06577c01d714898b44c6975..fba023e830262caaca034103c680a74781b3deb5 100644
--- a/substrate/client/rpc-api/src/state/mod.rs
+++ b/substrate/client/rpc-api/src/state/mod.rs
@@ -18,161 +18,122 @@
 
 //! Substrate state API.
 
-pub mod error;
-pub mod helpers;
-
-use self::error::FutureResult;
-use jsonrpc_core::Result as RpcResult;
-use jsonrpc_derive::rpc;
-use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId};
+use jsonrpsee::{core::RpcResult, proc_macros::rpc};
 use sp_core::{
 	storage::{StorageChangeSet, StorageData, StorageKey},
 	Bytes,
 };
 use sp_version::RuntimeVersion;
 
-pub use self::{gen_client::Client as StateClient, helpers::ReadProof};
+pub mod error;
+pub mod helpers;
+
+pub use self::helpers::ReadProof;
 
 /// Substrate state API
-#[rpc]
+#[rpc(client, server)]
 pub trait StateApi<Hash> {
-	/// RPC Metadata
-	type Metadata;
-
 	/// Call a contract at a block's state.
-	#[rpc(name = "state_call", alias("state_callAt"))]
-	fn call(&self, name: String, bytes: Bytes, hash: Option<Hash>) -> FutureResult<Bytes>;
+	#[method(name = "state_call", aliases = ["state_callAt"])]
+	async fn call(&self, name: String, bytes: Bytes, hash: Option<Hash>) -> RpcResult<Bytes>;
 
-	/// DEPRECATED: Please use `state_getKeysPaged` with proper paging support.
 	/// Returns the keys with prefix, leave empty to get all the keys.
-	#[rpc(name = "state_getKeys")]
-	fn storage_keys(&self, prefix: StorageKey, hash: Option<Hash>)
-		-> FutureResult<Vec<StorageKey>>;
+	#[method(name = "state_getKeys")]
+	#[deprecated(since = "2.0.0", note = "Please use `getKeysPaged` with proper paging support")]
+	async fn storage_keys(
+		&self,
+		prefix: StorageKey,
+		hash: Option<Hash>,
+	) -> RpcResult<Vec<StorageKey>>;
 
 	/// Returns the keys with prefix, leave empty to get all the keys
-	#[rpc(name = "state_getPairs")]
-	fn storage_pairs(
+	#[method(name = "state_getPairs")]
+	async fn storage_pairs(
 		&self,
 		prefix: StorageKey,
 		hash: Option<Hash>,
-	) -> FutureResult<Vec<(StorageKey, StorageData)>>;
+	) -> RpcResult<Vec<(StorageKey, StorageData)>>;
 
 	/// Returns the keys with prefix with pagination support.
 	/// Up to `count` keys will be returned.
 	/// If `start_key` is passed, return next keys in storage in lexicographic order.
-	#[rpc(name = "state_getKeysPaged", alias("state_getKeysPagedAt"))]
-	fn storage_keys_paged(
+	#[method(name = "state_getKeysPaged", aliases = ["state_getKeysPagedAt"])]
+	async fn storage_keys_paged(
 		&self,
 		prefix: Option<StorageKey>,
 		count: u32,
 		start_key: Option<StorageKey>,
 		hash: Option<Hash>,
-	) -> FutureResult<Vec<StorageKey>>;
+	) -> RpcResult<Vec<StorageKey>>;
 
 	/// Returns a storage entry at a specific block's state.
-	#[rpc(name = "state_getStorage", alias("state_getStorageAt"))]
-	fn storage(&self, key: StorageKey, hash: Option<Hash>) -> FutureResult<Option<StorageData>>;
+	#[method(name = "state_getStorage", aliases = ["state_getStorageAt"])]
+	async fn storage(&self, key: StorageKey, hash: Option<Hash>) -> RpcResult<Option<StorageData>>;
 
 	/// Returns the hash of a storage entry at a block's state.
-	#[rpc(name = "state_getStorageHash", alias("state_getStorageHashAt"))]
-	fn storage_hash(&self, key: StorageKey, hash: Option<Hash>) -> FutureResult<Option<Hash>>;
+	#[method(name = "state_getStorageHash", aliases = ["state_getStorageHashAt"])]
+	async fn storage_hash(&self, key: StorageKey, hash: Option<Hash>) -> RpcResult<Option<Hash>>;
 
 	/// Returns the size of a storage entry at a block's state.
-	#[rpc(name = "state_getStorageSize", alias("state_getStorageSizeAt"))]
-	fn storage_size(&self, key: StorageKey, hash: Option<Hash>) -> FutureResult<Option<u64>>;
+	#[method(name = "state_getStorageSize", aliases = ["state_getStorageSizeAt"])]
+	async fn storage_size(&self, key: StorageKey, hash: Option<Hash>) -> RpcResult<Option<u64>>;
 
 	/// Returns the runtime metadata as an opaque blob.
-	#[rpc(name = "state_getMetadata")]
-	fn metadata(&self, hash: Option<Hash>) -> FutureResult<Bytes>;
+	#[method(name = "state_getMetadata")]
+	async fn metadata(&self, hash: Option<Hash>) -> RpcResult<Bytes>;
 
 	/// Get the runtime version.
-	#[rpc(name = "state_getRuntimeVersion", alias("chain_getRuntimeVersion"))]
-	fn runtime_version(&self, hash: Option<Hash>) -> FutureResult<RuntimeVersion>;
+	#[method(name = "state_getRuntimeVersion", aliases = ["chain_getRuntimeVersion"])]
+	async fn runtime_version(&self, hash: Option<Hash>) -> RpcResult<RuntimeVersion>;
 
 	/// Query historical storage entries (by key) starting from a block given as the second
 	/// parameter.
 	///
 	/// NOTE This first returned result contains the initial state of storage for all keys.
 	/// Subsequent values in the vector represent changes to the previous state (diffs).
-	#[rpc(name = "state_queryStorage")]
-	fn query_storage(
+	#[method(name = "state_queryStorage")]
+	async fn query_storage(
 		&self,
 		keys: Vec<StorageKey>,
 		block: Hash,
 		hash: Option<Hash>,
-	) -> FutureResult<Vec<StorageChangeSet<Hash>>>;
+	) -> RpcResult<Vec<StorageChangeSet<Hash>>>;
 
 	/// Query storage entries (by key) starting at block hash given as the second parameter.
-	#[rpc(name = "state_queryStorageAt")]
-	fn query_storage_at(
+	#[method(name = "state_queryStorageAt")]
+	async fn query_storage_at(
 		&self,
 		keys: Vec<StorageKey>,
 		at: Option<Hash>,
-	) -> FutureResult<Vec<StorageChangeSet<Hash>>>;
+	) -> RpcResult<Vec<StorageChangeSet<Hash>>>;
 
 	/// Returns proof of storage entries at a specific block's state.
-	#[rpc(name = "state_getReadProof")]
-	fn read_proof(
+	#[method(name = "state_getReadProof")]
+	async fn read_proof(
 		&self,
 		keys: Vec<StorageKey>,
 		hash: Option<Hash>,
-	) -> FutureResult<ReadProof<Hash>>;
+	) -> RpcResult<ReadProof<Hash>>;
 
 	/// New runtime version subscription
-	#[pubsub(
-		subscription = "state_runtimeVersion",
-		subscribe,
-		name = "state_subscribeRuntimeVersion",
-		alias("chain_subscribeRuntimeVersion")
+	#[subscription(
+		name = "state_subscribeRuntimeVersion" => "state_runtimeVersion",
+		unsubscribe = "state_unsubscribeRuntimeVersion",
+		aliases = ["chain_subscribeRuntimeVersion"],
+		unsubscribe_aliases = ["chain_unsubscribeRuntimeVersion"],
+		item = RuntimeVersion,
 	)]
-	fn subscribe_runtime_version(
-		&self,
-		metadata: Self::Metadata,
-		subscriber: Subscriber<RuntimeVersion>,
-	);
+	fn subscribe_runtime_version(&self);
 
-	/// Unsubscribe from runtime version subscription
-	#[pubsub(
-		subscription = "state_runtimeVersion",
-		unsubscribe,
-		name = "state_unsubscribeRuntimeVersion",
-		alias("chain_unsubscribeRuntimeVersion")
+	/// New storage subscription
+	#[subscription(
+		name = "state_subscribeStorage" => "state_storage",
+		unsubscribe = "state_unsubscribeStorage",
+		item = StorageChangeSet<Hash>,
 	)]
-	fn unsubscribe_runtime_version(
-		&self,
-		metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool>;
-
-	/// Subscribe to the changes in the storage.
-	///
-	/// This RPC endpoint has two modes of operation:
-	///   1) When `keys` is not `None` you'll only be informed about the changes
-	///      done to the specified keys; this is RPC-safe.
-	///   2) When `keys` is `None` you'll be informed of *all* of the changes;
-	///      **this is RPC-unsafe**.
-	///
-	/// When subscribed to all of the changes this API will emit every storage
-	/// change for every block that is imported. These changes will only be sent
-	/// after a block is imported. If you require a consistent view across all changes
-	/// of every block, you need to take this into account.
-	#[pubsub(subscription = "state_storage", subscribe, name = "state_subscribeStorage")]
-	fn subscribe_storage(
-		&self,
-		metadata: Self::Metadata,
-		subscriber: Subscriber<StorageChangeSet<Hash>>,
-		keys: Option<Vec<StorageKey>>,
-	);
-
-	/// Unsubscribe from storage subscription
-	#[pubsub(subscription = "state_storage", unsubscribe, name = "state_unsubscribeStorage")]
-	fn unsubscribe_storage(
-		&self,
-		metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool>;
+	fn subscribe_storage(&self, keys: Option<Vec<StorageKey>>);
 
-	/// The `state_traceBlock` RPC provides a way to trace the re-execution of a single
+	/// The `traceBlock` RPC provides a way to trace the re-execution of a single
 	/// block, collecting Spans and Events from both the client and the relevant WASM runtime.
 	/// The Spans and Events are conceptually equivalent to those from the [Tracing][1] crate.
 	///
@@ -323,13 +284,13 @@ pub trait StateApi<Hash> {
 	/// narrow down the traces using a smaller set of targets and/or storage keys.
 	///
 	/// If you are having issues with maximum payload size you can use the flag
-	/// `-lstate_tracing=trace` to get some logging during tracing.
-	#[rpc(name = "state_traceBlock")]
-	fn trace_block(
+	/// `-ltracing=trace` to get some logging during tracing.
+	#[method(name = "state_traceBlock")]
+	async fn trace_block(
 		&self,
 		block: Hash,
 		targets: Option<String>,
 		storage_keys: Option<String>,
 		methods: Option<String>,
-	) -> FutureResult<sp_rpc::tracing::TraceBlockResponse>;
+	) -> RpcResult<sp_rpc::tracing::TraceBlockResponse>;
 }
diff --git a/substrate/client/rpc-api/src/system/error.rs b/substrate/client/rpc-api/src/system/error.rs
index 050d79b6ad63679839dcf9b8a45e5689819c45f3..777f8c6c6df0b99fcdd123c2446d14fd17823e4c 100644
--- a/substrate/client/rpc-api/src/system/error.rs
+++ b/substrate/client/rpc-api/src/system/error.rs
@@ -19,7 +19,10 @@
 //! System RPC module errors.
 
 use crate::system::helpers::Health;
-use jsonrpc_core as rpc;
+use jsonrpsee::{
+	core::Error as JsonRpseeError,
+	types::error::{CallError, ErrorObject},
+};
 
 /// System RPC Result type.
 pub type Result<T> = std::result::Result<T, Error>;
@@ -35,22 +38,24 @@ pub enum Error {
 	MalformattedPeerArg(String),
 }
 
-/// Base code for all system errors.
-const BASE_ERROR: i64 = 2000;
+// Base code for all system errors.
+const BASE_ERROR: i32 = 2000;
+// Provided block range couldn't be resolved to a list of blocks.
+const NOT_HEALTHY_ERROR: i32 = BASE_ERROR + 1;
+// Peer argument is malformatted.
+const MALFORMATTED_PEER_ARG_ERROR: i32 = BASE_ERROR + 2;
 
-impl From<Error> for rpc::Error {
+impl From<Error> for JsonRpseeError {
 	fn from(e: Error) -> Self {
 		match e {
-			Error::NotHealthy(ref h) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(BASE_ERROR + 1),
-				message: format!("{}", e),
-				data: serde_json::to_value(h).ok(),
-			},
-			Error::MalformattedPeerArg(ref e) => rpc::Error {
-				code: rpc::ErrorCode::ServerError(BASE_ERROR + 2),
-				message: e.clone(),
-				data: None,
-			},
+			Error::NotHealthy(ref h) =>
+				CallError::Custom(ErrorObject::owned(NOT_HEALTHY_ERROR, e.to_string(), Some(h))),
+			Error::MalformattedPeerArg(e) => CallError::Custom(ErrorObject::owned(
+				MALFORMATTED_PEER_ARG_ERROR + 2,
+				e,
+				None::<()>,
+			)),
 		}
+		.into()
 	}
 }
diff --git a/substrate/client/rpc-api/src/system/mod.rs b/substrate/client/rpc-api/src/system/mod.rs
index b610094f5b58d08b8538bf4cd0e05d1f1d8fdf3c..1e12d5be87ee89bcaa9e2a0e3a8238723bc34d82 100644
--- a/substrate/client/rpc-api/src/system/mod.rs
+++ b/substrate/client/rpc-api/src/system/mod.rs
@@ -18,65 +18,61 @@
 
 //! Substrate system API.
 
-pub mod error;
-pub mod helpers;
-
-use crate::helpers::Receiver;
-use jsonrpc_core::BoxFuture;
-use jsonrpc_derive::rpc;
+use jsonrpsee::{
+	core::{JsonValue, RpcResult},
+	proc_macros::rpc,
+};
 
-use self::error::Result as SystemResult;
+pub use self::helpers::{Health, NodeRole, PeerInfo, SyncState, SystemInfo};
 
-pub use self::{
-	gen_client::Client as SystemClient,
-	helpers::{Health, NodeRole, PeerInfo, SyncState, SystemInfo},
-};
+pub mod error;
+pub mod helpers;
 
 /// Substrate system RPC API
-#[rpc]
+#[rpc(client, server)]
 pub trait SystemApi<Hash, Number> {
 	/// Get the node's implementation name. Plain old string.
-	#[rpc(name = "system_name")]
-	fn system_name(&self) -> SystemResult<String>;
+	#[method(name = "system_name")]
+	fn system_name(&self) -> RpcResult<String>;
 
 	/// Get the node implementation's version. Should be a semver string.
-	#[rpc(name = "system_version")]
-	fn system_version(&self) -> SystemResult<String>;
+	#[method(name = "system_version")]
+	fn system_version(&self) -> RpcResult<String>;
 
 	/// Get the chain's name. Given as a string identifier.
-	#[rpc(name = "system_chain")]
-	fn system_chain(&self) -> SystemResult<String>;
+	#[method(name = "system_chain")]
+	fn system_chain(&self) -> RpcResult<String>;
 
 	/// Get the chain's type.
-	#[rpc(name = "system_chainType")]
-	fn system_type(&self) -> SystemResult<sc_chain_spec::ChainType>;
+	#[method(name = "system_chainType")]
+	fn system_type(&self) -> RpcResult<sc_chain_spec::ChainType>;
 
 	/// Get a custom set of properties as a JSON object, defined in the chain spec.
-	#[rpc(name = "system_properties")]
-	fn system_properties(&self) -> SystemResult<sc_chain_spec::Properties>;
+	#[method(name = "system_properties")]
+	fn system_properties(&self) -> RpcResult<sc_chain_spec::Properties>;
 
 	/// Return health status of the node.
 	///
 	/// Node is considered healthy if it is:
 	/// - connected to some peers (unless running in dev mode)
 	/// - not performing a major sync
-	#[rpc(name = "system_health", returns = "Health")]
-	fn system_health(&self) -> Receiver<Health>;
+	#[method(name = "system_health")]
+	async fn system_health(&self) -> RpcResult<Health>;
 
 	/// Returns the base58-encoded PeerId of the node.
-	#[rpc(name = "system_localPeerId", returns = "String")]
-	fn system_local_peer_id(&self) -> Receiver<String>;
+	#[method(name = "system_localPeerId")]
+	async fn system_local_peer_id(&self) -> RpcResult<String>;
 
-	/// Returns the multiaddresses that the local node is listening on
+	/// Returns the multi-addresses that the local node is listening on
 	///
 	/// The addresses include a trailing `/p2p/` with the local PeerId, and are thus suitable to
-	/// be passed to `system_addReservedPeer` or as a bootnode address for example.
-	#[rpc(name = "system_localListenAddresses", returns = "Vec<String>")]
-	fn system_local_listen_addresses(&self) -> Receiver<Vec<String>>;
+	/// be passed to `addReservedPeer` or as a bootnode address for example.
+	#[method(name = "system_localListenAddresses")]
+	async fn system_local_listen_addresses(&self) -> RpcResult<Vec<String>>;
 
 	/// Returns currently connected peers
-	#[rpc(name = "system_peers", returns = "Vec<PeerInfo<Hash, Number>>")]
-	fn system_peers(&self) -> BoxFuture<jsonrpc_core::Result<Vec<PeerInfo<Hash, Number>>>>;
+	#[method(name = "system_peers")]
+	async fn system_peers(&self) -> RpcResult<Vec<PeerInfo<Hash, Number>>>;
 
 	/// Returns current state of the network.
 	///
@@ -84,47 +80,44 @@ pub trait SystemApi<Hash, Number> {
 	/// as its format might change at any time.
 	// TODO: the future of this call is uncertain: https://github.com/paritytech/substrate/issues/1890
 	// https://github.com/paritytech/substrate/issues/5541
-	#[rpc(name = "system_unstable_networkState", returns = "jsonrpc_core::Value")]
-	fn system_network_state(&self) -> BoxFuture<jsonrpc_core::Result<jsonrpc_core::Value>>;
+	#[method(name = "system_unstable_networkState")]
+	async fn system_network_state(&self) -> RpcResult<JsonValue>;
 
 	/// Adds a reserved peer. Returns the empty string or an error. The string
 	/// parameter should encode a `p2p` multiaddr.
 	///
 	/// `/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV`
 	/// is an example of a valid, passing multiaddr with PeerId attached.
-	#[rpc(name = "system_addReservedPeer", returns = "()")]
-	fn system_add_reserved_peer(&self, peer: String) -> BoxFuture<Result<(), jsonrpc_core::Error>>;
+	#[method(name = "system_addReservedPeer")]
+	async fn system_add_reserved_peer(&self, peer: String) -> RpcResult<()>;
 
 	/// Remove a reserved peer. Returns the empty string or an error. The string
 	/// should encode only the PeerId e.g. `QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV`.
-	#[rpc(name = "system_removeReservedPeer", returns = "()")]
-	fn system_remove_reserved_peer(
-		&self,
-		peer_id: String,
-	) -> BoxFuture<Result<(), jsonrpc_core::Error>>;
+	#[method(name = "system_removeReservedPeer")]
+	async fn system_remove_reserved_peer(&self, peer_id: String) -> RpcResult<()>;
 
 	/// Returns the list of reserved peers
-	#[rpc(name = "system_reservedPeers", returns = "Vec<String>")]
-	fn system_reserved_peers(&self) -> Receiver<Vec<String>>;
+	#[method(name = "system_reservedPeers")]
+	async fn system_reserved_peers(&self) -> RpcResult<Vec<String>>;
 
 	/// Returns the roles the node is running as.
-	#[rpc(name = "system_nodeRoles", returns = "Vec<NodeRole>")]
-	fn system_node_roles(&self) -> Receiver<Vec<NodeRole>>;
+	#[method(name = "system_nodeRoles")]
+	async fn system_node_roles(&self) -> RpcResult<Vec<NodeRole>>;
 
 	/// Returns the state of the syncing of the node: starting block, current best block, highest
 	/// known block.
-	#[rpc(name = "system_syncState", returns = "SyncState<Number>")]
-	fn system_sync_state(&self) -> Receiver<SyncState<Number>>;
+	#[method(name = "system_syncState")]
+	async fn system_sync_state(&self) -> RpcResult<SyncState<Number>>;
 
 	/// Adds the supplied directives to the current log filter
 	///
 	/// The syntax is identical to the CLI `<target>=<level>`:
 	///
 	/// `sync=debug,state=trace`
-	#[rpc(name = "system_addLogFilter", returns = "()")]
-	fn system_add_log_filter(&self, directives: String) -> Result<(), jsonrpc_core::Error>;
+	#[method(name = "system_addLogFilter")]
+	fn system_add_log_filter(&self, directives: String) -> RpcResult<()>;
 
 	/// Resets the log filter to Substrate defaults
-	#[rpc(name = "system_resetLogFilter", returns = "()")]
-	fn system_reset_log_filter(&self) -> Result<(), jsonrpc_core::Error>;
+	#[method(name = "system_resetLogFilter")]
+	fn system_reset_log_filter(&self) -> RpcResult<()>;
 }
diff --git a/substrate/client/rpc-servers/Cargo.toml b/substrate/client/rpc-servers/Cargo.toml
index 4a13e9624a58e91a3ff5f51234c7f4a90cb36331..ad01f3bdd61993e06d51c5e334a51ec446c425e9 100644
--- a/substrate/client/rpc-servers/Cargo.toml
+++ b/substrate/client/rpc-servers/Cargo.toml
@@ -14,12 +14,8 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 futures = "0.3.21"
-http = { package = "jsonrpc-http-server", version = "18.0.0" }
-ipc = { package = "jsonrpc-ipc-server", version = "18.0.0" }
-jsonrpc-core = "18.0.0"
+jsonrpsee = { version = "0.12.0", features = ["server"] }
 log = "0.4.16"
-pubsub = { package = "jsonrpc-pubsub", version = "18.0.0" }
 serde_json = "1.0.79"
 tokio = { version = "1.17.0", features = ["parking_lot"] }
-ws = { package = "jsonrpc-ws-server", version = "18.0.0" }
 prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.10.0-dev", path = "../../utils/prometheus" }
diff --git a/substrate/client/rpc-servers/src/lib.rs b/substrate/client/rpc-servers/src/lib.rs
index 963d9aec072f578af73ab8d6f21c81395c50c525..4f69413895a9bc131b89ade7c020b68581e8f4a7 100644
--- a/substrate/client/rpc-servers/src/lib.rs
+++ b/substrate/client/rpc-servers/src/lib.rs
@@ -20,213 +20,193 @@
 
 #![warn(missing_docs)]
 
-mod middleware;
-
-use jsonrpc_core::{IoHandlerExtension, MetaIoHandler};
-use log::error;
-use prometheus_endpoint::{register, Counter, PrometheusError, Registry, U64};
-use pubsub::PubSubMetadata;
-use std::io;
+use jsonrpsee::{
+	http_server::{AccessControlBuilder, HttpServerBuilder, HttpServerHandle},
+	ws_server::{WsServerBuilder, WsServerHandle},
+	RpcModule,
+};
+use std::{error::Error as StdError, net::SocketAddr};
+
+pub use crate::middleware::{RpcMetrics, RpcMiddleware};
+pub use jsonrpsee::core::{
+	id_providers::{RandomIntegerIdProvider, RandomStringIdProvider},
+	traits::IdProvider,
+};
 
 const MEGABYTE: usize = 1024 * 1024;
 
 /// Maximal payload accepted by RPC servers.
 pub const RPC_MAX_PAYLOAD_DEFAULT: usize = 15 * MEGABYTE;
 
-/// Maximal buffer size in WS server.
-pub const WS_MAX_BUFFER_CAPACITY_DEFAULT: usize = 16 * MEGABYTE;
-
 /// Default maximum number of connections for WS RPC servers.
 const WS_MAX_CONNECTIONS: usize = 100;
 
-/// The RPC IoHandler containing all requested APIs.
-pub type RpcHandler<T> = pubsub::PubSubHandler<T, RpcMiddleware>;
-
-pub use middleware::{method_names, RpcMetrics, RpcMiddleware};
-
-/// Construct rpc `IoHandler`
-pub fn rpc_handler<M: PubSubMetadata>(
-	extension: impl IoHandlerExtension<M>,
-	rpc_middleware: RpcMiddleware,
-) -> RpcHandler<M> {
-	let io_handler = MetaIoHandler::with_middleware(rpc_middleware);
-	let mut io = pubsub::PubSubHandler::new(io_handler);
-	extension.augment(&mut io);
-
-	// add an endpoint to list all available methods.
-	let mut methods = io.iter().map(|x| x.0.clone()).collect::<Vec<String>>();
-	io.add_method("rpc_methods", {
-		methods.sort();
-		let methods = serde_json::to_value(&methods)
-			.expect("Serialization of Vec<String> is infallible; qed");
-
-		move |_| {
-			let methods = methods.clone();
-			async move {
-				Ok(serde_json::json!({
-					"version": 1,
-					"methods": methods,
-				}))
-			}
-		}
-	});
-	io
-}
+/// Default maximum number subscriptions per connection for WS RPC servers.
+const WS_MAX_SUBS_PER_CONN: usize = 1024;
 
-/// RPC server-specific prometheus metrics.
-#[derive(Debug, Clone, Default)]
-pub struct ServerMetrics {
-	/// Number of sessions opened.
-	session_opened: Option<Counter<U64>>,
-	/// Number of sessions closed.
-	session_closed: Option<Counter<U64>>,
-}
+pub mod middleware;
 
-impl ServerMetrics {
-	/// Create new WebSocket RPC server metrics.
-	pub fn new(registry: Option<&Registry>) -> Result<Self, PrometheusError> {
-		registry
-			.map(|r| {
-				Ok(Self {
-					session_opened: register(
-						Counter::new(
-							"substrate_rpc_sessions_opened",
-							"Number of persistent RPC sessions opened",
-						)?,
-						r,
-					)?
-					.into(),
-					session_closed: register(
-						Counter::new(
-							"substrate_rpc_sessions_closed",
-							"Number of persistent RPC sessions closed",
-						)?,
-						r,
-					)?
-					.into(),
-				})
-			})
-			.unwrap_or_else(|| Ok(Default::default()))
-	}
-}
-
-/// Type alias for ipc server
-pub type IpcServer = ipc::Server;
 /// Type alias for http server
-pub type HttpServer = http::Server;
+pub type HttpServer = HttpServerHandle;
 /// Type alias for ws server
-pub type WsServer = ws::Server;
-
-impl ws::SessionStats for ServerMetrics {
-	fn open_session(&self, _id: ws::SessionId) {
-		self.session_opened.as_ref().map(|m| m.inc());
-	}
+pub type WsServer = WsServerHandle;
+
+/// WebSocket specific settings on the server.
+pub struct WsConfig {
+	/// Maximum connections.
+	pub max_connections: Option<usize>,
+	/// Maximum subscriptions per connection.
+	pub max_subs_per_conn: Option<usize>,
+	/// Maximum rpc request payload size.
+	pub max_payload_in_mb: Option<usize>,
+	/// Maximum rpc response payload size.
+	pub max_payload_out_mb: Option<usize>,
+}
 
-	fn close_session(&self, _id: ws::SessionId) {
-		self.session_closed.as_ref().map(|m| m.inc());
+impl WsConfig {
+	// Deconstructs the config to get the finalized inner values.
+	//
+	// `Payload size` or `max subs per connection` bigger than u32::MAX will be truncated.
+	fn deconstruct(self) -> (u32, u32, u64, u32) {
+		let max_conns = self.max_connections.unwrap_or(WS_MAX_CONNECTIONS) as u64;
+		let max_payload_in_mb = payload_size_or_default(self.max_payload_in_mb) as u32;
+		let max_payload_out_mb = payload_size_or_default(self.max_payload_out_mb) as u32;
+		let max_subs_per_conn = self.max_subs_per_conn.unwrap_or(WS_MAX_SUBS_PER_CONN) as u32;
+
+		(max_payload_in_mb, max_payload_out_mb, max_conns, max_subs_per_conn)
 	}
 }
 
 /// Start HTTP server listening on given address.
-pub fn start_http<M: pubsub::PubSubMetadata + Default + Unpin>(
-	addr: &std::net::SocketAddr,
+pub async fn start_http<M: Send + Sync + 'static>(
+	addrs: [SocketAddr; 2],
 	cors: Option<&Vec<String>>,
-	io: RpcHandler<M>,
-	maybe_max_payload_mb: Option<usize>,
-	tokio_handle: tokio::runtime::Handle,
-) -> io::Result<http::Server> {
-	let max_request_body_size = maybe_max_payload_mb
-		.map(|mb| mb.saturating_mul(MEGABYTE))
-		.unwrap_or(RPC_MAX_PAYLOAD_DEFAULT);
-
-	http::ServerBuilder::new(io)
-		.threads(1)
-		.event_loop_executor(tokio_handle)
-		.health_api(("/health", "system_health"))
-		.allowed_hosts(hosts_filtering(cors.is_some()))
-		.rest_api(if cors.is_some() { http::RestApi::Secure } else { http::RestApi::Unsecure })
-		.cors(map_cors::<http::AccessControlAllowOrigin>(cors))
-		.max_request_body_size(max_request_body_size)
-		.start_http(addr)
-}
-
-/// Start IPC server listening on given path.
-pub fn start_ipc<M: pubsub::PubSubMetadata + Default>(
-	addr: &str,
-	io: RpcHandler<M>,
-	server_metrics: ServerMetrics,
-) -> io::Result<ipc::Server> {
-	let builder = ipc::ServerBuilder::new(io);
-	#[cfg(target_os = "unix")]
-	builder.set_security_attributes({
-		let security_attributes = ipc::SecurityAttributes::empty();
-		security_attributes.set_mode(0o600)?;
-		security_attributes
-	});
-	builder.session_stats(server_metrics).start(addr)
+	max_payload_in_mb: Option<usize>,
+	max_payload_out_mb: Option<usize>,
+	metrics: Option<RpcMetrics>,
+	rpc_api: RpcModule<M>,
+	rt: tokio::runtime::Handle,
+) -> Result<HttpServerHandle, Box<dyn StdError + Send + Sync>> {
+	let max_payload_in = payload_size_or_default(max_payload_in_mb);
+	let max_payload_out = payload_size_or_default(max_payload_out_mb);
+
+	let mut acl = AccessControlBuilder::new();
+
+	if let Some(cors) = cors {
+		// Whitelist listening address.
+		// NOTE: set_allowed_hosts will whitelist both ports but only one will used.
+		acl = acl.set_allowed_hosts(format_allowed_hosts(&addrs[..]))?;
+		acl = acl.set_allowed_origins(cors)?;
+	};
+
+	let builder = HttpServerBuilder::new()
+		.max_request_body_size(max_payload_in as u32)
+		.max_response_body_size(max_payload_out as u32)
+		.set_access_control(acl.build())
+		.custom_tokio_runtime(rt);
+
+	let rpc_api = build_rpc_api(rpc_api);
+	let (handle, addr) = if let Some(metrics) = metrics {
+		let middleware = RpcMiddleware::new(metrics, "http".into());
+		let builder = builder.set_middleware(middleware);
+		let server = builder.build(&addrs[..]).await?;
+		let addr = server.local_addr();
+		(server.start(rpc_api)?, addr)
+	} else {
+		let server = builder.build(&addrs[..]).await?;
+		let addr = server.local_addr();
+		(server.start(rpc_api)?, addr)
+	};
+
+	log::info!(
+		"Running JSON-RPC HTTP server: addr={}, allowed origins={:?}",
+		addr.map_or_else(|_| "unknown".to_string(), |a| a.to_string()),
+		cors
+	);
+
+	Ok(handle)
 }
 
 /// Start WS server listening on given address.
-pub fn start_ws<
-	M: pubsub::PubSubMetadata + From<futures::channel::mpsc::UnboundedSender<String>>,
->(
-	addr: &std::net::SocketAddr,
-	max_connections: Option<usize>,
+pub async fn start_ws<M: Send + Sync + 'static>(
+	addrs: [SocketAddr; 2],
 	cors: Option<&Vec<String>>,
-	io: RpcHandler<M>,
-	maybe_max_payload_mb: Option<usize>,
-	maybe_max_out_buffer_capacity_mb: Option<usize>,
-	server_metrics: ServerMetrics,
-	tokio_handle: tokio::runtime::Handle,
-) -> io::Result<ws::Server> {
-	let max_payload = maybe_max_payload_mb
-		.map(|mb| mb.saturating_mul(MEGABYTE))
-		.unwrap_or(RPC_MAX_PAYLOAD_DEFAULT);
-	let max_out_buffer_capacity = maybe_max_out_buffer_capacity_mb
-		.map(|mb| mb.saturating_mul(MEGABYTE))
-		.unwrap_or(WS_MAX_BUFFER_CAPACITY_DEFAULT);
-
-	if max_payload > max_out_buffer_capacity {
-		log::warn!(
-			"maximum payload ({}) is more than maximum output buffer ({}) size in ws server, the payload will actually be limited by the buffer size",
-			max_payload,
-			max_out_buffer_capacity,
-		)
+	ws_config: WsConfig,
+	metrics: Option<RpcMetrics>,
+	rpc_api: RpcModule<M>,
+	rt: tokio::runtime::Handle,
+	id_provider: Option<Box<dyn IdProvider>>,
+) -> Result<WsServerHandle, Box<dyn StdError + Send + Sync>> {
+	let (max_payload_in, max_payload_out, max_connections, max_subs_per_conn) =
+		ws_config.deconstruct();
+
+	let mut builder = WsServerBuilder::new()
+		.max_request_body_size(max_payload_in)
+		.max_response_body_size(max_payload_out)
+		.max_connections(max_connections)
+		.max_subscriptions_per_connection(max_subs_per_conn)
+		.custom_tokio_runtime(rt);
+
+	if let Some(provider) = id_provider {
+		builder = builder.set_id_provider(provider);
+	} else {
+		builder = builder.set_id_provider(RandomStringIdProvider::new(16));
+	};
+
+	if let Some(cors) = cors {
+		// Whitelist listening address.
+		// NOTE: set_allowed_hosts will whitelist both ports but only one will used.
+		builder = builder.set_allowed_hosts(format_allowed_hosts(&addrs[..]))?;
+		builder = builder.set_allowed_origins(cors)?;
 	}
 
-	ws::ServerBuilder::with_meta_extractor(io, |context: &ws::RequestContext| {
-		context.sender().into()
-	})
-	.event_loop_executor(tokio_handle)
-	.max_payload(max_payload)
-	.max_connections(max_connections.unwrap_or(WS_MAX_CONNECTIONS))
-	.max_out_buffer_capacity(max_out_buffer_capacity)
-	.allowed_origins(map_cors(cors))
-	.allowed_hosts(hosts_filtering(cors.is_some()))
-	.session_stats(server_metrics)
-	.start(addr)
-	.map_err(|err| match err {
-		ws::Error::Io(io) => io,
-		ws::Error::ConnectionClosed => io::ErrorKind::BrokenPipe.into(),
-		e => {
-			error!("{}", e);
-			io::ErrorKind::Other.into()
-		},
-	})
+	let rpc_api = build_rpc_api(rpc_api);
+	let (handle, addr) = if let Some(metrics) = metrics {
+		let middleware = RpcMiddleware::new(metrics, "ws".into());
+		let builder = builder.set_middleware(middleware);
+		let server = builder.build(&addrs[..]).await?;
+		let addr = server.local_addr();
+		(server.start(rpc_api)?, addr)
+	} else {
+		let server = builder.build(&addrs[..]).await?;
+		let addr = server.local_addr();
+		(server.start(rpc_api)?, addr)
+	};
+
+	log::info!(
+		"Running JSON-RPC WS server: addr={}, allowed origins={:?}",
+		addr.map_or_else(|_| "unknown".to_string(), |a| a.to_string()),
+		cors
+	);
+
+	Ok(handle)
 }
 
-fn map_cors<T: for<'a> From<&'a str>>(cors: Option<&Vec<String>>) -> http::DomainsValidation<T> {
-	cors.map(|x| x.iter().map(AsRef::as_ref).map(Into::into).collect::<Vec<_>>())
-		.into()
+fn format_allowed_hosts(addrs: &[SocketAddr]) -> Vec<String> {
+	let mut hosts = Vec::with_capacity(addrs.len() * 2);
+	for addr in addrs {
+		hosts.push(format!("localhost:{}", addr.port()));
+		hosts.push(format!("127.0.0.1:{}", addr.port()));
+	}
+	hosts
 }
 
-fn hosts_filtering(enable: bool) -> http::DomainsValidation<http::Host> {
-	if enable {
-		// NOTE The listening address is whitelisted by default.
-		// Setting an empty vector here enables the validation
-		// and allows only the listening address.
-		http::DomainsValidation::AllowOnly(vec![])
-	} else {
-		http::DomainsValidation::Disabled
-	}
+fn build_rpc_api<M: Send + Sync + 'static>(mut rpc_api: RpcModule<M>) -> RpcModule<M> {
+	let mut available_methods = rpc_api.method_names().collect::<Vec<_>>();
+	available_methods.sort_unstable();
+
+	rpc_api
+		.register_method("rpc_methods", move |_, _| {
+			Ok(serde_json::json!({
+				"version": 1,
+				"methods": available_methods,
+			}))
+		})
+		.expect("infallible all other methods have their own address space; qed");
+
+	rpc_api
+}
+
+fn payload_size_or_default(size_mb: Option<usize>) -> usize {
+	size_mb.map_or(RPC_MAX_PAYLOAD_DEFAULT, |mb| mb.saturating_mul(MEGABYTE))
 }
diff --git a/substrate/client/rpc-servers/src/middleware.rs b/substrate/client/rpc-servers/src/middleware.rs
index d4ac787ce9f0c5c2eb947e54e4ac720b25424ccb..5b2ee4bedb7ddc83e32779e72e0ecf2a942c95a2 100644
--- a/substrate/client/rpc-servers/src/middleware.rs
+++ b/substrate/client/rpc-servers/src/middleware.rs
@@ -16,34 +16,38 @@
 // You should have received a copy of the GNU General Public License
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
-//! Middleware for RPC requests.
+//! RPC middlware to collect prometheus metrics on RPC calls.
 
-use std::collections::HashSet;
-
-use jsonrpc_core::{FutureOutput, FutureResponse, Metadata, Middleware as RequestMiddleware};
+use jsonrpsee::core::middleware::Middleware;
 use prometheus_endpoint::{
-	register, CounterVec, HistogramOpts, HistogramVec, Opts, PrometheusError, Registry, U64,
+	register, Counter, CounterVec, HistogramOpts, HistogramVec, Opts, PrometheusError, Registry,
+	U64,
 };
 
-use futures::{future::Either, Future, FutureExt};
-use pubsub::PubSubMetadata;
-
-use crate::RpcHandler;
-
-/// Metrics for RPC middleware
+/// Metrics for RPC middleware storing information about the number of requests started/completed,
+/// calls started/completed and their timings.
 #[derive(Debug, Clone)]
 pub struct RpcMetrics {
+	/// Number of RPC requests received since the server started.
 	requests_started: CounterVec<U64>,
+	/// Number of RPC requests completed since the server started.
 	requests_finished: CounterVec<U64>,
+	/// Histogram over RPC execution times.
 	calls_time: HistogramVec,
+	/// Number of calls started.
 	calls_started: CounterVec<U64>,
+	/// Number of calls completed.
 	calls_finished: CounterVec<U64>,
+	/// Number of Websocket sessions opened (Websocket only).
+	ws_sessions_opened: Option<Counter<U64>>,
+	/// Number of Websocket sessions closed (Websocket only).
+	ws_sessions_closed: Option<Counter<U64>>,
 }
 
 impl RpcMetrics {
 	/// Create an instance of metrics
 	pub fn new(metrics_registry: Option<&Registry>) -> Result<Option<Self>, PrometheusError> {
-		if let Some(r) = metrics_registry {
+		if let Some(metrics_registry) = metrics_registry {
 			Ok(Some(Self {
 				requests_started: register(
 					CounterVec::new(
@@ -53,7 +57,7 @@ impl RpcMetrics {
 						),
 						&["protocol"],
 					)?,
-					r,
+					metrics_registry,
 				)?,
 				requests_finished: register(
 					CounterVec::new(
@@ -63,7 +67,7 @@ impl RpcMetrics {
 						),
 						&["protocol"],
 					)?,
-					r,
+					metrics_registry,
 				)?,
 				calls_time: register(
 					HistogramVec::new(
@@ -73,7 +77,7 @@ impl RpcMetrics {
 						),
 						&["protocol", "method"],
 					)?,
-					r,
+					metrics_registry,
 				)?,
 				calls_started: register(
 					CounterVec::new(
@@ -83,7 +87,7 @@ impl RpcMetrics {
 						),
 						&["protocol", "method"],
 					)?,
-					r,
+					metrics_registry,
 				)?,
 				calls_finished: register(
 					CounterVec::new(
@@ -93,8 +97,24 @@ impl RpcMetrics {
 						),
 						&["protocol", "method", "is_error"],
 					)?,
-					r,
+					metrics_registry,
 				)?,
+				ws_sessions_opened: register(
+					Counter::new(
+						"substrate_rpc_sessions_opened",
+						"Number of persistent RPC sessions opened",
+					)?,
+					metrics_registry,
+				)?
+				.into(),
+				ws_sessions_closed: register(
+					Counter::new(
+						"substrate_rpc_sessions_closed",
+						"Number of persistent RPC sessions closed",
+					)?,
+					metrics_registry,
+				)?
+				.into(),
 			}))
 		} else {
 			Ok(None)
@@ -102,140 +122,71 @@ impl RpcMetrics {
 	}
 }
 
-/// Instantiates a dummy `IoHandler` given a builder function to extract supported method names.
-pub fn method_names<F, M, E>(gen_handler: F) -> Result<HashSet<String>, E>
-where
-	F: FnOnce(RpcMiddleware) -> Result<RpcHandler<M>, E>,
-	M: PubSubMetadata,
-{
-	let io = gen_handler(RpcMiddleware::new(None, HashSet::new(), "dummy"))?;
-	Ok(io.iter().map(|x| x.0.clone()).collect())
-}
-
+#[derive(Clone)]
 /// Middleware for RPC calls
 pub struct RpcMiddleware {
-	metrics: Option<RpcMetrics>,
-	known_rpc_method_names: HashSet<String>,
-	transport_label: String,
+	metrics: RpcMetrics,
+	transport_label: &'static str,
 }
 
 impl RpcMiddleware {
-	/// Create an instance of middleware.
-	///
-	/// - `metrics`: Will be used to report statistics.
-	/// - `transport_label`: The label that is used when reporting the statistics.
-	pub fn new(
-		metrics: Option<RpcMetrics>,
-		known_rpc_method_names: HashSet<String>,
-		transport_label: &str,
-	) -> Self {
-		RpcMiddleware { metrics, known_rpc_method_names, transport_label: transport_label.into() }
+	/// Create a new [`RpcMiddleware`] with the provided [`RpcMetrics`].
+	pub fn new(metrics: RpcMetrics, transport_label: &'static str) -> Self {
+		Self { metrics, transport_label }
 	}
 }
 
-impl<M: Metadata> RequestMiddleware<M> for RpcMiddleware {
-	type Future = FutureResponse;
-	type CallFuture = FutureOutput;
-
-	fn on_request<F, X>(
-		&self,
-		request: jsonrpc_core::Request,
-		meta: M,
-		next: F,
-	) -> Either<Self::Future, X>
-	where
-		F: Fn(jsonrpc_core::Request, M) -> X + Send + Sync,
-		X: Future<Output = Option<jsonrpc_core::Response>> + Send + 'static,
-	{
-		let metrics = self.metrics.clone();
-		let transport_label = self.transport_label.clone();
-		if let Some(ref metrics) = metrics {
-			metrics.requests_started.with_label_values(&[transport_label.as_str()]).inc();
-		}
-		let r = next(request, meta);
-		Either::Left(
-			async move {
-				let r = r.await;
-				if let Some(ref metrics) = metrics {
-					metrics.requests_finished.with_label_values(&[transport_label.as_str()]).inc();
-				}
-				r
-			}
-			.boxed(),
-		)
+impl Middleware for RpcMiddleware {
+	type Instant = std::time::Instant;
+
+	fn on_connect(&self) {
+		self.metrics.ws_sessions_opened.as_ref().map(|counter| counter.inc());
 	}
 
-	fn on_call<F, X>(
-		&self,
-		call: jsonrpc_core::Call,
-		meta: M,
-		next: F,
-	) -> Either<Self::CallFuture, X>
-	where
-		F: Fn(jsonrpc_core::Call, M) -> X + Send + Sync,
-		X: Future<Output = Option<jsonrpc_core::Output>> + Send + 'static,
-	{
-		let start = std::time::Instant::now();
-		let name = call_name(&call, &self.known_rpc_method_names).to_owned();
-		let metrics = self.metrics.clone();
-		let transport_label = self.transport_label.clone();
-		log::trace!(target: "rpc_metrics", "[{}] {} call: {:?}", transport_label, name, &call);
-		if let Some(ref metrics) = metrics {
-			metrics
-				.calls_started
-				.with_label_values(&[transport_label.as_str(), name.as_str()])
-				.inc();
-		}
-		let r = next(call, meta);
-		Either::Left(
-			async move {
-				let r = r.await;
-				let micros = start.elapsed().as_micros();
-				if let Some(ref metrics) = metrics {
-					metrics
-						.calls_time
-						.with_label_values(&[transport_label.as_str(), name.as_str()])
-						.observe(micros as _);
-					metrics
-						.calls_finished
-						.with_label_values(&[
-							transport_label.as_str(),
-							name.as_str(),
-							if is_success(&r) { "true" } else { "false" },
-						])
-						.inc();
-				}
-				log::debug!(
-					target: "rpc_metrics",
-					"[{}] {} call took {} μs",
-					transport_label,
-					name,
-					micros,
-				);
-				r
-			}
-			.boxed(),
-		)
+	fn on_request(&self) -> Self::Instant {
+		let now = std::time::Instant::now();
+		self.metrics.requests_started.with_label_values(&[self.transport_label]).inc();
+		now
 	}
-}
 
-fn call_name<'a>(call: &'a jsonrpc_core::Call, known_methods: &HashSet<String>) -> &'a str {
-	// To prevent bloating metric with all invalid method names we filter them out here.
-	let only_known = |method: &'a String| {
-		if known_methods.contains(method) {
-			method.as_str()
-		} else {
-			"invalid method"
-		}
-	};
+	fn on_call(&self, name: &str) {
+		log::trace!(target: "rpc_metrics", "[{}] on_call name={}", self.transport_label, name);
+		self.metrics
+			.calls_started
+			.with_label_values(&[self.transport_label, name])
+			.inc();
+	}
 
-	match call {
-		jsonrpc_core::Call::Invalid { .. } => "invalid call",
-		jsonrpc_core::Call::MethodCall(ref call) => only_known(&call.method),
-		jsonrpc_core::Call::Notification(ref notification) => only_known(&notification.method),
+	fn on_result(&self, name: &str, success: bool, started_at: Self::Instant) {
+		let micros = started_at.elapsed().as_micros();
+		log::debug!(
+			target: "rpc_metrics",
+			"[{}] {} call took {} μs",
+			self.transport_label,
+			name,
+			micros,
+		);
+		self.metrics
+			.calls_time
+			.with_label_values(&[self.transport_label, name])
+			.observe(micros as _);
+
+		self.metrics
+			.calls_finished
+			.with_label_values(&[
+				self.transport_label,
+				name,
+				if success { "true" } else { "false" },
+			])
+			.inc();
+	}
+
+	fn on_response(&self, started_at: Self::Instant) {
+		log::trace!(target: "rpc_metrics", "[{}] on_response started_at={:?}", self.transport_label, started_at);
+		self.metrics.requests_finished.with_label_values(&[self.transport_label]).inc();
 	}
-}
 
-fn is_success(output: &Option<jsonrpc_core::Output>) -> bool {
-	matches!(output, Some(jsonrpc_core::Output::Success(..)))
+	fn on_disconnect(&self) {
+		self.metrics.ws_sessions_closed.as_ref().map(|counter| counter.inc());
+	}
 }
diff --git a/substrate/client/rpc/Cargo.toml b/substrate/client/rpc/Cargo.toml
index f76665d6c97a7e1e63e0d039420e428026b80bb6..515de401119d4b29c77e6b7af8c96c227ee391cd 100644
--- a/substrate/client/rpc/Cargo.toml
+++ b/substrate/client/rpc/Cargo.toml
@@ -16,11 +16,10 @@ targets = ["x86_64-unknown-linux-gnu"]
 codec = { package = "parity-scale-codec", version = "3.0.0" }
 futures = "0.3.21"
 hash-db = { version = "0.15.2", default-features = false }
-jsonrpc-pubsub = "18.0.0"
+jsonrpsee = { version = "0.12.0", features = ["server"] }
 lazy_static = { version = "1.4.0", optional = true }
 log = "0.4.16"
 parking_lot = "0.12.0"
-rpc = { package = "jsonrpc-core", version = "18.0.0" }
 serde_json = "1.0.79"
 sc-block-builder = { version = "0.10.0-dev", path = "../block-builder" }
 sc-chain-spec = { version = "4.0.0-dev", path = "../chain-spec" }
@@ -39,14 +38,19 @@ sp-runtime = { version = "6.0.0", path = "../../primitives/runtime" }
 sp-session = { version = "4.0.0-dev", path = "../../primitives/session" }
 sp-version = { version = "5.0.0", path = "../../primitives/version" }
 
+tokio = { version = "1.17.0", optional = true }
+
 [dev-dependencies]
+env_logger = "0.9"
 assert_matches = "1.3.0"
 lazy_static = "1.4.0"
+sc-block-builder = { version = "0.10.0-dev", path = "../block-builder" }
 sc-network = { version = "0.10.0-dev", path = "../network" }
 sc-transaction-pool = { version = "4.0.0-dev", path = "../transaction-pool" }
 sp-consensus = { version = "0.10.0-dev", path = "../../primitives/consensus/common" }
+tokio = "1.17.0"
 sp-io = { version = "6.0.0", path = "../../primitives/io" }
 substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" }
 
 [features]
-test-helpers = ["lazy_static"]
+test-helpers = ["lazy_static", "tokio"]
diff --git a/substrate/client/rpc/src/author/mod.rs b/substrate/client/rpc/src/author/mod.rs
index 2821eea2cc09dc79b905650c957d4df35fd423f9..d10398afc813b915d9cffdfd7b870d1e436e911d 100644
--- a/substrate/client/rpc/src/author/mod.rs
+++ b/substrate/client/rpc/src/author/mod.rs
@@ -23,26 +23,27 @@ mod tests;
 
 use std::sync::Arc;
 
-use sp_blockchain::HeaderBackend;
+use crate::SubscriptionTaskExecutor;
 
 use codec::{Decode, Encode};
-use futures::{
-	future::{FutureExt, TryFutureExt},
-	SinkExt, StreamExt as _,
+use futures::{FutureExt, TryFutureExt};
+use jsonrpsee::{
+	core::{async_trait, Error as JsonRpseeError, RpcResult},
+	PendingSubscription,
 };
-use jsonrpc_pubsub::{manager::SubscriptionManager, typed::Subscriber, SubscriptionId};
 use sc_rpc_api::DenyUnsafe;
 use sc_transaction_pool_api::{
 	error::IntoPoolError, BlockHash, InPoolTransaction, TransactionFor, TransactionPool,
-	TransactionSource, TransactionStatus, TxHash,
+	TransactionSource, TxHash,
 };
 use sp_api::ProvideRuntimeApi;
+use sp_blockchain::HeaderBackend;
 use sp_core::Bytes;
 use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
 use sp_runtime::{generic, traits::Block as BlockT};
 use sp_session::SessionKeys;
 
-use self::error::{Error, FutureResult, Result};
+use self::error::{Error, Result};
 /// Re-export the API for backward compatibility.
 pub use sc_rpc_api::author::*;
 
@@ -52,12 +53,12 @@ pub struct Author<P, Client> {
 	client: Arc<Client>,
 	/// Transactions pool
 	pool: Arc<P>,
-	/// Subscriptions manager
-	subscriptions: SubscriptionManager,
 	/// The key store.
 	keystore: SyncCryptoStorePtr,
 	/// Whether to deny unsafe calls
 	deny_unsafe: DenyUnsafe,
+	/// Executor to spawn subscriptions.
+	executor: SubscriptionTaskExecutor,
 }
 
 impl<P, Client> Author<P, Client> {
@@ -65,11 +66,11 @@ impl<P, Client> Author<P, Client> {
 	pub fn new(
 		client: Arc<Client>,
 		pool: Arc<P>,
-		subscriptions: SubscriptionManager,
 		keystore: SyncCryptoStorePtr,
 		deny_unsafe: DenyUnsafe,
+		executor: SubscriptionTaskExecutor,
 	) -> Self {
-		Author { client, pool, subscriptions, keystore, deny_unsafe }
+		Author { client, pool, keystore, deny_unsafe, executor }
 	}
 }
 
@@ -80,7 +81,8 @@ impl<P, Client> Author<P, Client> {
 /// some unique transactions via RPC and have them included in the pool.
 const TX_SOURCE: TransactionSource = TransactionSource::External;
 
-impl<P, Client> AuthorApi<TxHash<P>, BlockHash<P>> for Author<P, Client>
+#[async_trait]
+impl<P, Client> AuthorApiServer<TxHash<P>, BlockHash<P>> for Author<P, Client>
 where
 	P: TransactionPool + Sync + Send + 'static,
 	Client: HeaderBackend<P::Block> + ProvideRuntimeApi<P::Block> + Send + Sync + 'static,
@@ -88,9 +90,24 @@ where
 	P::Hash: Unpin,
 	<P::Block as BlockT>::Hash: Unpin,
 {
-	type Metadata = crate::Metadata;
+	async fn submit_extrinsic(&self, ext: Bytes) -> RpcResult<TxHash<P>> {
+		let xt = match Decode::decode(&mut &ext[..]) {
+			Ok(xt) => xt,
+			Err(err) => return Err(Error::Client(Box::new(err)).into()),
+		};
+		let best_block_hash = self.client.info().best_hash;
+		self.pool
+			.submit_one(&generic::BlockId::hash(best_block_hash), TX_SOURCE, xt)
+			.await
+			.map_err(|e| {
+				e.into_pool_error()
+					.map(|e| Error::Pool(e))
+					.unwrap_or_else(|e| Error::Verification(Box::new(e)))
+					.into()
+			})
+	}
 
-	fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> Result<()> {
+	fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> RpcResult<()> {
 		self.deny_unsafe.check_if_safe()?;
 
 		let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?;
@@ -99,7 +116,7 @@ where
 		Ok(())
 	}
 
-	fn rotate_keys(&self) -> Result<Bytes> {
+	fn rotate_keys(&self) -> RpcResult<Bytes> {
 		self.deny_unsafe.check_if_safe()?;
 
 		let best_block_hash = self.client.info().best_hash;
@@ -107,10 +124,10 @@ where
 			.runtime_api()
 			.generate_session_keys(&generic::BlockId::Hash(best_block_hash), None)
 			.map(Into::into)
-			.map_err(|e| Error::Client(Box::new(e)))
+			.map_err(|api_err| Error::Client(Box::new(api_err)).into())
 	}
 
-	fn has_session_keys(&self, session_keys: Bytes) -> Result<bool> {
+	fn has_session_keys(&self, session_keys: Bytes) -> RpcResult<bool> {
 		self.deny_unsafe.check_if_safe()?;
 
 		let best_block_hash = self.client.info().best_hash;
@@ -124,40 +141,22 @@ where
 		Ok(SyncCryptoStore::has_keys(&*self.keystore, &keys))
 	}
 
-	fn has_key(&self, public_key: Bytes, key_type: String) -> Result<bool> {
+	fn has_key(&self, public_key: Bytes, key_type: String) -> RpcResult<bool> {
 		self.deny_unsafe.check_if_safe()?;
 
 		let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?;
 		Ok(SyncCryptoStore::has_keys(&*self.keystore, &[(public_key.to_vec(), key_type)]))
 	}
 
-	fn submit_extrinsic(&self, ext: Bytes) -> FutureResult<TxHash<P>> {
-		let xt = match Decode::decode(&mut &ext[..]) {
-			Ok(xt) => xt,
-			Err(err) => return async move { Err(err.into()) }.boxed(),
-		};
-		let best_block_hash = self.client.info().best_hash;
-
-		self.pool
-			.submit_one(&generic::BlockId::hash(best_block_hash), TX_SOURCE, xt)
-			.map_err(|e| {
-				e.into_pool_error()
-					.map(Into::into)
-					.unwrap_or_else(|e| error::Error::Verification(Box::new(e)))
-			})
-			.boxed()
-	}
-
-	fn pending_extrinsics(&self) -> Result<Vec<Bytes>> {
+	fn pending_extrinsics(&self) -> RpcResult<Vec<Bytes>> {
 		Ok(self.pool.ready().map(|tx| tx.data().encode().into()).collect())
 	}
 
 	fn remove_extrinsic(
 		&self,
 		bytes_or_hash: Vec<hash::ExtrinsicOrHash<TxHash<P>>>,
-	) -> Result<Vec<TxHash<P>>> {
+	) -> RpcResult<Vec<TxHash<P>>> {
 		self.deny_unsafe.check_if_safe()?;
-
 		let hashes = bytes_or_hash
 			.into_iter()
 			.map(|x| match x {
@@ -177,20 +176,12 @@ where
 			.collect())
 	}
 
-	fn watch_extrinsic(
-		&self,
-		_metadata: Self::Metadata,
-		subscriber: Subscriber<TransactionStatus<TxHash<P>, BlockHash<P>>>,
-		xt: Bytes,
-	) {
+	fn watch_extrinsic(&self, pending: PendingSubscription, xt: Bytes) {
 		let best_block_hash = self.client.info().best_hash;
-		let dxt = match TransactionFor::<P>::decode(&mut &xt[..]).map_err(error::Error::from) {
-			Ok(tx) => tx,
-			Err(err) => {
-				log::debug!("Failed to submit extrinsic: {}", err);
-				// reject the subscriber (ignore errors - we don't care if subscriber is no longer
-				// there).
-				let _ = subscriber.reject(err.into());
+		let dxt = match TransactionFor::<P>::decode(&mut &xt[..]).map_err(|e| Error::from(e)) {
+			Ok(dxt) => dxt,
+			Err(e) => {
+				pending.reject(JsonRpseeError::from(e));
 				return
 			},
 		};
@@ -204,41 +195,25 @@ where
 					.unwrap_or_else(|e| error::Error::Verification(Box::new(e)))
 			});
 
-		let subscriptions = self.subscriptions.clone();
-
-		let future = async move {
-			let tx_stream = match submit.await {
-				Ok(s) => s,
+		let fut = async move {
+			let stream = match submit.await {
+				Ok(stream) => stream,
 				Err(err) => {
-					log::debug!("Failed to submit extrinsic: {}", err);
-					// reject the subscriber (ignore errors - we don't care if subscriber is no
-					// longer there).
-					let _ = subscriber.reject(err.into());
+					pending.reject(JsonRpseeError::from(err));
 					return
 				},
 			};
 
-			subscriptions.add(subscriber, move |sink| {
-				tx_stream
-					.map(|v| Ok(Ok(v)))
-					.forward(
-						sink.sink_map_err(|e| log::debug!("Error sending notifications: {:?}", e)),
-					)
-					.map(drop)
-			});
-		};
+			let mut sink = match pending.accept() {
+				Some(sink) => sink,
+				_ => return,
+			};
 
-		let res = self.subscriptions.executor().spawn_obj(future.boxed().into());
-		if res.is_err() {
-			log::warn!("Error spawning subscription RPC task.");
+			sink.pipe_from_stream(stream).await;
 		}
-	}
+		.boxed();
 
-	fn unwatch_extrinsic(
-		&self,
-		_metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> Result<bool> {
-		Ok(self.subscriptions.cancel(id))
+		self.executor
+			.spawn("substrate-rpc-subscription", Some("rpc"), fut.map(drop).boxed());
 	}
 }
diff --git a/substrate/client/rpc/src/author/tests.rs b/substrate/client/rpc/src/author/tests.rs
index c555465645a745fc5b827f908a265fa0bcd0ae61..f969812e5b14cc6903fdd75f4f766da17c18256e 100644
--- a/substrate/client/rpc/src/author/tests.rs
+++ b/substrate/client/rpc/src/author/tests.rs
@@ -18,21 +18,26 @@
 
 use super::*;
 
+use crate::testing::{test_executor, timeout_secs};
 use assert_matches::assert_matches;
 use codec::Encode;
-use futures::executor;
+use jsonrpsee::{
+	core::Error as RpcError,
+	types::{error::CallError, EmptyParams},
+	RpcModule,
+};
 use sc_transaction_pool::{BasicPool, FullChainApi};
+use sc_transaction_pool_api::TransactionStatus;
 use sp_core::{
 	blake2_256,
+	bytes::to_hex,
 	crypto::{ByteArray, CryptoTypePublicPair, Pair},
-	ed25519,
-	hexdisplay::HexDisplay,
-	sr25519,
+	ed25519, sr25519,
 	testing::{ED25519, SR25519},
 	H256,
 };
 use sp_keystore::testing::KeyStore;
-use std::{mem, sync::Arc};
+use std::sync::Arc;
 use substrate_test_runtime_client::{
 	self,
 	runtime::{Block, Extrinsic, SessionKeys, Transfer},
@@ -75,240 +80,253 @@ impl TestSetup {
 		Author {
 			client: self.client.clone(),
 			pool: self.pool.clone(),
-			subscriptions: SubscriptionManager::new(Arc::new(crate::testing::TaskExecutor)),
 			keystore: self.keystore.clone(),
 			deny_unsafe: DenyUnsafe::No,
+			executor: test_executor(),
 		}
 	}
-}
 
-#[test]
-fn submit_transaction_should_not_cause_error() {
-	let p = TestSetup::default().author();
-	let xt = uxt(AccountKeyring::Alice, 1).encode();
-	let h: H256 = blake2_256(&xt).into();
-
-	assert_matches!(
-		executor::block_on(AuthorApi::submit_extrinsic(&p, xt.clone().into())),
-		Ok(h2) if h == h2
-	);
-	assert!(executor::block_on(AuthorApi::submit_extrinsic(&p, xt.into())).is_err());
+	fn into_rpc() -> RpcModule<Author<FullTransactionPool, Client<Backend>>> {
+		Self::default().author().into_rpc()
+	}
 }
 
-#[test]
-fn submit_rich_transaction_should_not_cause_error() {
-	let p = TestSetup::default().author();
-	let xt = uxt(AccountKeyring::Alice, 0).encode();
-	let h: H256 = blake2_256(&xt).into();
+#[tokio::test]
+async fn author_submit_transaction_should_not_cause_error() {
+	let _ = env_logger::try_init();
+	let author = TestSetup::default().author();
+	let api = author.into_rpc();
+	let xt: Bytes = uxt(AccountKeyring::Alice, 1).encode().into();
+	let extrinsic_hash: H256 = blake2_256(&xt).into();
+	let response: H256 = api.call("author_submitExtrinsic", [xt.clone()]).await.unwrap();
+
+	assert_eq!(response, extrinsic_hash);
 
 	assert_matches!(
-		executor::block_on(AuthorApi::submit_extrinsic(&p, xt.clone().into())),
-		Ok(h2) if h == h2
+		api.call::<_, H256>("author_submitExtrinsic", [xt]).await,
+		Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Already Imported") && err.code() == 1013
 	);
-	assert!(executor::block_on(AuthorApi::submit_extrinsic(&p, xt.into())).is_err());
 }
 
-#[test]
-fn should_watch_extrinsic() {
-	// given
-	let setup = TestSetup::default();
-	let p = setup.author();
+#[tokio::test]
+async fn author_should_watch_extrinsic() {
+	let api = TestSetup::into_rpc();
+	let xt = to_hex(&uxt(AccountKeyring::Alice, 0).encode(), true);
 
-	let (subscriber, id_rx, data) = jsonrpc_pubsub::typed::Subscriber::new_test("test");
-
-	// when
-	p.watch_extrinsic(
-		Default::default(),
-		subscriber,
-		uxt(AccountKeyring::Alice, 0).encode().into(),
-	);
+	let mut sub = api.subscribe("author_submitAndWatchExtrinsic", [xt]).await.unwrap();
+	let (tx, sub_id) = timeout_secs(10, sub.next::<TransactionStatus<H256, Block>>())
+		.await
+		.unwrap()
+		.unwrap()
+		.unwrap();
 
-	let id = executor::block_on(id_rx).unwrap().unwrap();
-	assert_matches!(id, SubscriptionId::String(_));
+	assert_matches!(tx, TransactionStatus::Ready);
+	assert_eq!(&sub_id, sub.subscription_id());
 
-	let id = match id {
-		SubscriptionId::String(id) => id,
-		_ => unreachable!(),
-	};
-
-	// check notifications
-	let replacement = {
+	// Replace the extrinsic and observe the subscription is notified.
+	let (xt_replacement, xt_hash) = {
 		let tx = Transfer {
 			amount: 5,
 			nonce: 0,
 			from: AccountKeyring::Alice.into(),
 			to: AccountKeyring::Bob.into(),
 		};
-		tx.into_signed_tx()
+		let tx = tx.into_signed_tx().encode();
+		let hash = blake2_256(&tx);
+
+		(to_hex(&tx, true), hash)
 	};
-	executor::block_on(AuthorApi::submit_extrinsic(&p, replacement.encode().into())).unwrap();
-	let (res, data) = executor::block_on(data.into_future());
-
-	let expected = Some(format!(
-		r#"{{"jsonrpc":"2.0","method":"test","params":{{"result":"ready","subscription":"{}"}}}}"#,
-		id,
-	));
-	assert_eq!(res, expected);
-
-	let h = blake2_256(&replacement.encode());
-	let expected = Some(format!(
-		r#"{{"jsonrpc":"2.0","method":"test","params":{{"result":{{"usurped":"0x{}"}},"subscription":"{}"}}}}"#,
-		HexDisplay::from(&h),
-		id,
-	));
-
-	let res = executor::block_on(data.into_future()).0;
-	assert_eq!(res, expected);
+
+	let _ = api.call::<_, H256>("author_submitExtrinsic", [xt_replacement]).await.unwrap();
+
+	let (tx, sub_id) = timeout_secs(10, sub.next::<TransactionStatus<H256, Block>>())
+		.await
+		.unwrap()
+		.unwrap()
+		.unwrap();
+	assert_eq!(tx, TransactionStatus::Usurped(xt_hash.into()));
+	assert_eq!(&sub_id, sub.subscription_id());
 }
 
-#[test]
-fn should_return_watch_validation_error() {
-	// given
-	let setup = TestSetup::default();
-	let p = setup.author();
+#[tokio::test]
+async fn author_should_return_watch_validation_error() {
+	const METHOD: &'static str = "author_submitAndWatchExtrinsic";
 
-	let (subscriber, id_rx, _data) = jsonrpc_pubsub::typed::Subscriber::new_test("test");
+	let api = TestSetup::into_rpc();
+	let failed_sub = api
+		.subscribe(METHOD, [to_hex(&uxt(AccountKeyring::Alice, 179).encode(), true)])
+		.await;
 
-	// when
-	p.watch_extrinsic(
-		Default::default(),
-		subscriber,
-		uxt(AccountKeyring::Alice, 179).encode().into(),
+	assert_matches!(
+		failed_sub,
+		Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Invalid Transaction") && err.code() == 1010
 	);
-
-	// then
-	let res = executor::block_on(id_rx).unwrap();
-	assert!(res.is_err(), "Expected the transaction to be rejected as invalid.");
 }
 
-#[test]
-fn should_return_pending_extrinsics() {
-	let p = TestSetup::default().author();
+#[tokio::test]
+async fn author_should_return_pending_extrinsics() {
+	let api = TestSetup::into_rpc();
 
-	let ex = uxt(AccountKeyring::Alice, 0);
-	executor::block_on(AuthorApi::submit_extrinsic(&p, ex.encode().into())).unwrap();
-	assert_matches!(
-		p.pending_extrinsics(),
-		Ok(ref expected) if *expected == vec![Bytes(ex.encode())]
-	);
+	let xt_bytes: Bytes = uxt(AccountKeyring::Alice, 0).encode().into();
+	api.call::<_, H256>("author_submitExtrinsic", [to_hex(&xt_bytes, true)])
+		.await
+		.unwrap();
+
+	let pending: Vec<Bytes> =
+		api.call("author_pendingExtrinsics", EmptyParams::new()).await.unwrap();
+	assert_eq!(pending, vec![xt_bytes]);
 }
 
-#[test]
-fn should_remove_extrinsics() {
+#[tokio::test]
+async fn author_should_remove_extrinsics() {
+	const METHOD: &'static str = "author_removeExtrinsic";
 	let setup = TestSetup::default();
-	let p = setup.author();
-
-	let ex1 = uxt(AccountKeyring::Alice, 0);
-	executor::block_on(p.submit_extrinsic(ex1.encode().into())).unwrap();
-	let ex2 = uxt(AccountKeyring::Alice, 1);
-	executor::block_on(p.submit_extrinsic(ex2.encode().into())).unwrap();
-	let ex3 = uxt(AccountKeyring::Bob, 0);
-	let hash3 = executor::block_on(p.submit_extrinsic(ex3.encode().into())).unwrap();
+	let api = setup.author().into_rpc();
+
+	// Submit three extrinsics, then remove two of them (will cause the third to be removed as well,
+	// having a higher nonce)
+	let xt1_bytes = uxt(AccountKeyring::Alice, 0).encode();
+	let xt1 = to_hex(&xt1_bytes, true);
+	let xt1_hash: H256 = api.call("author_submitExtrinsic", [xt1]).await.unwrap();
+
+	let xt2 = to_hex(&uxt(AccountKeyring::Alice, 1).encode(), true);
+	let xt2_hash: H256 = api.call("author_submitExtrinsic", [xt2]).await.unwrap();
+
+	let xt3 = to_hex(&uxt(AccountKeyring::Bob, 0).encode(), true);
+	let xt3_hash: H256 = api.call("author_submitExtrinsic", [xt3]).await.unwrap();
 	assert_eq!(setup.pool.status().ready, 3);
 
-	// now remove all 3
-	let removed = p
-		.remove_extrinsic(vec![
-			hash::ExtrinsicOrHash::Hash(hash3),
-			// Removing this one will also remove ex2
-			hash::ExtrinsicOrHash::Extrinsic(ex1.encode().into()),
-		])
+	// Now remove all three.
+	// Notice how we need an extra `Vec` wrapping the `Vec` we want to submit as params.
+	let removed: Vec<H256> = api
+		.call(
+			METHOD,
+			vec![vec![
+				hash::ExtrinsicOrHash::Hash(xt3_hash),
+				// Removing this one will also remove xt2
+				hash::ExtrinsicOrHash::Extrinsic(xt1_bytes.into()),
+			]],
+		)
+		.await
 		.unwrap();
 
-	assert_eq!(removed.len(), 3);
+	assert_eq!(removed, vec![xt1_hash, xt2_hash, xt3_hash]);
 }
 
-#[test]
-fn should_insert_key() {
+#[tokio::test]
+async fn author_should_insert_key() {
 	let setup = TestSetup::default();
-	let p = setup.author();
-
+	let api = setup.author().into_rpc();
 	let suri = "//Alice";
-	let key_pair = ed25519::Pair::from_string(suri, None).expect("Generates keypair");
-	p.insert_key(
+	let keypair = ed25519::Pair::from_string(suri, None).expect("generates keypair");
+	let params: (String, String, Bytes) = (
 		String::from_utf8(ED25519.0.to_vec()).expect("Keytype is a valid string"),
 		suri.to_string(),
-		key_pair.public().0.to_vec().into(),
-	)
-	.expect("Insert key");
-
-	let public_keys = SyncCryptoStore::keys(&*setup.keystore, ED25519).unwrap();
+		keypair.public().0.to_vec().into(),
+	);
+	api.call::<_, ()>("author_insertKey", params).await.unwrap();
+	let pubkeys = SyncCryptoStore::keys(&*setup.keystore, ED25519).unwrap();
 
-	assert!(public_keys
-		.contains(&CryptoTypePublicPair(ed25519::CRYPTO_ID, key_pair.public().to_raw_vec())));
+	assert!(
+		pubkeys.contains(&CryptoTypePublicPair(ed25519::CRYPTO_ID, keypair.public().to_raw_vec()))
+	);
 }
 
-#[test]
-fn should_rotate_keys() {
+#[tokio::test]
+async fn author_should_rotate_keys() {
 	let setup = TestSetup::default();
-	let p = setup.author();
-
-	let new_public_keys = p.rotate_keys().expect("Rotates the keys");
+	let api = setup.author().into_rpc();
 
+	let new_pubkeys: Bytes = api.call("author_rotateKeys", EmptyParams::new()).await.unwrap();
 	let session_keys =
-		SessionKeys::decode(&mut &new_public_keys[..]).expect("SessionKeys decode successfully");
-
-	let ed25519_public_keys = SyncCryptoStore::keys(&*setup.keystore, ED25519).unwrap();
-	let sr25519_public_keys = SyncCryptoStore::keys(&*setup.keystore, SR25519).unwrap();
-
-	assert!(ed25519_public_keys
+		SessionKeys::decode(&mut &new_pubkeys[..]).expect("SessionKeys decode successfully");
+	let ed25519_pubkeys = SyncCryptoStore::keys(&*setup.keystore, ED25519).unwrap();
+	let sr25519_pubkeys = SyncCryptoStore::keys(&*setup.keystore, SR25519).unwrap();
+	assert!(ed25519_pubkeys
 		.contains(&CryptoTypePublicPair(ed25519::CRYPTO_ID, session_keys.ed25519.to_raw_vec())));
-	assert!(sr25519_public_keys
+	assert!(sr25519_pubkeys
 		.contains(&CryptoTypePublicPair(sr25519::CRYPTO_ID, session_keys.sr25519.to_raw_vec())));
 }
 
-#[test]
-fn test_has_session_keys() {
-	let setup = TestSetup::default();
-	let p = setup.author();
-
-	let non_existent_public_keys =
-		TestSetup::default().author().rotate_keys().expect("Rotates the keys");
-
-	let public_keys = p.rotate_keys().expect("Rotates the keys");
-	let test_vectors = vec![
-		(public_keys, Ok(true)),
-		(vec![1, 2, 3].into(), Err(Error::InvalidSessionKeys)),
-		(non_existent_public_keys, Ok(false)),
-	];
-
-	for (keys, result) in test_vectors {
-		assert_eq!(
-			result.map_err(|e| mem::discriminant(&e)),
-			p.has_session_keys(keys).map_err(|e| mem::discriminant(&e)),
-		);
-	}
+#[tokio::test]
+async fn author_has_session_keys() {
+	// Setup
+	let api = TestSetup::into_rpc();
+
+	// Add a valid session key
+	let pubkeys: Bytes = api
+		.call("author_rotateKeys", EmptyParams::new())
+		.await
+		.expect("Rotates the keys");
+
+	// Add a session key in a different keystore
+	let non_existent_pubkeys: Bytes = {
+		let api2 = TestSetup::default().author().into_rpc();
+		api2.call("author_rotateKeys", EmptyParams::new())
+			.await
+			.expect("Rotates the keys")
+	};
+
+	// Then…
+	let existing = api.call::<_, bool>("author_hasSessionKeys", vec![pubkeys]).await.unwrap();
+	assert!(existing, "Existing key is in the session keys");
+
+	let inexistent = api
+		.call::<_, bool>("author_hasSessionKeys", vec![non_existent_pubkeys])
+		.await
+		.unwrap();
+	assert_eq!(inexistent, false, "Inexistent key is not in the session keys");
+
+	assert_matches!(
+		api.call::<_, bool>("author_hasSessionKeys", vec![Bytes::from(vec![1, 2, 3])]).await,
+		Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Session keys are not encoded correctly")
+	);
 }
 
-#[test]
-fn test_has_key() {
-	let setup = TestSetup::default();
-	let p = setup.author();
+#[tokio::test]
+async fn author_has_key() {
+	let _ = env_logger::try_init();
 
+	let api = TestSetup::into_rpc();
 	let suri = "//Alice";
-	let alice_key_pair = ed25519::Pair::from_string(suri, None).expect("Generates keypair");
-	p.insert_key(
+	let alice_keypair = ed25519::Pair::from_string(suri, None).expect("Generates keypair");
+	let params = (
 		String::from_utf8(ED25519.0.to_vec()).expect("Keytype is a valid string"),
 		suri.to_string(),
-		alice_key_pair.public().0.to_vec().into(),
-	)
-	.expect("Insert key");
-	let bob_key_pair = ed25519::Pair::from_string("//Bob", None).expect("Generates keypair");
-
-	let test_vectors = vec![
-		(alice_key_pair.public().to_raw_vec().into(), ED25519, Ok(true)),
-		(alice_key_pair.public().to_raw_vec().into(), SR25519, Ok(false)),
-		(bob_key_pair.public().to_raw_vec().into(), ED25519, Ok(false)),
-	];
-
-	for (key, key_type, result) in test_vectors {
-		assert_eq!(
-			result.map_err(|e| mem::discriminant(&e)),
-			p.has_key(
-				key,
-				String::from_utf8(key_type.0.to_vec()).expect("Keytype is a valid string"),
-			)
-			.map_err(|e| mem::discriminant(&e)),
+		Bytes::from(alice_keypair.public().0.to_vec()),
+	);
+
+	api.call::<_, ()>("author_insertKey", params).await.expect("insertKey works");
+
+	let bob_keypair = ed25519::Pair::from_string("//Bob", None).expect("Generates keypair");
+
+	// Alice's ED25519 key is there
+	let has_alice_ed: bool = {
+		let params = (
+			Bytes::from(alice_keypair.public().to_raw_vec()),
+			String::from_utf8(ED25519.0.to_vec()).expect("Keytype is a valid string"),
 		);
-	}
+		api.call("author_hasKey", params).await.unwrap()
+	};
+	assert!(has_alice_ed);
+
+	// Alice's SR25519 key is not there
+	let has_alice_sr: bool = {
+		let params = (
+			Bytes::from(alice_keypair.public().to_raw_vec()),
+			String::from_utf8(SR25519.0.to_vec()).expect("Keytype is a valid string"),
+		);
+		api.call("author_hasKey", params).await.unwrap()
+	};
+	assert!(!has_alice_sr);
+
+	// Bob's ED25519 key is not there
+	let has_bob_ed: bool = {
+		let params = (
+			Bytes::from(bob_keypair.public().to_raw_vec()),
+			String::from_utf8(ED25519.0.to_vec()).expect("Keytype is a valid string"),
+		);
+		api.call("author_hasKey", params).await.unwrap()
+	};
+	assert!(!has_bob_ed);
 }
diff --git a/substrate/client/rpc/src/chain/chain_full.rs b/substrate/client/rpc/src/chain/chain_full.rs
index 288a825eb5bedac0ca8744e4d90b044cb63f3b9e..9ca6b3edcfe60992ca37e3b8d62e681eee69f861 100644
--- a/substrate/client/rpc/src/chain/chain_full.rs
+++ b/substrate/client/rpc/src/chain/chain_full.rs
@@ -18,34 +18,40 @@
 
 //! Blockchain API backend for full nodes.
 
-use super::{client_err, error::FutureResult, ChainBackend};
-use futures::FutureExt;
-use jsonrpc_pubsub::manager::SubscriptionManager;
+use super::{client_err, ChainBackend, Error};
+use crate::SubscriptionTaskExecutor;
+use std::{marker::PhantomData, sync::Arc};
+
+use futures::{
+	future::{self, FutureExt},
+	stream::{self, Stream, StreamExt},
+};
+use jsonrpsee::{core::async_trait, PendingSubscription};
 use sc_client_api::{BlockBackend, BlockchainEvents};
 use sp_blockchain::HeaderBackend;
 use sp_runtime::{
 	generic::{BlockId, SignedBlock},
 	traits::Block as BlockT,
 };
-use std::{marker::PhantomData, sync::Arc};
 
 /// Blockchain API backend for full nodes. Reads all the data from local database.
 pub struct FullChain<Block: BlockT, Client> {
 	/// Substrate client.
 	client: Arc<Client>,
-	/// Current subscriptions.
-	subscriptions: SubscriptionManager,
 	/// phantom member to pin the block type
 	_phantom: PhantomData<Block>,
+	/// Subscription executor.
+	executor: SubscriptionTaskExecutor,
 }
 
 impl<Block: BlockT, Client> FullChain<Block, Client> {
 	/// Create new Chain API RPC handler.
-	pub fn new(client: Arc<Client>, subscriptions: SubscriptionManager) -> Self {
-		Self { client, subscriptions, _phantom: PhantomData }
+	pub fn new(client: Arc<Client>, executor: SubscriptionTaskExecutor) -> Self {
+		Self { client, executor, _phantom: PhantomData }
 	}
 }
 
+#[async_trait]
 impl<Block, Client> ChainBackend<Client, Block> for FullChain<Block, Client>
 where
 	Block: BlockT + 'static,
@@ -56,17 +62,93 @@ where
 		&self.client
 	}
 
-	fn subscriptions(&self) -> &SubscriptionManager {
-		&self.subscriptions
+	async fn header(&self, hash: Option<Block::Hash>) -> Result<Option<Block::Header>, Error> {
+		self.client.header(BlockId::Hash(self.unwrap_or_best(hash))).map_err(client_err)
 	}
 
-	fn header(&self, hash: Option<Block::Hash>) -> FutureResult<Option<Block::Header>> {
-		let res = self.client.header(BlockId::Hash(self.unwrap_or_best(hash))).map_err(client_err);
-		async move { res }.boxed()
+	async fn block(&self, hash: Option<Block::Hash>) -> Result<Option<SignedBlock<Block>>, Error> {
+		self.client.block(&BlockId::Hash(self.unwrap_or_best(hash))).map_err(client_err)
 	}
 
-	fn block(&self, hash: Option<Block::Hash>) -> FutureResult<Option<SignedBlock<Block>>> {
-		let res = self.client.block(&BlockId::Hash(self.unwrap_or_best(hash))).map_err(client_err);
-		async move { res }.boxed()
+	fn subscribe_all_heads(&self, sink: PendingSubscription) {
+		subscribe_headers(
+			&self.client,
+			&self.executor,
+			sink,
+			|| self.client().info().best_hash,
+			|| {
+				self.client()
+					.import_notification_stream()
+					.map(|notification| notification.header)
+			},
+		)
 	}
+
+	fn subscribe_new_heads(&self, sink: PendingSubscription) {
+		subscribe_headers(
+			&self.client,
+			&self.executor,
+			sink,
+			|| self.client().info().best_hash,
+			|| {
+				self.client()
+					.import_notification_stream()
+					.filter(|notification| future::ready(notification.is_new_best))
+					.map(|notification| notification.header)
+			},
+		)
+	}
+
+	fn subscribe_finalized_heads(&self, sink: PendingSubscription) {
+		subscribe_headers(
+			&self.client,
+			&self.executor,
+			sink,
+			|| self.client().info().finalized_hash,
+			|| {
+				self.client()
+					.finality_notification_stream()
+					.map(|notification| notification.header)
+			},
+		)
+	}
+}
+
+/// Subscribe to new headers.
+fn subscribe_headers<Block, Client, F, G, S>(
+	client: &Arc<Client>,
+	executor: &SubscriptionTaskExecutor,
+	pending: PendingSubscription,
+	best_block_hash: G,
+	stream: F,
+) where
+	Block: BlockT + 'static,
+	Block::Header: Unpin,
+	Client: HeaderBackend<Block> + 'static,
+	F: FnOnce() -> S,
+	G: FnOnce() -> Block::Hash,
+	S: Stream<Item = Block::Header> + Send + Unpin + 'static,
+{
+	// send current head right at the start.
+	let maybe_header = client
+		.header(BlockId::Hash(best_block_hash()))
+		.map_err(client_err)
+		.and_then(|header| header.ok_or_else(|| Error::Other("Best header missing.".into())))
+		.map_err(|e| log::warn!("Best header error {:?}", e))
+		.ok();
+
+	// NOTE: by the time we set up the stream there might be a new best block and so there is a risk
+	// that the stream has a hole in it. The alternative would be to look up the best block *after*
+	// we set up the stream and chain it to the stream. Consuming code would need to handle
+	// duplicates at the beginning of the stream though.
+	let stream = stream::iter(maybe_header).chain(stream());
+
+	let fut = async move {
+		if let Some(mut sink) = pending.accept() {
+			sink.pipe_from_stream(stream).await;
+		}
+	}
+	.boxed();
+
+	executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.map(drop).boxed());
 }
diff --git a/substrate/client/rpc/src/chain/mod.rs b/substrate/client/rpc/src/chain/mod.rs
index 64231dd78c83c66e0c62dad3b654e3196b2f73fb..a79c66e0a18f6dcf96307829eda2251e372228d8 100644
--- a/substrate/client/rpc/src/chain/mod.rs
+++ b/substrate/client/rpc/src/chain/mod.rs
@@ -23,15 +23,14 @@ mod chain_full;
 #[cfg(test)]
 mod tests;
 
-use futures::{future, StreamExt, TryStreamExt};
-use log::warn;
-use rpc::{
-	futures::{stream, FutureExt, SinkExt, Stream},
-	Result as RpcResult,
-};
 use std::sync::Arc;
 
-use jsonrpc_pubsub::{manager::SubscriptionManager, typed::Subscriber, SubscriptionId};
+use crate::SubscriptionTaskExecutor;
+
+use jsonrpsee::{
+	core::{async_trait, RpcResult},
+	PendingSubscription,
+};
 use sc_client_api::BlockchainEvents;
 use sp_rpc::{list::ListOrValue, number::NumberOrHex};
 use sp_runtime::{
@@ -39,13 +38,14 @@ use sp_runtime::{
 	traits::{Block as BlockT, Header, NumberFor},
 };
 
-use self::error::{Error, FutureResult, Result};
+use self::error::Error;
 
 use sc_client_api::BlockBackend;
 pub use sc_rpc_api::chain::*;
 use sp_blockchain::HeaderBackend;
 
 /// Blockchain backend API
+#[async_trait]
 trait ChainBackend<Client, Block: BlockT>: Send + Sync + 'static
 where
 	Block: BlockT + 'static,
@@ -55,9 +55,6 @@ where
 	/// Get client reference.
 	fn client(&self) -> &Arc<Client>;
 
-	/// Get subscriptions reference.
-	fn subscriptions(&self) -> &SubscriptionManager;
-
 	/// Tries to unwrap passed block hash, or uses best block hash otherwise.
 	fn unwrap_or_best(&self, hash: Option<Block::Hash>) -> Block::Hash {
 		match hash {
@@ -67,15 +64,15 @@ where
 	}
 
 	/// Get header of a relay chain block.
-	fn header(&self, hash: Option<Block::Hash>) -> FutureResult<Option<Block::Header>>;
+	async fn header(&self, hash: Option<Block::Hash>) -> Result<Option<Block::Header>, Error>;
 
 	/// Get header and body of a relay chain block.
-	fn block(&self, hash: Option<Block::Hash>) -> FutureResult<Option<SignedBlock<Block>>>;
+	async fn block(&self, hash: Option<Block::Hash>) -> Result<Option<SignedBlock<Block>>, Error>;
 
 	/// Get hash of the n-th block in the canon chain.
 	///
 	/// By default returns latest block hash.
-	fn block_hash(&self, number: Option<NumberOrHex>) -> Result<Option<Block::Hash>> {
+	fn block_hash(&self, number: Option<NumberOrHex>) -> Result<Option<Block::Hash>, Error> {
 		match number {
 			None => Ok(Some(self.client().info().best_hash)),
 			Some(num_or_hex) => {
@@ -97,107 +94,31 @@ where
 	}
 
 	/// Get hash of the last finalized block in the canon chain.
-	fn finalized_head(&self) -> Result<Block::Hash> {
+	fn finalized_head(&self) -> Result<Block::Hash, Error> {
 		Ok(self.client().info().finalized_hash)
 	}
 
 	/// All new head subscription
-	fn subscribe_all_heads(
-		&self,
-		_metadata: crate::Metadata,
-		subscriber: Subscriber<Block::Header>,
-	) {
-		subscribe_headers(
-			self.client(),
-			self.subscriptions(),
-			subscriber,
-			|| self.client().info().best_hash,
-			|| {
-				self.client()
-					.import_notification_stream()
-					.map(|notification| Ok::<_, rpc::Error>(notification.header))
-			},
-		)
-	}
-
-	/// Unsubscribe from all head subscription.
-	fn unsubscribe_all_heads(
-		&self,
-		_metadata: Option<crate::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool> {
-		Ok(self.subscriptions().cancel(id))
-	}
+	fn subscribe_all_heads(&self, sink: PendingSubscription);
 
 	/// New best head subscription
-	fn subscribe_new_heads(
-		&self,
-		_metadata: crate::Metadata,
-		subscriber: Subscriber<Block::Header>,
-	) {
-		subscribe_headers(
-			self.client(),
-			self.subscriptions(),
-			subscriber,
-			|| self.client().info().best_hash,
-			|| {
-				self.client()
-					.import_notification_stream()
-					.filter(|notification| future::ready(notification.is_new_best))
-					.map(|notification| Ok::<_, rpc::Error>(notification.header))
-			},
-		)
-	}
-
-	/// Unsubscribe from new best head subscription.
-	fn unsubscribe_new_heads(
-		&self,
-		_metadata: Option<crate::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool> {
-		Ok(self.subscriptions().cancel(id))
-	}
+	fn subscribe_new_heads(&self, sink: PendingSubscription);
 
 	/// Finalized head subscription
-	fn subscribe_finalized_heads(
-		&self,
-		_metadata: crate::Metadata,
-		subscriber: Subscriber<Block::Header>,
-	) {
-		subscribe_headers(
-			self.client(),
-			self.subscriptions(),
-			subscriber,
-			|| self.client().info().finalized_hash,
-			|| {
-				self.client()
-					.finality_notification_stream()
-					.map(|notification| Ok::<_, rpc::Error>(notification.header))
-			},
-		)
-	}
-
-	/// Unsubscribe from finalized head subscription.
-	fn unsubscribe_finalized_heads(
-		&self,
-		_metadata: Option<crate::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool> {
-		Ok(self.subscriptions().cancel(id))
-	}
+	fn subscribe_finalized_heads(&self, sink: PendingSubscription);
 }
 
 /// Create new state API that works on full node.
 pub fn new_full<Block: BlockT, Client>(
 	client: Arc<Client>,
-	subscriptions: SubscriptionManager,
+	executor: SubscriptionTaskExecutor,
 ) -> Chain<Block, Client>
 where
 	Block: BlockT + 'static,
 	Block::Header: Unpin,
 	Client: BlockBackend<Block> + HeaderBackend<Block> + BlockchainEvents<Block> + 'static,
 {
-	Chain { backend: Box::new(self::chain_full::FullChain::new(client, subscriptions)) }
+	Chain { backend: Box::new(self::chain_full::FullChain::new(client, executor)) }
 }
 
 /// Chain API with subscriptions support.
@@ -205,122 +126,58 @@ pub struct Chain<Block: BlockT, Client> {
 	backend: Box<dyn ChainBackend<Client, Block>>,
 }
 
-impl<Block, Client> ChainApi<NumberFor<Block>, Block::Hash, Block::Header, SignedBlock<Block>>
+#[async_trait]
+impl<Block, Client> ChainApiServer<NumberFor<Block>, Block::Hash, Block::Header, SignedBlock<Block>>
 	for Chain<Block, Client>
 where
 	Block: BlockT + 'static,
 	Block::Header: Unpin,
 	Client: HeaderBackend<Block> + BlockchainEvents<Block> + 'static,
 {
-	type Metadata = crate::Metadata;
-
-	fn header(&self, hash: Option<Block::Hash>) -> FutureResult<Option<Block::Header>> {
-		self.backend.header(hash)
+	async fn header(&self, hash: Option<Block::Hash>) -> RpcResult<Option<Block::Header>> {
+		self.backend.header(hash).await.map_err(Into::into)
 	}
 
-	fn block(&self, hash: Option<Block::Hash>) -> FutureResult<Option<SignedBlock<Block>>> {
-		self.backend.block(hash)
+	async fn block(&self, hash: Option<Block::Hash>) -> RpcResult<Option<SignedBlock<Block>>> {
+		self.backend.block(hash).await.map_err(Into::into)
 	}
 
 	fn block_hash(
 		&self,
 		number: Option<ListOrValue<NumberOrHex>>,
-	) -> Result<ListOrValue<Option<Block::Hash>>> {
+	) -> RpcResult<ListOrValue<Option<Block::Hash>>> {
 		match number {
-			None => self.backend.block_hash(None).map(ListOrValue::Value),
-			Some(ListOrValue::Value(number)) =>
-				self.backend.block_hash(Some(number)).map(ListOrValue::Value),
+			None => self.backend.block_hash(None).map(ListOrValue::Value).map_err(Into::into),
+			Some(ListOrValue::Value(number)) => self
+				.backend
+				.block_hash(Some(number))
+				.map(ListOrValue::Value)
+				.map_err(Into::into),
 			Some(ListOrValue::List(list)) => Ok(ListOrValue::List(
 				list.into_iter()
 					.map(|number| self.backend.block_hash(Some(number)))
-					.collect::<Result<_>>()?,
+					.collect::<Result<_, _>>()?,
 			)),
 		}
 	}
 
-	fn finalized_head(&self) -> Result<Block::Hash> {
-		self.backend.finalized_head()
-	}
-
-	fn subscribe_all_heads(&self, metadata: Self::Metadata, subscriber: Subscriber<Block::Header>) {
-		self.backend.subscribe_all_heads(metadata, subscriber)
-	}
-
-	fn unsubscribe_all_heads(
-		&self,
-		metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool> {
-		self.backend.unsubscribe_all_heads(metadata, id)
-	}
-
-	fn subscribe_new_heads(&self, metadata: Self::Metadata, subscriber: Subscriber<Block::Header>) {
-		self.backend.subscribe_new_heads(metadata, subscriber)
+	fn finalized_head(&self) -> RpcResult<Block::Hash> {
+		self.backend.finalized_head().map_err(Into::into)
 	}
 
-	fn unsubscribe_new_heads(
-		&self,
-		metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool> {
-		self.backend.unsubscribe_new_heads(metadata, id)
+	fn subscribe_all_heads(&self, sink: PendingSubscription) {
+		self.backend.subscribe_all_heads(sink)
 	}
 
-	fn subscribe_finalized_heads(
-		&self,
-		metadata: Self::Metadata,
-		subscriber: Subscriber<Block::Header>,
-	) {
-		self.backend.subscribe_finalized_heads(metadata, subscriber)
+	fn subscribe_new_heads(&self, sink: PendingSubscription) {
+		self.backend.subscribe_new_heads(sink)
 	}
 
-	fn unsubscribe_finalized_heads(
-		&self,
-		metadata: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool> {
-		self.backend.unsubscribe_finalized_heads(metadata, id)
+	fn subscribe_finalized_heads(&self, sink: PendingSubscription) {
+		self.backend.subscribe_finalized_heads(sink)
 	}
 }
 
-/// Subscribe to new headers.
-fn subscribe_headers<Block, Client, F, G, S>(
-	client: &Arc<Client>,
-	subscriptions: &SubscriptionManager,
-	subscriber: Subscriber<Block::Header>,
-	best_block_hash: G,
-	stream: F,
-) where
-	Block: BlockT + 'static,
-	Block::Header: Unpin,
-	Client: HeaderBackend<Block> + 'static,
-	F: FnOnce() -> S,
-	G: FnOnce() -> Block::Hash,
-	S: Stream<Item = std::result::Result<Block::Header, rpc::Error>> + Send + 'static,
-{
-	subscriptions.add(subscriber, |sink| {
-		// send current head right at the start.
-		let header = client
-			.header(BlockId::Hash(best_block_hash()))
-			.map_err(client_err)
-			.and_then(|header| {
-				header.ok_or_else(|| Error::Other("Best header missing.".to_string()))
-			})
-			.map_err(Into::into);
-
-		// send further subscriptions
-		let stream = stream()
-			.inspect_err(|e| warn!("Block notification stream error: {:?}", e))
-			.map(Ok);
-
-		stream::iter(vec![Ok(header)])
-			.chain(stream)
-			.forward(sink.sink_map_err(|e| warn!("Error sending notifications: {:?}", e)))
-			// we ignore the resulting Stream (if the first stream is over we are unsubscribed)
-			.map(|_| ())
-	});
-}
-
 fn client_err(err: sp_blockchain::Error) -> Error {
 	Error::Client(Box::new(err))
 }
diff --git a/substrate/client/rpc/src/chain/tests.rs b/substrate/client/rpc/src/chain/tests.rs
index fa4473d35f30064ee6af878995b0547aee570c1b..f09da200ff58747ac787faeccd9437f129e23d2b 100644
--- a/substrate/client/rpc/src/chain/tests.rs
+++ b/substrate/client/rpc/src/chain/tests.rs
@@ -17,9 +17,9 @@
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
 use super::*;
-use crate::testing::TaskExecutor;
+use crate::testing::{test_executor, timeout_secs};
 use assert_matches::assert_matches;
-use futures::executor;
+use jsonrpsee::types::EmptyParams;
 use sc_block_builder::BlockBuilderProvider;
 use sp_consensus::BlockOrigin;
 use sp_rpc::list::ListOrValue;
@@ -28,221 +28,218 @@ use substrate_test_runtime_client::{
 	runtime::{Block, Header, H256},
 };
 
-#[test]
-fn should_return_header() {
+#[tokio::test]
+async fn should_return_header() {
 	let client = Arc::new(substrate_test_runtime_client::new());
-	let api = new_full(client.clone(), SubscriptionManager::new(Arc::new(TaskExecutor)));
+	let api = new_full(client.clone(), test_executor()).into_rpc();
 
-	assert_matches!(
-		executor::block_on(api.header(Some(client.genesis_hash()).into())),
-		Ok(Some(ref x)) if x == &Header {
+	let res: Header =
+		api.call("chain_getHeader", [H256::from(client.genesis_hash())]).await.unwrap();
+	assert_eq!(
+		res,
+		Header {
 			parent_hash: H256::from_low_u64_be(0),
 			number: 0,
-			state_root: x.state_root.clone(),
-			extrinsics_root:
-				"03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314".parse().unwrap(),
+			state_root: res.state_root.clone(),
+			extrinsics_root: "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314"
+				.parse()
+				.unwrap(),
 			digest: Default::default(),
 		}
 	);
 
-	assert_matches!(
-		executor::block_on(api.header(None.into())),
-		Ok(Some(ref x)) if x == &Header {
+	let res: Header = api.call("chain_getHeader", EmptyParams::new()).await.unwrap();
+	assert_eq!(
+		res,
+		Header {
 			parent_hash: H256::from_low_u64_be(0),
 			number: 0,
-			state_root: x.state_root.clone(),
-			extrinsics_root:
-				"03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314".parse().unwrap(),
+			state_root: res.state_root.clone(),
+			extrinsics_root: "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314"
+				.parse()
+				.unwrap(),
 			digest: Default::default(),
 		}
 	);
 
 	assert_matches!(
-		executor::block_on(api.header(Some(H256::from_low_u64_be(5)).into())),
-		Ok(None)
+		api.call::<_, Option<Header>>("chain_getHeader", [H256::from_low_u64_be(5)])
+			.await
+			.unwrap(),
+		None
 	);
 }
 
-#[test]
-fn should_return_a_block() {
+#[tokio::test]
+async fn should_return_a_block() {
 	let mut client = Arc::new(substrate_test_runtime_client::new());
-	let api = new_full(client.clone(), SubscriptionManager::new(Arc::new(TaskExecutor)));
+	let api = new_full(client.clone(), test_executor()).into_rpc();
 
 	let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
 	let block_hash = block.hash();
-	executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
+	client.import(BlockOrigin::Own, block).await.unwrap();
+
+	let res: SignedBlock<Block> =
+		api.call("chain_getBlock", [H256::from(client.genesis_hash())]).await.unwrap();
 
 	// Genesis block is not justified
-	assert_matches!(
-		executor::block_on(api.block(Some(client.genesis_hash()).into())),
-		Ok(Some(SignedBlock { justifications: None, .. }))
-	);
+	assert!(res.justifications.is_none());
 
-	assert_matches!(
-		executor::block_on(api.block(Some(block_hash).into())),
-		Ok(Some(ref x)) if x.block == Block {
+	let res: SignedBlock<Block> =
+		api.call("chain_getBlock", [H256::from(block_hash)]).await.unwrap();
+	assert_eq!(
+		res.block,
+		Block {
 			header: Header {
 				parent_hash: client.genesis_hash(),
 				number: 1,
-				state_root: x.block.header.state_root.clone(),
-				extrinsics_root:
-					"03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314".parse().unwrap(),
+				state_root: res.block.header.state_root.clone(),
+				extrinsics_root: "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314"
+					.parse()
+					.unwrap(),
 				digest: Default::default(),
 			},
 			extrinsics: vec![],
 		}
 	);
 
-	assert_matches!(
-		executor::block_on(api.block(None.into())),
-		Ok(Some(ref x)) if x.block == Block {
+	let res: SignedBlock<Block> = api.call("chain_getBlock", Vec::<H256>::new()).await.unwrap();
+	assert_eq!(
+		res.block,
+		Block {
 			header: Header {
 				parent_hash: client.genesis_hash(),
 				number: 1,
-				state_root: x.block.header.state_root.clone(),
-				extrinsics_root:
-					"03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314".parse().unwrap(),
+				state_root: res.block.header.state_root.clone(),
+				extrinsics_root: "03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314"
+					.parse()
+					.unwrap(),
 				digest: Default::default(),
 			},
 			extrinsics: vec![],
 		}
 	);
 
-	assert_matches!(executor::block_on(api.block(Some(H256::from_low_u64_be(5)).into())), Ok(None));
+	assert_matches!(
+		api.call::<_, Option<Header>>("chain_getBlock", [H256::from_low_u64_be(5)])
+			.await
+			.unwrap(),
+		None
+	);
 }
 
-#[test]
-fn should_return_block_hash() {
+#[tokio::test]
+async fn should_return_block_hash() {
 	let mut client = Arc::new(substrate_test_runtime_client::new());
-	let api = new_full(client.clone(), SubscriptionManager::new(Arc::new(TaskExecutor)));
+	let api = new_full(client.clone(), test_executor()).into_rpc();
 
-	assert_matches!(
-		api.block_hash(None.into()),
-		Ok(ListOrValue::Value(Some(ref x))) if x == &client.genesis_hash()
-	);
+	let res: ListOrValue<Option<H256>> =
+		api.call("chain_getBlockHash", EmptyParams::new()).await.unwrap();
 
 	assert_matches!(
-		api.block_hash(Some(ListOrValue::Value(0u64.into())).into()),
-		Ok(ListOrValue::Value(Some(ref x))) if x == &client.genesis_hash()
+		res,
+		ListOrValue::Value(Some(ref x)) if x == &client.genesis_hash()
 	);
 
+	let res: ListOrValue<Option<H256>> =
+		api.call("chain_getBlockHash", [ListOrValue::from(0_u64)]).await.unwrap();
 	assert_matches!(
-		api.block_hash(Some(ListOrValue::Value(1u64.into())).into()),
-		Ok(ListOrValue::Value(None))
+		res,
+		ListOrValue::Value(Some(ref x)) if x == &client.genesis_hash()
 	);
 
+	let res: Option<ListOrValue<Option<H256>>> =
+		api.call("chain_getBlockHash", [ListOrValue::from(1_u64)]).await.unwrap();
+	assert_matches!(res, None);
+
 	let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
-	executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap();
+	client.import(BlockOrigin::Own, block.clone()).await.unwrap();
 
+	let res: ListOrValue<Option<H256>> =
+		api.call("chain_getBlockHash", [ListOrValue::from(0_u64)]).await.unwrap();
 	assert_matches!(
-		api.block_hash(Some(ListOrValue::Value(0u64.into())).into()),
-		Ok(ListOrValue::Value(Some(ref x))) if x == &client.genesis_hash()
+		res,
+		ListOrValue::Value(Some(ref x)) if x == &client.genesis_hash()
 	);
+
+	let res: ListOrValue<Option<H256>> =
+		api.call("chain_getBlockHash", [ListOrValue::from(1_u64)]).await.unwrap();
 	assert_matches!(
-		api.block_hash(Some(ListOrValue::Value(1u64.into())).into()),
-		Ok(ListOrValue::Value(Some(ref x))) if x == &block.hash()
+		res,
+		ListOrValue::Value(Some(ref x)) if x == &block.hash()
 	);
+
+	let res: ListOrValue<Option<H256>> = api
+		.call("chain_getBlockHash", [ListOrValue::Value(sp_core::U256::from(1_u64))])
+		.await
+		.unwrap();
 	assert_matches!(
-		api.block_hash(Some(ListOrValue::Value(sp_core::U256::from(1u64).into())).into()),
-		Ok(ListOrValue::Value(Some(ref x))) if x == &block.hash()
+		res,
+		ListOrValue::Value(Some(ref x)) if x == &block.hash()
 	);
 
+	let res: ListOrValue<Option<H256>> = api
+		.call("chain_getBlockHash", [ListOrValue::List(vec![0_u64, 1_u64, 2_u64])])
+		.await
+		.unwrap();
 	assert_matches!(
-		api.block_hash(Some(vec![0u64.into(), 1u64.into(), 2u64.into()].into())),
-		Ok(ListOrValue::List(list)) if list == &[client.genesis_hash().into(), block.hash().into(), None]
+		res,
+		ListOrValue::List(list) if list == &[client.genesis_hash().into(), block.hash().into(), None]
 	);
 }
 
-#[test]
-fn should_return_finalized_hash() {
+#[tokio::test]
+async fn should_return_finalized_hash() {
 	let mut client = Arc::new(substrate_test_runtime_client::new());
-	let api = new_full(client.clone(), SubscriptionManager::new(Arc::new(TaskExecutor)));
+	let api = new_full(client.clone(), test_executor()).into_rpc();
 
-	assert_matches!(
-		api.finalized_head(),
-		Ok(ref x) if x == &client.genesis_hash()
-	);
+	let res: H256 = api.call("chain_getFinalizedHead", EmptyParams::new()).await.unwrap();
+	assert_eq!(res, client.genesis_hash());
 
 	// import new block
 	let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
-	executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
+	client.import(BlockOrigin::Own, block).await.unwrap();
+
 	// no finalization yet
-	assert_matches!(
-		api.finalized_head(),
-		Ok(ref x) if x == &client.genesis_hash()
-	);
+	let res: H256 = api.call("chain_getFinalizedHead", EmptyParams::new()).await.unwrap();
+	assert_eq!(res, client.genesis_hash());
 
 	// finalize
 	client.finalize_block(BlockId::number(1), None).unwrap();
-	assert_matches!(
-		api.finalized_head(),
-		Ok(ref x) if x == &client.block_hash(1).unwrap().unwrap()
-	);
+	let res: H256 = api.call("chain_getFinalizedHead", EmptyParams::new()).await.unwrap();
+	assert_eq!(res, client.block_hash(1).unwrap().unwrap());
 }
 
-#[test]
-fn should_notify_about_latest_block() {
-	let (subscriber, id, mut transport) = Subscriber::new_test("test");
-
-	{
-		let mut client = Arc::new(substrate_test_runtime_client::new());
-		let api = new_full(client.clone(), SubscriptionManager::new(Arc::new(TaskExecutor)));
-
-		api.subscribe_all_heads(Default::default(), subscriber);
-
-		// assert id assigned
-		assert!(matches!(executor::block_on(id), Ok(Ok(SubscriptionId::String(_)))));
-
-		let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
-		executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
-	}
-
-	// Check for the correct number of notifications
-	executor::block_on((&mut transport).take(2).collect::<Vec<_>>());
-	assert!(executor::block_on(transport.next()).is_none());
+#[tokio::test]
+async fn should_notify_about_latest_block() {
+	test_head_subscription("chain_subscribeAllHeads").await;
 }
 
-#[test]
-fn should_notify_about_best_block() {
-	let (subscriber, id, mut transport) = Subscriber::new_test("test");
-
-	{
-		let mut client = Arc::new(substrate_test_runtime_client::new());
-		let api = new_full(client.clone(), SubscriptionManager::new(Arc::new(TaskExecutor)));
-
-		api.subscribe_new_heads(Default::default(), subscriber);
-
-		// assert id assigned
-		assert!(matches!(executor::block_on(id), Ok(Ok(SubscriptionId::String(_)))));
-
-		let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
-		executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
-	}
-
-	// Assert that the correct number of notifications have been sent.
-	executor::block_on((&mut transport).take(2).collect::<Vec<_>>());
-	assert!(executor::block_on(transport.next()).is_none());
+#[tokio::test]
+async fn should_notify_about_best_block() {
+	test_head_subscription("chain_subscribeNewHeads").await;
 }
 
-#[test]
-fn should_notify_about_finalized_block() {
-	let (subscriber, id, mut transport) = Subscriber::new_test("test");
-
-	{
-		let mut client = Arc::new(substrate_test_runtime_client::new());
-		let api = new_full(client.clone(), SubscriptionManager::new(Arc::new(TaskExecutor)));
-
-		api.subscribe_finalized_heads(Default::default(), subscriber);
+#[tokio::test]
+async fn should_notify_about_finalized_block() {
+	test_head_subscription("chain_subscribeFinalizedHeads").await;
+}
 
-		// assert id assigned
-		assert!(matches!(executor::block_on(id), Ok(Ok(SubscriptionId::String(_)))));
+async fn test_head_subscription(method: &str) {
+	let mut client = Arc::new(substrate_test_runtime_client::new());
 
+	let mut sub = {
+		let api = new_full(client.clone(), test_executor()).into_rpc();
+		let sub = api.subscribe(method, EmptyParams::new()).await.unwrap();
 		let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
-		executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
+		client.import(BlockOrigin::Own, block).await.unwrap();
 		client.finalize_block(BlockId::number(1), None).unwrap();
-	}
+		sub
+	};
+
+	assert_matches!(timeout_secs(10, sub.next::<Header>()).await, Ok(Some(_)));
+	assert_matches!(timeout_secs(10, sub.next::<Header>()).await, Ok(Some(_)));
 
-	// Assert that the correct number of notifications have been sent.
-	executor::block_on((&mut transport).take(2).collect::<Vec<_>>());
-	assert!(executor::block_on(transport.next()).is_none());
+	sub.close();
+	assert_matches!(timeout_secs(10, sub.next::<Header>()).await, Ok(None));
 }
diff --git a/substrate/client/rpc/src/dev/mod.rs b/substrate/client/rpc/src/dev/mod.rs
index d782a03feae432725953429f88d44697696af558..7f4b68f56f6f6fa5085b28442606bae2a268a8d0 100644
--- a/substrate/client/rpc/src/dev/mod.rs
+++ b/substrate/client/rpc/src/dev/mod.rs
@@ -16,19 +16,15 @@
 // You should have received a copy of the GNU General Public License
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
-//! Implementation of the [`DevApi`] trait providing debug utilities for Substrate based
+//! Implementation of the [`DevApiServer`] trait providing debug utilities for Substrate based
 //! blockchains.
 
 #[cfg(test)]
 mod tests;
 
-pub use sc_rpc_api::dev::{BlockStats, DevApi};
-
+use jsonrpsee::core::RpcResult;
 use sc_client_api::{BlockBackend, HeaderBackend};
-use sc_rpc_api::{
-	dev::error::{Error, Result},
-	DenyUnsafe,
-};
+use sc_rpc_api::{dev::error::Error, DenyUnsafe};
 use sp_api::{ApiExt, Core, ProvideRuntimeApi};
 use sp_core::Encode;
 use sp_runtime::{
@@ -40,6 +36,8 @@ use std::{
 	sync::Arc,
 };
 
+pub use sc_rpc_api::dev::{BlockStats, DevApiServer};
+
 type HasherOf<Block> = <<Block as BlockT>::Header as Header>::Hashing;
 
 /// The Dev API. All methods are unsafe.
@@ -56,7 +54,7 @@ impl<Block: BlockT, Client> Dev<Block, Client> {
 	}
 }
 
-impl<Block, Client> DevApi<Block::Hash> for Dev<Block, Client>
+impl<Block, Client> DevApiServer<Block::Hash> for Dev<Block, Client>
 where
 	Block: BlockT + 'static,
 	Client: BlockBackend<Block>
@@ -67,7 +65,7 @@ where
 		+ 'static,
 	Client::Api: Core<Block>,
 {
-	fn block_stats(&self, hash: Block::Hash) -> Result<Option<BlockStats>> {
+	fn block_stats(&self, hash: Block::Hash) -> RpcResult<Option<BlockStats>> {
 		self.deny_unsafe.check_if_safe()?;
 
 		let block = {
diff --git a/substrate/client/rpc/src/dev/tests.rs b/substrate/client/rpc/src/dev/tests.rs
index 1d31abe38b640d98000e7403de15bcf09601bb18..b7a0de8f5ae0b452c963cd9d5b115fa196f5940f 100644
--- a/substrate/client/rpc/src/dev/tests.rs
+++ b/substrate/client/rpc/src/dev/tests.rs
@@ -18,25 +18,32 @@
 
 use super::*;
 use assert_matches::assert_matches;
-use futures::executor;
+use jsonrpsee::{core::Error as JsonRpseeError, types::error::CallError};
 use sc_block_builder::BlockBuilderProvider;
 use sp_blockchain::HeaderBackend;
 use sp_consensus::BlockOrigin;
 use substrate_test_runtime_client::{prelude::*, runtime::Block};
 
-#[test]
-fn block_stats_work() {
+#[tokio::test]
+async fn block_stats_work() {
 	let mut client = Arc::new(substrate_test_runtime_client::new());
-	let api = <Dev<Block, _>>::new(client.clone(), DenyUnsafe::No);
+	let api = <Dev<Block, _>>::new(client.clone(), DenyUnsafe::No).into_rpc();
 
 	let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
-	executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
+	client.import(BlockOrigin::Own, block).await.unwrap();
 
 	// Can't gather stats for a block without a parent.
-	assert_eq!(api.block_stats(client.genesis_hash()).unwrap(), None);
+	assert_eq!(
+		api.call::<_, Option<BlockStats>>("dev_getBlockStats", [client.genesis_hash()])
+			.await
+			.unwrap(),
+		None
+	);
 
 	assert_eq!(
-		api.block_stats(client.info().best_hash).unwrap(),
+		api.call::<_, Option<BlockStats>>("dev_getBlockStats", [client.info().best_hash])
+			.await
+			.unwrap(),
 		Some(BlockStats {
 			witness_len: 597,
 			witness_compact_len: 500,
@@ -46,13 +53,17 @@ fn block_stats_work() {
 	);
 }
 
-#[test]
-fn deny_unsafe_works() {
+#[tokio::test]
+async fn deny_unsafe_works() {
 	let mut client = Arc::new(substrate_test_runtime_client::new());
-	let api = <Dev<Block, _>>::new(client.clone(), DenyUnsafe::Yes);
+	let api = <Dev<Block, _>>::new(client.clone(), DenyUnsafe::Yes).into_rpc();
 
 	let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
-	executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
+	client.import(BlockOrigin::Own, block).await.unwrap();
 
-	assert_matches!(api.block_stats(client.info().best_hash), Err(Error::UnsafeRpcCalled(_)));
+	assert_matches!(
+		api.call::<_, Option<BlockStats>>("dev_getBlockStats", [client.info().best_hash])
+			.await,
+		Err(JsonRpseeError::Call(CallError::Custom(err))) if err.message().contains("RPC call is unsafe to be called externally")
+	);
 }
diff --git a/substrate/client/rpc/src/lib.rs b/substrate/client/rpc/src/lib.rs
index 59a1d542d365a3918159e26c0a15cd7bb1953d6e..a0e810eafbb625a497cb8442f8ff6e3e5a425fe6 100644
--- a/substrate/client/rpc/src/lib.rs
+++ b/substrate/client/rpc/src/lib.rs
@@ -22,15 +22,14 @@
 
 #![warn(missing_docs)]
 
-use futures::{
-	task::{FutureObj, Spawn, SpawnError},
-	FutureExt,
+pub use jsonrpsee::core::{
+	id_providers::{
+		RandomIntegerIdProvider as RandomIntegerSubscriptionId,
+		RandomStringIdProvider as RandomStringSubscriptionId,
+	},
+	traits::IdProvider as RpcSubscriptionIdProvider,
 };
-use sp_core::traits::SpawnNamed;
-use std::sync::Arc;
-
-pub use rpc::IoHandlerExtension as RpcExtension;
-pub use sc_rpc_api::{DenyUnsafe, Metadata};
+pub use sc_rpc_api::DenyUnsafe;
 
 pub mod author;
 pub mod chain;
@@ -43,24 +42,4 @@ pub mod system;
 pub mod testing;
 
 /// Task executor that is being used by RPC subscriptions.
-#[derive(Clone)]
-pub struct SubscriptionTaskExecutor(Arc<dyn SpawnNamed>);
-
-impl SubscriptionTaskExecutor {
-	/// Create a new `Self` with the given spawner.
-	pub fn new(spawn: impl SpawnNamed + 'static) -> Self {
-		Self(Arc::new(spawn))
-	}
-}
-
-impl Spawn for SubscriptionTaskExecutor {
-	fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> {
-		self.0
-			.spawn("substrate-rpc-subscription", Some("rpc"), future.map(drop).boxed());
-		Ok(())
-	}
-
-	fn status(&self) -> Result<(), SpawnError> {
-		Ok(())
-	}
-}
+pub type SubscriptionTaskExecutor = std::sync::Arc<dyn sp_core::traits::SpawnNamed>;
diff --git a/substrate/client/rpc/src/offchain/mod.rs b/substrate/client/rpc/src/offchain/mod.rs
index 67b97d31ab949c59408c41bdf864f3e48200e2ee..b66b78274a64eacdc54fdab5b5641010d5260b06 100644
--- a/substrate/client/rpc/src/offchain/mod.rs
+++ b/substrate/client/rpc/src/offchain/mod.rs
@@ -21,7 +21,8 @@
 #[cfg(test)]
 mod tests;
 
-use self::error::{Error, Result};
+use self::error::Error;
+use jsonrpsee::core::{async_trait, Error as JsonRpseeError, RpcResult};
 use parking_lot::RwLock;
 /// Re-export the API for backward compatibility.
 pub use sc_rpc_api::offchain::*;
@@ -47,27 +48,27 @@ impl<T: OffchainStorage> Offchain<T> {
 	}
 }
 
-impl<T: OffchainStorage + 'static> OffchainApi for Offchain<T> {
-	/// Set offchain local storage under given key and prefix.
-	fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<()> {
+#[async_trait]
+impl<T: OffchainStorage + 'static> OffchainApiServer for Offchain<T> {
+	fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> RpcResult<()> {
 		self.deny_unsafe.check_if_safe()?;
 
 		let prefix = match kind {
 			StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX,
-			StorageKind::LOCAL => return Err(Error::UnavailableStorageKind),
+			StorageKind::LOCAL => return Err(JsonRpseeError::from(Error::UnavailableStorageKind)),
 		};
 		self.storage.write().set(prefix, &*key, &*value);
 		Ok(())
 	}
 
-	/// Get offchain local storage under given key and prefix.
-	fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result<Option<Bytes>> {
+	fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> RpcResult<Option<Bytes>> {
 		self.deny_unsafe.check_if_safe()?;
 
 		let prefix = match kind {
 			StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX,
-			StorageKind::LOCAL => return Err(Error::UnavailableStorageKind),
+			StorageKind::LOCAL => return Err(JsonRpseeError::from(Error::UnavailableStorageKind)),
 		};
+
 		Ok(self.storage.read().get(prefix, &*key).map(Into::into))
 	}
 }
diff --git a/substrate/client/rpc/src/offchain/tests.rs b/substrate/client/rpc/src/offchain/tests.rs
index 219eeb192dfdde5dbcdd3ae50c5d630ae9b4fc0d..28a7b6115b6578a4455638bb0e15c0cdb12d5eed 100644
--- a/substrate/client/rpc/src/offchain/tests.rs
+++ b/substrate/client/rpc/src/offchain/tests.rs
@@ -39,6 +39,7 @@ fn local_storage_should_work() {
 
 #[test]
 fn offchain_calls_considered_unsafe() {
+	use jsonrpsee::types::error::CallError;
 	let storage = InMemOffchainStorage::default();
 	let offchain = Offchain::new(storage, DenyUnsafe::Yes);
 	let key = Bytes(b"offchain_storage".to_vec());
@@ -46,10 +47,14 @@ fn offchain_calls_considered_unsafe() {
 
 	assert_matches!(
 		offchain.set_local_storage(StorageKind::PERSISTENT, key.clone(), value.clone()),
-		Err(Error::UnsafeRpcCalled(_))
+		Err(JsonRpseeError::Call(CallError::Custom(err))) => {
+			assert_eq!(err.message(), "RPC call is unsafe to be called externally")
+		}
 	);
 	assert_matches!(
 		offchain.get_local_storage(StorageKind::PERSISTENT, key),
-		Err(Error::UnsafeRpcCalled(_))
+		Err(JsonRpseeError::Call(CallError::Custom(err))) => {
+			assert_eq!(err.message(), "RPC call is unsafe to be called externally")
+		}
 	);
 }
diff --git a/substrate/client/rpc/src/state/mod.rs b/substrate/client/rpc/src/state/mod.rs
index c9806a30b45493b2329162d1734162c65a6622bc..a45651c5e7990cf4a9c363a4ad968eff94741d83 100644
--- a/substrate/client/rpc/src/state/mod.rs
+++ b/substrate/client/rpc/src/state/mod.rs
@@ -23,11 +23,15 @@ mod state_full;
 #[cfg(test)]
 mod tests;
 
-use futures::FutureExt;
-use jsonrpc_pubsub::{manager::SubscriptionManager, typed::Subscriber, SubscriptionId};
-use rpc::Result as RpcResult;
 use std::sync::Arc;
 
+use crate::SubscriptionTaskExecutor;
+
+use jsonrpsee::{
+	core::{async_trait, Error as JsonRpseeError, RpcResult},
+	ws_server::PendingSubscription,
+};
+
 use sc_rpc_api::{state::ReadProof, DenyUnsafe};
 use sp_core::{
 	storage::{PrefixedStorageKey, StorageChangeSet, StorageData, StorageKey},
@@ -38,7 +42,7 @@ use sp_version::RuntimeVersion;
 
 use sp_api::{CallApiAt, Metadata, ProvideRuntimeApi};
 
-use self::error::{Error, FutureResult};
+use self::error::Error;
 
 use sc_client_api::{
 	Backend, BlockBackend, BlockchainEvents, ExecutorProvider, ProofProvider, StorageProvider,
@@ -49,144 +53,122 @@ use sp_blockchain::{HeaderBackend, HeaderMetadata};
 const STORAGE_KEYS_PAGED_MAX_COUNT: u32 = 1000;
 
 /// State backend API.
+#[async_trait]
 pub trait StateBackend<Block: BlockT, Client>: Send + Sync + 'static
 where
 	Block: BlockT + 'static,
 	Client: Send + Sync + 'static,
 {
 	/// Call runtime method at given block.
-	fn call(
+	async fn call(
 		&self,
 		block: Option<Block::Hash>,
 		method: String,
 		call_data: Bytes,
-	) -> FutureResult<Bytes>;
+	) -> Result<Bytes, Error>;
 
 	/// Returns the keys with prefix, leave empty to get all the keys.
-	fn storage_keys(
+	async fn storage_keys(
 		&self,
 		block: Option<Block::Hash>,
 		prefix: StorageKey,
-	) -> FutureResult<Vec<StorageKey>>;
+	) -> Result<Vec<StorageKey>, Error>;
 
 	/// Returns the keys with prefix along with their values, leave empty to get all the pairs.
-	fn storage_pairs(
+	async fn storage_pairs(
 		&self,
 		block: Option<Block::Hash>,
 		prefix: StorageKey,
-	) -> FutureResult<Vec<(StorageKey, StorageData)>>;
+	) -> Result<Vec<(StorageKey, StorageData)>, Error>;
 
 	/// Returns the keys with prefix with pagination support.
-	fn storage_keys_paged(
+	async fn storage_keys_paged(
 		&self,
 		block: Option<Block::Hash>,
 		prefix: Option<StorageKey>,
 		count: u32,
 		start_key: Option<StorageKey>,
-	) -> FutureResult<Vec<StorageKey>>;
+	) -> Result<Vec<StorageKey>, Error>;
 
 	/// Returns a storage entry at a specific block's state.
-	fn storage(
+	async fn storage(
 		&self,
 		block: Option<Block::Hash>,
 		key: StorageKey,
-	) -> FutureResult<Option<StorageData>>;
+	) -> Result<Option<StorageData>, Error>;
 
 	/// Returns the hash of a storage entry at a block's state.
-	fn storage_hash(
+	async fn storage_hash(
 		&self,
 		block: Option<Block::Hash>,
 		key: StorageKey,
-	) -> FutureResult<Option<Block::Hash>>;
+	) -> Result<Option<Block::Hash>, Error>;
 
 	/// Returns the size of a storage entry at a block's state.
 	///
 	/// If data is available at `key`, it is returned. Else, the sum of values who's key has `key`
 	/// prefix is returned, i.e. all the storage (double) maps that have this prefix.
-	fn storage_size(
+	async fn storage_size(
 		&self,
 		block: Option<Block::Hash>,
 		key: StorageKey,
-	) -> FutureResult<Option<u64>>;
+	) -> Result<Option<u64>, Error>;
 
 	/// Returns the runtime metadata as an opaque blob.
-	fn metadata(&self, block: Option<Block::Hash>) -> FutureResult<Bytes>;
+	async fn metadata(&self, block: Option<Block::Hash>) -> Result<Bytes, Error>;
 
 	/// Get the runtime version.
-	fn runtime_version(&self, block: Option<Block::Hash>) -> FutureResult<RuntimeVersion>;
+	async fn runtime_version(&self, block: Option<Block::Hash>) -> Result<RuntimeVersion, Error>;
 
 	/// Query historical storage entries (by key) starting from a block given as the second
 	/// parameter.
 	///
 	/// NOTE This first returned result contains the initial state of storage for all keys.
 	/// Subsequent values in the vector represent changes to the previous state (diffs).
-	fn query_storage(
+	async fn query_storage(
 		&self,
 		from: Block::Hash,
 		to: Option<Block::Hash>,
 		keys: Vec<StorageKey>,
-	) -> FutureResult<Vec<StorageChangeSet<Block::Hash>>>;
+	) -> Result<Vec<StorageChangeSet<Block::Hash>>, Error>;
 
 	/// Query storage entries (by key) starting at block hash given as the second parameter.
-	fn query_storage_at(
+	async fn query_storage_at(
 		&self,
 		keys: Vec<StorageKey>,
 		at: Option<Block::Hash>,
-	) -> FutureResult<Vec<StorageChangeSet<Block::Hash>>>;
+	) -> Result<Vec<StorageChangeSet<Block::Hash>>, Error>;
 
 	/// Returns proof of storage entries at a specific block's state.
-	fn read_proof(
+	async fn read_proof(
 		&self,
 		block: Option<Block::Hash>,
 		keys: Vec<StorageKey>,
-	) -> FutureResult<ReadProof<Block::Hash>>;
-
-	/// New runtime version subscription
-	fn subscribe_runtime_version(
-		&self,
-		_meta: crate::Metadata,
-		subscriber: Subscriber<RuntimeVersion>,
-	);
-
-	/// Unsubscribe from runtime version subscription
-	fn unsubscribe_runtime_version(
-		&self,
-		_meta: Option<crate::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool>;
-
-	/// New storage subscription
-	fn subscribe_storage(
-		&self,
-		_meta: crate::Metadata,
-		subscriber: Subscriber<StorageChangeSet<Block::Hash>>,
-		keys: Option<Vec<StorageKey>>,
-	);
-
-	/// Unsubscribe from storage subscription
-	fn unsubscribe_storage(
-		&self,
-		_meta: Option<crate::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool>;
+	) -> Result<ReadProof<Block::Hash>, Error>;
 
 	/// Trace storage changes for block
-	fn trace_block(
+	async fn trace_block(
 		&self,
 		block: Block::Hash,
 		targets: Option<String>,
 		storage_keys: Option<String>,
 		methods: Option<String>,
-	) -> FutureResult<sp_rpc::tracing::TraceBlockResponse>;
+	) -> Result<sp_rpc::tracing::TraceBlockResponse, Error>;
+
+	/// New runtime version subscription
+	fn subscribe_runtime_version(&self, sink: PendingSubscription);
+
+	/// New storage subscription
+	fn subscribe_storage(&self, sink: PendingSubscription, keys: Option<Vec<StorageKey>>);
 }
 
 /// Create new state API that works on full node.
 pub fn new_full<BE, Block: BlockT, Client>(
 	client: Arc<Client>,
-	subscriptions: SubscriptionManager,
+	executor: SubscriptionTaskExecutor,
 	deny_unsafe: DenyUnsafe,
 	rpc_max_payload: Option<usize>,
-) -> (State<Block, Client>, ChildState<Block, Client>)
+) -> (StateApi<Block, Client>, ChildState<Block, Client>)
 where
 	Block: BlockT + 'static,
 	Block::Hash: Unpin,
@@ -207,168 +189,127 @@ where
 {
 	let child_backend = Box::new(self::state_full::FullState::new(
 		client.clone(),
-		subscriptions.clone(),
+		executor.clone(),
 		rpc_max_payload,
 	));
-	let backend =
-		Box::new(self::state_full::FullState::new(client, subscriptions, rpc_max_payload));
-	(State { backend, deny_unsafe }, ChildState { backend: child_backend })
+	let backend = Box::new(self::state_full::FullState::new(client, executor, rpc_max_payload));
+	(StateApi { backend, deny_unsafe }, ChildState { backend: child_backend })
 }
 
 /// State API with subscriptions support.
-pub struct State<Block, Client> {
+pub struct StateApi<Block, Client> {
 	backend: Box<dyn StateBackend<Block, Client>>,
 	/// Whether to deny unsafe calls
 	deny_unsafe: DenyUnsafe,
 }
 
-impl<Block, Client> StateApi<Block::Hash> for State<Block, Client>
+#[async_trait]
+impl<Block, Client> StateApiServer<Block::Hash> for StateApi<Block, Client>
 where
 	Block: BlockT + 'static,
 	Client: Send + Sync + 'static,
 {
-	type Metadata = crate::Metadata;
-
-	fn call(&self, method: String, data: Bytes, block: Option<Block::Hash>) -> FutureResult<Bytes> {
-		self.backend.call(block, method, data)
+	async fn call(
+		&self,
+		method: String,
+		data: Bytes,
+		block: Option<Block::Hash>,
+	) -> RpcResult<Bytes> {
+		self.backend.call(block, method, data).await.map_err(Into::into)
 	}
 
-	fn storage_keys(
+	async fn storage_keys(
 		&self,
 		key_prefix: StorageKey,
 		block: Option<Block::Hash>,
-	) -> FutureResult<Vec<StorageKey>> {
-		self.backend.storage_keys(block, key_prefix)
+	) -> RpcResult<Vec<StorageKey>> {
+		self.backend.storage_keys(block, key_prefix).await.map_err(Into::into)
 	}
 
-	fn storage_pairs(
+	async fn storage_pairs(
 		&self,
 		key_prefix: StorageKey,
 		block: Option<Block::Hash>,
-	) -> FutureResult<Vec<(StorageKey, StorageData)>> {
-		if let Err(err) = self.deny_unsafe.check_if_safe() {
-			return async move { Err(err.into()) }.boxed()
-		}
-
-		self.backend.storage_pairs(block, key_prefix)
+	) -> RpcResult<Vec<(StorageKey, StorageData)>> {
+		self.deny_unsafe.check_if_safe()?;
+		self.backend.storage_pairs(block, key_prefix).await.map_err(Into::into)
 	}
 
-	fn storage_keys_paged(
+	async fn storage_keys_paged(
 		&self,
 		prefix: Option<StorageKey>,
 		count: u32,
 		start_key: Option<StorageKey>,
 		block: Option<Block::Hash>,
-	) -> FutureResult<Vec<StorageKey>> {
+	) -> RpcResult<Vec<StorageKey>> {
 		if count > STORAGE_KEYS_PAGED_MAX_COUNT {
-			return async move {
-				Err(Error::InvalidCount { value: count, max: STORAGE_KEYS_PAGED_MAX_COUNT })
-			}
-			.boxed()
+			return Err(JsonRpseeError::from(Error::InvalidCount {
+				value: count,
+				max: STORAGE_KEYS_PAGED_MAX_COUNT,
+			}))
 		}
-		self.backend.storage_keys_paged(block, prefix, count, start_key)
+		self.backend
+			.storage_keys_paged(block, prefix, count, start_key)
+			.await
+			.map_err(Into::into)
 	}
 
-	fn storage(
+	async fn storage(
 		&self,
 		key: StorageKey,
 		block: Option<Block::Hash>,
-	) -> FutureResult<Option<StorageData>> {
-		self.backend.storage(block, key)
+	) -> RpcResult<Option<StorageData>> {
+		self.backend.storage(block, key).await.map_err(Into::into)
 	}
 
-	fn storage_hash(
+	async fn storage_hash(
 		&self,
 		key: StorageKey,
 		block: Option<Block::Hash>,
-	) -> FutureResult<Option<Block::Hash>> {
-		self.backend.storage_hash(block, key)
+	) -> RpcResult<Option<Block::Hash>> {
+		self.backend.storage_hash(block, key).await.map_err(Into::into)
 	}
 
-	fn storage_size(
+	async fn storage_size(
 		&self,
 		key: StorageKey,
 		block: Option<Block::Hash>,
-	) -> FutureResult<Option<u64>> {
-		self.backend.storage_size(block, key)
+	) -> RpcResult<Option<u64>> {
+		self.backend.storage_size(block, key).await.map_err(Into::into)
+	}
+
+	async fn metadata(&self, block: Option<Block::Hash>) -> RpcResult<Bytes> {
+		self.backend.metadata(block).await.map_err(Into::into)
 	}
 
-	fn metadata(&self, block: Option<Block::Hash>) -> FutureResult<Bytes> {
-		self.backend.metadata(block)
+	async fn runtime_version(&self, at: Option<Block::Hash>) -> RpcResult<RuntimeVersion> {
+		self.backend.runtime_version(at).await.map_err(Into::into)
 	}
 
-	fn query_storage(
+	async fn query_storage(
 		&self,
 		keys: Vec<StorageKey>,
 		from: Block::Hash,
 		to: Option<Block::Hash>,
-	) -> FutureResult<Vec<StorageChangeSet<Block::Hash>>> {
-		if let Err(err) = self.deny_unsafe.check_if_safe() {
-			return async move { Err(err.into()) }.boxed()
-		}
-
-		self.backend.query_storage(from, to, keys)
+	) -> RpcResult<Vec<StorageChangeSet<Block::Hash>>> {
+		self.deny_unsafe.check_if_safe()?;
+		self.backend.query_storage(from, to, keys).await.map_err(Into::into)
 	}
 
-	fn query_storage_at(
+	async fn query_storage_at(
 		&self,
 		keys: Vec<StorageKey>,
 		at: Option<Block::Hash>,
-	) -> FutureResult<Vec<StorageChangeSet<Block::Hash>>> {
-		self.backend.query_storage_at(keys, at)
+	) -> RpcResult<Vec<StorageChangeSet<Block::Hash>>> {
+		self.backend.query_storage_at(keys, at).await.map_err(Into::into)
 	}
 
-	fn read_proof(
+	async fn read_proof(
 		&self,
 		keys: Vec<StorageKey>,
 		block: Option<Block::Hash>,
-	) -> FutureResult<ReadProof<Block::Hash>> {
-		self.backend.read_proof(block, keys)
-	}
-
-	fn subscribe_storage(
-		&self,
-		meta: Self::Metadata,
-		subscriber: Subscriber<StorageChangeSet<Block::Hash>>,
-		keys: Option<Vec<StorageKey>>,
-	) {
-		if keys.is_none() {
-			if let Err(err) = self.deny_unsafe.check_if_safe() {
-				subscriber.reject(err.into())
-				    .expect("subscription rejection can only fail if it's been already rejected, and we're rejecting it for the first time; qed");
-				return
-			}
-		}
-
-		self.backend.subscribe_storage(meta, subscriber, keys)
-	}
-
-	fn unsubscribe_storage(
-		&self,
-		meta: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool> {
-		self.backend.unsubscribe_storage(meta, id)
-	}
-
-	fn runtime_version(&self, at: Option<Block::Hash>) -> FutureResult<RuntimeVersion> {
-		self.backend.runtime_version(at)
-	}
-
-	fn subscribe_runtime_version(
-		&self,
-		meta: Self::Metadata,
-		subscriber: Subscriber<RuntimeVersion>,
-	) {
-		self.backend.subscribe_runtime_version(meta, subscriber);
-	}
-
-	fn unsubscribe_runtime_version(
-		&self,
-		meta: Option<Self::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool> {
-		self.backend.unsubscribe_runtime_version(meta, id)
+	) -> RpcResult<ReadProof<Block::Hash>> {
+		self.backend.read_proof(block, keys).await.map_err(Into::into)
 	}
 
 	/// Re-execute the given block with the tracing targets given in `targets`
@@ -376,88 +317,102 @@ where
 	///
 	/// Note: requires the node to run with `--rpc-methods=Unsafe`.
 	/// Note: requires runtimes compiled with wasm tracing support, `--features with-tracing`.
-	fn trace_block(
+	async fn trace_block(
 		&self,
 		block: Block::Hash,
 		targets: Option<String>,
 		storage_keys: Option<String>,
 		methods: Option<String>,
-	) -> FutureResult<sp_rpc::tracing::TraceBlockResponse> {
-		if let Err(err) = self.deny_unsafe.check_if_safe() {
-			return async move { Err(err.into()) }.boxed()
+	) -> RpcResult<sp_rpc::tracing::TraceBlockResponse> {
+		self.deny_unsafe.check_if_safe()?;
+		self.backend
+			.trace_block(block, targets, storage_keys, methods)
+			.await
+			.map_err(Into::into)
+	}
+
+	fn subscribe_runtime_version(&self, sink: PendingSubscription) {
+		self.backend.subscribe_runtime_version(sink)
+	}
+
+	fn subscribe_storage(&self, sink: PendingSubscription, keys: Option<Vec<StorageKey>>) {
+		if keys.is_none() {
+			if let Err(err) = self.deny_unsafe.check_if_safe() {
+				let _ = sink.reject(JsonRpseeError::from(err));
+				return
+			}
 		}
 
-		self.backend.trace_block(block, targets, storage_keys, methods)
+		self.backend.subscribe_storage(sink, keys)
 	}
 }
 
 /// Child state backend API.
+#[async_trait]
 pub trait ChildStateBackend<Block: BlockT, Client>: Send + Sync + 'static
 where
 	Block: BlockT + 'static,
 	Client: Send + Sync + 'static,
 {
 	/// Returns proof of storage for a child key entries at a specific block's state.
-	fn read_child_proof(
+	async fn read_child_proof(
 		&self,
 		block: Option<Block::Hash>,
 		storage_key: PrefixedStorageKey,
 		keys: Vec<StorageKey>,
-	) -> FutureResult<ReadProof<Block::Hash>>;
+	) -> Result<ReadProof<Block::Hash>, Error>;
 
 	/// Returns the keys with prefix from a child storage,
 	/// leave prefix empty to get all the keys.
-	fn storage_keys(
+	async fn storage_keys(
 		&self,
 		block: Option<Block::Hash>,
 		storage_key: PrefixedStorageKey,
 		prefix: StorageKey,
-	) -> FutureResult<Vec<StorageKey>>;
+	) -> Result<Vec<StorageKey>, Error>;
 
 	/// Returns the keys with prefix from a child storage with pagination support.
-	fn storage_keys_paged(
+	async fn storage_keys_paged(
 		&self,
 		block: Option<Block::Hash>,
 		storage_key: PrefixedStorageKey,
 		prefix: Option<StorageKey>,
 		count: u32,
 		start_key: Option<StorageKey>,
-	) -> FutureResult<Vec<StorageKey>>;
+	) -> Result<Vec<StorageKey>, Error>;
 
 	/// Returns a child storage entry at a specific block's state.
-	fn storage(
+	async fn storage(
 		&self,
 		block: Option<Block::Hash>,
 		storage_key: PrefixedStorageKey,
 		key: StorageKey,
-	) -> FutureResult<Option<StorageData>>;
+	) -> Result<Option<StorageData>, Error>;
 
 	/// Returns child storage entries at a specific block's state.
-	fn storage_entries(
+	async fn storage_entries(
 		&self,
 		block: Option<Block::Hash>,
 		storage_key: PrefixedStorageKey,
 		keys: Vec<StorageKey>,
-	) -> FutureResult<Vec<Option<StorageData>>>;
+	) -> Result<Vec<Option<StorageData>>, Error>;
 
 	/// Returns the hash of a child storage entry at a block's state.
-	fn storage_hash(
+	async fn storage_hash(
 		&self,
 		block: Option<Block::Hash>,
 		storage_key: PrefixedStorageKey,
 		key: StorageKey,
-	) -> FutureResult<Option<Block::Hash>>;
+	) -> Result<Option<Block::Hash>, Error>;
 
 	/// Returns the size of a child storage entry at a block's state.
-	fn storage_size(
+	async fn storage_size(
 		&self,
 		block: Option<Block::Hash>,
 		storage_key: PrefixedStorageKey,
 		key: StorageKey,
-	) -> FutureResult<Option<u64>> {
-		self.storage(block, storage_key, key)
-			.map(|x| x.map(|r| r.map(|v| v.0.len() as u64)))
-			.boxed()
+	) -> Result<Option<u64>, Error> {
+		self.storage(block, storage_key, key).await.map(|x| x.map(|x| x.0.len() as u64))
 	}
 }
 
@@ -466,76 +421,84 @@ pub struct ChildState<Block, Client> {
 	backend: Box<dyn ChildStateBackend<Block, Client>>,
 }
 
-impl<Block, Client> ChildStateApi<Block::Hash> for ChildState<Block, Client>
+#[async_trait]
+impl<Block, Client> ChildStateApiServer<Block::Hash> for ChildState<Block, Client>
 where
 	Block: BlockT + 'static,
 	Client: Send + Sync + 'static,
 {
-	type Metadata = crate::Metadata;
-
-	fn read_child_proof(
+	async fn storage_keys(
 		&self,
-		child_storage_key: PrefixedStorageKey,
-		keys: Vec<StorageKey>,
+		storage_key: PrefixedStorageKey,
+		key_prefix: StorageKey,
 		block: Option<Block::Hash>,
-	) -> FutureResult<ReadProof<Block::Hash>> {
-		self.backend.read_child_proof(block, child_storage_key, keys)
+	) -> RpcResult<Vec<StorageKey>> {
+		self.backend
+			.storage_keys(block, storage_key, key_prefix)
+			.await
+			.map_err(Into::into)
 	}
 
-	fn storage(
+	async fn storage_keys_paged(
 		&self,
 		storage_key: PrefixedStorageKey,
-		key: StorageKey,
+		prefix: Option<StorageKey>,
+		count: u32,
+		start_key: Option<StorageKey>,
 		block: Option<Block::Hash>,
-	) -> FutureResult<Option<StorageData>> {
-		self.backend.storage(block, storage_key, key)
+	) -> RpcResult<Vec<StorageKey>> {
+		self.backend
+			.storage_keys_paged(block, storage_key, prefix, count, start_key)
+			.await
+			.map_err(Into::into)
 	}
 
-	fn storage_entries(
+	async fn storage(
 		&self,
 		storage_key: PrefixedStorageKey,
-		keys: Vec<StorageKey>,
+		key: StorageKey,
 		block: Option<Block::Hash>,
-	) -> FutureResult<Vec<Option<StorageData>>> {
-		self.backend.storage_entries(block, storage_key, keys)
+	) -> RpcResult<Option<StorageData>> {
+		self.backend.storage(block, storage_key, key).await.map_err(Into::into)
 	}
 
-	fn storage_keys(
+	async fn storage_entries(
 		&self,
 		storage_key: PrefixedStorageKey,
-		key_prefix: StorageKey,
+		keys: Vec<StorageKey>,
 		block: Option<Block::Hash>,
-	) -> FutureResult<Vec<StorageKey>> {
-		self.backend.storage_keys(block, storage_key, key_prefix)
+	) -> RpcResult<Vec<Option<StorageData>>> {
+		self.backend.storage_entries(block, storage_key, keys).await.map_err(Into::into)
 	}
 
-	fn storage_keys_paged(
+	async fn storage_hash(
 		&self,
 		storage_key: PrefixedStorageKey,
-		prefix: Option<StorageKey>,
-		count: u32,
-		start_key: Option<StorageKey>,
+		key: StorageKey,
 		block: Option<Block::Hash>,
-	) -> FutureResult<Vec<StorageKey>> {
-		self.backend.storage_keys_paged(block, storage_key, prefix, count, start_key)
+	) -> RpcResult<Option<Block::Hash>> {
+		self.backend.storage_hash(block, storage_key, key).await.map_err(Into::into)
 	}
 
-	fn storage_hash(
+	async fn storage_size(
 		&self,
 		storage_key: PrefixedStorageKey,
 		key: StorageKey,
 		block: Option<Block::Hash>,
-	) -> FutureResult<Option<Block::Hash>> {
-		self.backend.storage_hash(block, storage_key, key)
+	) -> RpcResult<Option<u64>> {
+		self.backend.storage_size(block, storage_key, key).await.map_err(Into::into)
 	}
 
-	fn storage_size(
+	async fn read_child_proof(
 		&self,
-		storage_key: PrefixedStorageKey,
-		key: StorageKey,
+		child_storage_key: PrefixedStorageKey,
+		keys: Vec<StorageKey>,
 		block: Option<Block::Hash>,
-	) -> FutureResult<Option<u64>> {
-		self.backend.storage_size(block, storage_key, key)
+	) -> RpcResult<ReadProof<Block::Hash>> {
+		self.backend
+			.read_child_proof(block, child_storage_key, keys)
+			.await
+			.map_err(Into::into)
 	}
 }
 
diff --git a/substrate/client/rpc/src/state/state_full.rs b/substrate/client/rpc/src/state/state_full.rs
index 38f9b078d87a770bff835007a5e4f4a0b4699ec7..48165e912b03ae852d97c03c3a070cc6bf03aeef 100644
--- a/substrate/client/rpc/src/state/state_full.rs
+++ b/substrate/client/rpc/src/state/state_full.rs
@@ -18,17 +18,26 @@
 
 //! State API backend for full nodes.
 
-use futures::{
-	future,
-	future::{err, try_join_all},
-	stream, FutureExt, SinkExt, StreamExt,
+use std::{collections::HashMap, marker::PhantomData, sync::Arc};
+
+use super::{
+	client_err,
+	error::{Error, Result},
+	ChildStateBackend, StateBackend,
 };
-use jsonrpc_pubsub::{manager::SubscriptionManager, typed::Subscriber, SubscriptionId};
-use log::warn;
-use rpc::Result as RpcResult;
-use std::{collections::HashMap, sync::Arc};
+use crate::SubscriptionTaskExecutor;
 
+use futures::{future, stream, FutureExt, StreamExt};
+use jsonrpsee::{
+	core::{async_trait, Error as JsonRpseeError},
+	PendingSubscription,
+};
+use sc_client_api::{
+	Backend, BlockBackend, BlockchainEvents, CallExecutor, ExecutorProvider, ProofProvider,
+	StorageProvider,
+};
 use sc_rpc_api::state::ReadProof;
+use sp_api::{CallApiAt, Metadata, ProvideRuntimeApi};
 use sp_blockchain::{
 	CachedHeaderMetadata, Error as ClientError, HeaderBackend, HeaderMetadata,
 	Result as ClientResult,
@@ -42,19 +51,6 @@ use sp_core::{
 use sp_runtime::{generic::BlockId, traits::Block as BlockT};
 use sp_version::RuntimeVersion;
 
-use sp_api::{CallApiAt, Metadata, ProvideRuntimeApi};
-
-use super::{
-	client_err,
-	error::{Error, FutureResult, Result},
-	ChildStateBackend, StateBackend,
-};
-use sc_client_api::{
-	Backend, BlockBackend, BlockchainEvents, CallExecutor, ExecutorProvider, ProofProvider,
-	StorageNotification, StorageProvider,
-};
-use std::marker::PhantomData;
-
 /// Ranges to query in state_queryStorage.
 struct QueryStorageRange<Block: BlockT> {
 	/// Hashes of all the blocks in the range.
@@ -64,7 +60,7 @@ struct QueryStorageRange<Block: BlockT> {
 /// State API backend for full nodes.
 pub struct FullState<BE, Block: BlockT, Client> {
 	client: Arc<Client>,
-	subscriptions: SubscriptionManager,
+	executor: SubscriptionTaskExecutor,
 	_phantom: PhantomData<(BE, Block)>,
 	rpc_max_payload: Option<usize>,
 }
@@ -81,10 +77,10 @@ where
 	/// Create new state API backend for full nodes.
 	pub fn new(
 		client: Arc<Client>,
-		subscriptions: SubscriptionManager,
+		executor: SubscriptionTaskExecutor,
 		rpc_max_payload: Option<usize>,
 	) -> Self {
-		Self { client, subscriptions, _phantom: PhantomData, rpc_max_payload }
+		Self { client, executor, _phantom: PhantomData, rpc_max_payload }
 	}
 
 	/// Returns given block hash or best block hash if None is passed.
@@ -174,6 +170,7 @@ where
 	}
 }
 
+#[async_trait]
 impl<BE, Block, Client> StateBackend<Block, Client> for FullState<BE, Block, Client>
 where
 	Block: BlockT + 'static,
@@ -193,14 +190,13 @@ where
 		+ 'static,
 	Client::Api: Metadata<Block>,
 {
-	fn call(
+	async fn call(
 		&self,
 		block: Option<Block::Hash>,
 		method: String,
 		call_data: Bytes,
-	) -> FutureResult<Bytes> {
-		let r = self
-			.block_or_best(block)
+	) -> std::result::Result<Bytes, Error> {
+		self.block_or_best(block)
 			.and_then(|block| {
 				self.client
 					.executor()
@@ -213,43 +209,37 @@ where
 					)
 					.map(Into::into)
 			})
-			.map_err(client_err);
-		async move { r }.boxed()
+			.map_err(client_err)
 	}
 
-	fn storage_keys(
+	async fn storage_keys(
 		&self,
 		block: Option<Block::Hash>,
 		prefix: StorageKey,
-	) -> FutureResult<Vec<StorageKey>> {
-		let r = self
-			.block_or_best(block)
+	) -> std::result::Result<Vec<StorageKey>, Error> {
+		self.block_or_best(block)
 			.and_then(|block| self.client.storage_keys(&BlockId::Hash(block), &prefix))
-			.map_err(client_err);
-		async move { r }.boxed()
+			.map_err(client_err)
 	}
 
-	fn storage_pairs(
+	async fn storage_pairs(
 		&self,
 		block: Option<Block::Hash>,
 		prefix: StorageKey,
-	) -> FutureResult<Vec<(StorageKey, StorageData)>> {
-		let r = self
-			.block_or_best(block)
+	) -> std::result::Result<Vec<(StorageKey, StorageData)>, Error> {
+		self.block_or_best(block)
 			.and_then(|block| self.client.storage_pairs(&BlockId::Hash(block), &prefix))
-			.map_err(client_err);
-		async move { r }.boxed()
+			.map_err(client_err)
 	}
 
-	fn storage_keys_paged(
+	async fn storage_keys_paged(
 		&self,
 		block: Option<Block::Hash>,
 		prefix: Option<StorageKey>,
 		count: u32,
 		start_key: Option<StorageKey>,
-	) -> FutureResult<Vec<StorageKey>> {
-		let r = self
-			.block_or_best(block)
+	) -> std::result::Result<Vec<StorageKey>, Error> {
+		self.block_or_best(block)
 			.and_then(|block| {
 				self.client.storage_keys_iter(
 					&BlockId::Hash(block),
@@ -258,40 +248,36 @@ where
 				)
 			})
 			.map(|iter| iter.take(count as usize).collect())
-			.map_err(client_err);
-		async move { r }.boxed()
+			.map_err(client_err)
 	}
 
-	fn storage(
+	async fn storage(
 		&self,
 		block: Option<Block::Hash>,
 		key: StorageKey,
-	) -> FutureResult<Option<StorageData>> {
-		let r = self
-			.block_or_best(block)
+	) -> std::result::Result<Option<StorageData>, Error> {
+		self.block_or_best(block)
 			.and_then(|block| self.client.storage(&BlockId::Hash(block), &key))
-			.map_err(client_err);
-		async move { r }.boxed()
+			.map_err(client_err)
 	}
 
-	fn storage_size(
+	async fn storage_size(
 		&self,
 		block: Option<Block::Hash>,
 		key: StorageKey,
-	) -> FutureResult<Option<u64>> {
+	) -> std::result::Result<Option<u64>, Error> {
 		let block = match self.block_or_best(block) {
 			Ok(b) => b,
-			Err(e) => return async move { Err(client_err(e)) }.boxed(),
+			Err(e) => return Err(client_err(e)),
 		};
 
 		match self.client.storage(&BlockId::Hash(block), &key) {
-			Ok(Some(d)) => return async move { Ok(Some(d.0.len() as u64)) }.boxed(),
-			Err(e) => return async move { Err(client_err(e)) }.boxed(),
+			Ok(Some(d)) => return Ok(Some(d.0.len() as u64)),
+			Err(e) => return Err(client_err(e)),
 			Ok(None) => {},
 		}
 
-		let r = self
-			.client
+		self.client
 			.storage_pairs(&BlockId::Hash(block), &key)
 			.map(|kv| {
 				let item_sum = kv.iter().map(|(_, v)| v.0.len() as u64).sum::<u64>();
@@ -301,48 +287,46 @@ where
 					None
 				}
 			})
-			.map_err(client_err);
-		async move { r }.boxed()
+			.map_err(client_err)
 	}
 
-	fn storage_hash(
+	async fn storage_hash(
 		&self,
 		block: Option<Block::Hash>,
 		key: StorageKey,
-	) -> FutureResult<Option<Block::Hash>> {
-		let r = self
-			.block_or_best(block)
+	) -> std::result::Result<Option<Block::Hash>, Error> {
+		self.block_or_best(block)
 			.and_then(|block| self.client.storage_hash(&BlockId::Hash(block), &key))
-			.map_err(client_err);
-		async move { r }.boxed()
+			.map_err(client_err)
 	}
 
-	fn metadata(&self, block: Option<Block::Hash>) -> FutureResult<Bytes> {
-		let r = self.block_or_best(block).map_err(client_err).and_then(|block| {
+	async fn metadata(&self, block: Option<Block::Hash>) -> std::result::Result<Bytes, Error> {
+		self.block_or_best(block).map_err(client_err).and_then(|block| {
 			self.client
 				.runtime_api()
 				.metadata(&BlockId::Hash(block))
 				.map(Into::into)
 				.map_err(|e| Error::Client(Box::new(e)))
-		});
-		async move { r }.boxed()
+		})
 	}
 
-	fn runtime_version(&self, block: Option<Block::Hash>) -> FutureResult<RuntimeVersion> {
-		let r = self.block_or_best(block).map_err(client_err).and_then(|block| {
+	async fn runtime_version(
+		&self,
+		block: Option<Block::Hash>,
+	) -> std::result::Result<RuntimeVersion, Error> {
+		self.block_or_best(block).map_err(client_err).and_then(|block| {
 			self.client
 				.runtime_version_at(&BlockId::Hash(block))
 				.map_err(|e| Error::Client(Box::new(e)))
-		});
-		async move { r }.boxed()
+		})
 	}
 
-	fn query_storage(
+	async fn query_storage(
 		&self,
 		from: Block::Hash,
 		to: Option<Block::Hash>,
 		keys: Vec<StorageKey>,
-	) -> FutureResult<Vec<StorageChangeSet<Block::Hash>>> {
+	) -> std::result::Result<Vec<StorageChangeSet<Block::Hash>>, Error> {
 		let call_fn = move || {
 			let range = self.query_storage_range(from, to)?;
 			let mut changes = Vec::new();
@@ -350,168 +334,151 @@ where
 			self.query_storage_unfiltered(&range, &keys, &mut last_values, &mut changes)?;
 			Ok(changes)
 		};
-
-		let r = call_fn();
-		async move { r }.boxed()
+		call_fn()
 	}
 
-	fn query_storage_at(
+	async fn query_storage_at(
 		&self,
 		keys: Vec<StorageKey>,
 		at: Option<Block::Hash>,
-	) -> FutureResult<Vec<StorageChangeSet<Block::Hash>>> {
+	) -> std::result::Result<Vec<StorageChangeSet<Block::Hash>>, Error> {
 		let at = at.unwrap_or_else(|| self.client.info().best_hash);
-		self.query_storage(at, Some(at), keys)
+		self.query_storage(at, Some(at), keys).await
 	}
 
-	fn read_proof(
+	async fn read_proof(
 		&self,
 		block: Option<Block::Hash>,
 		keys: Vec<StorageKey>,
-	) -> FutureResult<ReadProof<Block::Hash>> {
-		let r = self
-			.block_or_best(block)
+	) -> std::result::Result<ReadProof<Block::Hash>, Error> {
+		self.block_or_best(block)
 			.and_then(|block| {
 				self.client
 					.read_proof(&BlockId::Hash(block), &mut keys.iter().map(|key| key.0.as_ref()))
 					.map(|proof| proof.iter_nodes().map(|node| node.into()).collect())
 					.map(|proof| ReadProof { at: block, proof })
 			})
-			.map_err(client_err);
-		async move { r }.boxed()
+			.map_err(client_err)
 	}
 
-	fn subscribe_runtime_version(
-		&self,
-		_meta: crate::Metadata,
-		subscriber: Subscriber<RuntimeVersion>,
-	) {
-		self.subscriptions.add(subscriber, |sink| {
-			let version = self
-				.block_or_best(None)
-				.and_then(|block| {
-					self.client.runtime_version_at(&BlockId::Hash(block)).map_err(Into::into)
-				})
-				.map_err(client_err)
-				.map_err(Into::into);
+	fn subscribe_runtime_version(&self, pending: PendingSubscription) {
+		let client = self.client.clone();
 
-			let client = self.client.clone();
-			let mut previous_version = version.clone();
+		let initial = match self
+			.block_or_best(None)
+			.and_then(|block| {
+				self.client.runtime_version_at(&BlockId::Hash(block)).map_err(Into::into)
+			})
+			.map_err(|e| Error::Client(Box::new(e)))
+		{
+			Ok(initial) => initial,
+			Err(e) => {
+				pending.reject(JsonRpseeError::from(e));
+				return
+			},
+		};
 
-			// A stream of all best blocks.
-			let stream =
-				client.import_notification_stream().filter(|n| future::ready(n.is_new_best));
+		let mut previous_version = initial.clone();
 
-			let stream = stream.filter_map(move |n| {
+		// A stream of new versions
+		let version_stream = client
+			.import_notification_stream()
+			.filter(|n| future::ready(n.is_new_best))
+			.filter_map(move |n| {
 				let version = client
 					.runtime_version_at(&BlockId::hash(n.hash))
-					.map_err(|e| Error::Client(Box::new(e)))
-					.map_err(Into::into);
-
-				if previous_version != version {
-					previous_version = version.clone();
-					future::ready(Some(Ok::<_, ()>(version)))
-				} else {
-					future::ready(None)
+					.map_err(|e| Error::Client(Box::new(e)));
+
+				match version {
+					Ok(version) if version != previous_version => {
+						previous_version = version.clone();
+						future::ready(Some(version))
+					},
+					_ => future::ready(None),
 				}
 			});
 
-			stream::iter(vec![Ok(version)])
-				.chain(stream)
-				.forward(sink.sink_map_err(|e| warn!("Error sending notifications: {:?}", e)))
-				// we ignore the resulting Stream (if the first stream is over we are unsubscribed)
-				.map(|_| ())
-		});
-	}
+		let stream = futures::stream::once(future::ready(initial)).chain(version_stream);
 
-	fn unsubscribe_runtime_version(
-		&self,
-		_meta: Option<crate::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool> {
-		Ok(self.subscriptions.cancel(id))
+		let fut = async move {
+			if let Some(mut sink) = pending.accept() {
+				sink.pipe_from_stream(stream).await;
+			}
+		}
+		.boxed();
+
+		self.executor
+			.spawn("substrate-rpc-subscription", Some("rpc"), fut.map(drop).boxed());
 	}
 
-	fn subscribe_storage(
-		&self,
-		_meta: crate::Metadata,
-		subscriber: Subscriber<StorageChangeSet<Block::Hash>>,
-		keys: Option<Vec<StorageKey>>,
-	) {
-		let keys = Into::<Option<Vec<_>>>::into(keys);
+	fn subscribe_storage(&self, pending: PendingSubscription, keys: Option<Vec<StorageKey>>) {
 		let stream = match self.client.storage_changes_notification_stream(keys.as_deref(), None) {
 			Ok(stream) => stream,
-			Err(err) => {
-				let _ = subscriber.reject(client_err(err).into());
+			Err(blockchain_err) => {
+				pending.reject(JsonRpseeError::from(Error::Client(Box::new(blockchain_err))));
 				return
 			},
 		};
 
 		// initial values
-		let initial = stream::iter(
-			keys.map(|keys| {
-				let block = self.client.info().best_hash;
-				let changes = keys
-					.into_iter()
-					.map(|key| {
-						let v = self.client.storage(&BlockId::Hash(block), &key).ok().flatten();
-						(key, v)
-					})
-					.collect();
-				vec![Ok(Ok(StorageChangeSet { block, changes }))]
-			})
-			.unwrap_or_default(),
-		);
-
-		self.subscriptions.add(subscriber, |sink| {
-			let stream = stream.map(|StorageNotification { block, changes }| {
-				Ok(Ok::<_, rpc::Error>(StorageChangeSet {
-					block,
-					changes: changes
-						.iter()
-						.filter_map(|(o_sk, k, v)| o_sk.is_none().then(|| (k.clone(), v.cloned())))
-						.collect(),
-				}))
-			});
-
-			initial
-				.chain(stream)
-				.forward(sink.sink_map_err(|e| warn!("Error sending notifications: {:?}", e)))
-				// we ignore the resulting Stream (if the first stream is over we are unsubscribed)
-				.map(|_| ())
+		let initial = stream::iter(keys.map(|keys| {
+			let block = self.client.info().best_hash;
+			let changes = keys
+				.into_iter()
+				.map(|key| {
+					let v = self.client.storage(&BlockId::Hash(block), &key).ok().flatten();
+					(key, v)
+				})
+				.collect();
+			StorageChangeSet { block, changes }
+		}));
+
+		// let storage_stream = stream.map(|(block, changes)| StorageChangeSet {
+		let storage_stream = stream.map(|storage_notif| StorageChangeSet {
+			block: storage_notif.block,
+			changes: storage_notif
+				.changes
+				.iter()
+				.filter_map(|(o_sk, k, v)| o_sk.is_none().then(|| (k.clone(), v.cloned())))
+				.collect(),
 		});
-	}
 
-	fn unsubscribe_storage(
-		&self,
-		_meta: Option<crate::Metadata>,
-		id: SubscriptionId,
-	) -> RpcResult<bool> {
-		Ok(self.subscriptions.cancel(id))
+		let stream = initial
+			.chain(storage_stream)
+			.filter(|storage| future::ready(!storage.changes.is_empty()));
+
+		let fut = async move {
+			if let Some(mut sink) = pending.accept() {
+				sink.pipe_from_stream(stream).await;
+			}
+		}
+		.boxed();
+
+		self.executor
+			.spawn("substrate-rpc-subscription", Some("rpc"), fut.map(drop).boxed());
 	}
 
-	fn trace_block(
+	async fn trace_block(
 		&self,
 		block: Block::Hash,
 		targets: Option<String>,
 		storage_keys: Option<String>,
 		methods: Option<String>,
-	) -> FutureResult<sp_rpc::tracing::TraceBlockResponse> {
-		let block_executor = sc_tracing::block::BlockExecutor::new(
+	) -> std::result::Result<sp_rpc::tracing::TraceBlockResponse, Error> {
+		sc_tracing::block::BlockExecutor::new(
 			self.client.clone(),
 			block,
 			targets,
 			storage_keys,
 			methods,
 			self.rpc_max_payload,
-		);
-		let r = block_executor
-			.trace_block()
-			.map_err(|e| invalid_block::<Block>(block, None, e.to_string()));
-		async move { r }.boxed()
+		)
+		.trace_block()
+		.map_err(|e| invalid_block::<Block>(block, None, e.to_string()))
 	}
 }
 
+#[async_trait]
 impl<BE, Block, Client> ChildStateBackend<Block, Client> for FullState<BE, Block, Client>
 where
 	Block: BlockT + 'static,
@@ -530,14 +497,13 @@ where
 		+ 'static,
 	Client::Api: Metadata<Block>,
 {
-	fn read_child_proof(
+	async fn read_child_proof(
 		&self,
 		block: Option<Block::Hash>,
 		storage_key: PrefixedStorageKey,
 		keys: Vec<StorageKey>,
-	) -> FutureResult<ReadProof<Block::Hash>> {
-		let r = self
-			.block_or_best(block)
+	) -> std::result::Result<ReadProof<Block::Hash>, Error> {
+		self.block_or_best(block)
 			.and_then(|block| {
 				let child_info = match ChildType::from_prefixed_key(&storage_key) {
 					Some((ChildType::ParentKeyId, storage_key)) =>
@@ -553,19 +519,16 @@ where
 					.map(|proof| proof.iter_nodes().map(|node| node.into()).collect())
 					.map(|proof| ReadProof { at: block, proof })
 			})
-			.map_err(client_err);
-
-		async move { r }.boxed()
+			.map_err(client_err)
 	}
 
-	fn storage_keys(
+	async fn storage_keys(
 		&self,
 		block: Option<Block::Hash>,
 		storage_key: PrefixedStorageKey,
 		prefix: StorageKey,
-	) -> FutureResult<Vec<StorageKey>> {
-		let r = self
-			.block_or_best(block)
+	) -> std::result::Result<Vec<StorageKey>, Error> {
+		self.block_or_best(block)
 			.and_then(|block| {
 				let child_info = match ChildType::from_prefixed_key(&storage_key) {
 					Some((ChildType::ParentKeyId, storage_key)) =>
@@ -574,21 +537,18 @@ where
 				};
 				self.client.child_storage_keys(&BlockId::Hash(block), &child_info, &prefix)
 			})
-			.map_err(client_err);
-
-		async move { r }.boxed()
+			.map_err(client_err)
 	}
 
-	fn storage_keys_paged(
+	async fn storage_keys_paged(
 		&self,
 		block: Option<Block::Hash>,
 		storage_key: PrefixedStorageKey,
 		prefix: Option<StorageKey>,
 		count: u32,
 		start_key: Option<StorageKey>,
-	) -> FutureResult<Vec<StorageKey>> {
-		let r = self
-			.block_or_best(block)
+	) -> std::result::Result<Vec<StorageKey>, Error> {
+		self.block_or_best(block)
 			.and_then(|block| {
 				let child_info = match ChildType::from_prefixed_key(&storage_key) {
 					Some((ChildType::ParentKeyId, storage_key)) =>
@@ -603,19 +563,16 @@ where
 				)
 			})
 			.map(|iter| iter.take(count as usize).collect())
-			.map_err(client_err);
-
-		async move { r }.boxed()
+			.map_err(client_err)
 	}
 
-	fn storage(
+	async fn storage(
 		&self,
 		block: Option<Block::Hash>,
 		storage_key: PrefixedStorageKey,
 		key: StorageKey,
-	) -> FutureResult<Option<StorageData>> {
-		let r = self
-			.block_or_best(block)
+	) -> std::result::Result<Option<StorageData>, Error> {
+		self.block_or_best(block)
 			.and_then(|block| {
 				let child_info = match ChildType::from_prefixed_key(&storage_key) {
 					Some((ChildType::ParentKeyId, storage_key)) =>
@@ -624,28 +581,25 @@ where
 				};
 				self.client.child_storage(&BlockId::Hash(block), &child_info, &key)
 			})
-			.map_err(client_err);
-
-		async move { r }.boxed()
+			.map_err(client_err)
 	}
 
-	fn storage_entries(
+	async fn storage_entries(
 		&self,
 		block: Option<Block::Hash>,
 		storage_key: PrefixedStorageKey,
 		keys: Vec<StorageKey>,
-	) -> FutureResult<Vec<Option<StorageData>>> {
-		let child_info = match ChildType::from_prefixed_key(&storage_key) {
-			Some((ChildType::ParentKeyId, storage_key)) =>
-				Arc::new(ChildInfo::new_default(storage_key)),
-			None => return err(client_err(sp_blockchain::Error::InvalidChildStorageKey)).boxed(),
-		};
-		let block = match self.block_or_best(block) {
-			Ok(b) => b,
-			Err(e) => return err(client_err(e)).boxed(),
+	) -> std::result::Result<Vec<Option<StorageData>>, Error> {
+		let child_info = if let Some((ChildType::ParentKeyId, storage_key)) =
+			ChildType::from_prefixed_key(&storage_key)
+		{
+			Arc::new(ChildInfo::new_default(storage_key))
+		} else {
+			return Err(client_err(sp_blockchain::Error::InvalidChildStorageKey))
 		};
+		let block = self.block_or_best(block).map_err(client_err)?;
 		let client = self.client.clone();
-		try_join_all(keys.into_iter().map(move |key| {
+		future::try_join_all(keys.into_iter().map(move |key| {
 			let res = client
 				.clone()
 				.child_storage(&BlockId::Hash(block), &child_info, &key)
@@ -653,17 +607,16 @@ where
 
 			async move { res }
 		}))
-		.boxed()
+		.await
 	}
 
-	fn storage_hash(
+	async fn storage_hash(
 		&self,
 		block: Option<Block::Hash>,
 		storage_key: PrefixedStorageKey,
 		key: StorageKey,
-	) -> FutureResult<Option<Block::Hash>> {
-		let r = self
-			.block_or_best(block)
+	) -> std::result::Result<Option<Block::Hash>, Error> {
+		self.block_or_best(block)
 			.and_then(|block| {
 				let child_info = match ChildType::from_prefixed_key(&storage_key) {
 					Some((ChildType::ParentKeyId, storage_key)) =>
@@ -672,9 +625,7 @@ where
 				};
 				self.client.child_storage_hash(&BlockId::Hash(block), &child_info, &key)
 			})
-			.map_err(client_err);
-
-		async move { r }.boxed()
+			.map_err(client_err)
 	}
 }
 
diff --git a/substrate/client/rpc/src/state/tests.rs b/substrate/client/rpc/src/state/tests.rs
index 19e474ee6459a464e009a4a397d8cc7c2bbd4bd2..a375a30d2c1a2bf6b99026001e4cea7a147fbf19 100644
--- a/substrate/client/rpc/src/state/tests.rs
+++ b/substrate/client/rpc/src/state/tests.rs
@@ -18,9 +18,13 @@
 
 use self::error::Error;
 use super::*;
-use crate::testing::TaskExecutor;
+use crate::testing::{test_executor, timeout_secs};
 use assert_matches::assert_matches;
-use futures::{executor, StreamExt};
+use futures::executor;
+use jsonrpsee::{
+	core::Error as RpcError,
+	types::{error::CallError as RpcCallError, EmptyParams, ErrorObject},
+};
 use sc_block_builder::BlockBuilderProvider;
 use sc_rpc_api::DenyUnsafe;
 use sp_consensus::BlockOrigin;
@@ -36,8 +40,8 @@ fn prefixed_storage_key() -> PrefixedStorageKey {
 	child_info.prefixed_storage_key()
 }
 
-#[test]
-fn should_return_storage() {
+#[tokio::test]
+async fn should_return_storage() {
 	const KEY: &[u8] = b":mock";
 	const VALUE: &[u8] = b"hello world";
 	const CHILD_VALUE: &[u8] = b"hello world !";
@@ -51,49 +55,46 @@ fn should_return_storage() {
 		.add_extra_storage(b":map:acc2".to_vec(), vec![1, 2, 3])
 		.build();
 	let genesis_hash = client.genesis_hash();
-	let (client, child) = new_full(
-		Arc::new(client),
-		SubscriptionManager::new(Arc::new(TaskExecutor)),
-		DenyUnsafe::No,
-		None,
-	);
+	let (client, child) = new_full(Arc::new(client), test_executor(), DenyUnsafe::No, None);
 	let key = StorageKey(KEY.to_vec());
 
 	assert_eq!(
-		executor::block_on(client.storage(key.clone(), Some(genesis_hash).into()))
+		client
+			.storage(key.clone(), Some(genesis_hash).into())
+			.await
 			.map(|x| x.map(|x| x.0.len()))
 			.unwrap()
 			.unwrap() as usize,
 		VALUE.len(),
 	);
 	assert_matches!(
-		executor::block_on(client.storage_hash(key.clone(), Some(genesis_hash).into()))
+		client
+			.storage_hash(key.clone(), Some(genesis_hash).into())
+			.await
 			.map(|x| x.is_some()),
 		Ok(true)
 	);
 	assert_eq!(
-		executor::block_on(client.storage_size(key.clone(), None)).unwrap().unwrap() as usize,
+		client.storage_size(key.clone(), None).await.unwrap().unwrap() as usize,
 		VALUE.len(),
 	);
 	assert_eq!(
-		executor::block_on(client.storage_size(StorageKey(b":map".to_vec()), None))
-			.unwrap()
-			.unwrap() as usize,
+		client.storage_size(StorageKey(b":map".to_vec()), None).await.unwrap().unwrap() as usize,
 		2 + 3,
 	);
 	assert_eq!(
-		executor::block_on(
-			child
-				.storage(prefixed_storage_key(), key, Some(genesis_hash).into())
-				.map(|x| x.map(|x| x.unwrap().0.len()))
-		)
-		.unwrap() as usize,
+		child
+			.storage(prefixed_storage_key(), key, Some(genesis_hash).into())
+			.await
+			.map(|x| x.map(|x| x.0.len()))
+			.unwrap()
+			.unwrap() as usize,
 		CHILD_VALUE.len(),
 	);
 }
 
-#[test]
-fn should_return_storage_entries() {
+#[tokio::test]
+async fn should_return_storage_entries() {
 	const KEY1: &[u8] = b":mock";
 	const KEY2: &[u8] = b":turtle";
 	const VALUE: &[u8] = b"hello world";
@@ -107,22 +108,15 @@ fn should_return_storage_entries() {
 		.add_extra_child_storage(&child_info, KEY2.to_vec(), CHILD_VALUE2.to_vec())
 		.build();
 	let genesis_hash = client.genesis_hash();
-	let (_client, child) = new_full(
-		Arc::new(client),
-		SubscriptionManager::new(Arc::new(TaskExecutor)),
-		DenyUnsafe::No,
-		None,
-	);
+	let (_client, child) = new_full(Arc::new(client), test_executor(), DenyUnsafe::No, None);
 
 	let keys = &[StorageKey(KEY1.to_vec()), StorageKey(KEY2.to_vec())];
 	assert_eq!(
-		executor::block_on(child.storage_entries(
-			prefixed_storage_key(),
-			keys.to_vec(),
-			Some(genesis_hash).into()
-		))
-		.map(|x| x.into_iter().map(|x| x.map(|x| x.0.len()).unwrap()).sum::<usize>())
-		.unwrap(),
+		child
+			.storage_entries(prefixed_storage_key(), keys.to_vec(), Some(genesis_hash).into())
+			.await
+			.map(|x| x.into_iter().map(|x| x.map(|x| x.0.len()).unwrap()).sum::<usize>())
+			.unwrap(),
 		CHILD_VALUE1.len() + CHILD_VALUE2.len()
 	);
 
@@ -130,18 +124,16 @@ fn should_return_storage_entries() {
 	let mut failing_keys = vec![StorageKey(b":soup".to_vec())];
 	failing_keys.extend_from_slice(keys);
 	assert_matches!(
-		executor::block_on(child.storage_entries(
-			prefixed_storage_key(),
-			failing_keys,
-			Some(genesis_hash).into()
-		))
-		.map(|x| x.iter().all(|x| x.is_some())),
+		child
+			.storage_entries(prefixed_storage_key(), failing_keys, Some(genesis_hash).into())
+			.await
+			.map(|x| x.iter().all(|x| x.is_some())),
 		Ok(false)
 	);
 }
 
-#[test]
-fn should_return_child_storage() {
+#[tokio::test]
+async fn should_return_child_storage() {
 	let child_info = ChildInfo::new_default(STORAGE_KEY);
 	let client = Arc::new(
 		substrate_test_runtime_client::TestClientBuilder::new()
@@ -149,49 +141,30 @@ fn should_return_child_storage() {
 			.build(),
 	);
 	let genesis_hash = client.genesis_hash();
-	let (_client, child) =
-		new_full(client, SubscriptionManager::new(Arc::new(TaskExecutor)), DenyUnsafe::No, None);
+	let (_client, child) = new_full(client, test_executor(), DenyUnsafe::No, None);
 	let child_key = prefixed_storage_key();
 	let key = StorageKey(b"key".to_vec());
 
 	assert_matches!(
-		executor::block_on(child.storage(
+		child.storage(
 			child_key.clone(),
 			key.clone(),
 			Some(genesis_hash).into(),
-		)),
+		).await,
 		Ok(Some(StorageData(ref d))) if d[0] == 42 && d.len() == 1
 	);
-
-	// should fail if key does not exist.
-	let failing_key = StorageKey(b":soup".to_vec());
 	assert_matches!(
-		executor::block_on(child.storage(
-			prefixed_storage_key(),
-			failing_key,
-			Some(genesis_hash).into()
-		))
-		.map(|x| x.is_some()),
-		Ok(false)
-	);
-
-	assert_matches!(
-		executor::block_on(child.storage_hash(
-			child_key.clone(),
-			key.clone(),
-			Some(genesis_hash).into(),
-		))
-		.map(|x| x.is_some()),
+		child
+			.storage_hash(child_key.clone(), key.clone(), Some(genesis_hash).into(),)
+			.await
+			.map(|x| x.is_some()),
 		Ok(true)
 	);
-	assert_matches!(
-		executor::block_on(child.storage_size(child_key.clone(), key.clone(), None)),
-		Ok(Some(1))
-	);
+	assert_matches!(child.storage_size(child_key.clone(), key.clone(), None).await, Ok(Some(1)));
 }
 
-#[test]
-fn should_return_child_storage_entries() {
+#[tokio::test]
+async fn should_return_child_storage_entries() {
 	let child_info = ChildInfo::new_default(STORAGE_KEY);
 	let client = Arc::new(
 		substrate_test_runtime_client::TestClientBuilder::new()
@@ -200,17 +173,14 @@ fn should_return_child_storage_entries() {
 			.build(),
 	);
 	let genesis_hash = client.genesis_hash();
-	let (_client, child) =
-		new_full(client, SubscriptionManager::new(Arc::new(TaskExecutor)), DenyUnsafe::No, None);
+	let (_client, child) = new_full(client, test_executor(), DenyUnsafe::No, None);
 	let child_key = prefixed_storage_key();
 	let keys = vec![StorageKey(b"key1".to_vec()), StorageKey(b"key2".to_vec())];
 
-	let res = executor::block_on(child.storage_entries(
-		child_key.clone(),
-		keys.clone(),
-		Some(genesis_hash).into(),
-	))
-	.unwrap();
+	let res = child
+		.storage_entries(child_key.clone(), keys.clone(), Some(genesis_hash).into())
+		.await
+		.unwrap();
 
 	assert_matches!(
 		res[0],
@@ -232,46 +202,37 @@ fn should_return_child_storage_entries() {
 		Ok(true)
 	);
 	assert_matches!(
-		executor::block_on(child.storage_size(child_key.clone(), keys[0].clone(), None)),
+		child.storage_size(child_key.clone(), keys[0].clone(), None).await,
 		Ok(Some(1))
 	);
 }
 
-#[test]
-fn should_call_contract() {
+#[tokio::test]
+async fn should_call_contract() {
 	let client = Arc::new(substrate_test_runtime_client::new());
 	let genesis_hash = client.genesis_hash();
-	let (client, _child) =
-		new_full(client, SubscriptionManager::new(Arc::new(TaskExecutor)), DenyUnsafe::No, None);
+	let (client, _child) = new_full(client, test_executor(), DenyUnsafe::No, None);
+
+	use jsonrpsee::{core::Error, types::error::CallError};
 
 	assert_matches!(
-		executor::block_on(client.call(
-			"balanceOf".into(),
-			Bytes(vec![1, 2, 3]),
-			Some(genesis_hash).into()
-		)),
-		Err(Error::Client(_))
+		client
+			.call("balanceOf".into(), Bytes(vec![1, 2, 3]), Some(genesis_hash).into())
+			.await,
+		Err(Error::Call(CallError::Failed(_)))
 	)
 }
 
-#[test]
-fn should_notify_about_storage_changes() {
-	let (subscriber, id, mut transport) = Subscriber::new_test("test");
-
-	{
+#[tokio::test]
+async fn should_notify_about_storage_changes() {
+	let mut sub = {
 		let mut client = Arc::new(substrate_test_runtime_client::new());
-		let (api, _child) = new_full(
-			client.clone(),
-			SubscriptionManager::new(Arc::new(TaskExecutor)),
-			DenyUnsafe::No,
-			None,
-		);
-
-		api.subscribe_storage(Default::default(), subscriber, None.into());
+		let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No, None);
 
-		// assert id assigned
-		assert!(matches!(executor::block_on(id), Ok(Ok(SubscriptionId::String(_)))));
+		let api_rpc = api.into_rpc();
+		let sub = api_rpc.subscribe("state_subscribeStorage", EmptyParams::new()).await.unwrap();
 
+		// Cause a change:
 		let mut builder = client.new_block(Default::default()).unwrap();
 		builder
 			.push_transfer(runtime::Transfer {
@@ -282,38 +243,32 @@ fn should_notify_about_storage_changes() {
 			})
 			.unwrap();
 		let block = builder.build().unwrap().block;
-		executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
-	}
+		client.import(BlockOrigin::Own, block).await.unwrap();
 
-	// Check notification sent to transport
-	executor::block_on((&mut transport).take(2).collect::<Vec<_>>());
-	assert!(executor::block_on(transport.next()).is_none());
-}
+		sub
+	};
 
-#[test]
-fn should_send_initial_storage_changes_and_notifications() {
-	let (subscriber, id, mut transport) = Subscriber::new_test("test");
+	// We should get a message back on our subscription about the storage change:
+	// NOTE: previous versions of the subscription code used to return an empty value for the
+	// "initial" storage change here
+	assert_matches!(timeout_secs(1, sub.next::<StorageChangeSet<H256>>()).await, Ok(Some(_)));
+	assert_matches!(timeout_secs(1, sub.next::<StorageChangeSet<H256>>()).await, Ok(None));
+}
 
-	{
+#[tokio::test]
+async fn should_send_initial_storage_changes_and_notifications() {
+	let mut sub = {
 		let mut client = Arc::new(substrate_test_runtime_client::new());
-		let (api, _child) = new_full(
-			client.clone(),
-			SubscriptionManager::new(Arc::new(TaskExecutor)),
-			DenyUnsafe::No,
-			None,
-		);
+		let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No, None);
 
 		let alice_balance_key =
 			blake2_256(&runtime::system::balance_of_key(AccountKeyring::Alice.into()));
 
-		api.subscribe_storage(
-			Default::default(),
-			subscriber,
-			Some(vec![StorageKey(alice_balance_key.to_vec())]).into(),
-		);
-
-		// assert id assigned
-		assert!(matches!(executor::block_on(id), Ok(Ok(SubscriptionId::String(_)))));
+		let api_rpc = api.into_rpc();
+		let sub = api_rpc
+			.subscribe("state_subscribeStorage", [[StorageKey(alice_balance_key.to_vec())]])
+			.await
+			.unwrap();
 
 		let mut builder = client.new_block(Default::default()).unwrap();
 		builder
@@ -325,23 +280,22 @@ fn should_send_initial_storage_changes_and_notifications() {
 			})
 			.unwrap();
 		let block = builder.build().unwrap().block;
-		executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
-	}
+		client.import(BlockOrigin::Own, block).await.unwrap();
+
+		sub
+	};
+
+	assert_matches!(timeout_secs(1, sub.next::<StorageChangeSet<H256>>()).await, Ok(Some(_)));
+	assert_matches!(timeout_secs(1, sub.next::<StorageChangeSet<H256>>()).await, Ok(Some(_)));
 
-	// Check for the correct number of notifications
-	executor::block_on((&mut transport).take(2).collect::<Vec<_>>());
-	assert!(executor::block_on(transport.next()).is_none());
+	// No more messages to follow
+	assert_matches!(timeout_secs(1, sub.next::<StorageChangeSet<H256>>()).await, Ok(None));
 }
 
-#[test]
-fn should_query_storage() {
-	fn run_tests(mut client: Arc<TestClient>) {
-		let (api, _child) = new_full(
-			client.clone(),
-			SubscriptionManager::new(Arc::new(TaskExecutor)),
-			DenyUnsafe::No,
-			None,
-		);
+#[tokio::test]
+async fn should_query_storage() {
+	async fn run_tests(mut client: Arc<TestClient>) {
+		let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No, None);
 
 		let mut add_block = |nonce| {
 			let mut builder = client.new_block(Default::default()).unwrap();
@@ -393,7 +347,7 @@ fn should_query_storage() {
 		let keys = (1..6).map(|k| StorageKey(vec![k])).collect::<Vec<_>>();
 		let result = api.query_storage(keys.clone(), genesis_hash, Some(block1_hash).into());
 
-		assert_eq!(executor::block_on(result).unwrap(), expected);
+		assert_eq!(result.await.unwrap(), expected);
 
 		// Query all changes
 		let result = api.query_storage(keys.clone(), genesis_hash, None.into());
@@ -406,23 +360,28 @@ fn should_query_storage() {
 				(StorageKey(vec![5]), Some(StorageData(vec![1]))),
 			],
 		});
-		assert_eq!(executor::block_on(result).unwrap(), expected);
+		assert_eq!(result.await.unwrap(), expected);
 
 		// Query changes up to block2.
 		let result = api.query_storage(keys.clone(), genesis_hash, Some(block2_hash));
 
-		assert_eq!(executor::block_on(result).unwrap(), expected);
+		assert_eq!(result.await.unwrap(), expected);
 
 		// Inverted range.
 		let result = api.query_storage(keys.clone(), block1_hash, Some(genesis_hash));
 
 		assert_eq!(
-			executor::block_on(result).map_err(|e| e.to_string()),
-			Err(Error::InvalidBlockRange {
-				from: format!("1 ({:?})", block1_hash),
-				to: format!("0 ({:?})", genesis_hash),
-				details: "from number > to number".to_owned(),
-			})
+			result.await.map_err(|e| e.to_string()),
+			Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned(
+				4001,
+				Error::InvalidBlockRange {
+					from: format!("1 ({:?})", block1_hash),
+					to: format!("0 ({:?})", genesis_hash),
+					details: "from number > to number".to_owned(),
+				}
+				.to_string(),
+				None::<()>,
+			))))
 			.map_err(|e| e.to_string())
 		);
 
@@ -433,15 +392,20 @@ fn should_query_storage() {
 		let result = api.query_storage(keys.clone(), genesis_hash, Some(random_hash1));
 
 		assert_eq!(
-			executor::block_on(result).map_err(|e| e.to_string()),
-			Err(Error::InvalidBlockRange {
-				from: format!("{:?}", genesis_hash),
-				to: format!("{:?}", Some(random_hash1)),
-				details: format!(
-					"UnknownBlock: Header was not found in the database: {:?}",
-					random_hash1
-				),
-			})
+			result.await.map_err(|e| e.to_string()),
+			Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned(
+				4001,
+				Error::InvalidBlockRange {
+					from: format!("{:?}", genesis_hash),
+					to: format!("{:?}", Some(random_hash1)),
+					details: format!(
+						"UnknownBlock: Header was not found in the database: {:?}",
+						random_hash1
+					),
+				}
+				.to_string(),
+				None::<()>,
+			))))
 			.map_err(|e| e.to_string())
 		);
 
@@ -449,15 +413,20 @@ fn should_query_storage() {
 		let result = api.query_storage(keys.clone(), random_hash1, Some(genesis_hash));
 
 		assert_eq!(
-			executor::block_on(result).map_err(|e| e.to_string()),
-			Err(Error::InvalidBlockRange {
-				from: format!("{:?}", random_hash1),
-				to: format!("{:?}", Some(genesis_hash)),
-				details: format!(
-					"UnknownBlock: Header was not found in the database: {:?}",
-					random_hash1
-				),
-			})
+			result.await.map_err(|e| e.to_string()),
+			Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned(
+				4001,
+				Error::InvalidBlockRange {
+					from: format!("{:?}", random_hash1),
+					to: format!("{:?}", Some(genesis_hash)),
+					details: format!(
+						"UnknownBlock: Header was not found in the database: {:?}",
+						random_hash1
+					),
+				}
+				.to_string(),
+				None::<()>,
+			))))
 			.map_err(|e| e.to_string()),
 		);
 
@@ -465,15 +434,20 @@ fn should_query_storage() {
 		let result = api.query_storage(keys.clone(), random_hash1, None);
 
 		assert_eq!(
-			executor::block_on(result).map_err(|e| e.to_string()),
-			Err(Error::InvalidBlockRange {
-				from: format!("{:?}", random_hash1),
-				to: format!("{:?}", Some(block2_hash)), // Best block hash.
-				details: format!(
-					"UnknownBlock: Header was not found in the database: {:?}",
-					random_hash1
-				),
-			})
+			result.await.map_err(|e| e.to_string()),
+			Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned(
+				4001,
+				Error::InvalidBlockRange {
+					from: format!("{:?}", random_hash1),
+					to: format!("{:?}", Some(block2_hash)), // Best block hash.
+					details: format!(
+						"UnknownBlock: Header was not found in the database: {:?}",
+						random_hash1
+					),
+				}
+				.to_string(),
+				None::<()>,
+			))))
 			.map_err(|e| e.to_string()),
 		);
 
@@ -481,15 +455,20 @@ fn should_query_storage() {
 		let result = api.query_storage(keys.clone(), random_hash1, Some(random_hash2));
 
 		assert_eq!(
-			executor::block_on(result).map_err(|e| e.to_string()),
-			Err(Error::InvalidBlockRange {
-				from: format!("{:?}", random_hash1), // First hash not found.
-				to: format!("{:?}", Some(random_hash2)),
-				details: format!(
-					"UnknownBlock: Header was not found in the database: {:?}",
-					random_hash1
-				),
-			})
+			result.await.map_err(|e| e.to_string()),
+			Err(RpcError::Call(RpcCallError::Custom(ErrorObject::owned(
+				4001,
+				Error::InvalidBlockRange {
+					from: format!("{:?}", random_hash1), // First hash not found.
+					to: format!("{:?}", Some(random_hash2)),
+					details: format!(
+						"UnknownBlock: Header was not found in the database: {:?}",
+						random_hash1
+					),
+				}
+				.to_string(),
+				None::<()>
+			))))
 			.map_err(|e| e.to_string()),
 		);
 
@@ -497,7 +476,7 @@ fn should_query_storage() {
 		let result = api.query_storage_at(keys.clone(), Some(block1_hash));
 
 		assert_eq!(
-			executor::block_on(result).unwrap(),
+			result.await.unwrap(),
 			vec![StorageChangeSet {
 				block: block1_hash,
 				changes: vec![
@@ -511,19 +490,14 @@ fn should_query_storage() {
 		);
 	}
 
-	run_tests(Arc::new(substrate_test_runtime_client::new()));
-	run_tests(Arc::new(TestClientBuilder::new().build()));
+	run_tests(Arc::new(substrate_test_runtime_client::new())).await;
+	run_tests(Arc::new(TestClientBuilder::new().build())).await;
 }
 
-#[test]
-fn should_return_runtime_version() {
+#[tokio::test]
+async fn should_return_runtime_version() {
 	let client = Arc::new(substrate_test_runtime_client::new());
-	let (api, _child) = new_full(
-		client.clone(),
-		SubscriptionManager::new(Arc::new(TaskExecutor)),
-		DenyUnsafe::No,
-		None,
-	);
+	let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No, None);
 
 	let result = "{\"specName\":\"test\",\"implName\":\"parity-test\",\"authoringVersion\":1,\
 		\"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",4],\
@@ -532,7 +506,7 @@ fn should_return_runtime_version() {
 		[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],[\"0xbc9d89904f5b923f\",1]],\
 		\"transactionVersion\":1,\"stateVersion\":1}";
 
-	let runtime_version = executor::block_on(api.runtime_version(None.into())).unwrap();
+	let runtime_version = api.runtime_version(None.into()).await.unwrap();
 	let serialized = serde_json::to_string(&runtime_version).unwrap();
 	assert_eq!(serialized, result);
 
@@ -540,28 +514,26 @@ fn should_return_runtime_version() {
 	assert_eq!(deserialized, runtime_version);
 }
 
-#[test]
-fn should_notify_on_runtime_version_initially() {
-	let (subscriber, id, mut transport) = Subscriber::new_test("test");
-
-	{
+#[tokio::test]
+async fn should_notify_on_runtime_version_initially() {
+	let mut sub = {
 		let client = Arc::new(substrate_test_runtime_client::new());
-		let (api, _child) = new_full(
-			client.clone(),
-			SubscriptionManager::new(Arc::new(TaskExecutor)),
-			DenyUnsafe::No,
-			None,
-		);
+		let (api, _child) = new_full(client, test_executor(), DenyUnsafe::No, None);
 
-		api.subscribe_runtime_version(Default::default(), subscriber);
+		let api_rpc = api.into_rpc();
+		let sub = api_rpc
+			.subscribe("state_subscribeRuntimeVersion", EmptyParams::new())
+			.await
+			.unwrap();
 
-		// assert id assigned
-		assert!(matches!(executor::block_on(id), Ok(Ok(SubscriptionId::String(_)))));
-	}
+		sub
+	};
 
 	// assert initial version sent.
-	executor::block_on((&mut transport).take(1).collect::<Vec<_>>());
-	assert!(executor::block_on(transport.next()).is_none());
+	assert_matches!(timeout_secs(10, sub.next::<RuntimeVersion>()).await, Ok(Some(_)));
+
+	sub.close();
+	assert_matches!(timeout_secs(10, sub.next::<RuntimeVersion>()).await, Ok(None));
 }
 
 #[test]
@@ -572,38 +544,24 @@ fn should_deserialize_storage_key() {
 	assert_eq!(k.0.len(), 32);
 }
 
-#[test]
-fn wildcard_storage_subscriptions_are_rpc_unsafe() {
-	let (subscriber, id, _) = Subscriber::new_test("test");
-
+#[tokio::test]
+async fn wildcard_storage_subscriptions_are_rpc_unsafe() {
 	let client = Arc::new(substrate_test_runtime_client::new());
-	let (api, _child) = new_full(
-		client.clone(),
-		SubscriptionManager::new(Arc::new(TaskExecutor)),
-		DenyUnsafe::Yes,
-		None,
-	);
+	let (api, _child) = new_full(client, test_executor(), DenyUnsafe::Yes, None);
 
-	api.subscribe_storage(Default::default(), subscriber, None.into());
-
-	let error = executor::block_on(id).unwrap().unwrap_err();
-	assert_eq!(error.to_string(), "Method not found: RPC call is unsafe to be called externally");
+	let api_rpc = api.into_rpc();
+	let err = api_rpc.subscribe("state_subscribeStorage", EmptyParams::new()).await;
+	assert_matches!(err, Err(RpcError::Call(RpcCallError::Custom(e))) if e.message() == "RPC call is unsafe to be called externally");
 }
 
-#[test]
-fn concrete_storage_subscriptions_are_rpc_safe() {
-	let (subscriber, id, _) = Subscriber::new_test("test");
-
+#[tokio::test]
+async fn concrete_storage_subscriptions_are_rpc_safe() {
 	let client = Arc::new(substrate_test_runtime_client::new());
-	let (api, _child) = new_full(
-		client.clone(),
-		SubscriptionManager::new(Arc::new(TaskExecutor)),
-		DenyUnsafe::Yes,
-		None,
-	);
+	let (api, _child) = new_full(client, test_executor(), DenyUnsafe::Yes, None);
+	let api_rpc = api.into_rpc();
 
 	let key = StorageKey(STORAGE_KEY.to_vec());
-	api.subscribe_storage(Default::default(), subscriber, Some(vec![key]));
+	let sub = api_rpc.subscribe("state_subscribeStorage", [[key]]).await;
 
-	assert!(matches!(executor::block_on(id), Ok(Ok(SubscriptionId::String(_)))));
+	assert!(sub.is_ok());
 }
diff --git a/substrate/client/rpc/src/system/mod.rs b/substrate/client/rpc/src/system/mod.rs
index 534e446e140ad02962e996051a945f6fb754f4d6..ea24524cd2ea97f1c1d2757ff6e7f74b76107b32 100644
--- a/substrate/client/rpc/src/system/mod.rs
+++ b/substrate/client/rpc/src/system/mod.rs
@@ -18,30 +18,23 @@
 
 //! Substrate system API.
 
-use self::error::Result;
-use futures::{channel::oneshot, FutureExt};
-use sc_rpc_api::{DenyUnsafe, Receiver};
+#[cfg(test)]
+mod tests;
+
+use futures::channel::oneshot;
+use jsonrpsee::{
+	core::{async_trait, error::Error as JsonRpseeError, JsonValue, RpcResult},
+	types::error::{CallError, ErrorCode, ErrorObject},
+};
+use sc_rpc_api::DenyUnsafe;
 use sc_tracing::logging;
 use sc_utils::mpsc::TracingUnboundedSender;
 use sp_runtime::traits::{self, Header as HeaderT};
 
-pub use self::{
-	gen_client::Client as SystemClient,
-	helpers::{Health, NodeRole, PeerInfo, SyncState, SystemInfo},
-};
-pub use sc_rpc_api::system::*;
-
-#[cfg(test)]
-mod tests;
+use self::error::Result;
 
-/// Early exit for RPCs that require `--rpc-methods=Unsafe` to be enabled
-macro_rules! bail_if_unsafe {
-	($value: expr) => {
-		if let Err(err) = $value.check_if_safe() {
-			return async move { Err(err.into()) }.boxed()
-		}
-	};
-}
+pub use self::helpers::{Health, NodeRole, PeerInfo, SyncState, SystemInfo};
+pub use sc_rpc_api::system::*;
 
 /// System API implementation
 pub struct System<B: traits::Block> {
@@ -62,7 +55,7 @@ pub enum Request<B: traits::Block> {
 	/// Must return information about the peers we are connected to.
 	Peers(oneshot::Sender<Vec<PeerInfo<B::Hash, <B::Header as HeaderT>::Number>>>),
 	/// Must return the state of the network.
-	NetworkState(oneshot::Sender<rpc::Value>),
+	NetworkState(oneshot::Sender<serde_json::Value>),
 	/// Must return any potential parse error.
 	NetworkAddReservedPeer(String, oneshot::Sender<Result<()>>),
 	/// Must return any potential parse error.
@@ -89,121 +82,123 @@ impl<B: traits::Block> System<B> {
 	}
 }
 
-impl<B: traits::Block> SystemApi<B::Hash, <B::Header as HeaderT>::Number> for System<B> {
-	fn system_name(&self) -> Result<String> {
+#[async_trait]
+impl<B: traits::Block> SystemApiServer<B::Hash, <B::Header as HeaderT>::Number> for System<B> {
+	fn system_name(&self) -> RpcResult<String> {
 		Ok(self.info.impl_name.clone())
 	}
 
-	fn system_version(&self) -> Result<String> {
+	fn system_version(&self) -> RpcResult<String> {
 		Ok(self.info.impl_version.clone())
 	}
 
-	fn system_chain(&self) -> Result<String> {
+	fn system_chain(&self) -> RpcResult<String> {
 		Ok(self.info.chain_name.clone())
 	}
 
-	fn system_type(&self) -> Result<sc_chain_spec::ChainType> {
+	fn system_type(&self) -> RpcResult<sc_chain_spec::ChainType> {
 		Ok(self.info.chain_type.clone())
 	}
 
-	fn system_properties(&self) -> Result<sc_chain_spec::Properties> {
+	fn system_properties(&self) -> RpcResult<sc_chain_spec::Properties> {
 		Ok(self.info.properties.clone())
 	}
 
-	fn system_health(&self) -> Receiver<Health> {
+	async fn system_health(&self) -> RpcResult<Health> {
 		let (tx, rx) = oneshot::channel();
 		let _ = self.send_back.unbounded_send(Request::Health(tx));
-		Receiver(rx)
+		rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
 	}
 
-	fn system_local_peer_id(&self) -> Receiver<String> {
+	async fn system_local_peer_id(&self) -> RpcResult<String> {
 		let (tx, rx) = oneshot::channel();
 		let _ = self.send_back.unbounded_send(Request::LocalPeerId(tx));
-		Receiver(rx)
+		rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
 	}
 
-	fn system_local_listen_addresses(&self) -> Receiver<Vec<String>> {
+	async fn system_local_listen_addresses(&self) -> RpcResult<Vec<String>> {
 		let (tx, rx) = oneshot::channel();
 		let _ = self.send_back.unbounded_send(Request::LocalListenAddresses(tx));
-		Receiver(rx)
+		rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
 	}
 
-	fn system_peers(
+	async fn system_peers(
 		&self,
-	) -> rpc::BoxFuture<rpc::Result<Vec<PeerInfo<B::Hash, <B::Header as HeaderT>::Number>>>> {
-		bail_if_unsafe!(self.deny_unsafe);
-
+	) -> RpcResult<Vec<PeerInfo<B::Hash, <B::Header as HeaderT>::Number>>> {
+		self.deny_unsafe.check_if_safe()?;
 		let (tx, rx) = oneshot::channel();
 		let _ = self.send_back.unbounded_send(Request::Peers(tx));
-
-		async move { rx.await.map_err(|_| rpc::Error::internal_error()) }.boxed()
+		rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
 	}
 
-	fn system_network_state(&self) -> rpc::BoxFuture<rpc::Result<rpc::Value>> {
-		bail_if_unsafe!(self.deny_unsafe);
-
+	async fn system_network_state(&self) -> RpcResult<JsonValue> {
+		self.deny_unsafe.check_if_safe()?;
 		let (tx, rx) = oneshot::channel();
 		let _ = self.send_back.unbounded_send(Request::NetworkState(tx));
-
-		async move { rx.await.map_err(|_| rpc::Error::internal_error()) }.boxed()
+		rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
 	}
 
-	fn system_add_reserved_peer(&self, peer: String) -> rpc::BoxFuture<rpc::Result<()>> {
-		bail_if_unsafe!(self.deny_unsafe);
-
+	async fn system_add_reserved_peer(&self, peer: String) -> RpcResult<()> {
+		self.deny_unsafe.check_if_safe()?;
 		let (tx, rx) = oneshot::channel();
 		let _ = self.send_back.unbounded_send(Request::NetworkAddReservedPeer(peer, tx));
-		async move {
-			match rx.await {
-				Ok(Ok(())) => Ok(()),
-				Ok(Err(e)) => Err(rpc::Error::from(e)),
-				Err(_) => Err(rpc::Error::internal_error()),
-			}
+		match rx.await {
+			Ok(Ok(())) => Ok(()),
+			Ok(Err(e)) => Err(JsonRpseeError::from(e)),
+			Err(e) => Err(JsonRpseeError::to_call_error(e)),
 		}
-		.boxed()
 	}
 
-	fn system_remove_reserved_peer(&self, peer: String) -> rpc::BoxFuture<rpc::Result<()>> {
-		bail_if_unsafe!(self.deny_unsafe);
-
+	async fn system_remove_reserved_peer(&self, peer: String) -> RpcResult<()> {
+		self.deny_unsafe.check_if_safe()?;
 		let (tx, rx) = oneshot::channel();
 		let _ = self.send_back.unbounded_send(Request::NetworkRemoveReservedPeer(peer, tx));
-		async move {
-			match rx.await {
-				Ok(Ok(())) => Ok(()),
-				Ok(Err(e)) => Err(rpc::Error::from(e)),
-				Err(_) => Err(rpc::Error::internal_error()),
-			}
+		match rx.await {
+			Ok(Ok(())) => Ok(()),
+			Ok(Err(e)) => Err(JsonRpseeError::from(e)),
+			Err(e) => Err(JsonRpseeError::to_call_error(e)),
 		}
-		.boxed()
 	}
 
-	fn system_reserved_peers(&self) -> Receiver<Vec<String>> {
+	async fn system_reserved_peers(&self) -> RpcResult<Vec<String>> {
 		let (tx, rx) = oneshot::channel();
 		let _ = self.send_back.unbounded_send(Request::NetworkReservedPeers(tx));
-		Receiver(rx)
+		rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
 	}
 
-	fn system_node_roles(&self) -> Receiver<Vec<NodeRole>> {
+	async fn system_node_roles(&self) -> RpcResult<Vec<NodeRole>> {
 		let (tx, rx) = oneshot::channel();
 		let _ = self.send_back.unbounded_send(Request::NodeRoles(tx));
-		Receiver(rx)
+		rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
 	}
 
-	fn system_sync_state(&self) -> Receiver<SyncState<<B::Header as HeaderT>::Number>> {
+	async fn system_sync_state(&self) -> RpcResult<SyncState<<B::Header as HeaderT>::Number>> {
 		let (tx, rx) = oneshot::channel();
 		let _ = self.send_back.unbounded_send(Request::SyncState(tx));
-		Receiver(rx)
+		rx.await.map_err(|e| JsonRpseeError::to_call_error(e))
 	}
 
-	fn system_add_log_filter(&self, directives: String) -> rpc::Result<()> {
+	fn system_add_log_filter(&self, directives: String) -> RpcResult<()> {
 		self.deny_unsafe.check_if_safe()?;
+
 		logging::add_directives(&directives);
-		logging::reload_filter().map_err(|_e| rpc::Error::internal_error())
+		logging::reload_filter().map_err(|e| {
+			JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
+				ErrorCode::InternalError.code(),
+				e,
+				None::<()>,
+			)))
+		})
 	}
 
-	fn system_reset_log_filter(&self) -> rpc::Result<()> {
+	fn system_reset_log_filter(&self) -> RpcResult<()> {
 		self.deny_unsafe.check_if_safe()?;
-		logging::reset_log_filter().map_err(|_e| rpc::Error::internal_error())
+		logging::reset_log_filter().map_err(|e| {
+			JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
+				ErrorCode::InternalError.code(),
+				e,
+				None::<()>,
+			)))
+		})
 	}
 }
diff --git a/substrate/client/rpc/src/system/tests.rs b/substrate/client/rpc/src/system/tests.rs
index 5d6945b71420086f6980310fe84eba977ff256a8..77acdf8418ccc41190b4a65660620c12a37270fc 100644
--- a/substrate/client/rpc/src/system/tests.rs
+++ b/substrate/client/rpc/src/system/tests.rs
@@ -16,12 +16,18 @@
 // You should have received a copy of the GNU General Public License
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
-use super::*;
-
+use super::{helpers::SyncState, *};
 use assert_matches::assert_matches;
-use futures::{executor, prelude::*};
+use futures::prelude::*;
+use jsonrpsee::{
+	core::Error as RpcError,
+	types::{error::CallError, EmptyParams},
+	RpcModule,
+};
 use sc_network::{self, config::Role, PeerId};
+use sc_rpc_api::system::helpers::PeerInfo;
 use sc_utils::mpsc::tracing_unbounded;
+use sp_core::H256;
 use std::{
 	env,
 	io::{BufRead, BufReader, Write},
@@ -43,7 +49,7 @@ impl Default for Status {
 	}
 }
 
-fn api<T: Into<Option<Status>>>(sync: T) -> System<Block> {
+fn api<T: Into<Option<Status>>>(sync: T) -> RpcModule<System<Block>> {
 	let status = sync.into().unwrap_or_default();
 	let should_have_peers = !status.is_dev;
 	let (tx, rx) = tracing_unbounded("rpc_system_tests");
@@ -136,98 +142,122 @@ fn api<T: Into<Option<Status>>>(sync: T) -> System<Block> {
 		tx,
 		sc_rpc_api::DenyUnsafe::No,
 	)
+	.into_rpc()
 }
 
-fn wait_receiver<T>(rx: Receiver<T>) -> T {
-	futures::executor::block_on(rx).unwrap()
+#[tokio::test]
+async fn system_name_works() {
+	assert_eq!(
+		api(None).call::<_, String>("system_name", EmptyParams::new()).await.unwrap(),
+		"testclient".to_string(),
+	);
 }
 
-#[test]
-fn system_name_works() {
-	assert_eq!(api(None).system_name().unwrap(), "testclient".to_owned());
+#[tokio::test]
+async fn system_version_works() {
+	assert_eq!(
+		api(None).call::<_, String>("system_version", EmptyParams::new()).await.unwrap(),
+		"0.2.0".to_string(),
+	);
 }
 
-#[test]
-fn system_version_works() {
-	assert_eq!(api(None).system_version().unwrap(), "0.2.0".to_owned());
+#[tokio::test]
+async fn system_chain_works() {
+	assert_eq!(
+		api(None).call::<_, String>("system_chain", EmptyParams::new()).await.unwrap(),
+		"testchain".to_string(),
+	);
 }
 
-#[test]
-fn system_chain_works() {
-	assert_eq!(api(None).system_chain().unwrap(), "testchain".to_owned());
-}
+#[tokio::test]
+async fn system_properties_works() {
+	type Map = serde_json::map::Map<String, serde_json::Value>;
 
-#[test]
-fn system_properties_works() {
-	assert_eq!(api(None).system_properties().unwrap(), serde_json::map::Map::new());
+	assert_eq!(
+		api(None).call::<_, Map>("system_properties", EmptyParams::new()).await.unwrap(),
+		Map::new()
+	);
 }
 
-#[test]
-fn system_type_works() {
-	assert_eq!(api(None).system_type().unwrap(), Default::default());
+#[tokio::test]
+async fn system_type_works() {
+	assert_eq!(
+		api(None)
+			.call::<_, String>("system_chainType", EmptyParams::new())
+			.await
+			.unwrap(),
+		"Live".to_owned(),
+	);
 }
 
-#[test]
-fn system_health() {
-	assert_matches!(
-		wait_receiver(api(None).system_health()),
-		Health { peers: 0, is_syncing: false, should_have_peers: true }
+#[tokio::test]
+async fn system_health() {
+	assert_eq!(
+		api(None).call::<_, Health>("system_health", EmptyParams::new()).await.unwrap(),
+		Health { peers: 0, is_syncing: false, should_have_peers: true },
 	);
 
-	assert_matches!(
-		wait_receiver(
-			api(Status { peer_id: PeerId::random(), peers: 5, is_syncing: true, is_dev: true })
-				.system_health()
-		),
-		Health { peers: 5, is_syncing: true, should_have_peers: false }
+	assert_eq!(
+		api(Status { peer_id: PeerId::random(), peers: 5, is_syncing: true, is_dev: true })
+			.call::<_, Health>("system_health", EmptyParams::new())
+			.await
+			.unwrap(),
+		Health { peers: 5, is_syncing: true, should_have_peers: false },
 	);
 
 	assert_eq!(
-		wait_receiver(
-			api(Status { peer_id: PeerId::random(), peers: 5, is_syncing: false, is_dev: false })
-				.system_health()
-		),
-		Health { peers: 5, is_syncing: false, should_have_peers: true }
+		api(Status { peer_id: PeerId::random(), peers: 5, is_syncing: false, is_dev: false })
+			.call::<_, Health>("system_health", EmptyParams::new())
+			.await
+			.unwrap(),
+		Health { peers: 5, is_syncing: false, should_have_peers: true },
 	);
 
 	assert_eq!(
-		wait_receiver(
-			api(Status { peer_id: PeerId::random(), peers: 0, is_syncing: false, is_dev: true })
-				.system_health()
-		),
-		Health { peers: 0, is_syncing: false, should_have_peers: false }
+		api(Status { peer_id: PeerId::random(), peers: 0, is_syncing: false, is_dev: true })
+			.call::<_, Health>("system_health", EmptyParams::new())
+			.await
+			.unwrap(),
+		Health { peers: 0, is_syncing: false, should_have_peers: false },
 	);
 }
 
-#[test]
-fn system_local_peer_id_works() {
+#[tokio::test]
+async fn system_local_peer_id_works() {
 	assert_eq!(
-		wait_receiver(api(None).system_local_peer_id()),
-		"QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV".to_owned(),
+		api(None)
+			.call::<_, String>("system_localPeerId", EmptyParams::new())
+			.await
+			.unwrap(),
+		"QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV".to_owned()
 	);
 }
 
-#[test]
-fn system_local_listen_addresses_works() {
+#[tokio::test]
+async fn system_local_listen_addresses_works() {
 	assert_eq!(
-		wait_receiver(api(None).system_local_listen_addresses()),
+		api(None)
+			.call::<_, Vec<String>>("system_localListenAddresses", EmptyParams::new())
+			.await
+			.unwrap(),
 		vec![
-			"/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV"
-				.to_string(),
+			"/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV",
 			"/ip4/127.0.0.1/tcp/30334/ws/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV"
-				.to_string(),
 		]
 	);
 }
 
-#[test]
-fn system_peers() {
+#[tokio::test]
+async fn system_peers() {
 	let peer_id = PeerId::random();
-	let req = api(Status { peer_id, peers: 1, is_syncing: false, is_dev: true }).system_peers();
-	let res = executor::block_on(req).unwrap();
+	let peer_info: Vec<PeerInfo<H256, u64>> =
+		api(Status { peer_id, peers: 1, is_syncing: false, is_dev: true })
+			.call("system_peers", EmptyParams::new())
+			.await
+			.unwrap();
 
 	assert_eq!(
-		res,
+		peer_info,
 		vec![PeerInfo {
 			peer_id: peer_id.to_base58(),
 			roles: "FULL".into(),
@@ -237,14 +267,16 @@ fn system_peers() {
 	);
 }
 
-#[test]
-fn system_network_state() {
-	let req = api(None).system_network_state();
-	let res = executor::block_on(req).unwrap();
-
+#[tokio::test]
+async fn system_network_state() {
+	use sc_network::network_state::NetworkState;
+	let network_state: NetworkState = api(None)
+		.call("system_unstable_networkState", EmptyParams::new())
+		.await
+		.unwrap();
 	assert_eq!(
-		serde_json::from_value::<sc_network::network_state::NetworkState>(res).unwrap(),
-		sc_network::network_state::NetworkState {
+		network_state,
+		NetworkState {
 			peer_id: String::new(),
 			listened_addresses: Default::default(),
 			external_addresses: Default::default(),
@@ -255,50 +287,59 @@ fn system_network_state() {
 	);
 }
 
-#[test]
-fn system_node_roles() {
-	assert_eq!(wait_receiver(api(None).system_node_roles()), vec![NodeRole::Authority]);
+#[tokio::test]
+async fn system_node_roles() {
+	let node_roles: Vec<NodeRole> =
+		api(None).call("system_nodeRoles", EmptyParams::new()).await.unwrap();
+	assert_eq!(node_roles, vec![NodeRole::Authority]);
 }
-
-#[test]
-fn system_sync_state() {
+#[tokio::test]
+async fn system_sync_state() {
+	let sync_state: SyncState<i32> =
+		api(None).call("system_syncState", EmptyParams::new()).await.unwrap();
 	assert_eq!(
-		wait_receiver(api(None).system_sync_state()),
+		sync_state,
 		SyncState { starting_block: 1, current_block: 2, highest_block: Some(3) }
 	);
 }
 
-#[test]
-fn system_network_add_reserved() {
+#[tokio::test]
+async fn system_network_add_reserved() {
 	let good_peer_id =
-		"/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV";
-	let bad_peer_id = "/ip4/198.51.100.19/tcp/30333";
+		["/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV"];
+	let _good: () = api(None)
+		.call("system_addReservedPeer", good_peer_id)
+		.await
+		.expect("good peer id works");
 
-	let good_fut = api(None).system_add_reserved_peer(good_peer_id.into());
-	let bad_fut = api(None).system_add_reserved_peer(bad_peer_id.into());
-	assert_eq!(executor::block_on(good_fut), Ok(()));
-	assert!(executor::block_on(bad_fut).is_err());
+	let bad_peer_id = ["/ip4/198.51.100.19/tcp/30333"];
+	assert_matches!(
+		api(None).call::<_, ()>("system_addReservedPeer", bad_peer_id).await,
+		Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Peer id is missing from the address")
+	);
 }
 
-#[test]
-fn system_network_remove_reserved() {
-	let good_peer_id = "QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV";
-	let bad_peer_id =
-		"/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV";
+#[tokio::test]
+async fn system_network_remove_reserved() {
+	let _good_peer: () = api(None)
+		.call("system_removeReservedPeer", ["QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV"])
+		.await
+		.expect("call with good peer id works");
 
-	let good_fut = api(None).system_remove_reserved_peer(good_peer_id.into());
-	let bad_fut = api(None).system_remove_reserved_peer(bad_peer_id.into());
-	assert_eq!(executor::block_on(good_fut), Ok(()));
-	assert!(executor::block_on(bad_fut).is_err());
-}
+	let bad_peer_id =
+		["/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV"];
 
-#[test]
-fn system_network_reserved_peers() {
-	assert_eq!(
-		wait_receiver(api(None).system_reserved_peers()),
-		vec!["QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV".to_string()]
+	assert_matches!(
+		api(None).call::<_, String>("system_removeReservedPeer", bad_peer_id).await,
+		Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("base-58 decode error: provided string contained invalid character '/' at byte 0")
 	);
 }
+#[tokio::test]
+async fn system_network_reserved_peers() {
+	let reserved_peers: Vec<String> =
+		api(None).call("system_reservedPeers", EmptyParams::new()).await.unwrap();
+	assert_eq!(reserved_peers, vec!["QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV".to_string()],);
+}
 
 #[test]
 fn test_add_reset_log_filter() {
@@ -315,15 +356,20 @@ fn test_add_reset_log_filter() {
 		for line in std::io::stdin().lock().lines() {
 			let line = line.expect("Failed to read bytes");
 			if line.contains("add_reload") {
-				api(None)
-					.system_add_log_filter("test_after_add".into())
-					.expect("`system_add_log_filter` failed");
+				let filter = "test_after_add";
+				let fut =
+					async move { api(None).call::<_, ()>("system_addLogFilter", [filter]).await };
+				futures::executor::block_on(fut).expect("`system_addLogFilter` failed");
 			} else if line.contains("add_trace") {
-				api(None)
-					.system_add_log_filter("test_before_add=trace".into())
-					.expect("`system_add_log_filter` failed");
+				let filter = "test_before_add=trace";
+				let fut =
+					async move { api(None).call::<_, ()>("system_addLogFilter", [filter]).await };
+				futures::executor::block_on(fut).expect("`system_addLogFilter (trace)` failed");
 			} else if line.contains("reset") {
-				api(None).system_reset_log_filter().expect("`system_reset_log_filter` failed");
+				let fut = async move {
+					api(None).call::<_, ()>("system_resetLogFilter", EmptyParams::new()).await
+				};
+				futures::executor::block_on(fut).expect("`system_resetLogFilter` failed");
 			} else if line.contains("exit") {
 				return
 			}
diff --git a/substrate/client/rpc/src/testing.rs b/substrate/client/rpc/src/testing.rs
index bfb91adb81d316a3750338877e265ac347df8a5b..584e4a9901eab94b99d95cdd8255e73e336792a6 100644
--- a/substrate/client/rpc/src/testing.rs
+++ b/substrate/client/rpc/src/testing.rs
@@ -18,29 +18,16 @@
 
 //! Testing utils used by the RPC tests.
 
-use futures::{
-	executor,
-	task::{FutureObj, Spawn, SpawnError},
-};
-
-// Executor shared by all tests.
-//
-// This shared executor is used to prevent `Too many open files` errors
-// on systems with a lot of cores.
-lazy_static::lazy_static! {
-	static ref EXECUTOR: executor::ThreadPool = executor::ThreadPool::new()
-		.expect("Failed to create thread pool executor for tests");
+use std::{future::Future, sync::Arc};
+
+use sp_core::testing::TaskExecutor;
+
+/// Executor for testing.
+pub fn test_executor() -> Arc<sp_core::testing::TaskExecutor> {
+	Arc::new(TaskExecutor::default())
 }
 
-/// Executor for use in testing
-pub struct TaskExecutor;
-impl Spawn for TaskExecutor {
-	fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> {
-		EXECUTOR.spawn_ok(future);
-		Ok(())
-	}
-
-	fn status(&self) -> Result<(), SpawnError> {
-		Ok(())
-	}
+/// Wrap a future in a timeout a little more concisely
+pub fn timeout_secs<I, F: Future<Output = I>>(s: u64, f: F) -> tokio::time::Timeout<F> {
+	tokio::time::timeout(std::time::Duration::from_secs(s), f)
 }
diff --git a/substrate/client/service/Cargo.toml b/substrate/client/service/Cargo.toml
index 7138d9d384eeb401c14f30145064c9726f017795..a62298a260aa42f4fcc8d926d4b805f709a16826 100644
--- a/substrate/client/service/Cargo.toml
+++ b/substrate/client/service/Cargo.toml
@@ -22,10 +22,9 @@ wasmtime = ["sc-executor/wasmtime"]
 test-helpers = []
 
 [dependencies]
+jsonrpsee = { version = "0.12.0", features = ["server"] }
 thiserror = "1.0.30"
 futures = "0.3.21"
-jsonrpc-pubsub = "18.0"
-jsonrpc-core = "18.0"
 rand = "0.7.3"
 parking_lot = "0.12.0"
 log = "0.4.16"
diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs
index cabf004b2f707dbf42ddaf8e8744e14b883262f2..5319bf24d5e72f1d5d3cd23ef5aac8bfe6d37ccb 100644
--- a/substrate/client/service/src/builder.rs
+++ b/substrate/client/service/src/builder.rs
@@ -25,7 +25,7 @@ use crate::{
 	start_rpc_servers, RpcHandlers, SpawnTaskHandle, TaskManager, TransactionPoolAdapter,
 };
 use futures::{channel::oneshot, future::ready, FutureExt, StreamExt};
-use jsonrpc_pubsub::manager::SubscriptionManager;
+use jsonrpsee::RpcModule;
 use log::info;
 use prometheus_endpoint::Registry;
 use sc_chain_spec::get_extension;
@@ -45,6 +45,14 @@ use sc_network::{
 	warp_request_handler::{self, RequestHandler as WarpSyncRequestHandler, WarpSyncProvider},
 	NetworkService,
 };
+use sc_rpc::{
+	author::AuthorApiServer,
+	chain::ChainApiServer,
+	offchain::OffchainApiServer,
+	state::{ChildStateApiServer, StateApiServer},
+	system::SystemApiServer,
+	DenyUnsafe, SubscriptionTaskExecutor,
+};
 use sc_telemetry::{telemetry, ConnectionMessage, Telemetry, TelemetryHandle, SUBSTRATE_INFO};
 use sc_transaction_pool_api::MaintainedTransactionPool;
 use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender};
@@ -62,69 +70,6 @@ use sp_runtime::{
 };
 use std::{str::FromStr, sync::Arc, time::SystemTime};
 
-/// A utility trait for building an RPC extension given a `DenyUnsafe` instance.
-/// This is useful since at service definition time we don't know whether the
-/// specific interface where the RPC extension will be exposed is safe or not.
-/// This trait allows us to lazily build the RPC extension whenever we bind the
-/// service to an interface.
-pub trait RpcExtensionBuilder {
-	/// The type of the RPC extension that will be built.
-	type Output: sc_rpc::RpcExtension<sc_rpc::Metadata>;
-
-	/// Returns an instance of the RPC extension for a particular `DenyUnsafe`
-	/// value, e.g. the RPC extension might not expose some unsafe methods.
-	fn build(
-		&self,
-		deny: sc_rpc::DenyUnsafe,
-		subscription_executor: sc_rpc::SubscriptionTaskExecutor,
-	) -> Result<Self::Output, Error>;
-}
-
-impl<F, R> RpcExtensionBuilder for F
-where
-	F: Fn(sc_rpc::DenyUnsafe, sc_rpc::SubscriptionTaskExecutor) -> Result<R, Error>,
-	R: sc_rpc::RpcExtension<sc_rpc::Metadata>,
-{
-	type Output = R;
-
-	fn build(
-		&self,
-		deny: sc_rpc::DenyUnsafe,
-		subscription_executor: sc_rpc::SubscriptionTaskExecutor,
-	) -> Result<Self::Output, Error> {
-		(*self)(deny, subscription_executor)
-	}
-}
-
-/// A utility struct for implementing an `RpcExtensionBuilder` given a cloneable
-/// `RpcExtension`, the resulting builder will simply ignore the provided
-/// `DenyUnsafe` instance and return a static `RpcExtension` instance.
-pub struct NoopRpcExtensionBuilder<R>(pub R);
-
-impl<R> RpcExtensionBuilder for NoopRpcExtensionBuilder<R>
-where
-	R: Clone + sc_rpc::RpcExtension<sc_rpc::Metadata>,
-{
-	type Output = R;
-
-	fn build(
-		&self,
-		_deny: sc_rpc::DenyUnsafe,
-		_subscription_executor: sc_rpc::SubscriptionTaskExecutor,
-	) -> Result<Self::Output, Error> {
-		Ok(self.0.clone())
-	}
-}
-
-impl<R> From<R> for NoopRpcExtensionBuilder<R>
-where
-	R: sc_rpc::RpcExtension<sc_rpc::Metadata>,
-{
-	fn from(e: R) -> NoopRpcExtensionBuilder<R> {
-		NoopRpcExtensionBuilder(e)
-	}
-}
-
 /// Full client type.
 pub type TFullClient<TBl, TRtApi, TExec> =
 	Client<TFullBackend<TBl>, TFullCallExecutor<TBl, TExec>, TBl, TRtApi>;
@@ -389,9 +334,9 @@ pub struct SpawnTasksParams<'a, TBl: BlockT, TCl, TExPool, TRpc, Backend> {
 	pub keystore: SyncCryptoStorePtr,
 	/// A shared transaction pool.
 	pub transaction_pool: Arc<TExPool>,
-	/// A RPC extension builder. Use `NoopRpcExtensionBuilder` if you just want to pass in the
-	/// extensions directly.
-	pub rpc_extensions_builder: Box<dyn RpcExtensionBuilder<Output = TRpc> + Send>,
+	/// Builds additional [`RpcModule`]s that should be added to the server
+	pub rpc_builder:
+		Box<dyn Fn(DenyUnsafe, SubscriptionTaskExecutor) -> Result<RpcModule<TRpc>, Error>>,
 	/// A shared network instance.
 	pub network: Arc<NetworkService<TBl, <TBl as BlockT>::Hash>>,
 	/// A Sender for RPC requests.
@@ -463,7 +408,6 @@ where
 	TExPool: MaintainedTransactionPool<Block = TBl, Hash = <TBl as BlockT>::Hash>
 		+ parity_util_mem::MallocSizeOf
 		+ 'static,
-	TRpc: sc_rpc::RpcExtension<sc_rpc::Metadata>,
 {
 	let SpawnTasksParams {
 		mut config,
@@ -472,7 +416,7 @@ where
 		backend,
 		keystore,
 		transaction_pool,
-		rpc_extensions_builder,
+		rpc_builder,
 		network,
 		system_rpc_tx,
 		telemetry,
@@ -536,35 +480,25 @@ where
 		metrics_service.run(client.clone(), transaction_pool.clone(), network.clone()),
 	);
 
-	// RPC
-	let gen_handler = |deny_unsafe: sc_rpc::DenyUnsafe,
-	                   rpc_middleware: sc_rpc_server::RpcMiddleware| {
-		gen_handler(
+	let rpc_id_provider = config.rpc_id_provider.take();
+
+	// jsonrpsee RPC
+	let gen_rpc_module = |deny_unsafe: DenyUnsafe| {
+		gen_rpc_module(
 			deny_unsafe,
-			rpc_middleware,
-			&config,
 			task_manager.spawn_handle(),
 			client.clone(),
 			transaction_pool.clone(),
 			keystore.clone(),
-			&*rpc_extensions_builder,
-			backend.offchain_storage(),
 			system_rpc_tx.clone(),
+			&config,
+			backend.offchain_storage(),
+			&*rpc_builder,
 		)
 	};
-	let rpc_metrics = sc_rpc_server::RpcMetrics::new(config.prometheus_registry())?;
-	let server_metrics = sc_rpc_server::ServerMetrics::new(config.prometheus_registry())?;
-	let rpc = start_rpc_servers(&config, gen_handler, rpc_metrics.clone(), server_metrics)?;
-	// This is used internally, so don't restrict access to unsafe RPC
-	let known_rpc_method_names =
-		sc_rpc_server::method_names(|m| gen_handler(sc_rpc::DenyUnsafe::No, m))?;
-	let rpc_handlers = RpcHandlers(Arc::new(
-		gen_handler(
-			sc_rpc::DenyUnsafe::No,
-			sc_rpc_server::RpcMiddleware::new(rpc_metrics, known_rpc_method_names, "inbrowser"),
-		)?
-		.into(),
-	));
+
+	let rpc = start_rpc_servers(&config, gen_rpc_module, rpc_id_provider)?;
+	let rpc_handlers = RpcHandlers(Arc::new(gen_rpc_module(sc_rpc::DenyUnsafe::No)?.into()));
 
 	// Spawn informant task
 	spawn_handle.spawn(
@@ -578,7 +512,7 @@ where
 		),
 	);
 
-	task_manager.keep_alive((config.base_path, rpc, rpc_handlers.clone()));
+	task_manager.keep_alive((config.base_path, rpc));
 
 	Ok(rpc_handlers)
 }
@@ -642,18 +576,17 @@ fn init_telemetry<TBl: BlockT, TCl: BlockBackend<TBl>>(
 	Ok(telemetry.handle())
 }
 
-fn gen_handler<TBl, TBackend, TExPool, TRpc, TCl>(
-	deny_unsafe: sc_rpc::DenyUnsafe,
-	rpc_middleware: sc_rpc_server::RpcMiddleware,
-	config: &Configuration,
+fn gen_rpc_module<TBl, TBackend, TCl, TRpc, TExPool>(
+	deny_unsafe: DenyUnsafe,
 	spawn_handle: SpawnTaskHandle,
 	client: Arc<TCl>,
 	transaction_pool: Arc<TExPool>,
 	keystore: SyncCryptoStorePtr,
-	rpc_extensions_builder: &(dyn RpcExtensionBuilder<Output = TRpc> + Send),
-	offchain_storage: Option<<TBackend as sc_client_api::backend::Backend<TBl>>::OffchainStorage>,
 	system_rpc_tx: TracingUnboundedSender<sc_rpc::system::Request<TBl>>,
-) -> Result<sc_rpc_server::RpcHandler<sc_rpc::Metadata>, Error>
+	config: &Configuration,
+	offchain_storage: Option<<TBackend as sc_client_api::backend::Backend<TBl>>::OffchainStorage>,
+	rpc_builder: &(dyn Fn(DenyUnsafe, SubscriptionTaskExecutor) -> Result<RpcModule<TRpc>, Error>),
+) -> Result<RpcModule<()>, Error>
 where
 	TBl: BlockT,
 	TCl: ProvideRuntimeApi<TBl>
@@ -668,15 +601,12 @@ where
 		+ Send
 		+ Sync
 		+ 'static,
-	TExPool: MaintainedTransactionPool<Block = TBl, Hash = <TBl as BlockT>::Hash> + 'static,
 	TBackend: sc_client_api::backend::Backend<TBl> + 'static,
-	TRpc: sc_rpc::RpcExtension<sc_rpc::Metadata>,
 	<TCl as ProvideRuntimeApi<TBl>>::Api: sp_session::SessionKeys<TBl> + sp_api::Metadata<TBl>,
+	TExPool: MaintainedTransactionPool<Block = TBl, Hash = <TBl as BlockT>::Hash> + 'static,
 	TBl::Hash: Unpin,
 	TBl::Header: Unpin,
 {
-	use sc_rpc::{author, chain, offchain, state, system};
-
 	let system_info = sc_rpc::system::SystemInfo {
 		chain_name: config.chain_spec.name().into(),
 		impl_name: config.impl_name.clone(),
@@ -685,42 +615,50 @@ where
 		chain_type: config.chain_spec.chain_type(),
 	};
 
-	let task_executor = sc_rpc::SubscriptionTaskExecutor::new(spawn_handle);
-	let subscriptions = SubscriptionManager::new(Arc::new(task_executor.clone()));
+	let mut rpc_api = RpcModule::new(());
+	let task_executor = Arc::new(spawn_handle);
 
 	let (chain, state, child_state) = {
-		// Full nodes
-		let chain = sc_rpc::chain::new_full(client.clone(), subscriptions.clone());
+		let chain = sc_rpc::chain::new_full(client.clone(), task_executor.clone()).into_rpc();
 		let (state, child_state) = sc_rpc::state::new_full(
 			client.clone(),
-			subscriptions.clone(),
+			task_executor.clone(),
 			deny_unsafe,
 			config.rpc_max_payload,
 		);
+		let state = state.into_rpc();
+		let child_state = child_state.into_rpc();
+
 		(chain, state, child_state)
 	};
 
-	let author =
-		sc_rpc::author::Author::new(client, transaction_pool, subscriptions, keystore, deny_unsafe);
-	let system = system::System::new(system_info, system_rpc_tx, deny_unsafe);
+	let author = sc_rpc::author::Author::new(
+		client.clone(),
+		transaction_pool,
+		keystore,
+		deny_unsafe,
+		task_executor.clone(),
+	)
+	.into_rpc();
 
-	let maybe_offchain_rpc = offchain_storage.map(|storage| {
-		let offchain = sc_rpc::offchain::Offchain::new(storage, deny_unsafe);
-		offchain::OffchainApi::to_delegate(offchain)
-	});
+	let system = sc_rpc::system::System::new(system_info, system_rpc_tx, deny_unsafe).into_rpc();
 
-	Ok(sc_rpc_server::rpc_handler(
-		(
-			state::StateApi::to_delegate(state),
-			state::ChildStateApi::to_delegate(child_state),
-			chain::ChainApi::to_delegate(chain),
-			maybe_offchain_rpc,
-			author::AuthorApi::to_delegate(author),
-			system::SystemApi::to_delegate(system),
-			rpc_extensions_builder.build(deny_unsafe, task_executor)?,
-		),
-		rpc_middleware,
-	))
+	if let Some(storage) = offchain_storage {
+		let offchain = sc_rpc::offchain::Offchain::new(storage, deny_unsafe).into_rpc();
+
+		rpc_api.merge(offchain).map_err(|e| Error::Application(e.into()))?;
+	}
+
+	rpc_api.merge(chain).map_err(|e| Error::Application(e.into()))?;
+	rpc_api.merge(author).map_err(|e| Error::Application(e.into()))?;
+	rpc_api.merge(system).map_err(|e| Error::Application(e.into()))?;
+	rpc_api.merge(state).map_err(|e| Error::Application(e.into()))?;
+	rpc_api.merge(child_state).map_err(|e| Error::Application(e.into()))?;
+	// Additional [`RpcModule`]s defined in the node to fit the specific blockchain
+	let extra_rpcs = rpc_builder(deny_unsafe, task_executor.clone())?;
+	rpc_api.merge(extra_rpcs).map_err(|e| Error::Application(e.into()))?;
+
+	Ok(rpc_api)
 }
 
 /// Parameters to pass into `build_network`.
diff --git a/substrate/client/service/src/config.rs b/substrate/client/service/src/config.rs
index e49e8b40a7b1a8b11434e552f91c88ba33bef6f9..35380da11fc71aaa38c2b3147e8179e54ae52487 100644
--- a/substrate/client/service/src/config.rs
+++ b/substrate/client/service/src/config.rs
@@ -100,6 +100,18 @@ pub struct Configuration {
 	pub rpc_methods: RpcMethods,
 	/// Maximum payload of rpc request/responses.
 	pub rpc_max_payload: Option<usize>,
+	/// Maximum payload of a rpc request
+	pub rpc_max_request_size: Option<usize>,
+	/// Maximum payload of a rpc request
+	pub rpc_max_response_size: Option<usize>,
+	/// Custom JSON-RPC subscription ID provider.
+	///
+	/// Default: [`crate::RandomStringSubscriptionId`].
+	pub rpc_id_provider: Option<Box<dyn crate::RpcSubscriptionIdProvider>>,
+	/// Maximum allowed subscriptions per rpc connection
+	///
+	/// Default: 1024.
+	pub rpc_max_subs_per_conn: Option<usize>,
 	/// Maximum size of the output buffer capacity for websocket connections.
 	pub ws_max_out_buffer_capacity: Option<usize>,
 	/// Prometheus endpoint configuration. `None` if disabled.
diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs
index 0d2461376d9617b3fd2dd8f032ccdf36a5a6a89e..027b704789635b8057236be9fea824611975b941 100644
--- a/substrate/client/service/src/lib.rs
+++ b/substrate/client/service/src/lib.rs
@@ -34,13 +34,15 @@ mod client;
 mod metrics;
 mod task_manager;
 
-use std::{collections::HashMap, io, net::SocketAddr, pin::Pin};
+use std::{collections::HashMap, net::SocketAddr};
 
 use codec::{Decode, Encode};
-use futures::{Future, FutureExt, StreamExt};
+use futures::{channel::mpsc, FutureExt, StreamExt};
+use jsonrpsee::{core::Error as JsonRpseeError, RpcModule};
 use log::{debug, error, warn};
-use sc_client_api::{BlockBackend, ProofProvider};
+use sc_client_api::{blockchain::HeaderBackend, BlockBackend, BlockchainEvents, ProofProvider};
 use sc_network::PeerId;
+use sc_rpc_server::WsConfig;
 use sc_utils::mpsc::TracingUnboundedReceiver;
 use sp_blockchain::HeaderMetadata;
 use sp_runtime::{
@@ -52,8 +54,7 @@ pub use self::{
 	builder::{
 		build_network, build_offchain_workers, new_client, new_db_backend, new_full_client,
 		new_full_parts, spawn_tasks, BuildNetworkParams, KeystoreContainer, NetworkStarter,
-		NoopRpcExtensionBuilder, RpcExtensionBuilder, SpawnTasksParams, TFullBackend,
-		TFullCallExecutor, TFullClient,
+		SpawnTasksParams, TFullBackend, TFullCallExecutor, TFullClient,
 	},
 	client::{ClientConfig, LocalCallExecutor},
 	error::Error,
@@ -65,12 +66,14 @@ pub use sc_chain_spec::{
 	ChainSpec, ChainType, Extension as ChainSpecExtension, GenericChainSpec, NoExtension,
 	Properties, RuntimeGenesis,
 };
-use sc_client_api::{blockchain::HeaderBackend, BlockchainEvents};
+
 pub use sc_consensus::ImportQueue;
 pub use sc_executor::NativeExecutionDispatch;
 #[doc(hidden)]
 pub use sc_network::config::{TransactionImport, TransactionImportFuture};
-pub use sc_rpc::Metadata as RpcMetadata;
+pub use sc_rpc::{
+	RandomIntegerSubscriptionId, RandomStringSubscriptionId, RpcSubscriptionIdProvider,
+};
 pub use sc_tracing::TracingReceiver;
 pub use sc_transaction_pool::Options as TransactionPoolOptions;
 pub use sc_transaction_pool_api::{error::IntoPoolError, InPoolTransaction, TransactionPool};
@@ -82,32 +85,27 @@ const DEFAULT_PROTOCOL_ID: &str = "sup";
 
 /// RPC handlers that can perform RPC queries.
 #[derive(Clone)]
-pub struct RpcHandlers(
-	Arc<jsonrpc_core::MetaIoHandler<sc_rpc::Metadata, sc_rpc_server::RpcMiddleware>>,
-);
+pub struct RpcHandlers(Arc<RpcModule<()>>);
 
 impl RpcHandlers {
 	/// Starts an RPC query.
 	///
-	/// The query is passed as a string and must be a JSON text similar to what an HTTP client
-	/// would for example send.
+	/// The query is passed as a string and must be valid JSON-RPC request object.
 	///
-	/// Returns a `Future` that contains the optional response.
+	/// Returns a response and a stream if the call successful, fails if the
+	/// query could not be decoded as a JSON-RPC request object.
 	///
-	/// If the request subscribes you to events, the `Sender` in the `RpcSession` object is used to
-	/// send back spontaneous events.
-	pub fn rpc_query(
+	/// If the request subscribes you to events, the `stream` can be used to
+	/// retrieve the events.
+	pub async fn rpc_query(
 		&self,
-		mem: &RpcSession,
-		request: &str,
-	) -> Pin<Box<dyn Future<Output = Option<String>> + Send>> {
-		self.0.handle_request(request, mem.metadata.clone()).boxed()
+		json_query: &str,
+	) -> Result<(String, mpsc::UnboundedReceiver<String>), JsonRpseeError> {
+		self.0.raw_json_request(json_query).await
 	}
 
-	/// Provides access to the underlying `MetaIoHandler`
-	pub fn io_handler(
-		&self,
-	) -> Arc<jsonrpc_core::MetaIoHandler<sc_rpc::Metadata, sc_rpc_server::RpcMiddleware>> {
+	/// Provides access to the underlying `RpcModule`
+	pub fn handle(&self) -> Arc<RpcModule<()>> {
 		self.0.clone()
 	}
 }
@@ -284,74 +282,41 @@ async fn build_network_future<
 // Wrapper for HTTP and WS servers that makes sure they are properly shut down.
 mod waiting {
 	pub struct HttpServer(pub Option<sc_rpc_server::HttpServer>);
-	impl Drop for HttpServer {
-		fn drop(&mut self) {
-			if let Some(server) = self.0.take() {
-				server.close_handle().close();
-				server.wait();
-			}
-		}
-	}
 
-	pub struct IpcServer(pub Option<sc_rpc_server::IpcServer>);
-	impl Drop for IpcServer {
+	impl Drop for HttpServer {
 		fn drop(&mut self) {
 			if let Some(server) = self.0.take() {
-				server.close_handle().close();
-				let _ = server.wait();
+				// This doesn't not wait for the server to be stopped but fires the signal.
+				let _ = server.stop();
 			}
 		}
 	}
 
 	pub struct WsServer(pub Option<sc_rpc_server::WsServer>);
+
 	impl Drop for WsServer {
 		fn drop(&mut self) {
 			if let Some(server) = self.0.take() {
-				server.close_handle().close();
-				let _ = server.wait();
+				// This doesn't not wait for the server to be stopped but fires the signal.
+				let _ = server.stop();
 			}
 		}
 	}
 }
 
-/// Starts RPC servers that run in their own thread, and returns an opaque object that keeps them
-/// alive.
-fn start_rpc_servers<
-	H: FnMut(
-		sc_rpc::DenyUnsafe,
-		sc_rpc_server::RpcMiddleware,
-	) -> Result<sc_rpc_server::RpcHandler<sc_rpc::Metadata>, Error>,
->(
+/// Starts RPC servers.
+fn start_rpc_servers<R>(
 	config: &Configuration,
-	mut gen_handler: H,
-	rpc_metrics: Option<sc_rpc_server::RpcMetrics>,
-	server_metrics: sc_rpc_server::ServerMetrics,
-) -> Result<Box<dyn std::any::Any + Send>, Error> {
-	fn maybe_start_server<T, F>(
-		address: Option<SocketAddr>,
-		mut start: F,
-	) -> Result<Option<T>, Error>
-	where
-		F: FnMut(&SocketAddr) -> Result<T, Error>,
-	{
-		address
-			.map(|mut address| {
-				start(&address).or_else(|e| match e {
-					Error::Io(e) => match e.kind() {
-						io::ErrorKind::AddrInUse | io::ErrorKind::PermissionDenied => {
-							warn!("Unable to bind RPC server to {}. Trying random port.", address);
-							address.set_port(0);
-							start(&address)
-						},
-						_ => Err(e.into()),
-					},
-					e => Err(e),
-				})
-			})
-			.transpose()
-	}
+	gen_rpc_module: R,
+	rpc_id_provider: Option<Box<dyn RpcSubscriptionIdProvider>>,
+) -> Result<Box<dyn std::any::Any + Send + Sync>, error::Error>
+where
+	R: Fn(sc_rpc::DenyUnsafe) -> Result<RpcModule<()>, Error>,
+{
+	let (max_request_size, ws_max_response_size, http_max_response_size) =
+		legacy_cli_parsing(config);
 
-	fn deny_unsafe(addr: &SocketAddr, methods: &RpcMethods) -> sc_rpc::DenyUnsafe {
+	fn deny_unsafe(addr: SocketAddr, methods: &RpcMethods) -> sc_rpc::DenyUnsafe {
 		let is_exposed_addr = !addr.ip().is_loopback();
 		match (is_exposed_addr, methods) {
 			| (_, RpcMethods::Unsafe) | (false, RpcMethods::Auto) => sc_rpc::DenyUnsafe::No,
@@ -359,85 +324,54 @@ fn start_rpc_servers<
 		}
 	}
 
-	let rpc_method_names = sc_rpc_server::method_names(|m| gen_handler(sc_rpc::DenyUnsafe::No, m))?;
-	Ok(Box::new((
-		config
-			.rpc_ipc
-			.as_ref()
-			.map(|path| {
-				sc_rpc_server::start_ipc(
-					&*path,
-					gen_handler(
-						sc_rpc::DenyUnsafe::No,
-						sc_rpc_server::RpcMiddleware::new(
-							rpc_metrics.clone(),
-							rpc_method_names.clone(),
-							"ipc",
-						),
-					)?,
-					server_metrics.clone(),
-				)
-				.map_err(Error::from)
-			})
-			.transpose()?,
-		maybe_start_server(config.rpc_http, |address| {
-			sc_rpc_server::start_http(
-				address,
-				config.rpc_cors.as_ref(),
-				gen_handler(
-					deny_unsafe(address, &config.rpc_methods),
-					sc_rpc_server::RpcMiddleware::new(
-						rpc_metrics.clone(),
-						rpc_method_names.clone(),
-						"http",
-					),
-				)?,
-				config.rpc_max_payload,
-				config.tokio_handle.clone(),
-			)
-			.map_err(Error::from)
-		})?
-		.map(|s| waiting::HttpServer(Some(s))),
-		maybe_start_server(config.rpc_ws, |address| {
-			sc_rpc_server::start_ws(
-				address,
-				config.rpc_ws_max_connections,
-				config.rpc_cors.as_ref(),
-				gen_handler(
-					deny_unsafe(address, &config.rpc_methods),
-					sc_rpc_server::RpcMiddleware::new(
-						rpc_metrics.clone(),
-						rpc_method_names.clone(),
-						"ws",
-					),
-				)?,
-				config.rpc_max_payload,
-				config.ws_max_out_buffer_capacity,
-				server_metrics.clone(),
-				config.tokio_handle.clone(),
-			)
-			.map_err(Error::from)
-		})?
-		.map(|s| waiting::WsServer(Some(s))),
-	)))
-}
+	let random_port = |mut addr: SocketAddr| {
+		addr.set_port(0);
+		addr
+	};
 
-/// An RPC session. Used to perform in-memory RPC queries (ie. RPC queries that don't go through
-/// the HTTP or WebSockets server).
-#[derive(Clone)]
-pub struct RpcSession {
-	metadata: sc_rpc::Metadata,
-}
+	let ws_addr = config
+		.rpc_ws
+		.unwrap_or_else(|| "127.0.0.1:9944".parse().expect("valid sockaddr; qed"));
+	let ws_addr2 = random_port(ws_addr);
+	let http_addr = config
+		.rpc_http
+		.unwrap_or_else(|| "127.0.0.1:9933".parse().expect("valid sockaddr; qed"));
+	let http_addr2 = random_port(http_addr);
+
+	let metrics = sc_rpc_server::RpcMetrics::new(config.prometheus_registry())?;
+
+	let http_fut = sc_rpc_server::start_http(
+		[http_addr, http_addr2],
+		config.rpc_cors.as_ref(),
+		max_request_size,
+		http_max_response_size,
+		metrics.clone(),
+		gen_rpc_module(deny_unsafe(ws_addr, &config.rpc_methods))?,
+		config.tokio_handle.clone(),
+	);
+
+	let ws_config = WsConfig {
+		max_connections: config.rpc_ws_max_connections,
+		max_payload_in_mb: max_request_size,
+		max_payload_out_mb: ws_max_response_size,
+		max_subs_per_conn: config.rpc_max_subs_per_conn,
+	};
 
-impl RpcSession {
-	/// Creates an RPC session.
-	///
-	/// The `sender` is stored inside the `RpcSession` and is used to communicate spontaneous JSON
-	/// messages.
-	///
-	/// The `RpcSession` must be kept alive in order to receive messages on the sender.
-	pub fn new(sender: futures::channel::mpsc::UnboundedSender<String>) -> RpcSession {
-		RpcSession { metadata: sender.into() }
+	let ws_fut = sc_rpc_server::start_ws(
+		[ws_addr, ws_addr2],
+		config.rpc_cors.as_ref(),
+		ws_config,
+		metrics,
+		gen_rpc_module(deny_unsafe(http_addr, &config.rpc_methods))?,
+		config.tokio_handle.clone(),
+		rpc_id_provider,
+	);
+
+	match tokio::task::block_in_place(|| {
+		config.tokio_handle.block_on(futures::future::try_join(http_fut, ws_fut))
+	}) {
+		Ok((http, ws)) => Ok(Box::new((http, ws))),
+		Err(e) => Err(Error::Application(e)),
 	}
 }
 
@@ -545,6 +479,44 @@ where
 	}
 }
 
+fn legacy_cli_parsing(config: &Configuration) -> (Option<usize>, Option<usize>, Option<usize>) {
+	let ws_max_response_size = config.ws_max_out_buffer_capacity.map(|max| {
+		eprintln!("DEPRECATED: `--ws_max_out_buffer_capacity` has been removed use `rpc-max-response-size or rpc-max-request-size` instead");
+		eprintln!("Setting WS `rpc-max-response-size` to `max(ws_max_out_buffer_capacity, rpc_max_response_size)`");
+		std::cmp::max(max, config.rpc_max_response_size.unwrap_or(0))
+	});
+
+	let max_request_size = match (config.rpc_max_payload, config.rpc_max_request_size) {
+		(Some(legacy_max), max) => {
+			eprintln!("DEPRECATED: `--rpc_max_payload` has been removed use `rpc-max-response-size or rpc-max-request-size` instead");
+			eprintln!(
+				"Setting `rpc-max-response-size` to `max(rpc_max_payload, rpc_max_request_size)`"
+			);
+			Some(std::cmp::max(legacy_max, max.unwrap_or(0)))
+		},
+		(None, Some(max)) => Some(max),
+		(None, None) => None,
+	};
+
+	let http_max_response_size = match (config.rpc_max_payload, config.rpc_max_request_size) {
+		(Some(legacy_max), max) => {
+			eprintln!("DEPRECATED: `--rpc_max_payload` has been removed use `rpc-max-response-size or rpc-max-request-size` instead");
+			eprintln!(
+				"Setting HTTP `rpc-max-response-size` to `max(rpc_max_payload, rpc_max_response_size)`"
+			);
+			Some(std::cmp::max(legacy_max, max.unwrap_or(0)))
+		},
+		(None, Some(max)) => Some(max),
+		(None, None) => None,
+	};
+
+	if config.rpc_ipc.is_some() {
+		eprintln!("DEPRECATED: `--ipc-path` has no effect anymore IPC support has been removed");
+	}
+
+	(max_request_size, ws_max_response_size, http_max_response_size)
+}
+
 #[cfg(test)]
 mod tests {
 	use super::*;
diff --git a/substrate/client/service/test/src/lib.rs b/substrate/client/service/test/src/lib.rs
index ef04e16a65d26fea86d0b59795d251d67791fe69..749c83c6eeac701527ac04f07ed52584ce0b3bfd 100644
--- a/substrate/client/service/test/src/lib.rs
+++ b/substrate/client/service/test/src/lib.rs
@@ -246,6 +246,10 @@ fn node_config<
 		rpc_cors: None,
 		rpc_methods: Default::default(),
 		rpc_max_payload: None,
+		rpc_max_request_size: None,
+		rpc_max_response_size: None,
+		rpc_id_provider: None,
+		rpc_max_subs_per_conn: None,
 		ws_max_out_buffer_capacity: None,
 		prometheus_config: None,
 		telemetry_endpoints: None,
diff --git a/substrate/client/sync-state-rpc/Cargo.toml b/substrate/client/sync-state-rpc/Cargo.toml
index a32849dc0e964c06cc67592f2483873cb7bd8981..f42c307ffa84cc29c442b0ed3de1eb94655acf24 100644
--- a/substrate/client/sync-state-rpc/Cargo.toml
+++ b/substrate/client/sync-state-rpc/Cargo.toml
@@ -14,9 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 codec = { package = "parity-scale-codec", version = "3.0.0" }
-jsonrpc-core = "18.0.0"
-jsonrpc-core-client = "18.0.0"
-jsonrpc-derive = "18.0.0"
+jsonrpsee = { version = "0.12.0", features = ["server", "macros"] }
 serde = { version = "1.0.136", features = ["derive"] }
 serde_json = "1.0.79"
 thiserror = "1.0.30"
diff --git a/substrate/client/sync-state-rpc/src/lib.rs b/substrate/client/sync-state-rpc/src/lib.rs
index d7696c662e856e691ad599ecf7d040cba0efb50f..a0a5b66cb86fc029d09dedf0b978aa9bdffc9825 100644
--- a/substrate/client/sync-state-rpc/src/lib.rs
+++ b/substrate/client/sync-state-rpc/src/lib.rs
@@ -37,10 +37,15 @@
 //! ```
 //!
 //! If the [`LightSyncStateExtension`] is not added as an extension to the chain spec,
-//! the [`SyncStateRpcHandler`] will fail at instantiation.
+//! the [`SyncStateRpc`] will fail at instantiation.
 
 #![deny(unused_crate_dependencies)]
 
+use jsonrpsee::{
+	core::{Error as JsonRpseeError, RpcResult},
+	proc_macros::rpc,
+	types::{error::CallError, ErrorObject},
+};
 use sc_client_api::StorageData;
 use sp_blockchain::HeaderBackend;
 use sp_runtime::{
@@ -49,8 +54,6 @@ use sp_runtime::{
 };
 use std::sync::Arc;
 
-use jsonrpc_derive::rpc;
-
 type SharedAuthoritySet<TBl> =
 	sc_finality_grandpa::SharedAuthoritySet<<TBl as BlockT>::Hash, NumberFor<TBl>>;
 type SharedEpochChanges<TBl> =
@@ -76,13 +79,13 @@ pub enum Error<Block: BlockT> {
 	LightSyncStateExtensionNotFound,
 }
 
-impl<Block: BlockT> From<Error<Block>> for jsonrpc_core::Error {
+impl<Block: BlockT> From<Error<Block>> for JsonRpseeError {
 	fn from(error: Error<Block>) -> Self {
 		let message = match error {
 			Error::JsonRpc(s) => s,
 			_ => error.to_string(),
 		};
-		jsonrpc_core::Error { message, code: jsonrpc_core::ErrorCode::ServerError(1), data: None }
+		CallError::Custom(ErrorObject::owned(1, message, None::<()>)).into()
 	}
 }
 
@@ -101,7 +104,7 @@ fn serialize_encoded<S: serde::Serializer, T: codec::Encode>(
 /// chain-spec as an extension.
 pub type LightSyncStateExtension = Option<serde_json::Value>;
 
-/// Hardcoded infomation that allows light clients to sync quickly.
+/// Hardcoded information that allows light clients to sync quickly.
 #[derive(serde::Serialize, Clone)]
 #[serde(rename_all = "camelCase")]
 #[serde(deny_unknown_fields)]
@@ -121,30 +124,30 @@ pub struct LightSyncState<Block: BlockT> {
 }
 
 /// An api for sync state RPC calls.
-#[rpc]
+#[rpc(client, server)]
 pub trait SyncStateRpcApi {
-	/// Returns the json-serialized chainspec running the node, with a sync state.
-	#[rpc(name = "sync_state_genSyncSpec", returns = "jsonrpc_core::Value")]
-	fn system_gen_sync_spec(&self, raw: bool) -> jsonrpc_core::Result<jsonrpc_core::Value>;
+	/// Returns the JSON serialized chainspec running the node, with a sync state.
+	#[method(name = "sync_state_genSyncSpec")]
+	fn system_gen_sync_spec(&self, raw: bool) -> RpcResult<String>;
 }
 
-/// The handler for sync state RPC calls.
-pub struct SyncStateRpcHandler<Block: BlockT, Backend> {
+/// An api for sync state RPC calls.
+pub struct SyncStateRpc<Block: BlockT, Client> {
 	chain_spec: Box<dyn sc_chain_spec::ChainSpec>,
-	client: Arc<Backend>,
+	client: Arc<Client>,
 	shared_authority_set: SharedAuthoritySet<Block>,
 	shared_epoch_changes: SharedEpochChanges<Block>,
 }
 
-impl<Block, Backend> SyncStateRpcHandler<Block, Backend>
+impl<Block, Client> SyncStateRpc<Block, Client>
 where
 	Block: BlockT,
-	Backend: HeaderBackend<Block> + sc_client_api::AuxStore + 'static,
+	Client: HeaderBackend<Block> + sc_client_api::AuxStore + 'static,
 {
-	/// Create a new handler.
+	/// Create a new sync state RPC helper.
 	pub fn new(
 		chain_spec: Box<dyn sc_chain_spec::ChainSpec>,
-		client: Arc<Backend>,
+		client: Arc<Client>,
 		shared_authority_set: SharedAuthoritySet<Block>,
 		shared_epoch_changes: SharedEpochChanges<Block>,
 	) -> Result<Self, Error<Block>> {
@@ -177,32 +180,23 @@ where
 	}
 }
 
-impl<Block, Backend> SyncStateRpcApi for SyncStateRpcHandler<Block, Backend>
+impl<Block, Backend> SyncStateRpcApiServer for SyncStateRpc<Block, Backend>
 where
 	Block: BlockT,
 	Backend: HeaderBackend<Block> + sc_client_api::AuxStore + 'static,
 {
-	fn system_gen_sync_spec(&self, raw: bool) -> jsonrpc_core::Result<jsonrpc_core::Value> {
+	fn system_gen_sync_spec(&self, raw: bool) -> RpcResult<String> {
+		let current_sync_state = self.build_sync_state()?;
 		let mut chain_spec = self.chain_spec.cloned_box();
 
-		let sync_state = self.build_sync_state().map_err(map_error::<Block, Error<Block>>)?;
-
 		let extension = sc_chain_spec::get_extension_mut::<LightSyncStateExtension>(
 			chain_spec.extensions_mut(),
 		)
-		.ok_or_else(|| {
-			Error::<Block>::JsonRpc("Could not find `LightSyncState` chain-spec extension!".into())
-		})?;
-
-		*extension =
-			Some(serde_json::to_value(&sync_state).map_err(|err| map_error::<Block, _>(err))?);
+		.ok_or(Error::<Block>::LightSyncStateExtensionNotFound)?;
 
-		let json_string = chain_spec.as_json(raw).map_err(map_error::<Block, _>)?;
+		let val = serde_json::to_value(&current_sync_state)?;
+		*extension = Some(val);
 
-		serde_json::from_str(&json_string).map_err(|err| map_error::<Block, _>(err))
+		chain_spec.as_json(raw).map_err(|e| Error::<Block>::JsonRpc(e).into())
 	}
 }
-
-fn map_error<Block: BlockT, S: ToString>(error: S) -> jsonrpc_core::Error {
-	Error::<Block>::JsonRpc(error.to_string()).into()
-}
diff --git a/substrate/client/tracing/src/block/mod.rs b/substrate/client/tracing/src/block/mod.rs
index 6de3a0220e15efc38124fabc8d8085e46bc8e4b1..2a034f06ce8a838bbe98ab1731d3f0c5f0473310 100644
--- a/substrate/client/tracing/src/block/mod.rs
+++ b/substrate/client/tracing/src/block/mod.rs
@@ -53,7 +53,7 @@ const AVG_SPAN: usize = 100 * 8;
 // are used for the RPC Id this may need to be adjusted. Note: The base payload
 // does not include the RPC result.
 //
-// The estimate is based on the JSONRPC response message which has the following format:
+// The estimate is based on the JSON-RPC response message which has the following format:
 // `{"jsonrpc":"2.0","result":[],"id":18446744073709551615}`.
 //
 // We care about the total size of the payload because jsonrpc-server will simply ignore
diff --git a/substrate/client/transaction-pool/api/src/error.rs b/substrate/client/transaction-pool/api/src/error.rs
index b093657f739b1bb23b882446331d17e2f6a69833..aada44734d053b0a6af948dfde5e0d9686442e0e 100644
--- a/substrate/client/transaction-pool/api/src/error.rs
+++ b/substrate/client/transaction-pool/api/src/error.rs
@@ -46,7 +46,7 @@ pub enum Error {
 	TemporarilyBanned,
 
 	#[error("[{0:?}] Already imported")]
-	AlreadyImported(Box<dyn std::any::Any + Send>),
+	AlreadyImported(Box<dyn std::any::Any + Send + Sync>),
 
 	#[error("Too low priority ({} > {})", old, new)]
 	TooLowPriority {
@@ -72,7 +72,7 @@ pub enum Error {
 }
 
 /// Transaction pool error conversion.
-pub trait IntoPoolError: std::error::Error + Send + Sized {
+pub trait IntoPoolError: std::error::Error + Send + Sized + Sync {
 	/// Try to extract original `Error`
 	///
 	/// This implementation is optional and used only to
diff --git a/substrate/client/transaction-pool/api/src/lib.rs b/substrate/client/transaction-pool/api/src/lib.rs
index 448578b327b0310fba97549873aae9fcc2ec1e39..0ebb8f9d4cd9cdb05431882aa720475455a7e902 100644
--- a/substrate/client/transaction-pool/api/src/lib.rs
+++ b/substrate/client/transaction-pool/api/src/lib.rs
@@ -22,16 +22,17 @@
 pub mod error;
 
 use futures::{Future, Stream};
-use serde::{Deserialize, Serialize};
-pub use sp_runtime::transaction_validity::{
-	TransactionLongevity, TransactionPriority, TransactionSource, TransactionTag,
-};
+use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use sp_runtime::{
 	generic::BlockId,
 	traits::{Block as BlockT, Member, NumberFor},
 };
 use std::{collections::HashMap, hash::Hash, pin::Pin, sync::Arc};
 
+pub use sp_runtime::transaction_validity::{
+	TransactionLongevity, TransactionPriority, TransactionSource, TransactionTag,
+};
+
 /// Transaction pool status.
 #[derive(Debug)]
 pub struct PoolStatus {
@@ -177,7 +178,7 @@ pub trait TransactionPool: Send + Sync {
 	/// Block type.
 	type Block: BlockT;
 	/// Transaction hash type.
-	type Hash: Hash + Eq + Member + Serialize;
+	type Hash: Hash + Eq + Member + Serialize + DeserializeOwned;
 	/// In-pool transaction type.
 	type InPoolTransaction: InPoolTransaction<
 		Transaction = TransactionFor<Self>,
diff --git a/substrate/frame/bags-list/remote-tests/Cargo.toml b/substrate/frame/bags-list/remote-tests/Cargo.toml
index 05675741ae51bc6f9ed606ff86a612c11c0b2be6..760115917c6d459bb1729caade9d2c184b3172ef 100644
--- a/substrate/frame/bags-list/remote-tests/Cargo.toml
+++ b/substrate/frame/bags-list/remote-tests/Cargo.toml
@@ -32,4 +32,3 @@ remote-externalities = { path = "../../../utils/frame/remote-externalities", ver
 
 # others
 log = "0.4.16"
-tokio = { version = "1", features = ["macros"] }
diff --git a/substrate/frame/contracts/rpc/Cargo.toml b/substrate/frame/contracts/rpc/Cargo.toml
index e506e78a2fbdcde2b2aceba071449231557dc29c..36f6c06328501cc7c7cb33be30480286ae8a3b99 100644
--- a/substrate/frame/contracts/rpc/Cargo.toml
+++ b/substrate/frame/contracts/rpc/Cargo.toml
@@ -14,9 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 codec = { package = "parity-scale-codec", version = "3.0.0" }
-jsonrpc-core = "18.0.0"
-jsonrpc-core-client = "18.0.0"
-jsonrpc-derive = "18.0.0"
+jsonrpsee = { version = "0.12.0", features = ["server", "macros"] }
 serde = { version = "1", features = ["derive"] }
 
 # Substrate Dependencies
diff --git a/substrate/frame/contracts/rpc/src/lib.rs b/substrate/frame/contracts/rpc/src/lib.rs
index e83e4e6249b9298af4cd253941fa26cc220b4ee7..599e80676cb19d7970ef026b7cbf360c41d7ed18 100644
--- a/substrate/frame/contracts/rpc/src/lib.rs
+++ b/substrate/frame/contracts/rpc/src/lib.rs
@@ -17,11 +17,16 @@
 
 //! Node-specific RPC methods for interaction with contracts.
 
-use std::sync::Arc;
+#![warn(unused_crate_dependencies)]
+
+use std::{marker::PhantomData, sync::Arc};
 
 use codec::Codec;
-use jsonrpc_core::{Error, ErrorCode, Result};
-use jsonrpc_derive::rpc;
+use jsonrpsee::{
+	core::{async_trait, Error as JsonRpseeError, RpcResult},
+	proc_macros::rpc,
+	types::error::{CallError, ErrorCode, ErrorObject},
+};
 use pallet_contracts_primitives::{
 	Code, CodeUploadResult, ContractExecResult, ContractInstantiateResult,
 };
@@ -37,8 +42,8 @@ use sp_runtime::{
 
 pub use pallet_contracts_rpc_runtime_api::ContractsApi as ContractsRuntimeApi;
 
-const RUNTIME_ERROR: i64 = 1;
-const CONTRACT_DOESNT_EXIST: i64 = 2;
+const RUNTIME_ERROR: i32 = 1;
+const CONTRACT_DOESNT_EXIST: i32 = 2;
 
 pub type Weight = u64;
 
@@ -58,15 +63,17 @@ const GAS_LIMIT: Weight = 5 * GAS_PER_SECOND;
 
 /// A private newtype for converting `ContractAccessError` into an RPC error.
 struct ContractAccessError(pallet_contracts_primitives::ContractAccessError);
-impl From<ContractAccessError> for Error {
-	fn from(e: ContractAccessError) -> Error {
+
+impl From<ContractAccessError> for JsonRpseeError {
+	fn from(e: ContractAccessError) -> Self {
 		use pallet_contracts_primitives::ContractAccessError::*;
 		match e.0 {
-			DoesntExist => Error {
-				code: ErrorCode::ServerError(CONTRACT_DOESNT_EXIST),
-				message: "The specified contract doesn't exist.".into(),
-				data: None,
-			},
+			DoesntExist => CallError::Custom(ErrorObject::owned(
+				CONTRACT_DOESNT_EXIST,
+				"The specified contract doesn't exist.",
+				None::<()>,
+			))
+			.into(),
 		}
 	}
 }
@@ -109,7 +116,7 @@ pub struct CodeUploadRequest<AccountId> {
 }
 
 /// Contracts RPC methods.
-#[rpc]
+#[rpc(client, server)]
 pub trait ContractsApi<BlockHash, BlockNumber, AccountId, Balance, Hash>
 where
 	Balance: Copy + TryFrom<NumberOrHex> + Into<NumberOrHex>,
@@ -121,12 +128,12 @@ where
 	///
 	/// This method is useful for calling getter-like methods on contracts or to dry-run a
 	/// a contract call in order to determine the `gas_limit`.
-	#[rpc(name = "contracts_call")]
+	#[method(name = "contracts_call")]
 	fn call(
 		&self,
 		call_request: CallRequest<AccountId>,
 		at: Option<BlockHash>,
-	) -> Result<ContractExecResult<Balance>>;
+	) -> RpcResult<ContractExecResult<Balance>>;
 
 	/// Instantiate a new contract.
 	///
@@ -134,12 +141,12 @@ where
 	/// is not actually created.
 	///
 	/// This method is useful for UIs to dry-run contract instantiations.
-	#[rpc(name = "contracts_instantiate")]
+	#[method(name = "contracts_instantiate")]
 	fn instantiate(
 		&self,
 		instantiate_request: InstantiateRequest<AccountId, Hash>,
 		at: Option<BlockHash>,
-	) -> Result<ContractInstantiateResult<AccountId, Balance>>;
+	) -> RpcResult<ContractInstantiateResult<AccountId, Balance>>;
 
 	/// Upload new code without instantiating a contract from it.
 	///
@@ -147,48 +154,50 @@ where
 	/// won't change any state.
 	///
 	/// This method is useful for UIs to dry-run code upload.
-	#[rpc(name = "contracts_upload_code")]
+	#[method(name = "contracts_upload_code")]
 	fn upload_code(
 		&self,
 		upload_request: CodeUploadRequest<AccountId>,
 		at: Option<BlockHash>,
-	) -> Result<CodeUploadResult<Hash, Balance>>;
+	) -> RpcResult<CodeUploadResult<Hash, Balance>>;
 
 	/// Returns the value under a specified storage `key` in a contract given by `address` param,
 	/// or `None` if it is not set.
-	#[rpc(name = "contracts_getStorage")]
+	#[method(name = "contracts_getStorage")]
 	fn get_storage(
 		&self,
 		address: AccountId,
 		key: H256,
 		at: Option<BlockHash>,
-	) -> Result<Option<Bytes>>;
+	) -> RpcResult<Option<Bytes>>;
 }
 
-/// An implementation of contract specific RPC methods.
-pub struct Contracts<C, B> {
-	client: Arc<C>,
-	_marker: std::marker::PhantomData<B>,
+/// Contracts RPC methods.
+pub struct ContractsRpc<Client, Block> {
+	client: Arc<Client>,
+	_marker: PhantomData<Block>,
 }
 
-impl<C, B> Contracts<C, B> {
+impl<Client, Block> ContractsRpc<Client, Block> {
 	/// Create new `Contracts` with the given reference to the client.
-	pub fn new(client: Arc<C>) -> Self {
-		Contracts { client, _marker: Default::default() }
+	pub fn new(client: Arc<Client>) -> Self {
+		Self { client, _marker: Default::default() }
 	}
 }
-impl<C, Block, AccountId, Balance, Hash>
-	ContractsApi<
+
+#[async_trait]
+impl<Client, Block, AccountId, Balance, Hash>
+	ContractsApiServer<
 		<Block as BlockT>::Hash,
 		<<Block as BlockT>::Header as HeaderT>::Number,
 		AccountId,
 		Balance,
 		Hash,
-	> for Contracts<C, Block>
+	> for ContractsRpc<Client, Block>
 where
 	Block: BlockT,
-	C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,
-	C::Api: ContractsRuntimeApi<
+	Client: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,
+	Client::Api: ContractsRuntimeApi<
 		Block,
 		AccountId,
 		Balance,
@@ -203,7 +212,7 @@ where
 		&self,
 		call_request: CallRequest<AccountId>,
 		at: Option<<Block as BlockT>::Hash>,
-	) -> Result<ContractExecResult<Balance>> {
+	) -> RpcResult<ContractExecResult<Balance>> {
 		let api = self.client.runtime_api();
 		let at = BlockId::hash(at.unwrap_or_else(||
 			// If the block hash is not supplied assume the best block.
@@ -226,7 +235,7 @@ where
 		&self,
 		instantiate_request: InstantiateRequest<AccountId, Hash>,
 		at: Option<<Block as BlockT>::Hash>,
-	) -> Result<ContractInstantiateResult<AccountId, Balance>> {
+	) -> RpcResult<ContractInstantiateResult<AccountId, Balance>> {
 		let api = self.client.runtime_api();
 		let at = BlockId::hash(at.unwrap_or_else(||
 			// If the block hash is not supplied assume the best block.
@@ -265,7 +274,7 @@ where
 		&self,
 		upload_request: CodeUploadRequest<AccountId>,
 		at: Option<<Block as BlockT>::Hash>,
-	) -> Result<CodeUploadResult<Hash, Balance>> {
+	) -> RpcResult<CodeUploadResult<Hash, Balance>> {
 		let api = self.client.runtime_api();
 		let at = BlockId::hash(at.unwrap_or_else(||
 			// If the block hash is not supplied assume the best block.
@@ -285,12 +294,9 @@ where
 		address: AccountId,
 		key: H256,
 		at: Option<<Block as BlockT>::Hash>,
-	) -> Result<Option<Bytes>> {
+	) -> RpcResult<Option<Bytes>> {
 		let api = self.client.runtime_api();
-		let at = BlockId::hash(at.unwrap_or_else(||
-			// If the block hash is not supplied assume the best block.
-			self.client.info().best_hash));
-
+		let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));
 		let result = api
 			.get_storage(&at, address, key.into())
 			.map_err(runtime_error_into_rpc_err)?
@@ -302,32 +308,35 @@ where
 }
 
 /// Converts a runtime trap into an RPC error.
-fn runtime_error_into_rpc_err(err: impl std::fmt::Display) -> Error {
-	Error {
-		code: ErrorCode::ServerError(RUNTIME_ERROR),
-		message: "Runtime error".into(),
-		data: Some(err.to_string().into()),
-	}
+fn runtime_error_into_rpc_err(err: impl std::fmt::Debug) -> JsonRpseeError {
+	CallError::Custom(ErrorObject::owned(
+		RUNTIME_ERROR,
+		"Runtime error",
+		Some(format!("{:?}", err)),
+	))
+	.into()
 }
 
-fn decode_hex<H: std::fmt::Debug + Copy, T: TryFrom<H>>(from: H, name: &str) -> Result<T> {
-	from.try_into().map_err(|_| Error {
-		code: ErrorCode::InvalidParams,
-		message: format!("{:?} does not fit into the {} type", from, name),
-		data: None,
+fn decode_hex<H: std::fmt::Debug + Copy, T: TryFrom<H>>(from: H, name: &str) -> RpcResult<T> {
+	from.try_into().map_err(|_| {
+		JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
+			ErrorCode::InvalidParams.code(),
+			format!("{:?} does not fit into the {} type", from, name),
+			None::<()>,
+		)))
 	})
 }
 
-fn limit_gas(gas_limit: Weight) -> Result<()> {
+fn limit_gas(gas_limit: Weight) -> RpcResult<()> {
 	if gas_limit > GAS_LIMIT {
-		Err(Error {
-			code: ErrorCode::InvalidParams,
-			message: format!(
+		Err(JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
+			ErrorCode::InvalidParams.code(),
+			format!(
 				"Requested gas limit is greater than maximum allowed: {} > {}",
 				gas_limit, GAS_LIMIT
 			),
-			data: None,
-		})
+			None::<()>,
+		))))
 	} else {
 		Ok(())
 	}
@@ -336,6 +345,7 @@ fn limit_gas(gas_limit: Weight) -> Result<()> {
 #[cfg(test)]
 mod tests {
 	use super::*;
+	use pallet_contracts_primitives::{ContractExecResult, ContractInstantiateResult};
 	use sp_core::U256;
 
 	fn trim(json: &str) -> String {
diff --git a/substrate/frame/merkle-mountain-range/rpc/Cargo.toml b/substrate/frame/merkle-mountain-range/rpc/Cargo.toml
index 9b3d3a43c60458f0fe0fb6336911e9c0075d5427..2d3bfebc6633f136d579a7cd2eadb037af88a69b 100644
--- a/substrate/frame/merkle-mountain-range/rpc/Cargo.toml
+++ b/substrate/frame/merkle-mountain-range/rpc/Cargo.toml
@@ -14,9 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 codec = { package = "parity-scale-codec", version = "3.0.0" }
-jsonrpc-core = "18.0.0"
-jsonrpc-core-client = "18.0.0"
-jsonrpc-derive = "18.0.0"
+jsonrpsee = { version = "0.12.0", features = ["server", "macros"] }
 serde = { version = "1.0.136", features = ["derive"] }
 sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" }
 sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" }
diff --git a/substrate/frame/merkle-mountain-range/rpc/src/lib.rs b/substrate/frame/merkle-mountain-range/rpc/src/lib.rs
index be1a74450d1f4e00bbe1dad5676cbfdb565f962f..12e4e11f882569bc5b62efb07cfb0936de68c7b3 100644
--- a/substrate/frame/merkle-mountain-range/rpc/src/lib.rs
+++ b/substrate/frame/merkle-mountain-range/rpc/src/lib.rs
@@ -16,14 +16,18 @@
 // limitations under the License.
 
 #![warn(missing_docs)]
+#![warn(unused_crate_dependencies)]
 
 //! Node-specific RPC methods for interaction with Merkle Mountain Range pallet.
 
-use std::sync::Arc;
+use std::{marker::PhantomData, sync::Arc};
 
 use codec::{Codec, Encode};
-use jsonrpc_core::{Error, ErrorCode, Result};
-use jsonrpc_derive::rpc;
+use jsonrpsee::{
+	core::{async_trait, RpcResult},
+	proc_macros::rpc,
+	types::error::{CallError, ErrorObject},
+};
 use serde::{Deserialize, Serialize};
 
 use sp_api::ProvideRuntimeApi;
@@ -34,6 +38,11 @@ use sp_runtime::{generic::BlockId, traits::Block as BlockT};
 
 pub use sp_mmr_primitives::MmrApi as MmrRuntimeApi;
 
+const RUNTIME_ERROR: i32 = 8000;
+const MMR_ERROR: i32 = 8010;
+const LEAF_NOT_FOUND_ERROR: i32 = MMR_ERROR + 1;
+const GENERATE_PROOF_ERROR: i32 = MMR_ERROR + 2;
+
 /// Retrieved MMR leaf and its proof.
 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
 #[serde(rename_all = "camelCase")]
@@ -86,7 +95,7 @@ impl<BlockHash> LeafBatchProof<BlockHash> {
 }
 
 /// MMR RPC methods.
-#[rpc]
+#[rpc(client, server)]
 pub trait MmrApi<BlockHash> {
 	/// Generate MMR proof for given leaf index.
 	///
@@ -96,12 +105,12 @@ pub trait MmrApi<BlockHash> {
 	///
 	/// Returns the (full) leaf itself and a proof for this leaf (compact encoding, i.e. hash of
 	/// the leaf). Both parameters are SCALE-encoded.
-	#[rpc(name = "mmr_generateProof")]
+	#[method(name = "mmr_generateProof")]
 	fn generate_proof(
 		&self,
 		leaf_index: LeafIndex,
 		at: Option<BlockHash>,
-	) -> Result<LeafProof<BlockHash>>;
+	) -> RpcResult<LeafProof<BlockHash>>;
 
 	/// Generate MMR proof for the given leaf indices.
 	///
@@ -113,43 +122,43 @@ pub trait MmrApi<BlockHash> {
 	/// the leaves). Both parameters are SCALE-encoded.
 	/// The order of entries in the `leaves` field of the returned struct
 	/// is the same as the order of the entries in `leaf_indices` supplied
-	#[rpc(name = "mmr_generateBatchProof")]
+	#[method(name = "mmr_generateBatchProof")]
 	fn generate_batch_proof(
 		&self,
 		leaf_indices: Vec<LeafIndex>,
 		at: Option<BlockHash>,
-	) -> Result<LeafBatchProof<BlockHash>>;
+	) -> RpcResult<LeafBatchProof<BlockHash>>;
 }
 
-/// An implementation of MMR specific RPC methods.
-pub struct Mmr<C, B> {
-	client: Arc<C>,
-	_marker: std::marker::PhantomData<B>,
+/// MMR RPC methods.
+pub struct MmrRpc<Client, Block> {
+	client: Arc<Client>,
+	_marker: PhantomData<Block>,
 }
 
-impl<C, B> Mmr<C, B> {
+impl<C, B> MmrRpc<C, B> {
 	/// Create new `Mmr` with the given reference to the client.
 	pub fn new(client: Arc<C>) -> Self {
 		Self { client, _marker: Default::default() }
 	}
 }
 
-impl<C, Block, MmrHash> MmrApi<<Block as BlockT>::Hash> for Mmr<C, (Block, MmrHash)>
+#[async_trait]
+impl<Client, Block, MmrHash> MmrApiServer<<Block as BlockT>::Hash>
+	for MmrRpc<Client, (Block, MmrHash)>
 where
 	Block: BlockT,
-	C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,
-	C::Api: MmrRuntimeApi<Block, MmrHash>,
+	Client: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,
+	Client::Api: MmrRuntimeApi<Block, MmrHash>,
 	MmrHash: Codec + Send + Sync + 'static,
 {
 	fn generate_proof(
 		&self,
 		leaf_index: LeafIndex,
 		at: Option<<Block as BlockT>::Hash>,
-	) -> Result<LeafProof<<Block as BlockT>::Hash>> {
+	) -> RpcResult<LeafProof<Block::Hash>> {
 		let api = self.client.runtime_api();
-		let block_hash = at.unwrap_or_else(||
-			// If the block hash is not supplied assume the best block.
-			self.client.info().best_hash);
+		let block_hash = at.unwrap_or_else(|| self.client.info().best_hash);
 
 		let (leaf, proof) = api
 			.generate_proof_with_context(
@@ -167,7 +176,7 @@ where
 		&self,
 		leaf_indices: Vec<LeafIndex>,
 		at: Option<<Block as BlockT>::Hash>,
-	) -> Result<LeafBatchProof<<Block as BlockT>::Hash>> {
+	) -> RpcResult<LeafBatchProof<<Block as BlockT>::Hash>> {
 		let api = self.client.runtime_api();
 		let block_hash = at.unwrap_or_else(||
 			// If the block hash is not supplied assume the best block.
@@ -186,37 +195,31 @@ where
 	}
 }
 
-const RUNTIME_ERROR: i64 = 8000;
-const MMR_ERROR: i64 = 8010;
-
-/// Converts a mmr-specific error into an RPC error.
-fn mmr_error_into_rpc_error(err: MmrError) -> Error {
+/// Converts a mmr-specific error into a [`CallError`].
+fn mmr_error_into_rpc_error(err: MmrError) -> CallError {
+	let data = format!("{:?}", err);
 	match err {
-		MmrError::LeafNotFound => Error {
-			code: ErrorCode::ServerError(MMR_ERROR + 1),
-			message: "Leaf was not found".into(),
-			data: Some(format!("{:?}", err).into()),
-		},
-		MmrError::GenerateProof => Error {
-			code: ErrorCode::ServerError(MMR_ERROR + 2),
-			message: "Error while generating the proof".into(),
-			data: Some(format!("{:?}", err).into()),
-		},
-		_ => Error {
-			code: ErrorCode::ServerError(MMR_ERROR),
-			message: "Unexpected MMR error".into(),
-			data: Some(format!("{:?}", err).into()),
-		},
+		MmrError::LeafNotFound => CallError::Custom(ErrorObject::owned(
+			LEAF_NOT_FOUND_ERROR,
+			"Leaf was not found",
+			Some(data),
+		)),
+		MmrError::GenerateProof => CallError::Custom(ErrorObject::owned(
+			GENERATE_PROOF_ERROR,
+			"Error while generating the proof",
+			Some(data),
+		)),
+		_ => CallError::Custom(ErrorObject::owned(MMR_ERROR, "Unexpected MMR error", Some(data))),
 	}
 }
 
-/// Converts a runtime trap into an RPC error.
-fn runtime_error_into_rpc_error(err: impl std::fmt::Display) -> Error {
-	Error {
-		code: ErrorCode::ServerError(RUNTIME_ERROR),
-		message: "Runtime trapped".into(),
-		data: Some(err.to_string().into()),
-	}
+/// Converts a runtime trap into a [`CallError`].
+fn runtime_error_into_rpc_error(err: impl std::fmt::Debug) -> CallError {
+	CallError::Custom(ErrorObject::owned(
+		RUNTIME_ERROR,
+		"Runtime trapped",
+		Some(format!("{:?}", err)),
+	))
 }
 
 #[cfg(test)]
diff --git a/substrate/frame/state-trie-migration/Cargo.toml b/substrate/frame/state-trie-migration/Cargo.toml
index cae3bb1a9f975458484a0eff4cecfdbb92cd9508..958ab50315427e53dd02674f08ecadd3b313b665 100644
--- a/substrate/frame/state-trie-migration/Cargo.toml
+++ b/substrate/frame/state-trie-migration/Cargo.toml
@@ -31,7 +31,7 @@ substrate-state-trie-migration-rpc = { optional = true, path = "../../utils/fram
 
 [dev-dependencies]
 parking_lot = "0.12.0"
-tokio = { version = "1.10", features = ["macros"] }
+tokio = { version = "1.17.0", features = ["macros"] }
 pallet-balances = { path = "../balances" }
 sp-tracing = { path = "../../primitives/tracing" }
 
diff --git a/substrate/frame/transaction-payment/rpc/Cargo.toml b/substrate/frame/transaction-payment/rpc/Cargo.toml
index b7a353916efbc2bcef48641d979e04dbc09cce5c..6133d3a4b6da1577876b5a2424e970bcd38e4d94 100644
--- a/substrate/frame/transaction-payment/rpc/Cargo.toml
+++ b/substrate/frame/transaction-payment/rpc/Cargo.toml
@@ -14,9 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 codec = { package = "parity-scale-codec", version = "3.0.0" }
-jsonrpc-core = "18.0.0"
-jsonrpc-core-client = "18.0.0"
-jsonrpc-derive = "18.0.0"
+jsonrpsee = { version = "0.12.0", features = ["server", "macros"] }
 pallet-transaction-payment-rpc-runtime-api = { version = "4.0.0-dev", path = "./runtime-api" }
 sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" }
 sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" }
diff --git a/substrate/frame/transaction-payment/rpc/src/lib.rs b/substrate/frame/transaction-payment/rpc/src/lib.rs
index 29d94fa260105129b3611764e611c2801805eea1..b0be19fdb22a9b70b59eee0a49724dc1423c4f5c 100644
--- a/substrate/frame/transaction-payment/rpc/src/lib.rs
+++ b/substrate/frame/transaction-payment/rpc/src/lib.rs
@@ -17,11 +17,14 @@
 
 //! RPC interface for the transaction payment pallet.
 
-pub use self::gen_client::Client as TransactionPaymentClient;
+use std::{convert::TryInto, sync::Arc};
+
 use codec::{Codec, Decode};
-use jsonrpc_core::{Error as RpcError, ErrorCode, Result};
-use jsonrpc_derive::rpc;
-pub use pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi as TransactionPaymentRuntimeApi;
+use jsonrpsee::{
+	core::{async_trait, Error as JsonRpseeError, RpcResult},
+	proc_macros::rpc,
+	types::error::{CallError, ErrorCode, ErrorObject},
+};
 use pallet_transaction_payment_rpc_runtime_api::{FeeDetails, InclusionFee, RuntimeDispatchInfo};
 use sp_api::ProvideRuntimeApi;
 use sp_blockchain::HeaderBackend;
@@ -31,28 +34,31 @@ use sp_runtime::{
 	generic::BlockId,
 	traits::{Block as BlockT, MaybeDisplay},
 };
-use std::sync::Arc;
 
-#[rpc]
+pub use pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi as TransactionPaymentRuntimeApi;
+
+#[rpc(client, server)]
 pub trait TransactionPaymentApi<BlockHash, ResponseType> {
-	#[rpc(name = "payment_queryInfo")]
-	fn query_info(&self, encoded_xt: Bytes, at: Option<BlockHash>) -> Result<ResponseType>;
-	#[rpc(name = "payment_queryFeeDetails")]
+	#[method(name = "payment_queryInfo")]
+	fn query_info(&self, encoded_xt: Bytes, at: Option<BlockHash>) -> RpcResult<ResponseType>;
+
+	#[method(name = "payment_queryFeeDetails")]
 	fn query_fee_details(
 		&self,
 		encoded_xt: Bytes,
 		at: Option<BlockHash>,
-	) -> Result<FeeDetails<NumberOrHex>>;
+	) -> RpcResult<FeeDetails<NumberOrHex>>;
 }
 
-/// A struct that implements the [`TransactionPaymentApi`].
-pub struct TransactionPayment<C, P> {
+/// Provides RPC methods to query a dispatchable's class, weight and fee.
+pub struct TransactionPaymentRpc<C, P> {
+	/// Shared reference to the client.
 	client: Arc<C>,
 	_marker: std::marker::PhantomData<P>,
 }
 
-impl<C, P> TransactionPayment<C, P> {
-	/// Create new `TransactionPayment` with the given reference to the client.
+impl<C, P> TransactionPaymentRpc<C, P> {
+	/// Creates a new instance of the TransactionPaymentRpc helper.
 	pub fn new(client: Arc<C>) -> Self {
 		Self { client, _marker: Default::default() }
 	}
@@ -66,8 +72,8 @@ pub enum Error {
 	RuntimeError,
 }
 
-impl From<Error> for i64 {
-	fn from(e: Error) -> i64 {
+impl From<Error> for i32 {
+	fn from(e: Error) -> i32 {
 		match e {
 			Error::RuntimeError => 1,
 			Error::DecodeError => 2,
@@ -75,66 +81,75 @@ impl From<Error> for i64 {
 	}
 }
 
-impl<C, Block, Balance> TransactionPaymentApi<<Block as BlockT>::Hash, RuntimeDispatchInfo<Balance>>
-	for TransactionPayment<C, Block>
+#[async_trait]
+impl<C, Block, Balance>
+	TransactionPaymentApiServer<<Block as BlockT>::Hash, RuntimeDispatchInfo<Balance>>
+	for TransactionPaymentRpc<C, Block>
 where
 	Block: BlockT,
-	C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,
+	C: ProvideRuntimeApi<Block> + HeaderBackend<Block> + Send + Sync + 'static,
 	C::Api: TransactionPaymentRuntimeApi<Block, Balance>,
-	Balance: Codec + MaybeDisplay + Copy + TryInto<NumberOrHex>,
+	Balance: Codec + MaybeDisplay + Copy + TryInto<NumberOrHex> + Send + Sync + 'static,
 {
 	fn query_info(
 		&self,
 		encoded_xt: Bytes,
-		at: Option<<Block as BlockT>::Hash>,
-	) -> Result<RuntimeDispatchInfo<Balance>> {
+		at: Option<Block::Hash>,
+	) -> RpcResult<RuntimeDispatchInfo<Balance>> {
 		let api = self.client.runtime_api();
-		let at = BlockId::hash(at.unwrap_or_else(||
-			// If the block hash is not supplied assume the best block.
-			self.client.info().best_hash));
+		let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));
 
 		let encoded_len = encoded_xt.len() as u32;
 
-		let uxt: Block::Extrinsic = Decode::decode(&mut &*encoded_xt).map_err(|e| RpcError {
-			code: ErrorCode::ServerError(Error::DecodeError.into()),
-			message: "Unable to query dispatch info.".into(),
-			data: Some(format!("{:?}", e).into()),
+		let uxt: Block::Extrinsic = Decode::decode(&mut &*encoded_xt).map_err(|e| {
+			CallError::Custom(ErrorObject::owned(
+				Error::DecodeError.into(),
+				"Unable to query dispatch info.",
+				Some(format!("{:?}", e)),
+			))
 		})?;
-		api.query_info(&at, uxt, encoded_len).map_err(|e| RpcError {
-			code: ErrorCode::ServerError(Error::RuntimeError.into()),
-			message: "Unable to query dispatch info.".into(),
-			data: Some(e.to_string().into()),
+		api.query_info(&at, uxt, encoded_len).map_err(|e| {
+			CallError::Custom(ErrorObject::owned(
+				Error::RuntimeError.into(),
+				"Unable to query dispatch info.",
+				Some(e.to_string()),
+			))
+			.into()
 		})
 	}
 
 	fn query_fee_details(
 		&self,
 		encoded_xt: Bytes,
-		at: Option<<Block as BlockT>::Hash>,
-	) -> Result<FeeDetails<NumberOrHex>> {
+		at: Option<Block::Hash>,
+	) -> RpcResult<FeeDetails<NumberOrHex>> {
 		let api = self.client.runtime_api();
-		let at = BlockId::hash(at.unwrap_or_else(||
-			// If the block hash is not supplied assume the best block.
-			self.client.info().best_hash));
+		let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));
 
 		let encoded_len = encoded_xt.len() as u32;
 
-		let uxt: Block::Extrinsic = Decode::decode(&mut &*encoded_xt).map_err(|e| RpcError {
-			code: ErrorCode::ServerError(Error::DecodeError.into()),
-			message: "Unable to query fee details.".into(),
-			data: Some(format!("{:?}", e).into()),
+		let uxt: Block::Extrinsic = Decode::decode(&mut &*encoded_xt).map_err(|e| {
+			CallError::Custom(ErrorObject::owned(
+				Error::DecodeError.into(),
+				"Unable to query fee details.",
+				Some(format!("{:?}", e)),
+			))
 		})?;
-		let fee_details = api.query_fee_details(&at, uxt, encoded_len).map_err(|e| RpcError {
-			code: ErrorCode::ServerError(Error::RuntimeError.into()),
-			message: "Unable to query fee details.".into(),
-			data: Some(e.to_string().into()),
+		let fee_details = api.query_fee_details(&at, uxt, encoded_len).map_err(|e| {
+			CallError::Custom(ErrorObject::owned(
+				Error::RuntimeError.into(),
+				"Unable to query fee details.",
+				Some(e.to_string()),
+			))
 		})?;
 
 		let try_into_rpc_balance = |value: Balance| {
-			value.try_into().map_err(|_| RpcError {
-				code: ErrorCode::InvalidParams,
-				message: format!("{} doesn't fit in NumberOrHex representation", value),
-				data: None,
+			value.try_into().map_err(|_| {
+				JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
+					ErrorCode::InvalidParams.code(),
+					format!("{} doesn't fit in NumberOrHex representation", value),
+					None::<()>,
+				)))
 			})
 		};
 
diff --git a/substrate/test-utils/client/src/lib.rs b/substrate/test-utils/client/src/lib.rs
index cdadfb0f10f03278d73f3bff34e8164f92d8577b..148f34246044d9dbbe51f723cc31b8d7f7f97ac5 100644
--- a/substrate/test-utils/client/src/lib.rs
+++ b/substrate/test-utils/client/src/lib.rs
@@ -28,7 +28,7 @@ pub use sc_client_api::{
 };
 pub use sc_client_db::{self, Backend};
 pub use sc_executor::{self, NativeElseWasmExecutor, WasmExecutionMethod};
-pub use sc_service::{client, RpcHandlers, RpcSession};
+pub use sc_service::{client, RpcHandlers};
 pub use sp_consensus;
 pub use sp_keyring::{
 	ed25519::Keyring as Ed25519Keyring, sr25519::Keyring as Sr25519Keyring, AccountKeyring,
@@ -37,10 +37,7 @@ pub use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
 pub use sp_runtime::{Storage, StorageChild};
 pub use sp_state_machine::ExecutionStrategy;
 
-use futures::{
-	future::{Future, FutureExt},
-	stream::StreamExt,
-};
+use futures::{future::Future, stream::StreamExt};
 use sc_client_api::BlockchainEvents;
 use sc_service::client::{ClientConfig, LocalCallExecutor};
 use serde::Deserialize;
@@ -297,16 +294,14 @@ impl<Block: BlockT, D, Backend, G: GenesisInit>
 /// The output of an RPC transaction.
 pub struct RpcTransactionOutput {
 	/// The output string of the transaction if any.
-	pub result: Option<String>,
-	/// The session object.
-	pub session: RpcSession,
+	pub result: String,
 	/// An async receiver if data will be returned via a callback.
 	pub receiver: futures::channel::mpsc::UnboundedReceiver<String>,
 }
 
 impl std::fmt::Debug for RpcTransactionOutput {
 	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-		write!(f, "RpcTransactionOutput {{ result: {:?}, session, receiver }}", self.result)
+		write!(f, "RpcTransactionOutput {{ result: {:?}, receiver }}", self.result)
 	}
 }
 
@@ -328,56 +323,51 @@ impl std::fmt::Display for RpcTransactionError {
 }
 
 /// An extension trait for `RpcHandlers`.
+#[async_trait::async_trait]
 pub trait RpcHandlersExt {
 	/// Send a transaction through the RpcHandlers.
-	fn send_transaction(
+	async fn send_transaction(
 		&self,
 		extrinsic: OpaqueExtrinsic,
-	) -> Pin<Box<dyn Future<Output = Result<RpcTransactionOutput, RpcTransactionError>> + Send>>;
+	) -> Result<RpcTransactionOutput, RpcTransactionError>;
 }
 
+#[async_trait::async_trait]
 impl RpcHandlersExt for RpcHandlers {
-	fn send_transaction(
+	async fn send_transaction(
 		&self,
 		extrinsic: OpaqueExtrinsic,
-	) -> Pin<Box<dyn Future<Output = Result<RpcTransactionOutput, RpcTransactionError>> + Send>> {
-		let (tx, rx) = futures::channel::mpsc::unbounded();
-		let mem = RpcSession::new(tx);
-		Box::pin(
-			self.rpc_query(
-				&mem,
-				&format!(
-					r#"{{
+	) -> Result<RpcTransactionOutput, RpcTransactionError> {
+		let (result, rx) = self
+			.rpc_query(&format!(
+				r#"{{
 						"jsonrpc": "2.0",
 						"method": "author_submitExtrinsic",
 						"params": ["0x{}"],
 						"id": 0
 					}}"#,
-					hex::encode(extrinsic.encode())
-				),
-			)
-			.map(move |result| parse_rpc_result(result, mem, rx)),
-		)
+				hex::encode(extrinsic.encode())
+			))
+			.await
+			.expect("valid JSON-RPC request object; qed");
+		parse_rpc_result(result, rx)
 	}
 }
 
 pub(crate) fn parse_rpc_result(
-	result: Option<String>,
-	session: RpcSession,
+	result: String,
 	receiver: futures::channel::mpsc::UnboundedReceiver<String>,
 ) -> Result<RpcTransactionOutput, RpcTransactionError> {
-	if let Some(ref result) = result {
-		let json: serde_json::Value =
-			serde_json::from_str(result).expect("the result can only be a JSONRPC string; qed");
-		let error = json.as_object().expect("JSON result is always an object; qed").get("error");
-
-		if let Some(error) = error {
-			return Err(serde_json::from_value(error.clone())
-				.expect("the JSONRPC result's error is always valid; qed"))
-		}
+	let json: serde_json::Value =
+		serde_json::from_str(&result).expect("the result can only be a JSONRPC string; qed");
+	let error = json.as_object().expect("JSON result is always an object; qed").get("error");
+
+	if let Some(error) = error {
+		return Err(serde_json::from_value(error.clone())
+			.expect("the JSONRPC result's error is always valid; qed"))
 	}
 
-	Ok(RpcTransactionOutput { result, session, receiver })
+	Ok(RpcTransactionOutput { result, receiver })
 }
 
 /// An extension trait for `BlockchainEvents`.
@@ -418,40 +408,23 @@ where
 
 #[cfg(test)]
 mod tests {
-	use sc_service::RpcSession;
-
-	fn create_session_and_receiver(
-	) -> (RpcSession, futures::channel::mpsc::UnboundedReceiver<String>) {
-		let (tx, rx) = futures::channel::mpsc::unbounded();
-		let mem = RpcSession::new(tx.into());
-
-		(mem, rx)
-	}
-
 	#[test]
 	fn parses_error_properly() {
-		let (mem, rx) = create_session_and_receiver();
-		assert!(super::parse_rpc_result(None, mem, rx).is_ok());
-
-		let (mem, rx) = create_session_and_receiver();
+		let (_, rx) = futures::channel::mpsc::unbounded();
 		assert!(super::parse_rpc_result(
-			Some(
-				r#"{
+			r#"{
 				"jsonrpc": "2.0",
 				"result": 19,
 				"id": 1
 			}"#
-				.to_string()
-			),
-			mem,
+			.to_string(),
 			rx
 		)
-		.is_ok(),);
+		.is_ok());
 
-		let (mem, rx) = create_session_and_receiver();
+		let (_, rx) = futures::channel::mpsc::unbounded();
 		let error = super::parse_rpc_result(
-			Some(
-				r#"{
+			r#"{
 				"jsonrpc": "2.0",
 				"error": {
 					"code": -32601,
@@ -459,9 +432,7 @@ mod tests {
 				},
 				"id": 1
 			}"#
-				.to_string(),
-			),
-			mem,
+			.to_string(),
 			rx,
 		)
 		.unwrap_err();
@@ -469,10 +440,9 @@ mod tests {
 		assert_eq!(error.message, "Method not found");
 		assert!(error.data.is_none());
 
-		let (mem, rx) = create_session_and_receiver();
+		let (_, rx) = futures::channel::mpsc::unbounded();
 		let error = super::parse_rpc_result(
-			Some(
-				r#"{
+			r#"{
 				"jsonrpc": "2.0",
 				"error": {
 					"code": -32601,
@@ -481,9 +451,7 @@ mod tests {
 				},
 				"id": 1
 			}"#
-				.to_string(),
-			),
-			mem,
+			.to_string(),
 			rx,
 		)
 		.unwrap_err();
diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs
index 94325ae9c1ab1f6c7704ca979ebb86a544b3c38a..aef061d952a967a8823bde7e47c56cf203be8c32 100644
--- a/substrate/test-utils/runtime/src/lib.rs
+++ b/substrate/test-utils/runtime/src/lib.rs
@@ -176,6 +176,19 @@ impl serde::Serialize for Extrinsic {
 	}
 }
 
+// rustc can't deduce this trait bound https://github.com/rust-lang/rust/issues/48214
+#[cfg(feature = "std")]
+impl<'a> serde::Deserialize<'a> for Extrinsic {
+	fn deserialize<D>(de: D) -> Result<Self, D::Error>
+	where
+		D: serde::Deserializer<'a>,
+	{
+		let r = sp_core::bytes::deserialize(de)?;
+		Decode::decode(&mut &r[..])
+			.map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
+	}
+}
+
 impl BlindCheckable for Extrinsic {
 	type Checked = Self;
 
diff --git a/substrate/utils/frame/remote-externalities/Cargo.toml b/substrate/utils/frame/remote-externalities/Cargo.toml
index 49c004c3c074d79729ae1e9548104a30f76344bc..4a931470eafaca17c050aebac8589b619e140c23 100644
--- a/substrate/utils/frame/remote-externalities/Cargo.toml
+++ b/substrate/utils/frame/remote-externalities/Cargo.toml
@@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 [dependencies]
 codec = { package = "parity-scale-codec", version = "3.0.0" }
 env_logger = "0.9"
-jsonrpsee = { version = "0.10.1", features = ["ws-client", "macros"] }
+jsonrpsee = { version = "0.12.0", features = ["ws-client", "macros"] }
 log = "0.4.16"
 serde = "1.0.136"
 serde_json = "1.0"
diff --git a/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml b/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml
index 3913bae425757adaf4a0cc01bed34e61094a9ded..726cc9f989ced8c670981997f6bd2b20fb06a506 100644
--- a/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml
+++ b/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml
@@ -25,9 +25,7 @@ sp-state-machine = { path = "../../../../primitives/state-machine" }
 sp-trie = { path = "../../../../primitives/trie" }
 trie-db = { version = "0.23.1" }
 
-jsonrpc-core = "18.0.0"
-jsonrpc-core-client = "18.0.0"
-jsonrpc-derive = "18.0.0"
+jsonrpsee = { version = "0.12.0", features = ["server", "macros"] }
 
 # Substrate Dependencies
 sc-client-api = { version = "4.0.0-dev", path = "../../../../client/api" }
diff --git a/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs b/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs
index 2e3dd08a7db7e2add54e342aa31a29253c089ae9..531bf463f65235be99ae02cf15db12570e769a89 100644
--- a/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs
+++ b/substrate/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs
@@ -17,8 +17,11 @@
 
 //! Rpc for state migration.
 
-use jsonrpc_core::{Error, ErrorCode, Result};
-use jsonrpc_derive::rpc;
+use jsonrpsee::{
+	core::{Error as JsonRpseeError, RpcResult},
+	proc_macros::rpc,
+	types::error::{CallError, ErrorCode, ErrorObject},
+};
 use sc_rpc_api::DenyUnsafe;
 use serde::{Deserialize, Serialize};
 use sp_runtime::{generic::BlockId, traits::Block as BlockT};
@@ -107,15 +110,15 @@ pub struct MigrationStatusResult {
 }
 
 /// Migration RPC methods.
-#[rpc]
+#[rpc(server)]
 pub trait StateMigrationApi<BlockHash> {
 	/// Check current migration state.
 	///
 	/// This call is performed locally without submitting any transactions. Thus executing this
 	/// won't change any state. Nonetheless it is a VERY costy call that should be
 	/// only exposed to trusted peers.
-	#[rpc(name = "state_trieMigrationStatus")]
-	fn call(&self, at: Option<BlockHash>) -> Result<MigrationStatusResult>;
+	#[method(name = "state_trieMigrationStatus")]
+	fn call(&self, at: Option<BlockHash>) -> RpcResult<MigrationStatusResult>;
 }
 
 /// An implementation of state migration specific RPC methods.
@@ -133,16 +136,14 @@ impl<C, B, BA> MigrationRpc<C, B, BA> {
 	}
 }
 
-impl<C, B, BA> StateMigrationApi<<B as BlockT>::Hash> for MigrationRpc<C, B, BA>
+impl<C, B, BA> StateMigrationApiServer<<B as BlockT>::Hash> for MigrationRpc<C, B, BA>
 where
 	B: BlockT,
 	C: Send + Sync + 'static + sc_client_api::HeaderBackend<B>,
 	BA: 'static + sc_client_api::backend::Backend<B>,
 {
-	fn call(&self, at: Option<<B as BlockT>::Hash>) -> Result<MigrationStatusResult> {
-		if let Err(err) = self.deny_unsafe.check_if_safe() {
-			return Err(err.into())
-		}
+	fn call(&self, at: Option<<B as BlockT>::Hash>) -> RpcResult<MigrationStatusResult> {
+		self.deny_unsafe.check_if_safe()?;
 
 		let block_id = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));
 		let state = self.backend.state_at(block_id).map_err(error_into_rpc_err)?;
@@ -155,10 +156,10 @@ where
 	}
 }
 
-fn error_into_rpc_err(err: impl std::fmt::Display) -> Error {
-	Error {
-		code: ErrorCode::InternalError,
-		message: "Error while checking migration state".into(),
-		data: Some(err.to_string().into()),
-	}
+fn error_into_rpc_err(err: impl std::fmt::Display) -> JsonRpseeError {
+	JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
+		ErrorCode::InternalError.code(),
+		"Error while checking migration state",
+		Some(err.to_string()),
+	)))
 }
diff --git a/substrate/utils/frame/rpc/support/Cargo.toml b/substrate/utils/frame/rpc/support/Cargo.toml
index f9967758928e85d9e3a26909503bccced984a869..0c6d082406421e857c89a919e9a8fa93277ee53f 100644
--- a/substrate/utils/frame/rpc/support/Cargo.toml
+++ b/substrate/utils/frame/rpc/support/Cargo.toml
@@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 [dependencies]
 codec = { package = "parity-scale-codec", version = "3.0.0" }
 futures = "0.3.21"
-jsonrpc-client-transports = { version = "18.0.0", features = ["http"] }
+jsonrpsee = { version = "0.12.0", features = ["jsonrpsee-types"] }
 serde = "1"
 frame-support = { version = "4.0.0-dev", path = "../../../../frame/support" }
 sc-rpc-api = { version = "0.10.0-dev", path = "../../../../client/rpc-api" }
@@ -25,5 +25,6 @@ sp-storage = { version = "6.0.0", path = "../../../../primitives/storage" }
 
 [dev-dependencies]
 scale-info = "2.0.1"
+jsonrpsee = { version = "0.12.0", features = ["ws-client", "jsonrpsee-types"] }
 tokio = "1.17.0"
 frame-system = { version = "4.0.0-dev", path = "../../../../frame/system" }
diff --git a/substrate/utils/frame/rpc/support/src/lib.rs b/substrate/utils/frame/rpc/support/src/lib.rs
index 5d7cba19f643c87980f279d7d0c7242b3eda8582..2ee007c84f0aa6afd83ba036a5e3e92f315287b6 100644
--- a/substrate/utils/frame/rpc/support/src/lib.rs
+++ b/substrate/utils/frame/rpc/support/src/lib.rs
@@ -15,7 +15,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Combines [sc_rpc_api::state::StateClient] with [frame_support::storage::generator] traits
+//! Combines [sc_rpc_api::state::StateApiClient] with [frame_support::storage::generator] traits
 //! to provide strongly typed chain state queries over rpc.
 
 #![warn(missing_docs)]
@@ -23,29 +23,26 @@
 use codec::{DecodeAll, FullCodec, FullEncode};
 use core::marker::PhantomData;
 use frame_support::storage::generator::{StorageDoubleMap, StorageMap, StorageValue};
-use jsonrpc_client_transports::RpcError;
-use sc_rpc_api::state::StateClient;
+use jsonrpsee::core::Error as RpcError;
+use sc_rpc_api::state::StateApiClient;
 use serde::{de::DeserializeOwned, Serialize};
 use sp_storage::{StorageData, StorageKey};
 
 /// A typed query on chain state usable from an RPC client.
 ///
 /// ```no_run
-/// # use jsonrpc_client_transports::RpcError;
-/// # use jsonrpc_client_transports::transports::http;
+/// # use jsonrpsee::core::Error as RpcError;
+/// # use jsonrpsee::ws_client::WsClientBuilder;
 /// # use codec::Encode;
 /// # use frame_support::{decl_storage, decl_module};
 /// # use substrate_frame_rpc_support::StorageQuery;
 /// # use frame_system::Config;
-/// # use sc_rpc_api::state::StateClient;
+/// # use sc_rpc_api::state::StateApiClient;
 /// #
 /// # // Hash would normally be <TestRuntime as frame_system::Config>::Hash, but we don't have
 /// # // frame_system::Config implemented for TestRuntime. Here we just pretend.
 /// # type Hash = ();
 /// #
-/// # fn main() -> Result<(), RpcError> {
-/// #     tokio::runtime::Runtime::new().unwrap().block_on(test())
-/// # }
 /// #
 /// # struct TestRuntime;
 /// #
@@ -66,24 +63,25 @@ use sp_storage::{StorageData, StorageKey};
 ///     }
 /// }
 ///
-/// # async fn test() -> Result<(), RpcError> {
-/// let conn = http::connect("http://[::1]:9933").await?;
-/// let cl = StateClient::<Hash>::new(conn);
+/// #[tokio::main]
+/// async fn main() -> Result<(), RpcError> {
+///     let cl = WsClientBuilder::default().build("ws://[::1]:9944").await?;
 ///
-/// let q = StorageQuery::value::<LastActionId>();
-/// let _: Option<u64> = q.get(&cl, None).await?;
+///     let q = StorageQuery::value::<LastActionId>();
+///     let hash = None::<Hash>;
+///     let _: Option<u64> = q.get(&cl, hash).await?;
 ///
-/// let q = StorageQuery::map::<Voxels, _>((0, 0, 0));
-/// let _: Option<Block> = q.get(&cl, None).await?;
+///     let q = StorageQuery::map::<Voxels, _>((0, 0, 0));
+///     let _: Option<Block> = q.get(&cl, hash).await?;
 ///
-/// let q = StorageQuery::map::<Actions, _>(12);
-/// let _: Option<Loc> = q.get(&cl, None).await?;
+///     let q = StorageQuery::map::<Actions, _>(12);
+///     let _: Option<Loc> = q.get(&cl, hash).await?;
 ///
-/// let q = StorageQuery::double_map::<Prefab, _, _>(3, (0, 0, 0));
-/// let _: Option<Block> = q.get(&cl, None).await?;
-/// #
-/// # Ok(())
-/// # }
+///     let q = StorageQuery::double_map::<Prefab, _, _>(3, (0, 0, 0));
+///     let _: Option<Block> = q.get(&cl, hash).await?;
+///
+///     Ok(())
+/// }
 /// ```
 #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
 pub struct StorageQuery<V> {
@@ -120,14 +118,18 @@ impl<V: FullCodec> StorageQuery<V> {
 	///
 	/// block_index indicates the block for which state will be queried. A value of None indicates
 	/// the latest block.
-	pub async fn get<Hash: Send + Sync + 'static + DeserializeOwned + Serialize>(
+	pub async fn get<Hash, StateClient>(
 		self,
-		state_client: &StateClient<Hash>,
+		state_client: &StateClient,
 		block_index: Option<Hash>,
-	) -> Result<Option<V>, RpcError> {
+	) -> Result<Option<V>, RpcError>
+	where
+		Hash: Send + Sync + 'static + DeserializeOwned + Serialize,
+		StateClient: StateApiClient<Hash> + Sync,
+	{
 		let opt: Option<StorageData> = state_client.storage(self.key, block_index).await?;
 		opt.map(|encoded| V::decode_all(&mut &encoded.0[..]))
 			.transpose()
-			.map_err(|decode_err| RpcError::Other(Box::new(decode_err)))
+			.map_err(|decode_err| RpcError::Custom(decode_err.to_string()))
 	}
 }
diff --git a/substrate/utils/frame/rpc/system/Cargo.toml b/substrate/utils/frame/rpc/system/Cargo.toml
index 5252d96af3f75b47f10cc19915cf240fa442fb51..c95ae4793ca6a3e7c6cde237bc2615e752606207 100644
--- a/substrate/utils/frame/rpc/system/Cargo.toml
+++ b/substrate/utils/frame/rpc/system/Cargo.toml
@@ -13,11 +13,10 @@ readme = "README.md"
 targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
+serde_json = "1"
 codec = { package = "parity-scale-codec", version = "3.0.0" }
+jsonrpsee = { version = "0.12.0", features = ["server"] }
 futures = "0.3.21"
-jsonrpc-core = "18.0.0"
-jsonrpc-core-client = "18.0.0"
-jsonrpc-derive = "18.0.0"
 log = "0.4.16"
 frame-system-rpc-runtime-api = { version = "4.0.0-dev", path = "../../../../frame/system/rpc/runtime-api" }
 sc-client-api = { version = "4.0.0-dev", path = "../../../../client/api" }
@@ -31,5 +30,7 @@ sp-runtime = { version = "6.0.0", path = "../../../../primitives/runtime" }
 
 [dev-dependencies]
 sc-transaction-pool = { version = "4.0.0-dev", path = "../../../../client/transaction-pool" }
+tokio = "1.17.0"
+assert_matches = "1.3.0"
 sp-tracing = { version = "5.0.0", path = "../../../../primitives/tracing" }
 substrate-test-runtime-client = { version = "2.0.0", path = "../../../../test-utils/runtime/client" }
diff --git a/substrate/utils/frame/rpc/system/src/lib.rs b/substrate/utils/frame/rpc/system/src/lib.rs
index b7da7730f09209ab2f5ae45f22f1faecf67f23f9..b044035c8120ea64783ef8f68a2ffb05eb52df66 100644
--- a/substrate/utils/frame/rpc/system/src/lib.rs
+++ b/substrate/utils/frame/rpc/system/src/lib.rs
@@ -17,12 +17,15 @@
 
 //! System FRAME specific RPC methods.
 
-use std::sync::Arc;
+use std::{fmt::Display, sync::Arc};
+
+use codec::{self, Codec, Decode, Encode};
+use jsonrpsee::{
+	core::{async_trait, RpcResult},
+	proc_macros::rpc,
+	types::error::{CallError, ErrorObject},
+};
 
-use codec::{Codec, Decode, Encode};
-use futures::FutureExt;
-use jsonrpc_core::{Error as RpcError, ErrorCode};
-use jsonrpc_derive::rpc;
 use sc_rpc_api::DenyUnsafe;
 use sc_transaction_pool_api::{InPoolTransaction, TransactionPool};
 use sp_api::ApiExt;
@@ -31,26 +34,22 @@ use sp_blockchain::HeaderBackend;
 use sp_core::{hexdisplay::HexDisplay, Bytes};
 use sp_runtime::{generic::BlockId, legacy, traits};
 
-pub use self::gen_client::Client as SystemClient;
 pub use frame_system_rpc_runtime_api::AccountNonceApi;
 
-/// Future that resolves to account nonce.
-type FutureResult<T> = jsonrpc_core::BoxFuture<Result<T, RpcError>>;
-
 /// System RPC methods.
-#[rpc]
+#[rpc(client, server)]
 pub trait SystemApi<BlockHash, AccountId, Index> {
 	/// Returns the next valid index (aka nonce) for given account.
 	///
 	/// This method takes into consideration all pending transactions
 	/// currently in the pool and if no transactions are found in the pool
 	/// it fallbacks to query the index from the runtime (aka. state nonce).
-	#[rpc(name = "system_accountNextIndex", alias("account_nextIndex"))]
-	fn nonce(&self, account: AccountId) -> FutureResult<Index>;
+	#[method(name = "system_accountNextIndex", aliases = ["account_nextIndex"])]
+	async fn nonce(&self, account: AccountId) -> RpcResult<Index>;
 
 	/// Dry run an extrinsic at a given block. Return SCALE encoded ApplyExtrinsicResult.
-	#[rpc(name = "system_dryRun", alias("system_dryRunAt"))]
-	fn dry_run(&self, extrinsic: Bytes, at: Option<BlockHash>) -> FutureResult<Bytes>;
+	#[method(name = "system_dryRun", aliases = ["system_dryRunAt"])]
+	async fn dry_run(&self, extrinsic: Bytes, at: Option<BlockHash>) -> RpcResult<Bytes>;
 }
 
 /// Error type of this RPC api.
@@ -61,8 +60,8 @@ pub enum Error {
 	RuntimeError,
 }
 
-impl From<Error> for i64 {
-	fn from(e: Error) -> i64 {
+impl From<Error> for i32 {
+	fn from(e: Error) -> i32 {
 		match e {
 			Error::RuntimeError => 1,
 			Error::DecodeError => 2,
@@ -71,22 +70,23 @@ impl From<Error> for i64 {
 }
 
 /// An implementation of System-specific RPC methods on full client.
-pub struct FullSystem<P: TransactionPool, C, B> {
+pub struct SystemRpc<P: TransactionPool, C, B> {
 	client: Arc<C>,
 	pool: Arc<P>,
 	deny_unsafe: DenyUnsafe,
 	_marker: std::marker::PhantomData<B>,
 }
 
-impl<P: TransactionPool, C, B> FullSystem<P, C, B> {
+impl<P: TransactionPool, C, B> SystemRpc<P, C, B> {
 	/// Create new `FullSystem` given client and transaction pool.
 	pub fn new(client: Arc<C>, pool: Arc<P>, deny_unsafe: DenyUnsafe) -> Self {
-		FullSystem { client, pool, deny_unsafe, _marker: Default::default() }
+		Self { client, pool, deny_unsafe, _marker: Default::default() }
 	}
 }
 
-impl<P, C, Block, AccountId, Index> SystemApi<<Block as traits::Block>::Hash, AccountId, Index>
-	for FullSystem<P, C, Block>
+#[async_trait]
+impl<P, C, Block, AccountId, Index>
+	SystemApiServer<<Block as traits::Block>::Hash, AccountId, Index> for SystemRpc<P, C, Block>
 where
 	C: sp_api::ProvideRuntimeApi<Block>,
 	C: HeaderBackend<Block>,
@@ -95,88 +95,83 @@ where
 	C::Api: BlockBuilder<Block>,
 	P: TransactionPool + 'static,
 	Block: traits::Block,
-	AccountId: Clone + std::fmt::Display + Codec,
-	Index: Clone + std::fmt::Display + Codec + Send + traits::AtLeast32Bit + 'static,
+	AccountId: Clone + Display + Codec + Send + 'static,
+	Index: Clone + Display + Codec + Send + traits::AtLeast32Bit + 'static,
 {
-	fn nonce(&self, account: AccountId) -> FutureResult<Index> {
-		let get_nonce = || {
-			let api = self.client.runtime_api();
-			let best = self.client.info().best_hash;
-			let at = BlockId::hash(best);
-
-			let nonce = api.account_nonce(&at, account.clone()).map_err(|e| RpcError {
-				code: ErrorCode::ServerError(Error::RuntimeError.into()),
-				message: "Unable to query nonce.".into(),
-				data: Some(e.to_string().into()),
-			})?;
-
-			Ok(adjust_nonce(&*self.pool, account, nonce))
-		};
-
-		let res = get_nonce();
-		async move { res }.boxed()
+	async fn nonce(&self, account: AccountId) -> RpcResult<Index> {
+		let api = self.client.runtime_api();
+		let best = self.client.info().best_hash;
+		let at = BlockId::hash(best);
+
+		let nonce = api.account_nonce(&at, account.clone()).map_err(|e| {
+			CallError::Custom(ErrorObject::owned(
+				Error::RuntimeError.into(),
+				"Unable to query nonce.",
+				Some(e.to_string()),
+			))
+		})?;
+		Ok(adjust_nonce(&*self.pool, account, nonce))
 	}
 
-	fn dry_run(
+	async fn dry_run(
 		&self,
 		extrinsic: Bytes,
 		at: Option<<Block as traits::Block>::Hash>,
-	) -> FutureResult<Bytes> {
-		if let Err(err) = self.deny_unsafe.check_if_safe() {
-			return async move { Err(err.into()) }.boxed()
-		}
+	) -> RpcResult<Bytes> {
+		self.deny_unsafe.check_if_safe()?;
+		let api = self.client.runtime_api();
+		let at = BlockId::<Block>::hash(at.unwrap_or_else(||
+			// If the block hash is not supplied assume the best block.
+			self.client.info().best_hash));
+
+		let uxt: <Block as traits::Block>::Extrinsic =
+			Decode::decode(&mut &*extrinsic).map_err(|e| {
+				CallError::Custom(ErrorObject::owned(
+					Error::DecodeError.into(),
+					"Unable to dry run extrinsic",
+					Some(e.to_string()),
+				))
+			})?;
 
-		let dry_run = || {
-			let api = self.client.runtime_api();
-			let at = BlockId::<Block>::hash(at.unwrap_or_else(||
-				// If the block hash is not supplied assume the best block.
-				self.client.info().best_hash));
-
-			let uxt: <Block as traits::Block>::Extrinsic = Decode::decode(&mut &*extrinsic)
-				.map_err(|e| RpcError {
-					code: ErrorCode::ServerError(Error::DecodeError.into()),
-					message: "Unable to dry run extrinsic.".into(),
-					data: Some(e.to_string().into()),
-				})?;
-
-			let api_version = api
-				.api_version::<dyn BlockBuilder<Block>>(&at)
-				.map_err(|e| RpcError {
-					code: ErrorCode::ServerError(Error::RuntimeError.into()),
-					message: "Unable to dry run extrinsic.".into(),
-					data: Some(e.to_string().into()),
-				})?
-				.ok_or_else(|| RpcError {
-					code: ErrorCode::ServerError(Error::RuntimeError.into()),
-					message: "Unable to dry run extrinsic.".into(),
-					data: Some(
-						format!("Could not find `BlockBuilder` api for block `{:?}`.", at).into(),
-					),
-				})?;
-
-			let result = if api_version < 6 {
-				#[allow(deprecated)]
-				api.apply_extrinsic_before_version_6(&at, uxt)
-					.map(legacy::byte_sized_error::convert_to_latest)
-					.map_err(|e| RpcError {
-						code: ErrorCode::ServerError(Error::RuntimeError.into()),
-						message: "Unable to dry run extrinsic.".into(),
-						data: Some(e.to_string().into()),
-					})?
-			} else {
-				api.apply_extrinsic(&at, uxt).map_err(|e| RpcError {
-					code: ErrorCode::ServerError(Error::RuntimeError.into()),
-					message: "Unable to dry run extrinsic.".into(),
-					data: Some(e.to_string().into()),
-				})?
-			};
+		let api_version = api
+			.api_version::<dyn BlockBuilder<Block>>(&at)
+			.map_err(|e| {
+				CallError::Custom(ErrorObject::owned(
+					Error::RuntimeError.into(),
+					"Unable to dry run extrinsic.",
+					Some(e.to_string()),
+				))
+			})?
+			.ok_or_else(|| {
+				CallError::Custom(ErrorObject::owned(
+					Error::RuntimeError.into(),
+					"Unable to dry run extrinsic.",
+					Some(format!("Could not find `BlockBuilder` api for block `{:?}`.", at)),
+				))
+			})?;
 
-			Ok(Encode::encode(&result).into())
+		let result = if api_version < 6 {
+			#[allow(deprecated)]
+			api.apply_extrinsic_before_version_6(&at, uxt)
+				.map(legacy::byte_sized_error::convert_to_latest)
+				.map_err(|e| {
+					CallError::Custom(ErrorObject::owned(
+						Error::RuntimeError.into(),
+						"Unable to dry run extrinsic.",
+						Some(e.to_string()),
+					))
+				})?
+		} else {
+			api.apply_extrinsic(&at, uxt).map_err(|e| {
+				CallError::Custom(ErrorObject::owned(
+					Error::RuntimeError.into(),
+					"Unable to dry run extrinsic.",
+					Some(e.to_string()),
+				))
+			})?
 		};
 
-		let res = dry_run();
-
-		async move { res }.boxed()
+		Ok(Encode::encode(&result).into())
 	}
 }
 
@@ -220,7 +215,9 @@ where
 mod tests {
 	use super::*;
 
+	use assert_matches::assert_matches;
 	use futures::executor::block_on;
+	use jsonrpsee::{core::Error as JsonRpseeError, types::error::CallError};
 	use sc_transaction_pool::BasicPool;
 	use sp_runtime::{
 		transaction_validity::{InvalidTransaction, TransactionValidityError},
@@ -228,8 +225,8 @@ mod tests {
 	};
 	use substrate_test_runtime_client::{runtime::Transfer, AccountKeyring};
 
-	#[test]
-	fn should_return_next_nonce_for_some_account() {
+	#[tokio::test]
+	async fn should_return_next_nonce_for_some_account() {
 		sp_tracing::try_init_simple();
 
 		// given
@@ -254,17 +251,17 @@ mod tests {
 		let ext1 = new_transaction(1);
 		block_on(pool.submit_one(&BlockId::number(0), source, ext1)).unwrap();
 
-		let accounts = FullSystem::new(client, pool, DenyUnsafe::Yes);
+		let accounts = SystemRpc::new(client, pool, DenyUnsafe::Yes);
 
 		// when
-		let nonce = accounts.nonce(AccountKeyring::Alice.into());
+		let nonce = accounts.nonce(AccountKeyring::Alice.into()).await;
 
 		// then
-		assert_eq!(block_on(nonce).unwrap(), 2);
+		assert_eq!(nonce.unwrap(), 2);
 	}
 
-	#[test]
-	fn dry_run_should_deny_unsafe() {
+	#[tokio::test]
+	async fn dry_run_should_deny_unsafe() {
 		sp_tracing::try_init_simple();
 
 		// given
@@ -273,17 +270,17 @@ mod tests {
 		let pool =
 			BasicPool::new_full(Default::default(), true.into(), None, spawner, client.clone());
 
-		let accounts = FullSystem::new(client, pool, DenyUnsafe::Yes);
+		let accounts = SystemRpc::new(client, pool, DenyUnsafe::Yes);
 
 		// when
-		let res = accounts.dry_run(vec![].into(), None);
-
-		// then
-		assert_eq!(block_on(res), Err(sc_rpc_api::UnsafeRpcError.into()));
+		let res = accounts.dry_run(vec![].into(), None).await;
+		assert_matches!(res, Err(JsonRpseeError::Call(CallError::Custom(e))) => {
+			assert!(e.message().contains("RPC call is unsafe to be called externally"));
+		});
 	}
 
-	#[test]
-	fn dry_run_should_work() {
+	#[tokio::test]
+	async fn dry_run_should_work() {
 		sp_tracing::try_init_simple();
 
 		// given
@@ -292,7 +289,7 @@ mod tests {
 		let pool =
 			BasicPool::new_full(Default::default(), true.into(), None, spawner, client.clone());
 
-		let accounts = FullSystem::new(client, pool, DenyUnsafe::No);
+		let accounts = SystemRpc::new(client, pool, DenyUnsafe::No);
 
 		let tx = Transfer {
 			from: AccountKeyring::Alice.into(),
@@ -303,16 +300,15 @@ mod tests {
 		.into_signed_tx();
 
 		// when
-		let res = accounts.dry_run(tx.encode().into(), None);
+		let bytes = accounts.dry_run(tx.encode().into(), None).await.expect("Call is successful");
 
 		// then
-		let bytes = block_on(res).unwrap().0;
-		let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_slice()).unwrap();
+		let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_ref()).unwrap();
 		assert_eq!(apply_res, Ok(Ok(())));
 	}
 
-	#[test]
-	fn dry_run_should_indicate_error() {
+	#[tokio::test]
+	async fn dry_run_should_indicate_error() {
 		sp_tracing::try_init_simple();
 
 		// given
@@ -321,7 +317,7 @@ mod tests {
 		let pool =
 			BasicPool::new_full(Default::default(), true.into(), None, spawner, client.clone());
 
-		let accounts = FullSystem::new(client, pool, DenyUnsafe::No);
+		let accounts = SystemRpc::new(client, pool, DenyUnsafe::No);
 
 		let tx = Transfer {
 			from: AccountKeyring::Alice.into(),
@@ -332,11 +328,10 @@ mod tests {
 		.into_signed_tx();
 
 		// when
-		let res = accounts.dry_run(tx.encode().into(), None);
+		let bytes = accounts.dry_run(tx.encode().into(), None).await.expect("Call is successful");
 
 		// then
-		let bytes = block_on(res).unwrap().0;
-		let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_slice()).unwrap();
+		let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_ref()).unwrap();
 		assert_eq!(apply_res, Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)));
 	}
 }
diff --git a/substrate/utils/frame/try-runtime/cli/Cargo.toml b/substrate/utils/frame/try-runtime/cli/Cargo.toml
index 2c0a2787b1dac2390154dab701b897ea783388ef..a5e658fc6847651742ff1af565384dec130b8f50 100644
--- a/substrate/utils/frame/try-runtime/cli/Cargo.toml
+++ b/substrate/utils/frame/try-runtime/cli/Cargo.toml
@@ -14,12 +14,12 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 clap = { version = "3.1.6", features = ["derive"] }
-jsonrpsee = { version = "0.10.1", default-features = false, features = ["ws-client"] }
 log = "0.4.16"
 parity-scale-codec = "3.0.0"
 serde = "1.0.136"
 zstd = { version = "0.10.0", default-features = false }
 remote-externalities = { version = "0.10.0-dev", path = "../../remote-externalities" }
+jsonrpsee = { version = "0.12.0", default-features = false, features = ["ws-client"] }
 sc-chain-spec = { version = "4.0.0-dev", path = "../../../../client/chain-spec" }
 sc-cli = { version = "0.10.0-dev", path = "../../../../client/cli" }
 sc-executor = { version = "0.10.0-dev", path = "../../../../client/executor" }