From d8bce81ef1753f8da604af42188392ead9609189 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sat, 27 Sep 2025 18:45:39 -0400 Subject: [PATCH 01/19] proxy precompile init impl --- common/src/lib.rs | 27 ++++ evm-tests/src/contracts/proxy.ts | 148 +++++++++++++++++++ pallets/admin-utils/src/lib.rs | 4 +- precompiles/src/lib.rs | 8 +- precompiles/src/proxy.rs | 222 +++++++++++++++++++++++++++++ precompiles/src/solidity/proxy.sol | 30 ++++ 6 files changed, 436 insertions(+), 3 deletions(-) create mode 100644 evm-tests/src/contracts/proxy.ts create mode 100644 precompiles/src/proxy.rs create mode 100644 precompiles/src/solidity/proxy.sol diff --git a/common/src/lib.rs b/common/src/lib.rs index a5d09ad974..e3fd723679 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -161,6 +161,33 @@ pub enum ProxyType { SubnetLeaseBeneficiary, // Used to operate the leased subnet } +impl TryFrom for ProxyType { + type Error = (); + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(Self::Any), + 1 => Ok(Self::Owner), + 2 => Ok(Self::NonCritical), + 3 => Ok(Self::NonTransfer), + 4 => Ok(Self::Senate), + 5 => Ok(Self::NonFungibile), + 6 => Ok(Self::Triumvirate), + 7 => Ok(Self::Governance), + 8 => Ok(Self::Staking), + 9 => Ok(Self::Registration), + 10 => Ok(Self::Transfer), + 11 => Ok(Self::SmallTransfer), + 12 => Ok(Self::RootWeights), + 13 => Ok(Self::ChildKeys), + 14 => Ok(Self::SudoUncheckedSetCode), + 15 => Ok(Self::SwapHotkey), + 16 => Ok(Self::SubnetLeaseBeneficiary), + _ => Err(()), + } + } +} + impl Default for ProxyType { // allow all Calls; required to be most permissive fn default() -> Self { diff --git a/evm-tests/src/contracts/proxy.ts b/evm-tests/src/contracts/proxy.ts new file mode 100644 index 0000000000..4059409276 --- /dev/null +++ b/evm-tests/src/contracts/proxy.ts @@ -0,0 +1,148 @@ +export const IPROXY_ADDRESS = "0x000000000000000000000000000000000000080b"; + +export const IProxyABI = [ + { + "inputs": [ + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "index", + "type": "uint16" + } + ], + "name": "createPureProxy", + "outputs": [ + { + "internalType": "bytes32", + "name": "proxy", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "spawner", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "index", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "height", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "ext_index", + "type": "uint32" + } + ], + "name": "killPureProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "real", + "type": "bytes32" + }, + { + "internalType": "uint8[]", + "name": "force_proxy_type", // optional + "type": "uint8[]" + }, + { + "internalType": "uint8[]", + "name": "call", + "type": "uint8[]" + } + ], + "name": "proxyCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "removeProxies", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + },{ + "inputs": [], + "name": "pokeDeposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + } + ], + "name": "removeProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + } + ], + "name": "addProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +]; diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 61e4b8a49b..e6332e398e 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -132,8 +132,8 @@ pub mod pallet { Alpha, /// Enum for crowdloan precompile Crowdloan, - /// Pure proxy precompile - PureProxy, + /// Proxy precompile + Proxy, /// Leasing precompile Leasing, } diff --git a/precompiles/src/lib.rs b/precompiles/src/lib.rs index f4de789dbf..95273dc724 100644 --- a/precompiles/src/lib.rs +++ b/precompiles/src/lib.rs @@ -33,6 +33,7 @@ use crate::extensions::*; use crate::leasing::*; use crate::metagraph::*; use crate::neuron::*; +use crate::proxy::*; use crate::sr25519::*; use crate::staking::*; use crate::storage_query::*; @@ -47,6 +48,7 @@ mod extensions; mod leasing; mod metagraph; mod neuron; +mod proxy; mod sr25519; mod staking; mod storage_query; @@ -107,7 +109,7 @@ where Self(Default::default()) } - pub fn used_addresses() -> [H160; 24] { + pub fn used_addresses() -> [H160; 25] { [ hash(1), hash(2), @@ -133,6 +135,7 @@ where hash(AlphaPrecompile::::INDEX), hash(CrowdloanPrecompile::::INDEX), hash(LeasingPrecompile::::INDEX), + hash(ProxyPrecompile::::INDEX), ] } } @@ -219,6 +222,9 @@ where a if a == hash(LeasingPrecompile::::INDEX) => { LeasingPrecompile::::try_execute::(handle, PrecompileEnum::Leasing) } + a if a == hash(ProxyPrecompile::::INDEX) => { + ProxyPrecompile::::try_execute::(handle, PrecompileEnum::Proxy) + } _ => None, } } diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs new file mode 100644 index 0000000000..b243c4c161 --- /dev/null +++ b/precompiles/src/proxy.rs @@ -0,0 +1,222 @@ +use core::marker::PhantomData; + +use crate::{PrecompileExt, PrecompileHandleExt}; + +use fp_evm::{ExitError, PrecompileFailure}; +use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo}; +use frame_system::RawOrigin; +use pallet_evm::{AddressMapping, PrecompileHandle}; +use precompile_utils::EvmResult; +use sp_core::H256; +use sp_runtime::{ + codec::DecodeLimit, + traits::{Dispatchable, StaticLookup}, +}; +use sp_std::boxed::Box; +use sp_std::vec::Vec; +use subtensor_runtime_common::ProxyType; +pub struct ProxyPrecompile(PhantomData); +const MAX_DECODE_DEPTH: u32 = 8; + +impl PrecompileExt for ProxyPrecompile +where + R: frame_system::Config + + pallet_evm::Config + + pallet_subtensor::Config + + pallet_proxy::Config, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, + ::AddressMapping: AddressMapping, + ::RuntimeCall: From> + + From> + + GetDispatchInfo + + Dispatchable, + ::AddressMapping: AddressMapping, + <::Lookup as StaticLookup>::Source: From, +{ + const INDEX: u64 = 2059; +} + +#[precompile_utils::precompile] +impl ProxyPrecompile +where + R: frame_system::Config + + pallet_evm::Config + + pallet_subtensor::Config + + pallet_proxy::Config, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, + ::AddressMapping: AddressMapping, + ::RuntimeCall: From> + + From> + + GetDispatchInfo + + Dispatchable, + <::Lookup as StaticLookup>::Source: From, +{ + #[precompile::public("createPureProxy(uint8,uint32,uint16)")] + pub fn create_pure_proxy( + handle: &mut impl PrecompileHandle, + proxy_type_: u8, + delay: u32, + index: u16, + ) -> EvmResult { + let account_id = handle.caller_account_id::(); + let proxy_type = + ProxyType::try_from(proxy_type_).map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid proxy type".into()), + })?; + + let call = pallet_proxy::Call::::create_pure { + proxy_type, + delay: delay.into(), + index, + }; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id.clone()))?; + + // Success! + // Try to get proxy address + let proxy_address: [u8; 32] = + pallet_proxy::pallet::Pallet::::pure_account(&account_id, &proxy_type, index, None) + .into(); + + // Check if in the proxies map + let proxy_entry = pallet_proxy::pallet::Pallet::::proxies(account_id.clone()); + if proxy_entry + .0 + .iter() + .any(|p| account_id == p.delegate && proxy_type == p.proxy_type) + { + return Ok(proxy_address.into()); + } + + Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Proxy not found".into()), + }) + } + + #[precompile::public("killPureProxy(bytes32,uint8,uint16,uint32,uint32)")] + pub fn kill_pure_proxy( + handle: &mut impl PrecompileHandle, + spawner: H256, + proxy_type: u8, + index: u16, + height: u32, + ext_index: u32, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + let proxy_type = ProxyType::try_from(proxy_type).map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid proxy type".into()), + })?; + + let call = pallet_proxy::Call::::kill_pure { + spawner: <::Lookup as StaticLookup>::Source::from( + spawner.0.into(), + ), + proxy_type, + index, + height: height.into(), + ext_index: ext_index.into(), + }; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("proxyCall(bytes32,uint8[],uint8[])")] + pub fn proxy_call( + handle: &mut impl PrecompileHandle, + real: H256, + force_proxy_type: Vec, + call: Vec, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + + let call = ::RuntimeCall::decode_with_depth_limit( + MAX_DECODE_DEPTH, + &mut &call[..], + ) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("The raw call data not correctly encoded".into()), + })?; + + let mut proxy_type: Option = None; + if let Some(p) = force_proxy_type.first() { + let proxy_type_ = ProxyType::try_from(*p).map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid proxy type".into()), + })?; + proxy_type = Some(proxy_type_); + }; + + let call = pallet_proxy::Call::::proxy { + real: <::Lookup as StaticLookup>::Source::from( + real.0.into(), + ), + force_proxy_type: proxy_type, + call: Box::new(call), + }; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("addProxy(bytes32,uint8,uint32)")] + pub fn add_proxy( + handle: &mut impl PrecompileHandle, + delegate: H256, + proxy_type: u8, + delay: u32, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + let proxy_type = ProxyType::try_from(proxy_type).map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid proxy type".into()), + })?; + + let call = pallet_proxy::Call::::add_proxy { + delegate: <::Lookup as StaticLookup>::Source::from( + delegate.0.into(), + ), + proxy_type, + delay: delay.into(), + }; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("removeProxy(bytes32,uint8,uint32)")] + pub fn remove_proxy( + handle: &mut impl PrecompileHandle, + delegate: H256, + proxy_type: u8, + delay: u32, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + let proxy_type = ProxyType::try_from(proxy_type).map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid proxy type".into()), + })?; + + let call = pallet_proxy::Call::::remove_proxy { + delegate: <::Lookup as StaticLookup>::Source::from( + delegate.0.into(), + ), + proxy_type, + delay: delay.into(), + }; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("removeProxies()")] + pub fn remove_proxies(handle: &mut impl PrecompileHandle) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + + let call = pallet_proxy::Call::::remove_proxies {}; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("pokeDeposit()")] + pub fn poke_deposit(handle: &mut impl PrecompileHandle) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + + let call = pallet_proxy::Call::::poke_deposit {}; + + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } +} diff --git a/precompiles/src/solidity/proxy.sol b/precompiles/src/solidity/proxy.sol new file mode 100644 index 0000000000..b0e03031bf --- /dev/null +++ b/precompiles/src/solidity/proxy.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant IPROXY_ADDRESS = 0x000000000000000000000000000000000000080b; + +interface IProxy { + function createPureProxy( + uint8 proxy_type, + uint32 delay, + uint16 index + ) external; + + function proxyCall(bytes32 real, uint8[] memory force_proxy_type, bytes memory call) external; + + function killPureProxy( + bytes32 spawner, + uint8 proxy_type, + uint16 index, + uint16 height, + uint32 ext_index + ) external; + + function addProxy(bytes32 delegate, uint8 proxy_type, uint32 delay) external; + + function removeProxy(bytes32 delegate, uint8 proxy_type, uint32 delay) external; + + function removeProxies() external; + + function pokeDeposit() external; +} From 895a29c2459365a1efe828999203367ea440c383 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Mon, 3 Nov 2025 14:26:32 -0500 Subject: [PATCH 02/19] impl --- Cargo.lock | 1 + pallets/proxy/src/lib.rs | 19 ++++++++++++++++++- pallets/subtensor/Cargo.toml | 1 + pallets/subtensor/src/tests/mock.rs | 2 ++ precompiles/src/proxy.rs | 21 ++++++++++++++++++++- 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 18ee1cd90f..05e79f5962 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8132,6 +8132,7 @@ dependencies = [ "pallet-drand", "pallet-membership", "pallet-preimage", + "pallet-proxy 40.1.0", "pallet-scheduler", "pallet-subtensor-swap", "pallet-utility 40.0.0", diff --git a/pallets/proxy/src/lib.rs b/pallets/proxy/src/lib.rs index a4325bd099..8d9be72bb2 100644 --- a/pallets/proxy/src/lib.rs +++ b/pallets/proxy/src/lib.rs @@ -41,6 +41,7 @@ use frame::{ }; pub use pallet::*; use subtensor_macros::freeze_struct; +use frame_system::pallet_prelude::BlockNumberFor as SystemBlockNumberFor; pub use weights::WeightInfo; type CallHashOf = <::CallHasher as Hash>::Output; @@ -747,6 +748,14 @@ pub mod pallet { NoSelfProxy, } + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_finalize(_n: SystemBlockNumberFor) { + // clear this map on end of each block + let _ = LastCallResult::::clear(u32::MAX, None); + } + } + /// The set of account proxies. Maps the account which has delegated to the accounts /// which are being delegated to, together with the amount held on deposit. #[pallet::storage] @@ -777,6 +786,11 @@ pub mod pallet { ValueQuery, >; + /// The result of the last call made by the proxy (key). + #[pallet::storage] + pub type LastCallResult = + StorageMap<_, Twox64Concat, T::AccountId, DispatchResult, OptionQuery>; + #[pallet::view_functions_experimental] impl Pallet { /// Check if a `RuntimeCall` is allowed for a given `ProxyType`. @@ -1022,7 +1036,7 @@ impl Pallet { ) { use frame::traits::{InstanceFilter as _, OriginTrait as _}; // This is a freshly authenticated new account, the origin restrictions doesn't apply. - let mut origin: T::RuntimeOrigin = frame_system::RawOrigin::Signed(real).into(); + let mut origin: T::RuntimeOrigin = frame_system::RawOrigin::Signed(real.clone()).into(); origin.add_filter(move |c: &::RuntimeCall| { let c = ::RuntimeCall::from_ref(c); // We make sure the proxy call does access this pallet to change modify proxies. @@ -1046,6 +1060,9 @@ impl Pallet { } }); let e = call.dispatch(origin); + + LastCallResult::::insert(real, e.map(|_| ()).map_err(|e| e.error)); + Self::deposit_event(Event::ProxyExecuted { result: e.map(|_| ()).map_err(|e| e.error), }); diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index ed40d8d36f..97ade5a97c 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -45,6 +45,7 @@ pallet-drand.workspace = true pallet-commitments.workspace = true pallet-collective.workspace = true pallet-membership.workspace = true +pallet-proxy.workspace = true hex-literal.workspace = true num-traits = { workspace = true, features = ["libm"] } tle.workspace = true diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 651c208ad4..df4f6b8afe 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -45,6 +45,7 @@ frame_support::construct_runtime!( Drand: pallet_drand::{Pallet, Call, Storage, Event} = 11, Swap: pallet_subtensor_swap::{Pallet, Call, Storage, Event} = 12, Crowdloan: pallet_crowdloan::{Pallet, Call, Storage, Event} = 13, + Proxy: pallet_proxy = 14, } ); @@ -710,6 +711,7 @@ pub fn test_ext_with_balances(balances: Vec<(U256, u128)>) -> sp_io::TestExterna pub(crate) fn step_block(n: u16) { for _ in 0..n { Scheduler::on_finalize(System::block_number()); + Proxy::on_finalize(System::block_number()); SubtensorModule::on_finalize(System::block_number()); System::on_finalize(System::block_number()); System::set_block_number(System::block_number() + 1); diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index b243c4c161..e7fcca447f 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -153,7 +153,26 @@ where call: Box::new(call), }; - handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id))?; + + let last_call_result = pallet_proxy::pallet::Pallet::::LastCallResult::get(real); + match last_call_result { + Some(last_call_result) => match last_call_result { + Ok(()) => { + return Ok(()); + } + Err(e) => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other(e.to_string()), + }); + } + }, + None => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Proxy execution failed".into()), + }); + } + } } #[precompile::public("addProxy(bytes32,uint8,uint32)")] From 431b1ca81a729dc3a5f520b514d8c237510db6c6 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Mon, 3 Nov 2025 14:26:49 -0500 Subject: [PATCH 03/19] chore: fmt --- pallets/proxy/src/lib.rs | 16 ++++++++-------- pallets/subtensor/src/tests/mock.rs | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pallets/proxy/src/lib.rs b/pallets/proxy/src/lib.rs index 8d9be72bb2..9c904fda75 100644 --- a/pallets/proxy/src/lib.rs +++ b/pallets/proxy/src/lib.rs @@ -39,9 +39,9 @@ use frame::{ prelude::*, traits::{Currency, InstanceFilter, ReservableCurrency}, }; +use frame_system::pallet_prelude::BlockNumberFor as SystemBlockNumberFor; pub use pallet::*; use subtensor_macros::freeze_struct; -use frame_system::pallet_prelude::BlockNumberFor as SystemBlockNumberFor; pub use weights::WeightInfo; type CallHashOf = <::CallHasher as Hash>::Output; @@ -748,13 +748,13 @@ pub mod pallet { NoSelfProxy, } - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_finalize(_n: SystemBlockNumberFor) { - // clear this map on end of each block - let _ = LastCallResult::::clear(u32::MAX, None); - } - } + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_finalize(_n: SystemBlockNumberFor) { + // clear this map on end of each block + let _ = LastCallResult::::clear(u32::MAX, None); + } + } /// The set of account proxies. Maps the account which has delegated to the accounts /// which are being delegated to, together with the amount held on deposit. diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index df4f6b8afe..a5ea598984 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -45,7 +45,7 @@ frame_support::construct_runtime!( Drand: pallet_drand::{Pallet, Call, Storage, Event} = 11, Swap: pallet_subtensor_swap::{Pallet, Call, Storage, Event} = 12, Crowdloan: pallet_crowdloan::{Pallet, Call, Storage, Event} = 13, - Proxy: pallet_proxy = 14, + Proxy: pallet_proxy = 14, } ); @@ -711,7 +711,7 @@ pub fn test_ext_with_balances(balances: Vec<(U256, u128)>) -> sp_io::TestExterna pub(crate) fn step_block(n: u16) { for _ in 0..n { Scheduler::on_finalize(System::block_number()); - Proxy::on_finalize(System::block_number()); + Proxy::on_finalize(System::block_number()); SubtensorModule::on_finalize(System::block_number()); System::on_finalize(System::block_number()); System::set_block_number(System::block_number() + 1); From d6eacc2ec4ff78042decd5ee54b7fcaff3074b0f Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Mon, 3 Nov 2025 14:26:56 -0500 Subject: [PATCH 04/19] evm test wip --- evm-tests/test/pure-proxy.precompile.test.ts | 120 +++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 evm-tests/test/pure-proxy.precompile.test.ts diff --git a/evm-tests/test/pure-proxy.precompile.test.ts b/evm-tests/test/pure-proxy.precompile.test.ts new file mode 100644 index 0000000000..c8e9715f2a --- /dev/null +++ b/evm-tests/test/pure-proxy.precompile.test.ts @@ -0,0 +1,120 @@ +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 { IProxyABI, IPROXY_ADDRESS } from "../src/contracts/proxy" +import { keccak256, ethers } from 'ethers'; +import { forceSetBalanceToEthAddress, forceSetBalanceToSs58Address } from "../src/subtensor"; +import { Signer } from "@polkadot/api/types"; + +async function getTransferCallCode(api: TypedApi, 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 + + 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(IPROXY_ADDRESS, IProxyABI, evmWallet) + console.log("evmWallet", evmWallet.address) + + const tx = await contract.createPureProxy() + const proxyAddress = await tx.wait() + assert.equal(proxyAddress.length, 1, "proxy should be set") + + const ss58Address = convertPublicKeyToSs58(proxyAddress[0]) + + await forceSetBalanceToSs58Address(api, ss58Address) + + const callCode = await getTransferCallCode(api, alice) + const tx2 = await contract.proxyCall(proxyAddress[0], callCode) + await tx2.wait() + }) + + it("Call createPureProxy, add multiple proxies", async () => { + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) + + let proxies = [] + for (let i = 0; i < 10; i++) { + const tx = await contract.createPureProxy() + const proxyAddressAfterCreate = await tx.wait() + assert.equal(proxyAddressAfterCreate.length, i + 1, "proxy should be set") + proxies.push(proxyAddressAfterCreate[0]) + } + + const tx = await contract.killPureProxy(proxies[proxies.length - 1]) + await tx.wait() + }) + + it("Call createPureProxy, edge cases", async () => { + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet2) + + const callCode = await getTransferCallCode(api, alice) + + // call without proxy + try { + const tx = await contract.proxyCall(callCode) + await tx.wait() + } catch (error) { + assert.notEqual(error, undefined, "should fail if proxy not set") + } + + const tx = await contract.createPureProxy() + const proxyAddress = await tx.wait() + + // 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.proxyCall(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[0]) + await forceSetBalanceToSs58Address(api, ss58Address) + + // try proxy call finally + const tx2 = await contract.proxyCall(proxyAddress[0], callCode) + await tx2.wait() + }) +}); From 3ef0ba914bf28fdab069f135dffe63b8d35717b9 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 08:54:26 +0800 Subject: [PATCH 05/19] typo --- common/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/lib.rs b/common/src/lib.rs index d66df115e7..28a33c2ae6 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -172,7 +172,7 @@ impl TryFrom for ProxyType { 2 => Ok(Self::NonCritical), 3 => Ok(Self::NonTransfer), 4 => Ok(Self::Senate), - 5 => Ok(Self::NonFungibile), + 5 => Ok(Self::NonFungible), 6 => Ok(Self::Triumvirate), 7 => Ok(Self::Governance), 8 => Ok(Self::Staking), From 3de77ee160d4c9622f561e3838d0779dbcb63bae Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 09:34:57 +0800 Subject: [PATCH 06/19] fix linter --- precompiles/src/proxy.rs | 46 ++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index e7fcca447f..c64d177b3d 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -2,6 +2,7 @@ use core::marker::PhantomData; use crate::{PrecompileExt, PrecompileHandleExt}; +use alloc::format; use fp_evm::{ExitError, PrecompileFailure}; use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo}; use frame_system::RawOrigin; @@ -23,11 +24,11 @@ where R: frame_system::Config + pallet_evm::Config + pallet_subtensor::Config - + pallet_proxy::Config, + + pallet_subtensor_proxy::Config, R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::AddressMapping: AddressMapping, ::RuntimeCall: From> - + From> + + From> + GetDispatchInfo + Dispatchable, ::AddressMapping: AddressMapping, @@ -42,11 +43,11 @@ where R: frame_system::Config + pallet_evm::Config + pallet_subtensor::Config - + pallet_proxy::Config, + + pallet_subtensor_proxy::Config, R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::AddressMapping: AddressMapping, ::RuntimeCall: From> - + From> + + From> + GetDispatchInfo + Dispatchable, <::Lookup as StaticLookup>::Source: From, @@ -64,7 +65,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_proxy::Call::::create_pure { + let call = pallet_subtensor_proxy::Call::::create_pure { proxy_type, delay: delay.into(), index, @@ -74,12 +75,19 @@ where // Success! // Try to get proxy address - let proxy_address: [u8; 32] = - pallet_proxy::pallet::Pallet::::pure_account(&account_id, &proxy_type, index, None) - .into(); + let proxy_address: [u8; 32] = pallet_subtensor_proxy::pallet::Pallet::::pure_account( + &account_id, + &proxy_type, + index, + None, + ) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Proxy not found".into()), + })? + .into(); // Check if in the proxies map - let proxy_entry = pallet_proxy::pallet::Pallet::::proxies(account_id.clone()); + let proxy_entry = pallet_subtensor_proxy::pallet::Pallet::::proxies(account_id.clone()); if proxy_entry .0 .iter() @@ -107,7 +115,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_proxy::Call::::kill_pure { + let call = pallet_subtensor_proxy::Call::::kill_pure { spawner: <::Lookup as StaticLookup>::Source::from( spawner.0.into(), ), @@ -129,7 +137,7 @@ where ) -> EvmResult<()> { let account_id = handle.caller_account_id::(); - let call = ::RuntimeCall::decode_with_depth_limit( + let call = ::RuntimeCall::decode_with_depth_limit( MAX_DECODE_DEPTH, &mut &call[..], ) @@ -145,7 +153,7 @@ where proxy_type = Some(proxy_type_); }; - let call = pallet_proxy::Call::::proxy { + let call = pallet_subtensor_proxy::Call::::proxy { real: <::Lookup as StaticLookup>::Source::from( real.0.into(), ), @@ -155,7 +163,9 @@ where handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id))?; - let last_call_result = pallet_proxy::pallet::Pallet::::LastCallResult::get(real); + let real_account_id = R::AccountId::from(real.0.into()); + + let last_call_result = pallet_subtensor_proxy::LastCallResult::::get(real_account_id); match last_call_result { Some(last_call_result) => match last_call_result { Ok(()) => { @@ -163,7 +173,7 @@ where } Err(e) => { return Err(PrecompileFailure::Error { - exit_status: ExitError::Other(e.to_string()), + exit_status: ExitError::Other(format!("{:?}", e).into()), }); } }, @@ -187,7 +197,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_proxy::Call::::add_proxy { + let call = pallet_subtensor_proxy::Call::::add_proxy { delegate: <::Lookup as StaticLookup>::Source::from( delegate.0.into(), ), @@ -210,7 +220,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_proxy::Call::::remove_proxy { + let call = pallet_subtensor_proxy::Call::::remove_proxy { delegate: <::Lookup as StaticLookup>::Source::from( delegate.0.into(), ), @@ -225,7 +235,7 @@ where pub fn remove_proxies(handle: &mut impl PrecompileHandle) -> EvmResult<()> { let account_id = handle.caller_account_id::(); - let call = pallet_proxy::Call::::remove_proxies {}; + let call = pallet_subtensor_proxy::Call::::remove_proxies {}; handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) } @@ -234,7 +244,7 @@ where pub fn poke_deposit(handle: &mut impl PrecompileHandle) -> EvmResult<()> { let account_id = handle.caller_account_id::(); - let call = pallet_proxy::Call::::poke_deposit {}; + let call = pallet_subtensor_proxy::Call::::poke_deposit {}; handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) } From c0f1720c48eeb49b15e218681882a2f8bcb7b7b1 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 09:46:52 +0800 Subject: [PATCH 07/19] commit Cargo.lock --- pallets/subtensor/Cargo.toml | 1 + pallets/subtensor/src/tests/mock.rs | 8 +++++++- precompiles/src/proxy.rs | 12 ++++++------ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index fdd5e5f9ab..e05779e2c2 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -59,6 +59,7 @@ pallet-subtensor-proxy.workspace = true [dev-dependencies] pallet-balances = { workspace = true, features = ["std"] } pallet-scheduler.workspace = true +pallet-subtensor-proxy.workspace = true pallet-subtensor-swap.workspace = true sp-version.workspace = true # Substrate diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index f381e4cc61..9c41de0079 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -46,7 +46,7 @@ frame_support::construct_runtime!( Drand: pallet_drand::{Pallet, Call, Storage, Event} = 11, Swap: pallet_subtensor_swap::{Pallet, Call, Storage, Event} = 12, Crowdloan: pallet_crowdloan::{Pallet, Call, Storage, Event} = 13, - Proxy: pallet_proxy = 14, + Proxy: pallet_subtensor_proxy = 14, } ); @@ -426,6 +426,12 @@ impl pallet_crowdloan::Config for Test { type MaxContributors = MaxContributors; } +impl pallet_subtensor_proxy::Config for Test { + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = pallet_subtensor_proxy::weights::SubstrateWeight; +} + mod test_crypto { use super::KEY_TYPE; use sp_core::{ diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index c64d177b3d..8738a2ab1f 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -169,18 +169,18 @@ where match last_call_result { Some(last_call_result) => match last_call_result { Ok(()) => { - return Ok(()); + Ok(()) } Err(e) => { - return Err(PrecompileFailure::Error { - exit_status: ExitError::Other(format!("{:?}", e).into()), - }); + Err(PrecompileFailure::Error { + exit_status: ExitError::Other(format!("{e:?}").into()), + }) } }, None => { - return Err(PrecompileFailure::Error { + Err(PrecompileFailure::Error { exit_status: ExitError::Other("Proxy execution failed".into()), - }); + }) } } } From 506c1eaa3f75cb11e15c2ee202fa76fc7f6fc7c1 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 09:58:53 +0800 Subject: [PATCH 08/19] commit Cargo.lock --- pallets/subtensor/Cargo.toml | 1 + pallets/subtensor/src/tests/mock.rs | 31 ++++++++++++-- precompiles/src/proxy.rs | 65 +++++++++++++---------------- 3 files changed, 56 insertions(+), 41 deletions(-) diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index e05779e2c2..2e35a89d19 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -60,6 +60,7 @@ pallet-subtensor-proxy.workspace = true pallet-balances = { workspace = true, features = ["std"] } pallet-scheduler.workspace = true pallet-subtensor-proxy.workspace = true +subtensor-runtime-common.workspace = true pallet-subtensor-swap.workspace = true sp-version.workspace = true # Substrate diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 9c41de0079..690fcee00b 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -18,6 +18,7 @@ use frame_support::{ }; use frame_system as system; use frame_system::{EnsureRoot, RawOrigin, limits, offchain::CreateTransactionBase}; +use pallet_subtensor_proxy as pallet_proxy; use pallet_subtensor_utility as pallet_utility; use sp_core::{ConstU64, Get, H256, U256, offchain::KeyTypeId}; use sp_runtime::Perbill; @@ -30,7 +31,6 @@ use sp_tracing::tracing_subscriber; use subtensor_runtime_common::{NetUid, TaoCurrency}; use subtensor_swap_interface::{Order, SwapHandler}; use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt}; - type Block = frame_system::mocking::MockBlock; // Configure a mock runtime to test the pallet. @@ -426,10 +426,33 @@ impl pallet_crowdloan::Config for Test { type MaxContributors = MaxContributors; } -impl pallet_subtensor_proxy::Config for Test { +// Proxy Pallet config +parameter_types! { + // One storage item; key size sizeof(AccountId) = 32, value sizeof(Balance) = 8; 40 total + pub const ProxyDepositBase: Balance = 1; + // Adding 32 bytes + sizeof(ProxyType) = 32 + 1 + pub const ProxyDepositFactor: Balance = 1; + pub const MaxProxies: u32 = 20; // max num proxies per acct + pub const MaxPending: u32 = 15 * 5; // max blocks pending ~15min + // 16 bytes + pub const AnnouncementDepositBase: Balance = 1; + // 68 bytes per announcement + pub const AnnouncementDepositFactor: Balance = 1; +} + +impl pallet_proxy::Config for Test { type RuntimeCall = RuntimeCall; - type PalletsOrigin = OriginCaller; - type WeightInfo = pallet_subtensor_proxy::weights::SubstrateWeight; + type Currency = Balances; + type ProxyType = subtensor_runtime_common::ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = pallet_proxy::weights::SubstrateWeight; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; + type BlockNumberProvider = System; } mod test_crypto { diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index 8738a2ab1f..c2a1c41a38 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -7,6 +7,7 @@ use fp_evm::{ExitError, PrecompileFailure}; use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo}; use frame_system::RawOrigin; use pallet_evm::{AddressMapping, PrecompileHandle}; +use pallet_subtensor_proxy as pallet_proxy; use precompile_utils::EvmResult; use sp_core::H256; use sp_runtime::{ @@ -24,11 +25,11 @@ where R: frame_system::Config + pallet_evm::Config + pallet_subtensor::Config - + pallet_subtensor_proxy::Config, + + pallet_proxy::Config, R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::AddressMapping: AddressMapping, ::RuntimeCall: From> - + From> + + From> + GetDispatchInfo + Dispatchable, ::AddressMapping: AddressMapping, @@ -43,11 +44,11 @@ where R: frame_system::Config + pallet_evm::Config + pallet_subtensor::Config - + pallet_subtensor_proxy::Config, + + pallet_proxy::Config, R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::AddressMapping: AddressMapping, ::RuntimeCall: From> - + From> + + From> + GetDispatchInfo + Dispatchable, <::Lookup as StaticLookup>::Source: From, @@ -65,7 +66,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_subtensor_proxy::Call::::create_pure { + let call = pallet_proxy::Call::::create_pure { proxy_type, delay: delay.into(), index, @@ -75,19 +76,15 @@ where // Success! // Try to get proxy address - let proxy_address: [u8; 32] = pallet_subtensor_proxy::pallet::Pallet::::pure_account( - &account_id, - &proxy_type, - index, - None, - ) - .map_err(|_| PrecompileFailure::Error { - exit_status: ExitError::Other("Proxy not found".into()), - })? - .into(); + let proxy_address: [u8; 32] = + pallet_proxy::pallet::Pallet::::pure_account(&account_id, &proxy_type, index, None) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other("Proxy not found".into()), + })? + .into(); // Check if in the proxies map - let proxy_entry = pallet_subtensor_proxy::pallet::Pallet::::proxies(account_id.clone()); + let proxy_entry = pallet_proxy::pallet::Pallet::::proxies(account_id.clone()); if proxy_entry .0 .iter() @@ -115,7 +112,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_subtensor_proxy::Call::::kill_pure { + let call = pallet_proxy::Call::::kill_pure { spawner: <::Lookup as StaticLookup>::Source::from( spawner.0.into(), ), @@ -137,7 +134,7 @@ where ) -> EvmResult<()> { let account_id = handle.caller_account_id::(); - let call = ::RuntimeCall::decode_with_depth_limit( + let call = ::RuntimeCall::decode_with_depth_limit( MAX_DECODE_DEPTH, &mut &call[..], ) @@ -153,7 +150,7 @@ where proxy_type = Some(proxy_type_); }; - let call = pallet_subtensor_proxy::Call::::proxy { + let call = pallet_proxy::Call::::proxy { real: <::Lookup as StaticLookup>::Source::from( real.0.into(), ), @@ -165,23 +162,17 @@ where let real_account_id = R::AccountId::from(real.0.into()); - let last_call_result = pallet_subtensor_proxy::LastCallResult::::get(real_account_id); + let last_call_result = pallet_proxy::LastCallResult::::get(real_account_id); match last_call_result { Some(last_call_result) => match last_call_result { - Ok(()) => { - Ok(()) - } - Err(e) => { - Err(PrecompileFailure::Error { - exit_status: ExitError::Other(format!("{e:?}").into()), - }) - } + Ok(()) => Ok(()), + Err(e) => Err(PrecompileFailure::Error { + exit_status: ExitError::Other(format!("{e:?}").into()), + }), }, - None => { - Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Proxy execution failed".into()), - }) - } + None => Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Proxy execution failed".into()), + }), } } @@ -197,7 +188,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_subtensor_proxy::Call::::add_proxy { + let call = pallet_proxy::Call::::add_proxy { delegate: <::Lookup as StaticLookup>::Source::from( delegate.0.into(), ), @@ -220,7 +211,7 @@ where exit_status: ExitError::Other("Invalid proxy type".into()), })?; - let call = pallet_subtensor_proxy::Call::::remove_proxy { + let call = pallet_proxy::Call::::remove_proxy { delegate: <::Lookup as StaticLookup>::Source::from( delegate.0.into(), ), @@ -235,7 +226,7 @@ where pub fn remove_proxies(handle: &mut impl PrecompileHandle) -> EvmResult<()> { let account_id = handle.caller_account_id::(); - let call = pallet_subtensor_proxy::Call::::remove_proxies {}; + let call = pallet_proxy::Call::::remove_proxies {}; handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) } @@ -244,7 +235,7 @@ where pub fn poke_deposit(handle: &mut impl PrecompileHandle) -> EvmResult<()> { let account_id = handle.caller_account_id::(); - let call = pallet_subtensor_proxy::Call::::poke_deposit {}; + let call = pallet_proxy::Call::::poke_deposit {}; handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) } From c754affe8810086f816a5d7fe24d50fd0f062d83 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 10:00:41 +0800 Subject: [PATCH 09/19] commit Cargo.lock --- pallets/subtensor/src/tests/mock.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 690fcee00b..549d062058 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -8,7 +8,7 @@ use core::num::NonZeroU64; use crate::utils::rate_limiting::TransactionType; use crate::*; -use frame_support::traits::{Contains, Everything, InherentBuilder, InsideBoth}; +use frame_support::traits::{Contains, Everything, InherentBuilder, InsideBoth, InstanceFilter}; use frame_support::weights::Weight; use frame_support::weights::constants::RocksDbWeight; use frame_support::{PalletId, derive_impl}; @@ -447,7 +447,7 @@ impl pallet_proxy::Config for Test { type ProxyDepositBase = ProxyDepositBase; type ProxyDepositFactor = ProxyDepositFactor; type MaxProxies = MaxProxies; - type WeightInfo = pallet_proxy::weights::SubstrateWeight; + type WeightInfo = pallet_proxy::weights::SubstrateWeight; type MaxPending = MaxPending; type CallHasher = BlakeTwo256; type AnnouncementDepositBase = AnnouncementDepositBase; @@ -455,6 +455,20 @@ impl pallet_proxy::Config for Test { type BlockNumberProvider = System; } +impl InstanceFilter for subtensor_runtime_common::ProxyType { + fn filter(&self, _c: &RuntimeCall) -> bool { + // In tests, allow all proxy types to pass through + true + } + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (subtensor_runtime_common::ProxyType::Any, _) => true, + _ => false, + } + } +} + mod test_crypto { use super::KEY_TYPE; use sp_core::{ From ed5971b136814bd16bd09057e1aa15c4560c7cc5 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 18:21:09 +0800 Subject: [PATCH 10/19] update test case --- evm-tests/src/address-utils.ts | 2 +- evm-tests/src/contracts/proxy.ts | 288 +++++++++---------- evm-tests/test/pure-proxy.precompile.test.ts | 137 +++++---- 3 files changed, 232 insertions(+), 195 deletions(-) diff --git a/evm-tests/src/address-utils.ts b/evm-tests/src/address-utils.ts index ed3abc5008..753eed2530 100644 --- a/evm-tests/src/address-utils.ts +++ b/evm-tests/src/address-utils.ts @@ -1,6 +1,6 @@ import { Address } from "viem" import { encodeAddress } from "@polkadot/util-crypto"; -import { ss58Address } from "@polkadot-labs/hdkd-helpers"; +import { ss58Address, ss58Decode } from "@polkadot-labs/hdkd-helpers"; import { hexToU8a } from "@polkadot/util"; import { blake2AsU8a, decodeAddress } from "@polkadot/util-crypto"; import { Binary } from "polkadot-api"; diff --git a/evm-tests/src/contracts/proxy.ts b/evm-tests/src/contracts/proxy.ts index 4059409276..48ffd0a50f 100644 --- a/evm-tests/src/contracts/proxy.ts +++ b/evm-tests/src/contracts/proxy.ts @@ -1,148 +1,148 @@ export const IPROXY_ADDRESS = "0x000000000000000000000000000000000000080b"; export const IProxyABI = [ - { - "inputs": [ - { - "internalType": "uint8", - "name": "proxy_type", - "type": "uint8" - }, - { - "internalType": "uint32", - "name": "delay", - "type": "uint32" - }, - { - "internalType": "uint16", - "name": "index", - "type": "uint16" - } - ], - "name": "createPureProxy", - "outputs": [ - { - "internalType": "bytes32", - "name": "proxy", - "type": "bytes32" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "spawner", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "proxy_type", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "index", - "type": "uint16" - }, - { - "internalType": "uint32", - "name": "height", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "ext_index", - "type": "uint32" - } - ], - "name": "killPureProxy", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "real", - "type": "bytes32" - }, - { - "internalType": "uint8[]", - "name": "force_proxy_type", // optional - "type": "uint8[]" - }, - { - "internalType": "uint8[]", - "name": "call", - "type": "uint8[]" - } - ], - "name": "proxyCall", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "removeProxies", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - },{ - "inputs": [], - "name": "pokeDeposit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "delegate", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "proxy_type", - "type": "uint8" - }, - { - "internalType": "uint32", - "name": "delay", - "type": "uint32" - } - ], - "name": "removeProxy", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "delegate", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "proxy_type", - "type": "uint8" - }, - { - "internalType": "uint32", - "name": "delay", - "type": "uint32" - } - ], - "name": "addProxy", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } + { + "inputs": [ + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + }, + { + "internalType": "uint16", + "name": "index", + "type": "uint16" + } + ], + "name": "createPureProxy", + "outputs": [ + { + "internalType": "bytes32", + "name": "proxy", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "spawner", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "index", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "height", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "ext_index", + "type": "uint32" + } + ], + "name": "killPureProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "real", + "type": "bytes32" + }, + { + "internalType": "uint8[]", + "name": "force_proxy_type", // optional + "type": "uint8[]" + }, + { + "internalType": "uint8[]", + "name": "call", + "type": "uint8[]" + } + ], + "name": "proxyCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "removeProxies", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { + "inputs": [], + "name": "pokeDeposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + } + ], + "name": "removeProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "proxy_type", + "type": "uint8" + }, + { + "internalType": "uint32", + "name": "delay", + "type": "uint32" + } + ], + "name": "addProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } ]; diff --git a/evm-tests/test/pure-proxy.precompile.test.ts b/evm-tests/test/pure-proxy.precompile.test.ts index c8e9715f2a..7ef3ace5c1 100644 --- a/evm-tests/test/pure-proxy.precompile.test.ts +++ b/evm-tests/test/pure-proxy.precompile.test.ts @@ -1,22 +1,24 @@ import * as assert from "assert"; -import { getAliceSigner, getDevnetApi } from "../src/substrate" +import { getAliceSigner, getDevnetApi, getRandomSubstrateKeypair } 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 { convertH160ToSS58, convertPublicKeyToSs58 } from "../src/address-utils" import { IProxyABI, IPROXY_ADDRESS } from "../src/contracts/proxy" import { keccak256, ethers } from 'ethers'; import { forceSetBalanceToEthAddress, forceSetBalanceToSs58Address } from "../src/subtensor"; import { Signer } from "@polkadot/api/types"; +import { KeyPair } from "@polkadot-labs/hdkd-helpers"; -async function getTransferCallCode(api: TypedApi, signer: PolkadotSigner) { - const transferAmount = BigInt(1000000000); +import { decodeAddress } from "@polkadot/util-crypto"; + +async function getTransferCallCode(api: TypedApi, receiver: KeyPair, transferAmount: number) { const unsignedTx = api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(convertPublicKeyToSs58(signer.publicKey)), - value: transferAmount, + dest: MultiAddress.Id(convertPublicKeyToSs58(receiver.publicKey)), + value: BigInt(1000000000), }); const encodedCallDataBytes = await unsignedTx.getEncodedData(); @@ -28,9 +30,26 @@ async function getTransferCallCode(api: TypedApi, signer: Polkado return [...data] } +async function getProxies(api: TypedApi, address: string) { + const entries = await api.query.Proxy.Proxies.getEntries() + const result = [] + for (const entry of entries) { + const proxyAddress = entry.keyArgs[0] + const values = entry.value + const proxies = values[0] + for (const proxy of proxies) { + if (proxy.delegate === address) { + result.push(proxyAddress) + } + } + } + return result +} + describe("Test pure proxy precompile", () => { const evmWallet = generateRandomEthersWallet(); const evmWallet2 = generateRandomEthersWallet(); + const receiver = getRandomSubstrateKeypair(); let api: TypedApi @@ -46,75 +65,93 @@ describe("Test pure proxy precompile", () => { }) it("Call createPureProxy, then use proxy to call transfer", async () => { + const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) console.log("evmWallet", evmWallet.address) - const tx = await contract.createPureProxy() - const proxyAddress = await tx.wait() - assert.equal(proxyAddress.length, 1, "proxy should be set") + const type = 0; + const delay = 0; + const index = 0; + const tx = await contract.createPureProxy(type, delay, index) + const response = await tx.wait() + console.log("response", response.blockNumber) - const ss58Address = convertPublicKeyToSs58(proxyAddress[0]) + const proxiesAfterAdd = await getProxies(api, convertH160ToSS58(evmWallet.address)) - await forceSetBalanceToSs58Address(api, ss58Address) + const length = proxiesAfterAdd.length + assert.equal(length, proxies.length + 1, "proxy should be set") + const proxy = proxiesAfterAdd[proxiesAfterAdd.length - 1] - const callCode = await getTransferCallCode(api, alice) - const tx2 = await contract.proxyCall(proxyAddress[0], callCode) - await tx2.wait() - }) + await forceSetBalanceToSs58Address(api, proxy) + const balance = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free - it("Call createPureProxy, add multiple proxies", async () => { - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) + const amount = 1000000000; - let proxies = [] - for (let i = 0; i < 10; i++) { - const tx = await contract.createPureProxy() - const proxyAddressAfterCreate = await tx.wait() - assert.equal(proxyAddressAfterCreate.length, i + 1, "proxy should be set") - proxies.push(proxyAddressAfterCreate[0]) - } + const callCode = await getTransferCallCode(api, receiver, amount) + const tx2 = await contract.proxyCall(decodeAddress(proxy), [type], callCode) + await tx2.wait() - const tx = await contract.killPureProxy(proxies[proxies.length - 1]) - await tx.wait() + const balanceAfter = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free + assert.equal(balanceAfter, balance + BigInt(amount), "balance should be increased") }) - it("Call createPureProxy, edge cases", async () => { - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet2) + it("Call createPureProxy, add kill one", async () => { + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) + const type = 0; + const delay = 0; + const index = 0; + const extIndex = 1; + + const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) + const length = proxies.length + const addTx = await contract.createPureProxy(type, delay, index) + const response = await addTx.wait() + const createBlockNumber = response.blockNumber - const callCode = await getTransferCallCode(api, alice) + const currentLength = (await getProxies(api, convertH160ToSS58(evmWallet.address))).length + assert.equal(currentLength, length + 1, "proxy should be set") - // call without proxy try { - const tx = await contract.proxyCall(callCode) + const tx = await contract.killPureProxy(decodeAddress(proxies[proxies.length - 1]), type, index, + createBlockNumber, extIndex) await tx.wait() } catch (error) { - assert.notEqual(error, undefined, "should fail if proxy not set") + console.log("error", error) } - const tx = await contract.createPureProxy() - const proxyAddress = await tx.wait() + const proxiesAfterRemove = await getProxies(api, convertH160ToSS58(evmWallet.address)) + assert.equal(proxiesAfterRemove.length, 0, "proxies should be removed") + }) - // set the proxy again - try { - const tx = await contract.createPureProxy() + it("Call createPureProxy, add multiple proxies", async () => { + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) + const type = 0; + const delay = 0; + const index = 0; + const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) + const length = proxies.length + for (let i = 0; i < 5; i++) { + const tx = await contract.createPureProxy(type, delay, index) await tx.wait() - } catch (error) { - assert.notEqual(error, undefined, "should fail if set proxy again") + + await new Promise(resolve => setTimeout(resolve, 500)); + const currentProxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) + assert.equal(currentProxies.length, length + i + 1, "proxy should be set") } + }) - // send extrinsic without token + it("Call createPureProxy, edge cases, call via wrong proxy", async () => { + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet2) + const amount = 1000000000; + const callCode = await getTransferCallCode(api, receiver, amount) + const type = 0; + + // call with wrong proxy try { - const tx = await contract.proxyCall(callCode) + const tx = await contract.proxyCall(receiver, [type], callCode) await tx.wait() } catch (error) { - assert.notEqual(error, undefined, "should fail if proxy without balance") + assert.notEqual(error, undefined, "should fail if proxy not set") } - - // set balance for proxy account - const ss58Address = convertPublicKeyToSs58(proxyAddress[0]) - await forceSetBalanceToSs58Address(api, ss58Address) - - // try proxy call finally - const tx2 = await contract.proxyCall(proxyAddress[0], callCode) - await tx2.wait() }) }); From 596633575d99270a82cd9a183bb3fc3bfc5fe7ff Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 21:44:24 +0800 Subject: [PATCH 11/19] add more e2e test --- evm-tests/test/pure-proxy.precompile.test.ts | 48 +++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/evm-tests/test/pure-proxy.precompile.test.ts b/evm-tests/test/pure-proxy.precompile.test.ts index 7ef3ace5c1..4ab4eabf3e 100644 --- a/evm-tests/test/pure-proxy.precompile.test.ts +++ b/evm-tests/test/pure-proxy.precompile.test.ts @@ -1,15 +1,13 @@ import * as assert from "assert"; import { getAliceSigner, getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate" -import { generateRandomEthersWallet, generateRandomEthWallet } from "../src/utils"; +import { generateRandomEthersWallet } from "../src/utils"; import { devnet, MultiAddress } from "@polkadot-api/descriptors" -import { hexToU8a } from "@polkadot/util"; import { PolkadotSigner, TypedApi } from "polkadot-api"; -import { convertH160ToSS58, convertPublicKeyToSs58 } from "../src/address-utils" +import { convertH160ToPublicKey, convertH160ToSS58, convertPublicKeyToSs58 } from "../src/address-utils" import { IProxyABI, IPROXY_ADDRESS } from "../src/contracts/proxy" -import { keccak256, ethers } from 'ethers'; +import { ethers } from 'ethers'; import { forceSetBalanceToEthAddress, forceSetBalanceToSs58Address } from "../src/subtensor"; -import { Signer } from "@polkadot/api/types"; import { KeyPair } from "@polkadot-labs/hdkd-helpers"; import { decodeAddress } from "@polkadot/util-crypto"; @@ -49,6 +47,8 @@ async function getProxies(api: TypedApi, address: string) { describe("Test pure proxy precompile", () => { const evmWallet = generateRandomEthersWallet(); const evmWallet2 = generateRandomEthersWallet(); + const evmWallet3 = generateRandomEthersWallet(); + const delegate = getRandomSubstrateKeypair(); const receiver = getRandomSubstrateKeypair(); let api: TypedApi @@ -61,7 +61,8 @@ describe("Test pure proxy precompile", () => { await forceSetBalanceToEthAddress(api, evmWallet.address) await forceSetBalanceToEthAddress(api, evmWallet2.address) - + await forceSetBalanceToEthAddress(api, evmWallet3.address) + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(delegate.publicKey)) }) it("Call createPureProxy, then use proxy to call transfer", async () => { @@ -154,4 +155,39 @@ describe("Test pure proxy precompile", () => { assert.notEqual(error, undefined, "should fail if proxy not set") } }) + + it("Call createProxy, then use proxy to call transfer", async () => { + const proxies = await api.query.Proxy.Proxies.getValue(convertH160ToSS58(evmWallet2.address)) + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet2) + + const type = 0; + const delay = 0; + + const tx = await contract.addProxy(convertH160ToPublicKey(evmWallet3.address), type, delay) + await tx.wait() + + + const proxiesAfterAdd = await await api.query.Proxy.Proxies.getValue(convertH160ToSS58(evmWallet2.address)) + + const length = proxiesAfterAdd[0].length + assert.equal(length, proxies[0].length + 1, "proxy should be set") + const proxy = proxiesAfterAdd[0][proxiesAfterAdd[0].length - 1] + + assert.equal(proxy.delegate, convertH160ToSS58(evmWallet3.address), "proxy should be set") + + + const balance = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free + + const amount = 1000000000; + + const contract2 = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet3) + + + const callCode = await getTransferCallCode(api, receiver, amount) + const tx2 = await contract2.proxyCall(convertH160ToPublicKey(evmWallet2.address), [type], callCode) + await tx2.wait() + + const balanceAfter = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free + assert.equal(balanceAfter, balance + BigInt(amount), "balance should be increased") + }) }); From 59711241c1a2bbf89ecfe3361b5b9edc0a879910 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 21:44:51 +0800 Subject: [PATCH 12/19] bump version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index d8277164e2..548c192ece 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 334, + spec_version: 335, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 8f585ec480639def5002e9faa938895bc0284801 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 4 Nov 2025 21:45:41 +0800 Subject: [PATCH 13/19] clean up code --- evm-tests/test/pure-proxy.precompile.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/evm-tests/test/pure-proxy.precompile.test.ts b/evm-tests/test/pure-proxy.precompile.test.ts index 4ab4eabf3e..e9b9397918 100644 --- a/evm-tests/test/pure-proxy.precompile.test.ts +++ b/evm-tests/test/pure-proxy.precompile.test.ts @@ -48,7 +48,6 @@ describe("Test pure proxy precompile", () => { const evmWallet = generateRandomEthersWallet(); const evmWallet2 = generateRandomEthersWallet(); const evmWallet3 = generateRandomEthersWallet(); - const delegate = getRandomSubstrateKeypair(); const receiver = getRandomSubstrateKeypair(); let api: TypedApi @@ -62,7 +61,6 @@ describe("Test pure proxy precompile", () => { await forceSetBalanceToEthAddress(api, evmWallet.address) await forceSetBalanceToEthAddress(api, evmWallet2.address) await forceSetBalanceToEthAddress(api, evmWallet3.address) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(delegate.publicKey)) }) it("Call createPureProxy, then use proxy to call transfer", async () => { From e36f94cd7db87c429f2d2a9f6d190969af4d147c Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 5 Nov 2025 10:37:47 +0800 Subject: [PATCH 14/19] fix bug --- precompiles/src/proxy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index c2a1c41a38..1399177766 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -84,7 +84,7 @@ where .into(); // Check if in the proxies map - let proxy_entry = pallet_proxy::pallet::Pallet::::proxies(account_id.clone()); + let proxy_entry = pallet_proxy::pallet::Pallet::::proxies(proxy_address.into()); if proxy_entry .0 .iter() From fe78a6094626a9bc5144f8e558f0c63425636561 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 5 Nov 2025 10:39:46 +0800 Subject: [PATCH 15/19] cargo fix --- evm-tests/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm-tests/package.json b/evm-tests/package.json index ae756ae55f..653ea3fbdf 100644 --- a/evm-tests/package.json +++ b/evm-tests/package.json @@ -1,6 +1,6 @@ { "scripts": { - "test": "mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register test/*test.ts" + "test": "mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register test/pure*test.ts" }, "keywords": [], "author": "", From bf0ee07602655da65c242b13f0d3c45dc8bb7582 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 5 Nov 2025 10:47:17 +0800 Subject: [PATCH 16/19] remove kill case --- evm-tests/package.json | 2 +- evm-tests/test/pure-proxy.precompile.test.ts | 34 ++------------------ 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/evm-tests/package.json b/evm-tests/package.json index 653ea3fbdf..ae756ae55f 100644 --- a/evm-tests/package.json +++ b/evm-tests/package.json @@ -1,6 +1,6 @@ { "scripts": { - "test": "mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register test/pure*test.ts" + "test": "mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register test/*test.ts" }, "keywords": [], "author": "", diff --git a/evm-tests/test/pure-proxy.precompile.test.ts b/evm-tests/test/pure-proxy.precompile.test.ts index e9b9397918..b308438263 100644 --- a/evm-tests/test/pure-proxy.precompile.test.ts +++ b/evm-tests/test/pure-proxy.precompile.test.ts @@ -94,47 +94,19 @@ describe("Test pure proxy precompile", () => { assert.equal(balanceAfter, balance + BigInt(amount), "balance should be increased") }) - it("Call createPureProxy, add kill one", async () => { - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) - const type = 0; - const delay = 0; - const index = 0; - const extIndex = 1; - - const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) - const length = proxies.length - const addTx = await contract.createPureProxy(type, delay, index) - const response = await addTx.wait() - const createBlockNumber = response.blockNumber - - const currentLength = (await getProxies(api, convertH160ToSS58(evmWallet.address))).length - assert.equal(currentLength, length + 1, "proxy should be set") - - try { - const tx = await contract.killPureProxy(decodeAddress(proxies[proxies.length - 1]), type, index, - createBlockNumber, extIndex) - await tx.wait() - } catch (error) { - console.log("error", error) - } - - const proxiesAfterRemove = await getProxies(api, convertH160ToSS58(evmWallet.address)) - assert.equal(proxiesAfterRemove.length, 0, "proxies should be removed") - }) - it("Call createPureProxy, add multiple proxies", async () => { - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet1) const type = 0; const delay = 0; const index = 0; - const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) + const proxies = await getProxies(api, convertH160ToSS58(evmWallet1.address)) const length = proxies.length for (let i = 0; i < 5; i++) { const tx = await contract.createPureProxy(type, delay, index) await tx.wait() await new Promise(resolve => setTimeout(resolve, 500)); - const currentProxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) + const currentProxies = await getProxies(api, convertH160ToSS58(evmWallet1.address)) assert.equal(currentProxies.length, length + i + 1, "proxy should be set") } }) From 757b846e284fb17477b5c56573309ffa2d641782 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 5 Nov 2025 14:02:22 +0800 Subject: [PATCH 17/19] fix wrong variable --- evm-tests/test/pure-proxy.precompile.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/evm-tests/test/pure-proxy.precompile.test.ts b/evm-tests/test/pure-proxy.precompile.test.ts index b308438263..1a34b02cf7 100644 --- a/evm-tests/test/pure-proxy.precompile.test.ts +++ b/evm-tests/test/pure-proxy.precompile.test.ts @@ -95,18 +95,18 @@ describe("Test pure proxy precompile", () => { }) it("Call createPureProxy, add multiple proxies", async () => { - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet1) + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) const type = 0; const delay = 0; const index = 0; - const proxies = await getProxies(api, convertH160ToSS58(evmWallet1.address)) + const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) const length = proxies.length for (let i = 0; i < 5; i++) { const tx = await contract.createPureProxy(type, delay, index) await tx.wait() await new Promise(resolve => setTimeout(resolve, 500)); - const currentProxies = await getProxies(api, convertH160ToSS58(evmWallet1.address)) + const currentProxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) assert.equal(currentProxies.length, length + i + 1, "proxy should be set") } }) From b2385bd6fc304d116e3e1c9de78ef0070c51203b Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 5 Nov 2025 21:45:38 +0800 Subject: [PATCH 18/19] add comments for pallet config in test --- pallets/subtensor/src/tests/mock.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 549d062058..cf6f4b9a75 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -428,15 +428,17 @@ impl pallet_crowdloan::Config for Test { // Proxy Pallet config parameter_types! { - // One storage item; key size sizeof(AccountId) = 32, value sizeof(Balance) = 8; 40 total + // Set as 1 for testing purposes pub const ProxyDepositBase: Balance = 1; - // Adding 32 bytes + sizeof(ProxyType) = 32 + 1 + // Set as 1 for testing purposes pub const ProxyDepositFactor: Balance = 1; + // Set as 20 for testing purposes pub const MaxProxies: u32 = 20; // max num proxies per acct - pub const MaxPending: u32 = 15 * 5; // max blocks pending ~15min - // 16 bytes + // Set as 15 for testing purposes + pub const MaxPending: u32 = 15; // max blocks pending ~15min + // Set as 1 for testing purposes pub const AnnouncementDepositBase: Balance = 1; - // 68 bytes per announcement + // Set as 1 for testing purposes pub const AnnouncementDepositFactor: Balance = 1; } From bfb1111863432a9e659101722a36a6839c0f6196 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 12 Nov 2025 17:56:33 +0800 Subject: [PATCH 19/19] bump version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5e17dc6f7d..f6843c50ee 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 345, + spec_version: 346, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1,