diff --git a/operator/Cargo.lock b/operator/Cargo.lock
index a58b2ee8..9f7d0738 100644
--- a/operator/Cargo.lock
+++ b/operator/Cargo.lock
@@ -1309,6 +1309,17 @@ dependencies = [
"tinyvec",
]
+[[package]]
+name = "bstr"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
+dependencies = [
+ "memchr",
+ "regex-automata 0.4.9",
+ "serde",
+]
+
[[package]]
name = "build-helper"
version = "0.1.1"
@@ -1409,6 +1420,12 @@ dependencies = [
"thiserror 1.0.69",
]
+[[package]]
+name = "case"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c"
+
[[package]]
name = "cc"
version = "1.2.22"
@@ -2355,6 +2372,7 @@ dependencies = [
"hex-literal 0.3.4",
"log",
"num-bigint",
+ "num_enum",
"pallet-authorship",
"pallet-babe",
"pallet-balances",
@@ -2368,6 +2386,12 @@ dependencies = [
"pallet-ethereum",
"pallet-evm",
"pallet-evm-chain-id",
+ "pallet-evm-precompile-blake2",
+ "pallet-evm-precompile-bn128",
+ "pallet-evm-precompile-modexp",
+ "pallet-evm-precompile-registry",
+ "pallet-evm-precompile-sha3fips",
+ "pallet-evm-precompile-simple",
"pallet-external-validators",
"pallet-external-validators-rewards",
"pallet-external-validators-rewards-runtime-api",
@@ -2405,6 +2429,7 @@ dependencies = [
"parity-scale-codec",
"polkadot-primitives",
"polkadot-runtime-common",
+ "precompile-utils",
"scale-info",
"serde_json",
"shp-constants",
@@ -2579,6 +2604,7 @@ dependencies = [
"hex-literal 0.3.4",
"log",
"num-bigint",
+ "num_enum",
"pallet-authorship",
"pallet-babe",
"pallet-balances",
@@ -2592,6 +2618,12 @@ dependencies = [
"pallet-ethereum",
"pallet-evm",
"pallet-evm-chain-id",
+ "pallet-evm-precompile-blake2",
+ "pallet-evm-precompile-bn128",
+ "pallet-evm-precompile-modexp",
+ "pallet-evm-precompile-registry",
+ "pallet-evm-precompile-sha3fips",
+ "pallet-evm-precompile-simple",
"pallet-external-validators",
"pallet-external-validators-rewards",
"pallet-external-validators-rewards-runtime-api",
@@ -2629,6 +2661,7 @@ dependencies = [
"parity-scale-codec",
"polkadot-primitives",
"polkadot-runtime-common",
+ "precompile-utils",
"scale-info",
"serde_json",
"shp-constants",
@@ -2704,6 +2737,7 @@ dependencies = [
"hex-literal 0.3.4",
"log",
"num-bigint",
+ "num_enum",
"pallet-authorship",
"pallet-babe",
"pallet-balances",
@@ -2717,6 +2751,12 @@ dependencies = [
"pallet-ethereum",
"pallet-evm",
"pallet-evm-chain-id",
+ "pallet-evm-precompile-blake2",
+ "pallet-evm-precompile-bn128",
+ "pallet-evm-precompile-modexp",
+ "pallet-evm-precompile-registry",
+ "pallet-evm-precompile-sha3fips",
+ "pallet-evm-precompile-simple",
"pallet-external-validators",
"pallet-external-validators-rewards",
"pallet-external-validators-rewards-runtime-api",
@@ -2754,6 +2794,7 @@ dependencies = [
"parity-scale-codec",
"polkadot-primitives",
"polkadot-runtime-common",
+ "precompile-utils",
"scale-info",
"serde_json",
"shp-constants",
@@ -2925,6 +2966,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
+ "unicode-xid",
]
[[package]]
@@ -3841,12 +3883,6 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
-[[package]]
-name = "fixedbitset"
-version = "0.5.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
-
[[package]]
name = "float-cmp"
version = "0.9.0"
@@ -5989,6 +6025,9 @@ name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+dependencies = [
+ "spin 0.9.8",
+]
[[package]]
name = "lazycell"
@@ -7347,6 +7386,20 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "num"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
+dependencies = [
+ "num-bigint",
+ "num-complex",
+ "num-integer",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+]
+
[[package]]
name = "num-bigint"
version = "0.4.6"
@@ -7391,6 +7444,17 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "num-iter"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
[[package]]
name = "num-rational"
version = "0.4.2"
@@ -7599,7 +7663,7 @@ dependencies = [
"expander",
"indexmap 2.9.0",
"itertools 0.11.0",
- "petgraph 0.6.5",
+ "petgraph",
"proc-macro-crate 3.3.0",
"proc-macro2",
"quote",
@@ -7961,6 +8025,70 @@ dependencies = [
"scale-info",
]
+[[package]]
+name = "pallet-evm-precompile-blake2"
+version = "2.0.0-dev"
+source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
+dependencies = [
+ "fp-evm",
+]
+
+[[package]]
+name = "pallet-evm-precompile-bn128"
+version = "2.0.0-dev"
+source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
+dependencies = [
+ "fp-evm",
+ "sp-core",
+ "substrate-bn",
+]
+
+[[package]]
+name = "pallet-evm-precompile-modexp"
+version = "2.0.0-dev"
+source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
+dependencies = [
+ "fp-evm",
+ "num",
+]
+
+[[package]]
+name = "pallet-evm-precompile-registry"
+version = "0.1.0"
+dependencies = [
+ "fp-evm",
+ "frame-support",
+ "frame-system",
+ "pallet-balances",
+ "pallet-evm",
+ "pallet-timestamp",
+ "parity-scale-codec",
+ "precompile-utils",
+ "scale-info",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+]
+
+[[package]]
+name = "pallet-evm-precompile-sha3fips"
+version = "2.0.0-dev"
+source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
+dependencies = [
+ "fp-evm",
+ "tiny-keccak",
+]
+
+[[package]]
+name = "pallet-evm-precompile-simple"
+version = "2.0.0-dev"
+source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
+dependencies = [
+ "fp-evm",
+ "ripemd",
+ "sp-io",
+]
+
[[package]]
name = "pallet-external-validators"
version = "0.1.0"
@@ -8871,17 +8999,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [
- "fixedbitset 0.4.2",
- "indexmap 2.9.0",
-]
-
-[[package]]
-name = "petgraph"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
-dependencies = [
- "fixedbitset 0.5.7",
+ "fixedbitset",
"indexmap 2.9.0",
]
@@ -9438,6 +9556,49 @@ dependencies = [
"zerocopy",
]
+[[package]]
+name = "precompile-utils"
+version = "0.1.0"
+source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
+dependencies = [
+ "derive_more 1.0.0",
+ "environmental",
+ "evm",
+ "fp-evm",
+ "frame-support",
+ "frame-system",
+ "hex",
+ "hex-literal 0.4.1",
+ "impl-trait-for-tuples",
+ "log",
+ "num_enum",
+ "pallet-evm",
+ "parity-scale-codec",
+ "precompile-utils-macro",
+ "scale-info",
+ "serde",
+ "similar-asserts",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-weights",
+ "staging-xcm",
+]
+
+[[package]]
+name = "precompile-utils-macro"
+version = "0.1.0"
+source = "git+https://github.com/polkadot-evm/frontier?rev=75329a2df49e2cc7981485392c31160929d1bd48#75329a2df49e2cc7981485392c31160929d1bd48"
+dependencies = [
+ "case",
+ "num_enum",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2412-6)",
+ "syn 2.0.101",
+]
+
[[package]]
name = "predicates"
version = "2.1.5"
@@ -9726,7 +9887,7 @@ dependencies = [
"log",
"multimap",
"once_cell",
- "petgraph 0.7.1",
+ "petgraph",
"prettyplease",
"prost 0.13.5",
"prost-types",
@@ -12448,6 +12609,26 @@ dependencies = [
"wide",
]
+[[package]]
+name = "similar"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
+dependencies = [
+ "bstr",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "similar-asserts"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a"
+dependencies = [
+ "console",
+ "similar",
+]
+
[[package]]
name = "simple-dns"
version = "0.9.3"
@@ -14280,6 +14461,19 @@ dependencies = [
"zeroize",
]
+[[package]]
+name = "substrate-bn"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c"
+dependencies = [
+ "byteorder",
+ "crunchy",
+ "lazy_static",
+ "rand 0.8.5",
+ "rustc-hex",
+]
+
[[package]]
name = "substrate-build-script-utils"
version = "11.0.0"
diff --git a/operator/Cargo.toml b/operator/Cargo.toml
index b463c3b8..a46c4610 100644
--- a/operator/Cargo.toml
+++ b/operator/Cargo.toml
@@ -10,6 +10,7 @@ members = [
"node",
"pallets/outbound-commitment-store",
"pallets/*",
+ "precompiles/*",
"primitives/bridge",
"runtime/*",
]
@@ -29,6 +30,7 @@ pallet-external-validators-rewards = { path = "./pallets/external-validators-rew
pallet-external-validators-rewards-runtime-api = { path = "./pallets/external-validators-rewards/runtime-api", default-features = false }
pallet-outbound-commitment-store = { path = "./pallets/outbound-commitment-store", default-features = false }
pallet-datahaven-native-transfer = { path = "./pallets/datahaven-native-transfer", default-features = false }
+pallet-evm-precompile-registry = { path = "./precompiles/precompile-registry", default-features = false }
# Crates.io (wasm)
alloy-core = { version = "0.8.15", default-features = false }
@@ -54,6 +56,7 @@ libsecp256k1 = { version = "0.7", default-features = false }
log = { version = "0.4.25" }
milagro-bls = { version = "1.5.4", default-features = false, package = "snowbridge-milagro-bls" }
num-bigint = { version = "0.4.3", default-features = false }
+num_enum = { version = "0.7.3", default-features = false }
openssl-sys = { version = "0.9", features = [
"vendored",
] } # This is just to set the "vendored" feature required for the crossbuild, so that OpenSSL builds from source
@@ -211,10 +214,14 @@ pallet-dynamic-fee = { git = "https://github.com/polkadot-evm/frontier", rev="75
pallet-ethereum = { git = "https://github.com/polkadot-evm/frontier/", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-evm = { git = "https://github.com/polkadot-evm/frontier/", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-evm-chain-id = { git = "https://github.com/polkadot-evm/frontier/", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
+pallet-evm-precompile-blake2 = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
+pallet-evm-precompile-bn128 = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-evm-precompile-modexp = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-evm-precompile-sha3fips = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-evm-precompile-simple = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
pallet-hotfix-sufficients = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
+precompile-utils = { git = "https://github.com/polkadot-evm/frontier/", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
+precompile-utils-macro = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
# Frontier (client)
fc-api = { git = "https://github.com/polkadot-evm/frontier", rev="75329a2df49e2cc7981485392c31160929d1bd48", default-features = false }
diff --git a/operator/precompiles/precompile-registry/Cargo.toml b/operator/precompiles/precompile-registry/Cargo.toml
new file mode 100644
index 00000000..8dee9cf8
--- /dev/null
+++ b/operator/precompiles/precompile-registry/Cargo.toml
@@ -0,0 +1,42 @@
+[package]
+name = "pallet-evm-precompile-registry"
+authors = ["The DataHaven Team"]
+description = "Registry of active precompiles"
+edition = "2021"
+version = "0.1.0"
+
+[dependencies]
+# Substrate
+frame-support = { workspace = true }
+frame-system = { workspace = true }
+sp-core = { workspace = true }
+sp-io = { workspace = true }
+
+# Frontier
+fp-evm = { workspace = true }
+pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }
+precompile-utils = { workspace = true }
+
+[dev-dependencies]
+# Precompile utils for testing
+precompile-utils = { workspace = true, features = ["std", "testing"] }
+
+# Substrate
+pallet-balances = { workspace = true, features = ["insecure_zero_ed", "std"] }
+pallet-timestamp = { workspace = true, features = ["std"] }
+parity-scale-codec = { workspace = true, features = ["max-encoded-len", "std"] }
+scale-info = { workspace = true, features = ["derive", "std"] }
+sp-runtime = { workspace = true, features = ["std"] }
+
+[features]
+default = ["std"]
+std = [
+ "fp-evm/std",
+ "frame-support/std",
+ "frame-system/std",
+ "pallet-evm/std",
+ "parity-scale-codec/std",
+ "precompile-utils/std",
+ "sp-core/std",
+ "sp-io/std",
+]
\ No newline at end of file
diff --git a/operator/precompiles/precompile-registry/PrecompileRegistry.sol b/operator/precompiles/precompile-registry/PrecompileRegistry.sol
new file mode 100644
index 00000000..4c27fe06
--- /dev/null
+++ b/operator/precompiles/precompile-registry/PrecompileRegistry.sol
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-3.0-only
+pragma solidity >=0.8.3;
+
+/// @dev The PrecompileRegistry contract's address.
+address constant PRECOMPILE_REGISTRY_ADDRESS = 0x0000000000000000000000000000000000000815;
+
+/// @dev The PrecompileRegistry contract's instance.
+PrecompileRegistry constant PRECOMPILE_REGISTRY_CONTRACT = PrecompileRegistry(
+ PRECOMPILE_REGISTRY_ADDRESS
+);
+
+/// @author The Moonbeam Team
+/// @title Precompile Registry
+/// @dev Interface to the set of available precompiles.
+/// @custom:address 0x0000000000000000000000000000000000000815
+interface PrecompileRegistry {
+ /// @dev Query if the given address is a precompile. Note that deactivated precompiles
+ /// are still considered precompiles and will return `true`.
+ /// @param a: Address to query
+ /// @return output Is this address a precompile?
+ /// @custom:selector 446b450e
+ function isPrecompile(address a) external view returns (bool);
+
+ /// @dev Query if the given address is an active precompile. Will return false if the
+ /// address is not a precompile or if this precompile is deactivated.
+ /// @param a: Address to query
+ /// @return output Is this address an active precompile?
+ /// @custom:selector 6f5e23cf
+ function isActivePrecompile(address a) external view returns (bool);
+
+ /// @dev Update the account code of a precompile address.
+ /// As precompiles are implemented inside the Runtime, they don't have a bytecode, and
+ /// their account code is empty by default. However in Solidity calling a function of a
+ /// contract often automatically adds a check that the contract bytecode is non-empty.
+ /// For that reason a dummy code (0x60006000fd) can be inserted at the precompile address
+ /// to pass that check. This function allows any user to insert that code to precompile address
+ /// if they need it.
+ /// @param a: Address of the precompile.
+ /// @custom:selector 48ceb1b4
+ function updateAccountCode(address a) external;
+}
diff --git a/operator/precompiles/precompile-registry/src/lib.rs b/operator/precompiles/precompile-registry/src/lib.rs
new file mode 100644
index 00000000..e81bb477
--- /dev/null
+++ b/operator/precompiles/precompile-registry/src/lib.rs
@@ -0,0 +1,99 @@
+// Copyright 2019-2025 PureStake Inc.
+// This file is part of Moonbeam.
+
+// Moonbeam 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.
+
+// Moonbeam 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 Moonbeam. If not, see .
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#[cfg(test)]
+mod mock;
+#[cfg(test)]
+mod tests;
+
+use core::marker::PhantomData;
+use fp_evm::{ExitError, IsPrecompileResult, PrecompileFailure};
+use precompile_utils::{
+ precompile_set::{is_precompile_or_fail, IsActivePrecompile},
+ prelude::*,
+};
+use sp_core::Get;
+
+const DUMMY_CODE: [u8; 5] = [0x60, 0x00, 0x60, 0x00, 0xfd];
+
+pub struct PrecompileRegistry(PhantomData);
+
+#[precompile_utils::precompile]
+impl PrecompileRegistry
+where
+ Runtime: pallet_evm::Config,
+ Runtime::PrecompilesType: IsActivePrecompile,
+{
+ #[precompile::public("isPrecompile(address)")]
+ #[precompile::view]
+ fn is_precompile(handle: &mut impl PrecompileHandle, address: Address) -> EvmResult {
+ // We consider the precompile set is optimized to do at most one storage read.
+ // In the case of moonbeam, the storage item that can be read is pallet_asset::Asset
+ // (TODO make it more generic, maybe add a const generic on PrecompileRegistry type)
+ // Storage item: Asset:
+ // Blake2_128(16) + AssetId(16) + AssetDetails((4 * AccountId(20)) + (3 * Balance(16)) + 15)
+ handle.record_db_read::(175)?;
+ is_precompile_or_fail::(address.0, handle.remaining_gas())
+ }
+
+ #[precompile::public("isActivePrecompile(address)")]
+ #[precompile::view]
+ fn is_active_precompile(
+ handle: &mut impl PrecompileHandle,
+ address: Address,
+ ) -> EvmResult {
+ // We consider the precompile set is optimized to do at most one storage read.
+ // In the case of moonbeam, the storage item that can be read is pallet_asset::Asset
+ // (TODO make it more generic, maybe add a const generic on PrecompileRegistry type)
+ // Storage item: Asset:
+ // Blake2_128(16) + AssetId(16) + AssetDetails((4 * AccountId(20)) + (3 * Balance(16)) + 15)
+ handle.record_db_read::(175)?;
+ match ::get()
+ .is_active_precompile(address.0, handle.remaining_gas())
+ {
+ IsPrecompileResult::Answer { is_precompile, .. } => Ok(is_precompile),
+ IsPrecompileResult::OutOfGas => Err(PrecompileFailure::Error {
+ exit_status: ExitError::OutOfGas,
+ }),
+ }
+ }
+
+ #[precompile::public("updateAccountCode(address)")]
+ fn update_account_code(handle: &mut impl PrecompileHandle, address: Address) -> EvmResult<()> {
+ // Prevent touching addresses that are not precompiles.
+ //
+ // We consider the precompile set is optimized to do at most one storage read.
+ // In the case of moonbeam, the storage item that can be read is pallet_asset::Asset
+ // (TODO make it more generic, maybe add a const generic on PrecompileRegistry type)
+ // Storage item: Asset:
+ // Blake2_128(16) + AssetId(16) + AssetDetails((4 * AccountId(20)) + (3 * Balance(16)) + 15)
+ handle.record_db_read::(175)?;
+ if !is_precompile_or_fail::(address.0, handle.remaining_gas())? {
+ return Err(revert("provided address is not a precompile"));
+ }
+
+ // pallet_evm::create_account read storage item pallet_evm::AccountCodes
+ //
+ // AccountCodes: Blake2128(16) + H160(20) + Vec(5)
+ // We asume an existing precompile can hold at most 5 bytes worth of dummy code.
+ handle.record_db_read::(41)?;
+ pallet_evm::Pallet::::create_account(address.0, DUMMY_CODE.to_vec());
+
+ Ok(())
+ }
+}
diff --git a/operator/precompiles/precompile-registry/src/mock.rs b/operator/precompiles/precompile-registry/src/mock.rs
new file mode 100644
index 00000000..c7bf887f
--- /dev/null
+++ b/operator/precompiles/precompile-registry/src/mock.rs
@@ -0,0 +1,198 @@
+// Copyright 2019-2025 PureStake Inc.
+// This file is part of Moonbeam.
+
+// Moonbeam 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.
+
+// Moonbeam 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 Moonbeam. If not, see .
+
+use super::*;
+
+use frame_support::traits::Everything;
+use frame_support::{construct_runtime, pallet_prelude::*, parameter_types};
+use pallet_evm::{EnsureAddressNever, EnsureAddressRoot, FrameSystemAccountProvider};
+use precompile_utils::{mock_account, precompile_set::*, testing::MockAccount};
+use sp_core::H256;
+use sp_runtime::BuildStorage;
+use sp_runtime::{
+ traits::{BlakeTwo256, IdentityLookup},
+ Perbill,
+};
+
+pub type AccountId = MockAccount;
+pub type Balance = u128;
+
+type Block = frame_system::mocking::MockBlockU32;
+
+construct_runtime!(
+ pub enum Runtime {
+ System: frame_system,
+ Balances: pallet_balances,
+ Evm: pallet_evm,
+ Timestamp: pallet_timestamp,
+ }
+);
+
+parameter_types! {
+ pub const BlockHashCount: u32 = 250;
+ pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 1);
+ pub const MaximumBlockLength: u32 = 2 * 1024;
+ pub const AvailableBlockRatio: Perbill = Perbill::one();
+ pub const SS58Prefix: u8 = 42;
+}
+
+impl frame_system::Config for Runtime {
+ type BaseCallFilter = Everything;
+ type DbWeight = ();
+ type RuntimeOrigin = RuntimeOrigin;
+ type RuntimeTask = RuntimeTask;
+ type Nonce = u64;
+ type Block = Block;
+ type RuntimeCall = RuntimeCall;
+ type Hash = H256;
+ type Hashing = BlakeTwo256;
+ type AccountId = AccountId;
+ type Lookup = IdentityLookup;
+ type RuntimeEvent = RuntimeEvent;
+ type BlockHashCount = BlockHashCount;
+ type Version = ();
+ type PalletInfo = PalletInfo;
+ type AccountData = pallet_balances::AccountData;
+ type OnNewAccount = ();
+ type OnKilledAccount = ();
+ type SystemWeightInfo = ();
+ type BlockWeights = ();
+ type BlockLength = ();
+ type SS58Prefix = SS58Prefix;
+ type OnSetCode = ();
+ type MaxConsumers = frame_support::traits::ConstU32<16>;
+ type SingleBlockMigrations = ();
+ type MultiBlockMigrator = ();
+ type PreInherents = ();
+ type PostInherents = ();
+ type PostTransactions = ();
+ type ExtensionsWeightInfo = ();
+}
+
+parameter_types! {
+ pub const ExistentialDeposit: u128 = 0;
+}
+
+impl pallet_balances::Config for Runtime {
+ type MaxReserves = ();
+ type ReserveIdentifier = [u8; 4];
+ type MaxLocks = ();
+ type Balance = Balance;
+ type RuntimeEvent = RuntimeEvent;
+ type DustRemoval = ();
+ type ExistentialDeposit = ExistentialDeposit;
+ type AccountStore = System;
+ type WeightInfo = ();
+ type RuntimeHoldReason = ();
+ type FreezeIdentifier = ();
+ type MaxFreezes = ();
+ type RuntimeFreezeReason = ();
+ type DoneSlashHandler = ();
+}
+
+mock_account!(Registry, |_| MockAccount::from_u64(1));
+mock_account!(Removed, |_| MockAccount::from_u64(2));
+mock_account!(SmartContract, |_| MockAccount::from_u64(3));
+
+pub type Precompiles = PrecompileSetBuilder<
+ R,
+ (
+ PrecompileAt, PrecompileRegistry>,
+ RemovedPrecompileAt>,
+ ),
+>;
+
+pub type PCall = PrecompileRegistryCall;
+
+parameter_types! {
+ pub PrecompilesValue: Precompiles = Precompiles::new();
+ pub const WeightPerGas: Weight = Weight::from_parts(1, 0);
+}
+
+impl pallet_evm::Config for Runtime {
+ type FeeCalculator = ();
+ type GasWeightMapping = pallet_evm::FixedGasWeightMapping;
+ type WeightPerGas = WeightPerGas;
+ type CallOrigin = EnsureAddressRoot;
+ type WithdrawOrigin = EnsureAddressNever;
+ type AddressMapping = AccountId;
+ type Currency = Balances;
+ type RuntimeEvent = RuntimeEvent;
+ type Runner = pallet_evm::runner::stack::Runner;
+ type PrecompilesType = Precompiles;
+ type PrecompilesValue = PrecompilesValue;
+ type ChainId = ();
+ type OnChargeTransaction = ();
+ type BlockGasLimit = ();
+ type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping;
+ type FindAuthor = ();
+ type OnCreate = ();
+ type GasLimitPovSizeRatio = ();
+ type GasLimitStorageGrowthRatio = ();
+ type Timestamp = Timestamp;
+ type WeightInfo = pallet_evm::weights::SubstrateWeight;
+ type AccountProvider = FrameSystemAccountProvider;
+}
+
+parameter_types! {
+ pub const MinimumPeriod: u64 = 5;
+}
+impl pallet_timestamp::Config for Runtime {
+ type Moment = u64;
+ type OnTimestampSet = ();
+ type MinimumPeriod = MinimumPeriod;
+ type WeightInfo = ();
+}
+
+pub(crate) struct ExtBuilder {
+ // endowed accounts with balances
+ balances: Vec<(AccountId, Balance)>,
+}
+
+impl Default for ExtBuilder {
+ fn default() -> ExtBuilder {
+ ExtBuilder { balances: vec![] }
+ }
+}
+
+impl ExtBuilder {
+ pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
+ self.balances = balances;
+ self
+ }
+
+ pub(crate) fn build(self) -> sp_io::TestExternalities {
+ let mut t = frame_system::GenesisConfig::::default()
+ .build_storage()
+ .expect("Frame system builds valid default genesis config");
+
+ pallet_balances::GenesisConfig:: {
+ balances: self.balances,
+ }
+ .assimilate_storage(&mut t)
+ .expect("Pallet balances storage can be assimilated");
+
+ let mut ext = sp_io::TestExternalities::new(t);
+ ext.execute_with(|| {
+ System::set_block_number(1);
+ pallet_evm::Pallet::::create_account(
+ SmartContract.into(),
+ b"SmartContract".to_vec(),
+ );
+ });
+ ext
+ }
+}
diff --git a/operator/precompiles/precompile-registry/src/tests.rs b/operator/precompiles/precompile-registry/src/tests.rs
new file mode 100644
index 00000000..347beeee
--- /dev/null
+++ b/operator/precompiles/precompile-registry/src/tests.rs
@@ -0,0 +1,202 @@
+// Copyright 2019-2025 PureStake Inc.
+// This file is part of Moonbeam.
+
+// Moonbeam 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.
+
+// Moonbeam 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 Moonbeam. If not, see .
+
+use crate::mock::{
+ ExtBuilder, PCall, Precompiles, PrecompilesValue, Registry, Removed, Runtime, SmartContract,
+};
+use precompile_utils::{prelude::*, testing::*};
+use sp_core::H160;
+
+fn precompiles() -> Precompiles {
+ PrecompilesValue::get()
+}
+
+mod selectors {
+ use super::*;
+
+ #[test]
+ fn selectors() {
+ assert!(PCall::is_precompile_selectors().contains(&0x446b450e));
+ assert!(PCall::is_active_precompile_selectors().contains(&0x6f5e23cf));
+ assert!(PCall::update_account_code_selectors().contains(&0x48ceb1b4));
+ }
+
+ #[test]
+ fn modifiers() {
+ ExtBuilder::default()
+ .with_balances(vec![(CryptoAlith.into(), 1000)])
+ .build()
+ .execute_with(|| {
+ let mut tester =
+ PrecompilesModifierTester::new(precompiles(), CryptoAlith, Registry);
+
+ tester.test_view_modifier(PCall::is_precompile_selectors());
+ tester.test_view_modifier(PCall::is_active_precompile_selectors());
+ tester.test_default_modifier(PCall::update_account_code_selectors());
+ });
+ }
+}
+
+mod is_precompile {
+
+ use super::*;
+
+ fn call(target_address: impl Into, output: bool) {
+ ExtBuilder::default()
+ .with_balances(vec![(CryptoAlith.into(), 1000)])
+ .build()
+ .execute_with(|| {
+ precompiles()
+ .prepare_test(
+ Alice, // can be anyone
+ Registry,
+ PCall::is_precompile {
+ address: Address(target_address.into()),
+ },
+ )
+ .expect_no_logs()
+ .execute_returns(output);
+ });
+ }
+
+ #[test]
+ fn works_on_precompile() {
+ call(Registry, true);
+ }
+
+ #[test]
+ fn works_on_removed_precompile() {
+ call(Removed, true);
+ }
+
+ #[test]
+ fn works_on_eoa() {
+ call(CryptoAlith, false);
+ }
+
+ #[test]
+ fn works_on_smart_contract() {
+ call(SmartContract, false);
+ }
+}
+
+mod is_active_precompile {
+
+ use super::*;
+
+ fn call(target_address: impl Into, output: bool) {
+ ExtBuilder::default()
+ .with_balances(vec![(CryptoAlith.into(), 1000)])
+ .build()
+ .execute_with(|| {
+ precompiles()
+ .prepare_test(
+ Alice, // can be anyone
+ Registry,
+ PCall::is_active_precompile {
+ address: Address(target_address.into()),
+ },
+ )
+ .expect_no_logs()
+ .execute_returns(output);
+ });
+ }
+
+ #[test]
+ fn works_on_precompile() {
+ call(Registry, true);
+ }
+
+ #[test]
+ fn works_on_removed_precompile() {
+ call(Removed, false);
+ }
+
+ #[test]
+ fn works_on_eoa() {
+ call(CryptoAlith, false);
+ }
+
+ #[test]
+ fn works_on_smart_contract() {
+ call(SmartContract, false);
+ }
+}
+
+mod update_account_code {
+ use super::*;
+
+ fn call(target_address: impl Into, expect_changes: bool) {
+ ExtBuilder::default()
+ .with_balances(vec![(CryptoAlith.into(), 1000)])
+ .build()
+ .execute_with(|| {
+ let target_address = target_address.into();
+
+ let precompiles = precompiles();
+ let tester = precompiles.prepare_test(
+ Alice, // can be anyone
+ Registry,
+ PCall::update_account_code {
+ address: Address(target_address),
+ },
+ );
+
+ if expect_changes {
+ tester.execute_returns(());
+ let new_code = pallet_evm::AccountCodes::::get(target_address);
+ assert_eq!(&new_code, &[0x60, 0x00, 0x60, 0x00, 0xfd]);
+ } else {
+ let current_code = pallet_evm::AccountCodes::::get(target_address);
+
+ tester.execute_reverts(|revert| {
+ revert == b"provided address is not a precompile"
+ });
+
+ let new_code = pallet_evm::AccountCodes::::get(target_address);
+ assert_eq!(current_code, new_code);
+ }
+ });
+ }
+
+ #[test]
+ fn works_on_precompile() {
+ call(Registry, true);
+ }
+
+ #[test]
+ fn works_on_removed_precompile() {
+ call(Removed, true);
+ }
+
+ #[test]
+ fn works_on_eoa() {
+ call(CryptoAlith, false);
+ }
+
+ #[test]
+ fn works_on_smart_contract() {
+ call(SmartContract, false);
+ }
+}
+
+#[test]
+fn test_solidity_interface() {
+ check_precompile_implements_solidity_interfaces(
+ &["PrecompileRegistry.sol"],
+ PCall::supports_selector,
+ )
+}
diff --git a/operator/runtime/mainnet/Cargo.toml b/operator/runtime/mainnet/Cargo.toml
index 53c2155a..8252ec0a 100644
--- a/operator/runtime/mainnet/Cargo.toml
+++ b/operator/runtime/mainnet/Cargo.toml
@@ -33,6 +33,7 @@ hex = { workspace = true }
hex-literal = { workspace = true }
log = { workspace = true }
num-bigint = { workspace = true }
+num_enum = { workspace = true }
pallet-authorship = { workspace = true }
pallet-babe = { workspace = true }
pallet-balances = { workspace = true, features = ["insecure_zero_ed"] }
@@ -40,10 +41,14 @@ pallet-beefy = { workspace = true }
pallet-beefy-mmr = { workspace = true }
pallet-collective = { workspace = true }
pallet-conviction-voting = { workspace = true }
-pallet-ethereum = { workspace = true }
-pallet-referenda = { workspace = true }
-pallet-evm = { workspace = true }
+pallet-ethereum = { workspace = true, features = ["forbid-evm-reentrancy"] }
+pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }
pallet-evm-chain-id = { workspace = true }
+pallet-evm-precompile-blake2 = { workspace = true }
+pallet-evm-precompile-bn128 = { workspace = true }
+pallet-evm-precompile-modexp = { workspace = true }
+pallet-evm-precompile-sha3fips = { workspace = true }
+pallet-evm-precompile-simple = { workspace = true }
pallet-external-validators = { workspace = true }
pallet-external-validators-rewards = { workspace = true }
pallet-external-validators-rewards-runtime-api = { workspace = true }
@@ -59,17 +64,19 @@ pallet-datahaven-native-transfer = { workspace = true }
pallet-parameters = { workspace = true }
pallet-preimage = { workspace = true }
pallet-proxy = { workspace = true }
+pallet-referenda = { workspace = true }
pallet-scheduler = { workspace = true }
pallet-session = { workspace = true }
pallet-sudo = { workspace = true }
pallet-timestamp = { workspace = true }
-pallet-transaction-payment = { workspace = true }
+pallet-transaction-payment = { workspace = true }
pallet-transaction-payment-rpc-runtime-api = { workspace = true }
pallet-treasury = { workspace = true }
pallet-utility = { workspace = true }
pallet-whitelist = { workspace = true }
polkadot-primitives = { workspace = true }
polkadot-runtime-common = { workspace = true }
+precompile-utils = { workspace = true }
scale-info = { workspace = true, features = ["derive", "serde"] }
serde_json = { workspace = true, default-features = false, features = [
"alloc",
@@ -112,6 +119,8 @@ xcm-executor = { workspace = true }
strum = { workspace = true }
strum_macros = { workspace = true }
+# DataHaven precompiles
+pallet-evm-precompile-registry = { workspace = true }
# StorageHub
pallet-bucket-nfts = { workspace = true }
@@ -135,7 +144,6 @@ shp-file-key-verifier = { workspace = true }
shp-data-price-updater = { workspace = true }
sp-trie = { workspace = true }
-
[build-dependencies]
substrate-wasm-builder = { workspace = true, optional = true, default-features = true }
@@ -145,6 +153,8 @@ frame-support-test = { workspace = true }
sp-io = { workspace = true }
sp-tracing = { workspace = true }
+precompile-utils = { workspace = true, features = ["std", "testing"] }
+
# Snowbridge testing
snowbridge-core = { workspace = true }
snowbridge-pallet-system = { workspace = true }
@@ -157,6 +167,7 @@ std = [
"codec/std",
"datahaven-runtime-common/std",
"fp-account/std",
+ "fp-evm/std",
"frame-benchmarking?/std",
"frame-executive/std",
"frame-metadata-hash-extension/std",
@@ -175,6 +186,7 @@ std = [
"pallet-ethereum/std",
"pallet-evm-chain-id/std",
"pallet-evm/std",
+ "pallet-evm-precompile-registry/std",
"pallet-external-validators/std",
"pallet-external-validators-rewards/std",
"pallet-external-validators-rewards-runtime-api/std",
@@ -200,6 +212,7 @@ std = [
"pallet-whitelist/std",
"polkadot-primitives/std",
"polkadot-runtime-common/std",
+ "precompile-utils/std",
"scale-info/std",
"serde_json/std",
"snowbridge-beacon-primitives/std",
diff --git a/operator/runtime/mainnet/src/configs/mod.rs b/operator/runtime/mainnet/src/configs/mod.rs
index f8e29f42..405162fa 100644
--- a/operator/runtime/mainnet/src/configs/mod.rs
+++ b/operator/runtime/mainnet/src/configs/mod.rs
@@ -28,14 +28,14 @@ pub mod runtime_params;
mod storagehub;
use super::{
- currency::*, AccountId, Babe, Balance, Balances, BeefyMmrLeaf, Block, BlockNumber,
- EthereumBeaconClient, EthereumOutboundQueueV2, EvmChainId, ExistentialDeposit,
- ExternalValidators, ExternalValidatorsRewards, Hash, Historical, ImOnline, MessageQueue, Nonce,
- Offences, OriginCaller, OutboundCommitmentStore, PalletInfo, Preimage, Referenda, Runtime,
- RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask,
- Scheduler, Session, SessionKeys, Signature, System, Timestamp, Treasury, BLOCK_HASH_COUNT,
- EXTRINSIC_BASE_WEIGHT, MAXIMUM_BLOCK_WEIGHT, NORMAL_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO,
- SLOT_DURATION, VERSION,
+ currency::*, precompiles::DataHavenPrecompiles, AccountId, Babe, Balance, Balances,
+ BeefyMmrLeaf, Block, BlockNumber, EthereumBeaconClient, EthereumOutboundQueueV2, EvmChainId,
+ ExistentialDeposit, ExternalValidators, ExternalValidatorsRewards, Hash, Historical, ImOnline,
+ MessageQueue, Nonce, Offences, OriginCaller, OutboundCommitmentStore, PalletInfo, Preimage,
+ Referenda, Runtime, RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason,
+ RuntimeOrigin, RuntimeTask, Scheduler, Session, SessionKeys, Signature, System, Timestamp,
+ Treasury, BLOCK_HASH_COUNT, EXTRINSIC_BASE_WEIGHT, MAXIMUM_BLOCK_WEIGHT, NORMAL_BLOCK_WEIGHT,
+ NORMAL_DISPATCH_RATIO, SLOT_DURATION, VERSION,
};
use codec::{Decode, Encode};
use datahaven_runtime_common::{
@@ -774,7 +774,7 @@ datahaven_runtime_common::impl_on_charge_evm_transaction!();
parameter_types! {
pub BlockGasLimit: U256
= U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS);
- // pub PrecompilesValue: TemplatePrecompiles = TemplatePrecompiles::<_>::new();
+ pub PrecompilesValue: DataHavenPrecompiles = DataHavenPrecompiles::<_>::new();
pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0);
pub SuicideQuickClearLimit: u32 = 0;
/// The amount of gas per pov. A ratio of 16 if we convert ref_time to gas and we compare
@@ -800,8 +800,8 @@ impl pallet_evm::Config for Runtime {
type AddressMapping = IdentityAddressMapping;
type Currency = Balances;
type RuntimeEvent = RuntimeEvent;
- type PrecompilesType = ();
- type PrecompilesValue = ();
+ type PrecompilesType = DataHavenPrecompiles;
+ type PrecompilesValue = PrecompilesValue;
type ChainId = EvmChainId;
type BlockGasLimit = BlockGasLimit;
type Runner = pallet_evm::runner::stack::Runner;
diff --git a/operator/runtime/mainnet/src/lib.rs b/operator/runtime/mainnet/src/lib.rs
index 4fb5487d..267f297a 100644
--- a/operator/runtime/mainnet/src/lib.rs
+++ b/operator/runtime/mainnet/src/lib.rs
@@ -9,6 +9,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
#[cfg(feature = "runtime-benchmarks")]
mod benchmarks;
pub mod configs;
+pub mod precompiles;
pub mod weights;
// Re-export governance for tests
diff --git a/operator/runtime/mainnet/src/precompiles.rs b/operator/runtime/mainnet/src/precompiles.rs
new file mode 100644
index 00000000..199b82fa
--- /dev/null
+++ b/operator/runtime/mainnet/src/precompiles.rs
@@ -0,0 +1,67 @@
+// Copyright 2019-2025 The DataHaven Team
+// This file is part of DataHaven.
+
+// DataHaven 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.
+
+// DataHaven 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 DataHaven. If not, see .
+
+use pallet_evm_precompile_blake2::Blake2F;
+use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing};
+use pallet_evm_precompile_modexp::Modexp;
+use pallet_evm_precompile_registry::PrecompileRegistry;
+use pallet_evm_precompile_sha3fips::Sha3FIPS256;
+use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
+use precompile_utils::precompile_set::*;
+
+type EthereumPrecompilesChecks = (AcceptDelegateCall, CallableByContract, CallableByPrecompile);
+
+/// EVM precompiles available in the DataHaven Mainnet runtime.
+#[precompile_utils::precompile_name_from_address]
+type DataHavenPrecompilesAt = (
+ // Ethereum precompiles:
+ // We allow DELEGATECALL to stay compliant with Ethereum behavior.
+ PrecompileAt, ECRecover, EthereumPrecompilesChecks>,
+ PrecompileAt, Sha256, EthereumPrecompilesChecks>,
+ PrecompileAt, Ripemd160, EthereumPrecompilesChecks>,
+ PrecompileAt, Identity, EthereumPrecompilesChecks>,
+ PrecompileAt, Modexp, EthereumPrecompilesChecks>,
+ PrecompileAt, Bn128Add, EthereumPrecompilesChecks>,
+ PrecompileAt, Bn128Mul, EthereumPrecompilesChecks>,
+ PrecompileAt, Bn128Pairing, EthereumPrecompilesChecks>,
+ PrecompileAt, Blake2F, EthereumPrecompilesChecks>,
+ // Non-DataHaven specific nor Ethereum precompiles :
+ PrecompileAt, Sha3FIPS256, (CallableByContract, CallableByPrecompile)>,
+ RemovedPrecompileAt>,
+ PrecompileAt, ECRecoverPublicKey, (CallableByContract, CallableByPrecompile)>,
+ RemovedPrecompileAt>,
+ // DataHaven specific precompiles:
+ PrecompileAt<
+ AddressU64<2069>,
+ PrecompileRegistry,
+ (CallableByContract, CallableByPrecompile),
+ >,
+);
+
+/// The PrecompileSet installed in the DataHaven runtime.
+/// We include the nine Istanbul precompiles
+/// (https://github.com/ethereum/go-ethereum/blob/3c46f557/core/vm/contracts.go#L69)
+/// The following distribution has been decided for the precompiles
+/// 0-1023: Ethereum Mainnet Precompiles
+/// 1024-2047 Precompiles that are not in Ethereum Mainnet but are neither DataHaven specific
+/// 2048-4095 DataHaven specific precompiles
+pub type DataHavenPrecompiles = PrecompileSetBuilder<
+ R,
+ (
+ // Skip precompiles if out of range.
+ PrecompilesInRangeInclusive<(AddressU64<1>, AddressU64<4095>), DataHavenPrecompilesAt>,
+ ),
+>;
diff --git a/operator/runtime/stagenet/Cargo.toml b/operator/runtime/stagenet/Cargo.toml
index 6e67bf85..b482979e 100644
--- a/operator/runtime/stagenet/Cargo.toml
+++ b/operator/runtime/stagenet/Cargo.toml
@@ -33,6 +33,7 @@ hex = { workspace = true }
hex-literal = { workspace = true }
log = { workspace = true }
num-bigint = { workspace = true }
+num_enum = { workspace = true }
pallet-authorship = { workspace = true }
pallet-babe = { workspace = true }
pallet-balances = { workspace = true, features = ["insecure_zero_ed"]}
@@ -40,10 +41,14 @@ pallet-beefy = { workspace = true }
pallet-beefy-mmr = { workspace = true }
pallet-collective = { workspace = true }
pallet-conviction-voting = { workspace = true }
-pallet-ethereum = { workspace = true }
-pallet-referenda = { workspace = true }
-pallet-evm = { workspace = true }
+pallet-ethereum = { workspace = true, features = ["forbid-evm-reentrancy"] }
+pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }
pallet-evm-chain-id = { workspace = true }
+pallet-evm-precompile-blake2 = { workspace = true }
+pallet-evm-precompile-bn128 = { workspace = true }
+pallet-evm-precompile-modexp = { workspace = true }
+pallet-evm-precompile-sha3fips = { workspace = true }
+pallet-evm-precompile-simple = { workspace = true }
pallet-external-validators = { workspace = true }
pallet-external-validators-rewards = { workspace = true }
pallet-external-validators-rewards-runtime-api = { workspace = true }
@@ -59,6 +64,7 @@ pallet-datahaven-native-transfer = { workspace = true }
pallet-parameters = { workspace = true }
pallet-preimage = { workspace = true }
pallet-proxy = { workspace = true }
+pallet-referenda = { workspace = true }
pallet-scheduler = { workspace = true }
pallet-session = { workspace = true }
pallet-sudo = { workspace = true }
@@ -70,6 +76,7 @@ pallet-utility = { workspace = true }
pallet-whitelist = { workspace = true }
polkadot-primitives = { workspace = true }
polkadot-runtime-common = { workspace = true }
+precompile-utils = { workspace = true }
scale-info = { workspace = true, features = ["derive", "serde"] }
serde_json = { workspace = true, default-features = false, features = [
"alloc",
@@ -112,6 +119,9 @@ xcm = { workspace = true }
xcm-builder = { workspace = true }
xcm-executor = { workspace = true }
+# DataHaven precompiles
+pallet-evm-precompile-registry = { workspace = true }
+
# StorageHub
pallet-bucket-nfts = { workspace = true }
pallet-nfts = { workspace = true }
@@ -143,6 +153,8 @@ frame-support-test = { workspace = true }
sp-io = { workspace = true }
sp-tracing = { workspace = true }
+precompile-utils = { workspace = true, features = ["std", "testing"] }
+
# Snowbridge testing
snowbridge-core = { workspace = true }
snowbridge-pallet-system = { workspace = true }
@@ -155,6 +167,7 @@ std = [
"codec/std",
"datahaven-runtime-common/std",
"fp-account/std",
+ "fp-evm/std",
"frame-benchmarking?/std",
"frame-executive/std",
"frame-metadata-hash-extension/std",
@@ -173,6 +186,7 @@ std = [
"pallet-ethereum/std",
"pallet-evm-chain-id/std",
"pallet-evm/std",
+ "pallet-evm-precompile-registry/std",
"pallet-external-validators/std",
"pallet-external-validators-rewards/std",
"pallet-external-validators-rewards-runtime-api/std",
@@ -198,6 +212,7 @@ std = [
"pallet-whitelist/std",
"polkadot-primitives/std",
"polkadot-runtime-common/std",
+ "precompile-utils/std",
"scale-info/std",
"serde_json/std",
"snowbridge-beacon-primitives/std",
diff --git a/operator/runtime/stagenet/src/configs/mod.rs b/operator/runtime/stagenet/src/configs/mod.rs
index 1119ecb7..efa71fb8 100644
--- a/operator/runtime/stagenet/src/configs/mod.rs
+++ b/operator/runtime/stagenet/src/configs/mod.rs
@@ -28,14 +28,14 @@ pub mod runtime_params;
mod storagehub;
use super::{
- currency::*, AccountId, Babe, Balance, Balances, BeefyMmrLeaf, Block, BlockNumber,
- EthereumBeaconClient, EthereumOutboundQueueV2, EvmChainId, ExistentialDeposit,
- ExternalValidators, ExternalValidatorsRewards, Hash, Historical, ImOnline, MessageQueue, Nonce,
- Offences, OriginCaller, OutboundCommitmentStore, PalletInfo, Preimage, Referenda, Runtime,
- RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask,
- Scheduler, Session, SessionKeys, Signature, System, Timestamp, Treasury, BLOCK_HASH_COUNT,
- EXTRINSIC_BASE_WEIGHT, MAXIMUM_BLOCK_WEIGHT, NORMAL_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO,
- SLOT_DURATION, VERSION,
+ currency::*, precompiles::DataHavenPrecompiles, AccountId, Babe, Balance, Balances,
+ BeefyMmrLeaf, Block, BlockNumber, EthereumBeaconClient, EthereumOutboundQueueV2, EvmChainId,
+ ExistentialDeposit, ExternalValidators, ExternalValidatorsRewards, Hash, Historical, ImOnline,
+ MessageQueue, Nonce, Offences, OriginCaller, OutboundCommitmentStore, PalletInfo, Preimage,
+ Referenda, Runtime, RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason,
+ RuntimeOrigin, RuntimeTask, Scheduler, Session, SessionKeys, Signature, System, Timestamp,
+ Treasury, BLOCK_HASH_COUNT, EXTRINSIC_BASE_WEIGHT, MAXIMUM_BLOCK_WEIGHT, NORMAL_BLOCK_WEIGHT,
+ NORMAL_DISPATCH_RATIO, SLOT_DURATION, VERSION,
};
use codec::{Decode, Encode};
use datahaven_runtime_common::{
@@ -773,7 +773,7 @@ datahaven_runtime_common::impl_on_charge_evm_transaction!();
parameter_types! {
pub BlockGasLimit: U256
= U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS);
- // pub PrecompilesValue: TemplatePrecompiles = TemplatePrecompiles::<_>::new();
+ pub PrecompilesValue: DataHavenPrecompiles = DataHavenPrecompiles::<_>::new();
pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0);
pub SuicideQuickClearLimit: u32 = 0;
/// The amount of gas per pov. A ratio of 16 if we convert ref_time to gas and we compare
@@ -799,8 +799,8 @@ impl pallet_evm::Config for Runtime {
type AddressMapping = IdentityAddressMapping;
type Currency = Balances;
type RuntimeEvent = RuntimeEvent;
- type PrecompilesType = ();
- type PrecompilesValue = ();
+ type PrecompilesType = DataHavenPrecompiles;
+ type PrecompilesValue = PrecompilesValue;
type ChainId = EvmChainId;
type BlockGasLimit = BlockGasLimit;
type Runner = pallet_evm::runner::stack::Runner;
diff --git a/operator/runtime/stagenet/src/lib.rs b/operator/runtime/stagenet/src/lib.rs
index 6860c626..ab072071 100644
--- a/operator/runtime/stagenet/src/lib.rs
+++ b/operator/runtime/stagenet/src/lib.rs
@@ -9,6 +9,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
#[cfg(feature = "runtime-benchmarks")]
mod benchmarks;
pub mod configs;
+pub mod precompiles;
pub mod weights;
// Re-export governance for tests
diff --git a/operator/runtime/stagenet/src/precompiles.rs b/operator/runtime/stagenet/src/precompiles.rs
new file mode 100644
index 00000000..15081a31
--- /dev/null
+++ b/operator/runtime/stagenet/src/precompiles.rs
@@ -0,0 +1,67 @@
+// Copyright 2019-2025 The DataHaven Team
+// This file is part of DataHaven.
+
+// DataHaven 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.
+
+// DataHaven 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 DataHaven. If not, see .
+
+use pallet_evm_precompile_blake2::Blake2F;
+use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing};
+use pallet_evm_precompile_modexp::Modexp;
+use pallet_evm_precompile_registry::PrecompileRegistry;
+use pallet_evm_precompile_sha3fips::Sha3FIPS256;
+use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
+use precompile_utils::precompile_set::*;
+
+type EthereumPrecompilesChecks = (AcceptDelegateCall, CallableByContract, CallableByPrecompile);
+
+/// EVM precompiles available in the DataHaven Stagenet runtime.
+#[precompile_utils::precompile_name_from_address]
+type DataHavenPrecompilesAt = (
+ // Ethereum precompiles:
+ // We allow DELEGATECALL to stay compliant with Ethereum behavior.
+ PrecompileAt, ECRecover, EthereumPrecompilesChecks>,
+ PrecompileAt, Sha256, EthereumPrecompilesChecks>,
+ PrecompileAt, Ripemd160, EthereumPrecompilesChecks>,
+ PrecompileAt, Identity, EthereumPrecompilesChecks>,
+ PrecompileAt, Modexp, EthereumPrecompilesChecks>,
+ PrecompileAt, Bn128Add, EthereumPrecompilesChecks>,
+ PrecompileAt, Bn128Mul, EthereumPrecompilesChecks>,
+ PrecompileAt, Bn128Pairing, EthereumPrecompilesChecks>,
+ PrecompileAt, Blake2F, EthereumPrecompilesChecks>,
+ // Non-DataHaven specific nor Ethereum precompiles :
+ PrecompileAt, Sha3FIPS256, (CallableByContract, CallableByPrecompile)>,
+ RemovedPrecompileAt>,
+ PrecompileAt, ECRecoverPublicKey, (CallableByContract, CallableByPrecompile)>,
+ RemovedPrecompileAt>,
+ // DataHaven specific precompiles:
+ PrecompileAt<
+ AddressU64<2069>,
+ PrecompileRegistry,
+ (CallableByContract, CallableByPrecompile),
+ >,
+);
+
+/// The PrecompileSet installed in the DataHaven runtime.
+/// We include the nine Istanbul precompiles
+/// (https://github.com/ethereum/go-ethereum/blob/3c46f557/core/vm/contracts.go#L69)
+/// The following distribution has been decided for the precompiles
+/// 0-1023: Ethereum Mainnet Precompiles
+/// 1024-2047 Precompiles that are not in Ethereum Mainnet but are neither DataHaven specific
+/// 2048-4095 DataHaven specific precompiles
+pub type DataHavenPrecompiles = PrecompileSetBuilder<
+ R,
+ (
+ // Skip precompiles if out of range.
+ PrecompilesInRangeInclusive<(AddressU64<1>, AddressU64<4095>), DataHavenPrecompilesAt>,
+ ),
+>;
diff --git a/operator/runtime/testnet/Cargo.toml b/operator/runtime/testnet/Cargo.toml
index 7040c7fe..3ced4e27 100644
--- a/operator/runtime/testnet/Cargo.toml
+++ b/operator/runtime/testnet/Cargo.toml
@@ -33,6 +33,7 @@ hex = { workspace = true }
hex-literal = { workspace = true }
log = { workspace = true }
num-bigint = { workspace = true }
+num_enum = { workspace = true }
pallet-authorship = { workspace = true }
pallet-babe = { workspace = true }
pallet-balances = { workspace = true, features = ["insecure_zero_ed"] }
@@ -41,10 +42,14 @@ pallet-beefy-mmr = { workspace = true }
pallet-collective = { workspace = true }
pallet-conviction-voting = { workspace = true }
pallet-datahaven-native-transfer = { workspace = true }
-pallet-referenda = { workspace = true }
-pallet-ethereum = { workspace = true }
-pallet-evm = { workspace = true }
+pallet-ethereum = { workspace = true, features = ["forbid-evm-reentrancy"] }
+pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }
pallet-evm-chain-id = { workspace = true }
+pallet-evm-precompile-blake2 = { workspace = true }
+pallet-evm-precompile-bn128 = { workspace = true }
+pallet-evm-precompile-modexp = { workspace = true }
+pallet-evm-precompile-sha3fips = { workspace = true }
+pallet-evm-precompile-simple = { workspace = true }
pallet-external-validators = { workspace = true }
pallet-external-validators-rewards = { workspace = true }
pallet-external-validators-rewards-runtime-api = { workspace = true }
@@ -59,6 +64,7 @@ pallet-outbound-commitment-store = { workspace = true }
pallet-parameters = { workspace = true }
pallet-preimage = { workspace = true }
pallet-proxy = { workspace = true }
+pallet-referenda = { workspace = true }
pallet-scheduler = { workspace = true }
pallet-session = { workspace = true }
pallet-sudo = { workspace = true }
@@ -70,6 +76,7 @@ pallet-utility = { workspace = true }
pallet-whitelist = { workspace = true }
polkadot-primitives = { workspace = true }
polkadot-runtime-common = { workspace = true }
+precompile-utils = { workspace = true }
scale-info = { workspace = true, features = ["derive", "serde"] }
serde_json = { workspace = true, default-features = false, features = [
"alloc",
@@ -112,6 +119,9 @@ xcm-executor = { workspace = true }
strum = { workspace = true }
strum_macros = { workspace = true }
+# DataHaven precompiles
+pallet-evm-precompile-registry = { workspace = true }
+
# StorageHub
pallet-bucket-nfts = { workspace = true }
pallet-nfts = { workspace = true }
@@ -143,6 +153,8 @@ frame-support-test = { workspace = true }
sp-io = { workspace = true }
sp-tracing = { workspace = true }
+precompile-utils = { workspace = true, features = ["std", "testing"] }
+
# Snowbridge testing
snowbridge-core = { workspace = true }
snowbridge-outbound-queue-primitives = { workspace = true }
@@ -155,6 +167,7 @@ std = [
"codec/std",
"datahaven-runtime-common/std",
"fp-account/std",
+ "fp-evm/std",
"frame-benchmarking?/std",
"frame-executive/std",
"frame-metadata-hash-extension/std",
@@ -173,6 +186,7 @@ std = [
"pallet-ethereum/std",
"pallet-evm-chain-id/std",
"pallet-evm/std",
+ "pallet-evm-precompile-registry/std",
"pallet-grandpa/std",
"pallet-identity/std",
"pallet-im-online/std",
@@ -195,6 +209,7 @@ std = [
"pallet-whitelist/std",
"polkadot-primitives/std",
"polkadot-runtime-common/std",
+ "precompile-utils/std",
"scale-info/std",
"serde_json/std",
"snowbridge-beacon-primitives/std",
diff --git a/operator/runtime/testnet/src/configs/mod.rs b/operator/runtime/testnet/src/configs/mod.rs
index e9bb1e4e..d43a9f01 100644
--- a/operator/runtime/testnet/src/configs/mod.rs
+++ b/operator/runtime/testnet/src/configs/mod.rs
@@ -28,14 +28,14 @@ pub mod runtime_params;
mod storagehub;
use super::{
- currency::*, AccountId, Babe, Balance, Balances, BeefyMmrLeaf, Block, BlockNumber,
- EthereumBeaconClient, EthereumOutboundQueueV2, EvmChainId, ExistentialDeposit,
- ExternalValidators, ExternalValidatorsRewards, Hash, Historical, ImOnline, MessageQueue, Nonce,
- Offences, OriginCaller, OutboundCommitmentStore, PalletInfo, Preimage, Referenda, Runtime,
- RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask,
- Scheduler, Session, SessionKeys, Signature, System, Timestamp, Treasury, BLOCK_HASH_COUNT,
- EXTRINSIC_BASE_WEIGHT, MAXIMUM_BLOCK_WEIGHT, NORMAL_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO,
- SLOT_DURATION, VERSION,
+ currency::*, precompiles::DataHavenPrecompiles, AccountId, Babe, Balance, Balances,
+ BeefyMmrLeaf, Block, BlockNumber, EthereumBeaconClient, EthereumOutboundQueueV2, EvmChainId,
+ ExistentialDeposit, ExternalValidators, ExternalValidatorsRewards, Hash, Historical, ImOnline,
+ MessageQueue, Nonce, Offences, OriginCaller, OutboundCommitmentStore, PalletInfo, Preimage,
+ Referenda, Runtime, RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason,
+ RuntimeOrigin, RuntimeTask, Scheduler, Session, SessionKeys, Signature, System, Timestamp,
+ Treasury, BLOCK_HASH_COUNT, EXTRINSIC_BASE_WEIGHT, MAXIMUM_BLOCK_WEIGHT, NORMAL_BLOCK_WEIGHT,
+ NORMAL_DISPATCH_RATIO, SLOT_DURATION, VERSION,
};
use codec::{Decode, Encode};
use datahaven_runtime_common::{
@@ -773,7 +773,7 @@ datahaven_runtime_common::impl_on_charge_evm_transaction!();
parameter_types! {
pub BlockGasLimit: U256
= U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS);
- // pub PrecompilesValue: TemplatePrecompiles = TemplatePrecompiles::<_>::new();
+ pub PrecompilesValue: DataHavenPrecompiles = DataHavenPrecompiles::<_>::new();
pub WeightPerGas: Weight = Weight::from_parts(WEIGHT_PER_GAS, 0);
pub SuicideQuickClearLimit: u32 = 0;
/// The amount of gas per pov. A ratio of 16 if we convert ref_time to gas and we compare
@@ -799,8 +799,8 @@ impl pallet_evm::Config for Runtime {
type AddressMapping = IdentityAddressMapping;
type Currency = Balances;
type RuntimeEvent = RuntimeEvent;
- type PrecompilesType = ();
- type PrecompilesValue = ();
+ type PrecompilesType = DataHavenPrecompiles;
+ type PrecompilesValue = PrecompilesValue;
type ChainId = EvmChainId;
type BlockGasLimit = BlockGasLimit;
type Runner = pallet_evm::runner::stack::Runner;
diff --git a/operator/runtime/testnet/src/lib.rs b/operator/runtime/testnet/src/lib.rs
index b8d25d5b..c79b01c0 100644
--- a/operator/runtime/testnet/src/lib.rs
+++ b/operator/runtime/testnet/src/lib.rs
@@ -9,6 +9,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
#[cfg(feature = "runtime-benchmarks")]
mod benchmarks;
pub mod configs;
+pub mod precompiles;
pub mod weights;
// Re-export governance for tests
pub use configs::governance;
diff --git a/operator/runtime/testnet/src/precompiles.rs b/operator/runtime/testnet/src/precompiles.rs
new file mode 100644
index 00000000..6f453657
--- /dev/null
+++ b/operator/runtime/testnet/src/precompiles.rs
@@ -0,0 +1,67 @@
+// Copyright 2019-2025 The DataHaven Team
+// This file is part of DataHaven.
+
+// DataHaven 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.
+
+// DataHaven 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 DataHaven. If not, see .
+
+use pallet_evm_precompile_blake2::Blake2F;
+use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing};
+use pallet_evm_precompile_modexp::Modexp;
+use pallet_evm_precompile_registry::PrecompileRegistry;
+use pallet_evm_precompile_sha3fips::Sha3FIPS256;
+use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
+use precompile_utils::precompile_set::*;
+
+type EthereumPrecompilesChecks = (AcceptDelegateCall, CallableByContract, CallableByPrecompile);
+
+/// EVM precompiles available in the DataHaven Testnet runtime.
+#[precompile_utils::precompile_name_from_address]
+type DataHavenPrecompilesAt = (
+ // Ethereum precompiles:
+ // We allow DELEGATECALL to stay compliant with Ethereum behavior.
+ PrecompileAt, ECRecover, EthereumPrecompilesChecks>,
+ PrecompileAt, Sha256, EthereumPrecompilesChecks>,
+ PrecompileAt, Ripemd160, EthereumPrecompilesChecks>,
+ PrecompileAt, Identity, EthereumPrecompilesChecks>,
+ PrecompileAt, Modexp, EthereumPrecompilesChecks>,
+ PrecompileAt, Bn128Add, EthereumPrecompilesChecks>,
+ PrecompileAt, Bn128Mul, EthereumPrecompilesChecks>,
+ PrecompileAt, Bn128Pairing, EthereumPrecompilesChecks>,
+ PrecompileAt, Blake2F, EthereumPrecompilesChecks>,
+ // Non-DataHaven specific nor Ethereum precompiles :
+ PrecompileAt, Sha3FIPS256, (CallableByContract, CallableByPrecompile)>,
+ RemovedPrecompileAt>,
+ PrecompileAt, ECRecoverPublicKey, (CallableByContract, CallableByPrecompile)>,
+ RemovedPrecompileAt>,
+ // DataHaven specific precompiles:
+ PrecompileAt<
+ AddressU64<2069>,
+ PrecompileRegistry,
+ (CallableByContract, CallableByPrecompile),
+ >,
+);
+
+/// The PrecompileSet installed in the DataHaven runtime.
+/// We include the nine Istanbul precompiles
+/// (https://github.com/ethereum/go-ethereum/blob/3c46f557/core/vm/contracts.go#L69)
+/// The following distribution has been decided for the precompiles
+/// 0-1023: Ethereum Mainnet Precompiles
+/// 1024-2047 Precompiles that are not in Ethereum Mainnet but are neither DataHaven specific
+/// 2048-4095 DataHaven specific precompiles
+pub type DataHavenPrecompiles = PrecompileSetBuilder<
+ R,
+ (
+ // Skip precompiles if out of range.
+ PrecompilesInRangeInclusive<(AddressU64<1>, AddressU64<4095>), DataHavenPrecompilesAt>,
+ ),
+>;