Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
7a678b0
init solution
open-junius Jun 20, 2025
e2f000d
cargo clippy
open-junius Jun 20, 2025
24cd3a3
change hash algorithm
open-junius Jun 20, 2025
675ac0d
fix transaction signature
open-junius Jun 20, 2025
bf43ae3
commit Cargo.lock
open-junius Jun 20, 2025
93cfd46
commit Cargo.lock
open-junius Jun 21, 2025
92aeff1
commit Cargo.lock
open-junius Jun 21, 2025
16e2d34
fix linter
open-junius Jun 21, 2025
528e4f7
commit Cargo.lock
open-junius Jun 21, 2025
882495c
commit Cargo.lock
open-junius Jun 21, 2025
9a2aeca
commit Cargo.lock
open-junius Jun 21, 2025
436f580
commit Cargo.lock
open-junius Jun 21, 2025
abd76c3
commit Cargo.lock
open-junius Jun 21, 2025
4ccae8d
commit Cargo.lock
open-junius Jun 21, 2025
12a78bc
commit Cargo.lock
open-junius Jun 21, 2025
9537db0
commit Cargo.lock
open-junius Jun 21, 2025
9680f49
commit Cargo.lock
open-junius Jun 21, 2025
7c70fda
fix linter
open-junius Jun 21, 2025
967b6a2
fix benchmark
open-junius Jun 22, 2025
e7cfc3b
commit Cargo.lock
open-junius Jun 22, 2025
a105a1a
fix missed std
open-junius Jun 22, 2025
8307eef
commit Cargo.lock
open-junius Jun 22, 2025
cdd18f8
commit Cargo.lock
open-junius Jun 22, 2025
b434ac1
commit Cargo.lock
open-junius Jun 22, 2025
24748a8
commit Cargo.lock
open-junius Jun 22, 2025
14327bc
commit Cargo.lock
open-junius Jun 22, 2025
c4de59e
commit Cargo.lock
open-junius Jun 22, 2025
b644f55
add more edge cases
open-junius Jun 22, 2025
3c3dce1
add check for existed proxy account
open-junius Jun 22, 2025
1fb8a49
all edge cases passed
open-junius Jun 22, 2025
9db133d
fix bug
open-junius Jun 22, 2025
632b106
cargo clippy
open-junius Jun 22, 2025
436dec2
fix benchmark
open-junius Jun 22, 2025
cf72a39
clean up code
open-junius Jun 23, 2025
e8bf673
Merge branch 'devnet-ready' into pure-proxy-precompile
open-junius Jun 24, 2025
8d2c236
upgrade runtime
open-junius Jun 24, 2025
01e542a
fix conflict
open-junius Jun 26, 2025
5cfbe5f
fix conflict
open-junius Jun 27, 2025
3f8a2c5
merge with devnet-ready
open-junius Jul 17, 2025
5482539
update contract id
open-junius Jul 17, 2025
0134c77
commit Cargo.lock
open-junius Jul 17, 2025
f24d165
commit Cargo.lock
open-junius Jul 17, 2025
4c19e16
commit Cargo.lock
open-junius Jul 17, 2025
e2a1e6e
Merge branch 'devnet-ready' into pure-proxy-precompile
open-junius Jul 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions evm-tests/src/contracts/pureProxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export const IPURE_PROXY_ADDRESS = "0x000000000000000000000000000000000000080a";

export const IPureProxyABI = [
{
"inputs": [],
"name": "createPureProxy",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "getPureProxy",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint8[]",
"name": "call",
"type": "uint8[]"
}
],
"name": "pureProxyCall",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
];
12 changes: 11 additions & 1 deletion evm-tests/src/subtensor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { devnet, MultiAddress } from '@polkadot-api/descriptors';
import { TypedApi, TxCallData } from 'polkadot-api';
import { KeyPair } from "@polkadot-labs/hdkd-helpers"
import { getAliceSigner, waitForTransactionCompletion, getSignerFromKeypair, waitForTransactionWithRetry } from './substrate'
import { convertH160ToSS58, convertPublicKeyToSs58 } from './address-utils'
import { convertH160ToSS58, convertPublicKeyToSs58, ethAddressToH160 } from './address-utils'
import { tao } from './balance-math'
import internal from "stream";

