From 3ad54bb059af642008c3a827cce6961233335adc Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Thu, 2 Jan 2025 19:50:53 +0100 Subject: [PATCH 1/4] Extend staking contract with proxy setting --- runtime/src/precompiles/solidity/staking.abi | 28 ++++++++- runtime/src/precompiles/solidity/staking.sol | 14 +++++ runtime/src/precompiles/staking.rs | 60 ++++++++++++++------ 3 files changed, 85 insertions(+), 17 deletions(-) diff --git a/runtime/src/precompiles/solidity/staking.abi b/runtime/src/precompiles/solidity/staking.abi index 44b1829c4..39ff90d57 100644 --- a/runtime/src/precompiles/solidity/staking.abi +++ b/runtime/src/precompiles/solidity/staking.abi @@ -1,4 +1,17 @@ [ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + } + ], + "name": "addProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -17,6 +30,19 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + } + ], + "name": "removeProxy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -37,7 +63,7 @@ ], "name": "removeStake", "outputs": [], - "stateMutability": "payable", + "stateMutability": "nonpayable", "type": "function" } ] diff --git a/runtime/src/precompiles/solidity/staking.sol b/runtime/src/precompiles/solidity/staking.sol index ec7fb7297..0710b23e5 100644 --- a/runtime/src/precompiles/solidity/staking.sol +++ b/runtime/src/precompiles/solidity/staking.sol @@ -42,4 +42,18 @@ interface IStaking { * - The existing stake amount must be not lower than specified amount */ function removeStake(bytes32 hotkey, uint256 amount, uint16 netuid) external; + + /** + * @dev Delegates staking to a proxy account. + * + * @param delegate The public key (32 bytes) of the delegate. + */ + function addProxy(bytes32 delegate) external; + + /** + * @dev Removes staking proxy account. + * + * @param delegate The public key (32 bytes) of the delegate. + */ + function removeProxy(bytes32 delegate) external; } diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index e6237dfcf..53c0904b8 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -36,7 +36,10 @@ use sp_runtime::traits::Dispatchable; use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto}; use sp_runtime::AccountId32; -use crate::precompiles::{get_method_id, get_slice}; +use crate::{ + precompiles::{get_method_id, get_slice}, + ProxyType, +}; use sp_std::vec; use crate::{Runtime, RuntimeCall}; @@ -52,21 +55,23 @@ impl StakingPrecompile { .get(4..) .map_or_else(vec::Vec::new, |slice| slice.to_vec()); // Avoiding borrowing conflicts - match method_id { - id if id == get_method_id("addStake(bytes32,uint16)") => { - Self::add_stake(handle, &method_input) - } - id if id == get_method_id("removeStake(bytes32,uint256,uint16)") => { - Self::remove_stake(handle, &method_input) - } - _ => Err(PrecompileFailure::Error { + if method_id == get_method_id("addStake(bytes32,uint16)") { + Self::add_stake(handle, &method_input) + } else if method_id == get_method_id("removeStake(bytes32,uint256,uint16)") { + Self::remove_stake(handle, &method_input) + } else if method_id == get_method_id("addProxy(bytes32)") { + Self::add_proxy(handle, &method_input) + } else if method_id == get_method_id("removeProxy(bytes32)") { + Self::remove_proxy(handle, &method_input) + } else { + Err(PrecompileFailure::Error { exit_status: ExitError::InvalidRange, - }), + }) } } fn add_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let hotkey = Self::parse_hotkey(data)?.into(); + let hotkey = Self::parse_pub_key(data)?.into(); let amount: U256 = handle.context().apparent_value; let amount_sub = ::BalanceConverter::into_substrate_balance(amount) @@ -80,8 +85,9 @@ impl StakingPrecompile { // Dispatch the add_stake call Self::dispatch(handle, call) } + fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let hotkey = Self::parse_hotkey(data)?.into(); + let hotkey = Self::parse_pub_key(data)?.into(); // We have to treat this as uint256 (because of Solidity ABI encoding rules, it pads uint64), // but this will never exceed 8 bytes, se we will ignore higher bytes and will only use lower @@ -101,15 +107,37 @@ impl StakingPrecompile { Self::dispatch(handle, call) } - fn parse_hotkey(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { + fn add_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let delegate = Self::parse_pub_key(data)?; + let call = RuntimeCall::Proxy(pallet_proxy::Call::::add_proxy { + delegate, + proxy_type: ProxyType::Staking, + delay: 0, + }); + + Self::dispatch(handle, call) + } + + fn remove_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let delegate = Self::parse_pub_key(data)?; + let call = RuntimeCall::Proxy(pallet_proxy::Call::::remove_proxy { + delegate, + proxy_type: ProxyType::Staking, + delay: 0, + }); + + Self::dispatch(handle, call) + } + + fn parse_pub_key(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { if data.len() < 32 { return Err(PrecompileFailure::Error { exit_status: ExitError::InvalidRange, }); } - let mut hotkey = [0u8; 32]; - hotkey.copy_from_slice(get_slice(data, 0, 32)?); - Ok(hotkey) + let mut pubkey = [0u8; 32]; + pubkey.copy_from_slice(get_slice(data, 0, 32)?); + Ok(pubkey) } fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult { From d581823d3a7f8de0b2b7453ed95637a6e25b5b79 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Thu, 2 Jan 2025 20:47:22 +0100 Subject: [PATCH 2/4] Reformat --- runtime/src/precompiles/staking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index 53c0904b8..60d38ec49 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -108,7 +108,7 @@ impl StakingPrecompile { } fn add_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let delegate = Self::parse_pub_key(data)?; + let delegate = sp_runtime::MultiAddress::Address32(Self::parse_pub_key(data)?); let call = RuntimeCall::Proxy(pallet_proxy::Call::::add_proxy { delegate, proxy_type: ProxyType::Staking, @@ -119,7 +119,7 @@ impl StakingPrecompile { } fn remove_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let delegate = Self::parse_pub_key(data)?; + let delegate = sp_runtime::MultiAddress::Address32(Self::parse_pub_key(data)?); let call = RuntimeCall::Proxy(pallet_proxy::Call::::remove_proxy { delegate, proxy_type: ProxyType::Staking, From 3c159bf98959c1044f1c451782c624e837fe6f60 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Tue, 14 Jan 2025 03:15:20 +0100 Subject: [PATCH 3/4] Fix account id lookup in proxy precompile --- runtime/src/precompiles/staking.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index 60d38ec49..51dd350af 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -31,9 +31,9 @@ use pallet_evm::{ ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, }; use sp_core::crypto::Ss58Codec; -use sp_core::U256; +use sp_core::{U256, H256}; use sp_runtime::traits::Dispatchable; -use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto}; +use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto, StaticLookup}; use sp_runtime::AccountId32; use crate::{ @@ -108,7 +108,8 @@ impl StakingPrecompile { } fn add_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let delegate = sp_runtime::MultiAddress::Address32(Self::parse_pub_key(data)?); + let delegate = AccountId32::from(Self::parse_pub_key(data)?); + let delegate = ::Lookup::unlookup(delegate); let call = RuntimeCall::Proxy(pallet_proxy::Call::::add_proxy { delegate, proxy_type: ProxyType::Staking, @@ -119,7 +120,8 @@ impl StakingPrecompile { } fn remove_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let delegate = sp_runtime::MultiAddress::Address32(Self::parse_pub_key(data)?); + let delegate = AccountId32::from(Self::parse_pub_key(data)?); + let delegate = ::Lookup::unlookup(delegate); let call = RuntimeCall::Proxy(pallet_proxy::Call::::remove_proxy { delegate, proxy_type: ProxyType::Staking, From 2874d95411bd04cb0b46733cc6e578e9a152900f Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Tue, 14 Jan 2025 23:53:48 +0100 Subject: [PATCH 4/4] Lint --- runtime/src/precompiles/staking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index a8f54498f..86d257a19 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -31,7 +31,7 @@ use pallet_evm::{ ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, }; use sp_core::crypto::Ss58Codec; -use sp_core::{H256, U256}; +use sp_core::U256; use sp_runtime::traits::Dispatchable; use sp_runtime::traits::{BlakeTwo256, StaticLookup, UniqueSaturatedInto}; use sp_runtime::AccountId32;