diff --git a/Cargo.lock b/Cargo.lock
index b0c56ffb2e..1ec256be62 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6235,6 +6235,18 @@ dependencies = [
"scale-info",
]
+[[package]]
+name = "pallet-evm-precompile-dispatch"
+version = "2.0.0-dev"
+source = "git+https://github.com/opentensor/frontier?rev=cd6bca14a3#cd6bca14a366cc7cb1c3b1b1d7bc8213667e4126"
+dependencies = [
+ "fp-evm",
+ "frame-support",
+ "pallet-evm",
+ "parity-scale-codec",
+ "sp-runtime",
+]
+
[[package]]
name = "pallet-evm-precompile-modexp"
version = "2.0.0-dev"
@@ -11070,6 +11082,7 @@ dependencies = [
"pallet-admin-utils",
"pallet-balances",
"pallet-evm",
+ "pallet-evm-precompile-dispatch",
"pallet-evm-precompile-modexp",
"pallet-evm-precompile-sha3fips",
"pallet-evm-precompile-simple",
@@ -11077,6 +11090,7 @@ dependencies = [
"pallet-subtensor",
"precompile-utils",
"sp-core",
+ "sp-io",
"sp-runtime",
"sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-stable2409-7)",
"subtensor-runtime-common",
diff --git a/Cargo.toml b/Cargo.toml
index b429ac9341..8641f907ec 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -208,6 +208,7 @@ pallet-base-fee = { git = "https://github.com/opentensor/frontier", rev = "cd6bc
pallet-dynamic-fee = { git = "https://github.com/opentensor/frontier", rev = "cd6bca14a3", default-features = false }
pallet-ethereum = { git = "https://github.com/opentensor/frontier", rev = "cd6bca14a3", default-features = false }
pallet-evm = { git = "https://github.com/opentensor/frontier", rev = "cd6bca14a3", default-features = false }
+pallet-evm-precompile-dispatch = { git = "https://github.com/opentensor/frontier", rev = "cd6bca14a3", default-features = false }
pallet-evm-chain-id = { git = "https://github.com/opentensor/frontier", rev = "cd6bca14a3", default-features = false }
pallet-evm-precompile-modexp = { git = "https://github.com/opentensor/frontier", rev = "cd6bca14a3", default-features = false }
pallet-evm-precompile-sha3fips = { git = "https://github.com/opentensor/frontier", rev = "cd6bca14a3", default-features = false }
diff --git a/evm-tests/src/config.ts b/evm-tests/src/config.ts
index 00b942f802..7fb40e67cb 100644
--- a/evm-tests/src/config.ts
+++ b/evm-tests/src/config.ts
@@ -35,4 +35,8 @@ export const IBalanceTransferABI = [
stateMutability: "payable",
type: "function",
},
-];
\ No newline at end of file
+];
+
+export const IDISPATCH_ADDRESS = "0x0000000000000000000000000000000000000006";
+
+export const ISTORAGE_QUERY_ADDRESS = "0x0000000000000000000000000000000000000807";
\ No newline at end of file
diff --git a/evm-tests/src/subtensor.ts b/evm-tests/src/subtensor.ts
index 3111d90544..e3d5526268 100644
--- a/evm-tests/src/subtensor.ts
+++ b/evm-tests/src/subtensor.ts
@@ -343,4 +343,12 @@ export async function startCall(api: TypedApi, netuid: number, ke
const callStarted = await api.query.SubtensorModule.FirstEmissionBlockNumber
.getValue(netuid);
assert.notEqual(callStarted, undefined);
+}
+
+export async function setMaxChildkeyTake(api: TypedApi, take: number) {
+ const alice = getAliceSigner()
+ const internalCall = api.tx.SubtensorModule.sudo_set_max_childkey_take({ take })
+ const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall })
+
+ await waitForTransactionWithRetry(api, tx, alice)
}
\ No newline at end of file
diff --git a/evm-tests/test/runtime.call.precompile.test.ts b/evm-tests/test/runtime.call.precompile.test.ts
new file mode 100644
index 0000000000..4cd7690724
--- /dev/null
+++ b/evm-tests/test/runtime.call.precompile.test.ts
@@ -0,0 +1,75 @@
+import * as assert from "assert";
+import { getAliceSigner, getDevnetApi } from "../src/substrate"
+import { generateRandomEthersWallet, getPublicClient } from "../src/utils";
+import { IDISPATCH_ADDRESS, ISTORAGE_QUERY_ADDRESS, ETH_LOCAL_URL } from "../src/config";
+import { devnet, MultiAddress } from "@polkadot-api/descriptors"
+import { hexToNumber, PublicClient } from "viem";
+import { PolkadotSigner, TypedApi } from "polkadot-api";
+import { convertPublicKeyToSs58 } from "../src/address-utils"
+import { forceSetBalanceToEthAddress, setMaxChildkeyTake } from "../src/subtensor";
+import { xxhashAsU8a } from '@polkadot/util-crypto';
+import { u8aToHex } from '@polkadot/util';
+
+describe("Test the dispatch precompile", () => {
+ let publicClient: PublicClient;
+ const wallet1 = generateRandomEthersWallet();
+ let api: TypedApi
+ let alice: PolkadotSigner;
+
+ before(async () => {
+ publicClient = await getPublicClient(ETH_LOCAL_URL)
+ api = await getDevnetApi()
+ alice = await getAliceSigner()
+ await forceSetBalanceToEthAddress(api, wallet1.address)
+ })
+
+ it("Dispatch transfer call via precompile contract works correctly", async () => {
+ // call for transfer 1 token to alice
+ const transferAmount = BigInt(1000000000);
+
+ const unsignedTx = api.tx.Balances.transfer_keep_alive({
+ dest: MultiAddress.Id(convertPublicKeyToSs58(alice.publicKey)),
+ value: transferAmount,
+ });
+ const encodedCallDataBytes = await unsignedTx.getEncodedData();
+
+ // encoded call should be 0x050300d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d02286bee
+ const transferCall = encodedCallDataBytes.asHex()
+
+ const aliceBalance = (await api.query.System.Account.getValue( convertPublicKeyToSs58(alice.publicKey))).data.free
+ const txResponse = await wallet1.sendTransaction({
+ to: IDISPATCH_ADDRESS,
+ data: transferCall,
+ })
+ await txResponse.wait()
+
+ const aliceBalanceAfterTransfer = (await api.query.System.Account.getValue( convertPublicKeyToSs58(alice.publicKey))).data.free
+
+ assert.equal(aliceBalance + transferAmount, aliceBalanceAfterTransfer)
+ })
+
+
+ it("Storage query call via precompile contract works correctly", async () => {
+ const palletPrefixBytes = xxhashAsU8a("SubtensorModule", 128);
+ const storageItemPrefixBytes = xxhashAsU8a("MaxChildkeyTake", 128);
+ const fullStorageKeyBytes = new Uint8Array([...palletPrefixBytes, ...storageItemPrefixBytes]);
+ // 0x658faa385070e074c85bf6b568cf0555dba018859cab7e989f77669457b394be
+ // key for max child key take
+ const fullStorageKeyHex = u8aToHex(fullStorageKeyBytes);
+
+ let maxChildkeyTake = 257;
+ await setMaxChildkeyTake(api, maxChildkeyTake)
+
+ api.query.SubtensorModule.MaxChildkeyTake.getValue();
+ const rawCallResponse = await publicClient.call({
+ to: ISTORAGE_QUERY_ADDRESS,
+ data: fullStorageKeyHex,
+ })
+ const rawResultData = rawCallResponse.data;
+ if (rawResultData === undefined) {
+ throw new Error("rawResultData is undefined");
+ }
+ let value = hexToNumber(rawResultData);
+ assert.equal(value, maxChildkeyTake, "value should be 257")
+ })
+});
diff --git a/precompiles/Cargo.toml b/precompiles/Cargo.toml
index ec46e6aee2..f19e8f2d1e 100644
--- a/precompiles/Cargo.toml
+++ b/precompiles/Cargo.toml
@@ -18,12 +18,14 @@ frame-system = { workspace = true }
log = { workspace = true }
pallet-balances = { workspace = true }
pallet-evm = { workspace = true }
+pallet-evm-precompile-dispatch = { workspace = true }
pallet-evm-precompile-modexp = { workspace = true }
pallet-evm-precompile-sha3fips = { workspace = true }
pallet-evm-precompile-simple = { workspace = true }
pallet-proxy = { workspace = true }
precompile-utils = { workspace = true }
sp-core = { workspace = true }
+sp-io = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }
subtensor-runtime-common = { workspace = true }
@@ -44,6 +46,7 @@ std = [
"log/std",
"pallet-admin-utils/std",
"pallet-balances/std",
+ "pallet-evm-precompile-dispatch/std",
"pallet-evm-precompile-modexp/std",
"pallet-evm-precompile-sha3fips/std",
"pallet-evm-precompile-simple/std",
@@ -52,6 +55,7 @@ std = [
"pallet-subtensor/std",
"precompile-utils/std",
"sp-core/std",
+ "sp-io/std",
"sp-runtime/std",
"sp-std/std",
"subtensor-runtime-common/std",
diff --git a/precompiles/src/lib.rs b/precompiles/src/lib.rs
index 4c6824e07b..89902ec9ee 100644
--- a/precompiles/src/lib.rs
+++ b/precompiles/src/lib.rs
@@ -4,11 +4,15 @@ extern crate alloc;
use core::marker::PhantomData;
-use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo};
+use frame_support::{
+ dispatch::{GetDispatchInfo, PostDispatchInfo},
+ pallet_prelude::Decode,
+};
use pallet_evm::{
AddressMapping, IsPrecompileResult, Precompile, PrecompileHandle, PrecompileResult,
PrecompileSet,
};
+use pallet_evm_precompile_dispatch::Dispatch;
use pallet_evm_precompile_modexp::Modexp;
use pallet_evm_precompile_sha3fips::Sha3FIPS256;
use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
@@ -25,6 +29,7 @@ use crate::extensions::*;
use crate::metagraph::*;
use crate::neuron::*;
use crate::staking::*;
+use crate::storage_query::*;
use crate::subnet::*;
use crate::uid_lookup::*;
@@ -34,9 +39,9 @@ mod extensions;
mod metagraph;
mod neuron;
mod staking;
+mod storage_query;
mod subnet;
mod uid_lookup;
-
pub struct Precompiles(PhantomData);
impl Default for Precompiles
@@ -86,13 +91,14 @@ where
Self(Default::default())
}
- pub fn used_addresses() -> [H160; 15] {
+ pub fn used_addresses() -> [H160; 17] {
[
hash(1),
hash(2),
hash(3),
hash(4),
hash(5),
+ hash(6),
hash(1024),
hash(1025),
hash(Ed25519Verify::::INDEX),
@@ -102,6 +108,7 @@ where
hash(MetagraphPrecompile::::INDEX),
hash(NeuronPrecompile::::INDEX),
hash(StakingPrecompileV2::::INDEX),
+ hash(StorageQueryPrecompile::::INDEX),
hash(UidLookupPrecompile::::INDEX),
]
}
@@ -120,7 +127,10 @@ where
+ From>
+ From>
+ GetDispatchInfo
- + Dispatchable,
+ + Dispatchable
+ + Decode,
+ <::RuntimeCall as Dispatchable>::RuntimeOrigin:
+ From