Expand Down Expand Up @@ -353,6 +353,16 @@ export async function setMaxChildkeyTake(api: TypedApi<typeof devnet>, take: num
await waitForTransactionWithRetry(api, tx, alice)
}

// use the alice as wrong mapped account to send extrinsic
export async function setPureProxyAccount(api: TypedApi<typeof devnet>, address: string, account: string) {
const alice = getAliceSigner()
const call = api.tx.SubtensorModule.set_pure_proxy_account({
address: ethAddressToH160(address),
account
})
await waitForTransactionWithRetry(api, call, alice)
}

// Swap coldkey to contract address
export async function swapColdkey(
api: TypedApi<typeof devnet>,
Expand Down
130 changes: 130 additions & 0 deletions evm-tests/test/pure-proxy.precompile.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import * as assert from "assert";

import { getAliceSigner, getDevnetApi } from "../src/substrate"
import { generateRandomEthersWallet, generateRandomEthWallet } from "../src/utils";
import { devnet, MultiAddress } from "@polkadot-api/descriptors"
import { hexToU8a } from "@polkadot/util";
import { PolkadotSigner, TypedApi } from "polkadot-api";
import { convertPublicKeyToSs58 } from "../src/address-utils"
import { IPureProxyABI, IPURE_PROXY_ADDRESS } from "../src/contracts/pureProxy"
import { keccak256, ethers } from 'ethers';
import { forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, setPureProxyAccount } from "../src/subtensor";
import { Signer } from "@polkadot/api/types";

function getPureProxyAccount(address: string) {

const prefix = new TextEncoder().encode("pureproxy:")

const addressH160 = hexToU8a(address)

const data = new Uint8Array([...prefix, ...addressH160]);

return keccak256(data)
}

async function getTransferCallCode(api: TypedApi<typeof devnet>, signer: PolkadotSigner) {
const transferAmount = BigInt(1000000000);

const unsignedTx = api.tx.Balances.transfer_keep_alive({
dest: MultiAddress.Id(convertPublicKeyToSs58(signer.publicKey)),
value: transferAmount,
});
const encodedCallDataBytes = await unsignedTx.getEncodedData();

// encoded call should be 0x050300d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d02286bee
// const transferCall = encodedCallDataBytes

const data = encodedCallDataBytes.asBytes()

return [...data]
}

describe("Test pure proxy precompile", () => {
const evmWallet = generateRandomEthersWallet();
const evmWallet2 = generateRandomEthersWallet();

let api: TypedApi<typeof devnet>

let alice: PolkadotSigner;

before(async () => {
api = await getDevnetApi()
alice = await getAliceSigner();

await forceSetBalanceToEthAddress(api, evmWallet.address)
await forceSetBalanceToEthAddress(api, evmWallet2.address)

})

it("Call createPureProxy, then use proxy to call transfer", async () => {
const contract = new ethers.Contract(IPURE_PROXY_ADDRESS, IPureProxyABI, evmWallet)

const tx = await contract.createPureProxy()
await tx.wait()
const proxyAddress = await contract.getPureProxy();

const expected = getPureProxyAccount(evmWallet.address)
assert.equal(proxyAddress, expected, "the proxy account not the same as expected")

const ss58Address = convertPublicKeyToSs58(proxyAddress)

await forceSetBalanceToSs58Address(api, ss58Address)

const callCode = await getTransferCallCode(api, alice)
const tx2 = await contract.pureProxyCall(callCode)
await tx2.wait()

})

it("Call createPureProxy, edge cases", async () => {
const contract = new ethers.Contract(IPURE_PROXY_ADDRESS, IPureProxyABI, evmWallet2)
const proxyAddressBeforeCreate = await contract.getPureProxy();
console.log(proxyAddressBeforeCreate)
assert.equal(proxyAddressBeforeCreate, "0x0000000000000000000000000000000000000000000000000000000000000000", "proxy should be undefined before set")

// use papi to set proxy with wrong mapped account
await setPureProxyAccount(api, evmWallet2.address, convertPublicKeyToSs58(alice.publicKey))
assert.equal(proxyAddressBeforeCreate, "0x0000000000000000000000000000000000000000000000000000000000000000", "proxy should be zero after wrong signer")

const callCode = await getTransferCallCode(api, alice)

// call without proxy
try {
const tx = await contract.pureProxyCall(callCode)
await tx.wait()
} catch (error) {
assert.notEqual(error, undefined, "should fail if proxy not set")
}

const tx = await contract.createPureProxy()
await tx.wait()
const proxyAddress = await contract.getPureProxy();

const expected = getPureProxyAccount(evmWallet2.address)
assert.equal(proxyAddress, expected, "the proxy account not the same as expected")

// set the proxy again
try {
const tx = await contract.createPureProxy()
await tx.wait()
} catch (error) {
assert.notEqual(error, undefined, "should fail if set proxy again")
}

// send extrinsic without token
try {
const tx = await contract.pureProxyCall(callCode)
await tx.wait()
} catch (error) {
assert.notEqual(error, undefined, "should fail if proxy without balance")
}

// set balance for proxy account
const ss58Address = convertPublicKeyToSs58(proxyAddress)
await forceSetBalanceToSs58Address(api, ss58Address)

// try proxy call finally
const tx2 = await contract.pureProxyCall(callCode)
await tx2.wait()
})
});
6 changes: 3 additions & 3 deletions evm-tests/test/runtime.call.precompile.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as assert from "assert";
import { getAliceSigner, getDevnetApi } from "../src/substrate"
import { generateRandomEthersWallet, getPublicClient } from "../src/utils";
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";
Expand Down Expand Up @@ -36,14 +36,14 @@ describe("Test the dispatch precompile", () => {
// encoded call should be 0x050300d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d02286bee
const transferCall = encodedCallDataBytes.asHex()

const aliceBalance = (await api.query.System.Account.getValue( convertPublicKeyToSs58(alice.publicKey))).data.free
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
const aliceBalanceAfterTransfer = (await api.query.System.Account.getValue(convertPublicKeyToSs58(alice.publicKey))).data.free

assert.equal(aliceBalance + transferAmount, aliceBalanceAfterTransfer)
})
Expand Down
4 changes: 4 additions & 0 deletions pallets/admin-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pallet-scheduler = { workspace = true }
pallet-grandpa = { workspace = true }
sp-std = { workspace = true }
pallet-subtensor-swap = { workspace = true }
pallet-evm = { workspace = true }
pallet-crowdloan = { workspace = true, default-features = false }
pallet-preimage = { workspace = true, default-features = false }

Expand All @@ -58,6 +59,7 @@ std = [
"log/std",
"pallet-balances/std",
"pallet-drand/std",
"pallet-evm/std",
"pallet-evm-chain-id/std",
"pallet-grandpa/std",
"pallet-scheduler/std",
Expand All @@ -84,6 +86,7 @@ runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-drand/runtime-benchmarks",
"pallet-evm/runtime-benchmarks",
"pallet-grandpa/runtime-benchmarks",
"pallet-scheduler/runtime-benchmarks",
"pallet-subtensor/runtime-benchmarks",
Expand All @@ -97,6 +100,7 @@ try-runtime = [
"frame-system/try-runtime",
"pallet-balances/try-runtime",
"pallet-drand/try-runtime",
"pallet-evm/try-runtime",
"pallet-evm-chain-id/try-runtime",
"pallet-grandpa/try-runtime",
"pallet-scheduler/try-runtime",
Expand Down
2 changes: 2 additions & 0 deletions pallets/admin-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ pub mod pallet {
Alpha,
/// Enum for crowdloan precompile
Crowdloan,
/// Pure proxy precompile
PureProxy,
}

#[pallet::type_value]
Expand Down
9 changes: 9 additions & 0 deletions pallets/admin-utils/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ use subtensor_runtime_common::NetUid;

type Block = frame_system::mocking::MockBlock<Test>;

pub struct DummyAddressMap;

impl pallet_evm::AddressMapping<AccountId> for DummyAddressMap {
fn into_account_id(address: sp_core::H160) -> AccountId {
let account = pallet_evm::HashedAddressMapping::<BlakeTwo256>::into_account_id(address);
U256::from_big_endian(account.as_ref())
}
}
// Configure a mock runtime to test the pallet.
frame_support::construct_runtime!(
pub enum Test {
Expand Down Expand Up @@ -222,6 +230,7 @@ impl pallet_subtensor::Config for Test {
type SwapInterface = Swap;
type KeySwapOnSubnetCost = InitialKeySwapOnSubnetCost;
type HotkeySwapOnSubnetInterval = HotkeySwapOnSubnetInterval;
type AddressMapping = DummyAddressMap;
type ProxyInterface = ();
type LeaseDividendsDistributionInterval = LeaseDividendsDistributionInterval;
}
Expand Down
4 changes: 4 additions & 0 deletions pallets/subtensor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ subtensor-runtime-common = { workspace = true, features = ["approx"] }

pallet-collective = { version = "4.0.0-dev", default-features = false, path = "../collective" }
pallet-drand = { path = "../drand", default-features = false }
pallet-evm = { workspace = true }
pallet-membership = { workspace = true }
hex-literal = { workspace = true }
num-traits = { version = "0.2.19", default-features = false, features = [
Expand Down Expand Up @@ -92,6 +93,7 @@ std = [
"pallet-balances/std",
"pallet-collective/std",
"pallet-drand/std",
"pallet-evm/std",
"pallet-membership/std",
"pallet-preimage/std",
"pallet-scheduler/std",
Expand Down Expand Up @@ -134,6 +136,7 @@ runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-collective/runtime-benchmarks",
"pallet-evm/runtime-benchmarks",
"pallet-drand/runtime-benchmarks",
"pallet-membership/runtime-benchmarks",
"pallet-preimage/runtime-benchmarks",
Expand All @@ -149,6 +152,7 @@ try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"pallet-balances/try-runtime",
"pallet-evm/try-runtime",
"pallet-membership/try-runtime",
"pallet-preimage/try-runtime",
"pallet-scheduler/try-runtime",
Expand Down
13 changes: 12 additions & 1 deletion pallets/subtensor/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use frame_benchmarking::v2::*;
use frame_support::{StorageDoubleMap, assert_ok};
use frame_system::{RawOrigin, pallet_prelude::BlockNumberFor};
pub use pallet::*;
use sp_core::H256;
use sp_core::{H160, H256};
use sp_runtime::{
BoundedVec, Percent,
traits::{BlakeTwo256, Hash},
Expand All @@ -20,6 +20,7 @@ use subtensor_runtime_common::{AlphaCurrency, NetUid};
#[frame_benchmarking::v2::benchmarks]
mod pallet_benchmarks {
use super::*;
use pallet_evm::AddressMapping;

#[benchmark]
fn register() {
Expand Down Expand Up @@ -1381,6 +1382,16 @@ mod pallet_benchmarks {
_(RawOrigin::Signed(coldkey), hotkey);
}

#[benchmark]
fn set_pure_proxy_account() {
let address: H160 = H160::zero();
let mapped_account = T::AddressMapping::into_account_id(address);
let proxy_account: T::AccountId = account("A", 0, 7);

#[extrinsic_call]
_(RawOrigin::Signed(mapped_account), address, proxy_account);
}

#[benchmark]
fn remove_stake_full_limit() {
let netuid = NetUid::from(1);
Expand Down
5 changes: 5 additions & 0 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1699,6 +1699,11 @@ pub mod pallet {
pub type AssociatedEvmAddress<T: Config> =
StorageDoubleMap<_, Twox64Concat, NetUid, Twox64Concat, u16, (H160, u64), OptionQuery>;

#[pallet::storage]
/// --- MAP (H160) --> T::AccountId
pub type PureProxyAccount<T: Config> =
StorageMap<_, Twox64Concat, H160, T::AccountId, OptionQuery>;

/// ========================
/// ==== Subnet Leasing ====
/// ========================
Expand Down
Loading
Loading