mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 01:38:32 +00:00
feat: ✨ add collective precompile (#204)
## Add Collective Precompile Adds the pallet collective precompile to `mainnet`, `stagenet`, and `testnet` according to Moonbeam's configuration. ### Changes: - Added `pallet-evm-precompile-collective` dependency to workspace - Configured collective precompile at address `2064` using `TreasuryCouncilInstanc` - Configured collective precompile at address `2068` using `TechnicalCommitteeInstance` The precompile provides EVM access to collective governance functionality including proposal execution, voting, and membership management. --------- Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
This commit is contained in:
parent
65e245a82e
commit
f7d441d9e5
16 changed files with 2234 additions and 500 deletions
1007
operator/Cargo.lock
generated
1007
operator/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -35,9 +35,10 @@ pallet-datahaven-native-transfer = { path = "./pallets/datahaven-native-transfer
|
|||
pallet-evm-precompile-balances-erc20 = { path = "./precompiles/erc20-balances", default-features = false }
|
||||
pallet-evm-precompile-batch = { path = "./precompiles/batch", default-features = false }
|
||||
pallet-evm-precompile-call-permit = { path = "./precompiles/call-permit", default-features = false }
|
||||
pallet-evm-precompile-collective = { path = "./precompiles/collective", default-features = false }
|
||||
pallet-evm-precompile-identity = { path = "./precompiles/identity", default-features = false }
|
||||
pallet-evm-precompile-proxy = { path = "./precompiles/proxy", default-features = false }
|
||||
pallet-evm-precompile-registry = { path = "./precompiles/precompile-registry", default-features = false }
|
||||
pallet-evm-precompile-identity = { path = "./precompiles/identity", default-features = false }
|
||||
|
||||
# Crates.io (wasm)
|
||||
alloy-core = { version = "0.8.15", default-features = false }
|
||||
|
|
|
|||
50
operator/precompiles/collective/Cargo.toml
Normal file
50
operator/precompiles/collective/Cargo.toml
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
[package]
|
||||
name = "pallet-evm-precompile-collective"
|
||||
authors = { workspace = true }
|
||||
description = "A Precompile wrapping the collective pallet."
|
||||
edition = "2021"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
# Substrate
|
||||
frame-support = { workspace = true }
|
||||
frame-system = { workspace = true }
|
||||
pallet-collective = { workspace = true }
|
||||
parity-scale-codec = { workspace = true, features = ["max-encoded-len"] }
|
||||
sp-core = { workspace = true }
|
||||
sp-io = { workspace = true }
|
||||
sp-runtime = { workspace = true }
|
||||
sp-std = { workspace = true }
|
||||
|
||||
# Frontier
|
||||
evm = { workspace = true, features = ["with-codec"] }
|
||||
fp-evm = { workspace = true }
|
||||
pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }
|
||||
precompile-utils = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
#similar-asserts = { workspace = true }
|
||||
|
||||
pallet-balances = { workspace = true, features = ["std"] }
|
||||
pallet-timestamp = { workspace = true, features = ["std"] }
|
||||
pallet-treasury = { workspace = true, features = ["std"] }
|
||||
parity-scale-codec = { workspace = true, features = ["max-encoded-len"] }
|
||||
precompile-utils = { workspace = true, features = ["std", "testing"] }
|
||||
scale-info = { workspace = true, features = ["derive", "std"] }
|
||||
sp-runtime = { workspace = true, features = ["std"] }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"fp-evm/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"pallet-collective/std",
|
||||
"pallet-evm/std",
|
||||
"parity-scale-codec/std",
|
||||
"precompile-utils/std",
|
||||
"sp-core/std",
|
||||
"sp-io/std",
|
||||
"sp-std/std",
|
||||
]
|
||||
runtime-benchmarks = []
|
||||
137
operator/precompiles/collective/Collective.sol
Normal file
137
operator/precompiles/collective/Collective.sol
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
pragma solidity >=0.8.3;
|
||||
|
||||
/// @dev The Collective Council contract's address.
|
||||
address constant COLLECTIVE_COUNCIL_ADDRESS = 0x000000000000000000000000000000000000080e;
|
||||
/// @dev The Collective Technical Committee contract's address.
|
||||
address constant COLLECTIVE_TECHNICAL_ADDRESS = 0x000000000000000000000000000000000000080F;
|
||||
/// @dev The Collective Treasury Council contract's address.
|
||||
address constant COLLECTIVE_TREASURY_ADDRESS = 0x0000000000000000000000000000000000000810;
|
||||
|
||||
/// @dev The Collective Council contract's instance.
|
||||
Collective constant COLLECTIVE_COUNCIL_CONTRACT = Collective(
|
||||
COLLECTIVE_COUNCIL_ADDRESS
|
||||
);
|
||||
/// @dev The Collective Technical Committee contract's instance.
|
||||
Collective constant COLLECTIVE_TECHNICAL_CONTRACT = Collective(
|
||||
COLLECTIVE_TECHNICAL_ADDRESS
|
||||
);
|
||||
/// @dev The Collective Treasury Council contract's instance.
|
||||
Collective constant COLLECTIVE_TREASURY_CONTRACT = Collective(
|
||||
COLLECTIVE_TREASURY_ADDRESS
|
||||
);
|
||||
|
||||
/// @title Collective precompile
|
||||
/// Allows to interact with Substrate pallet_collective from the EVM.
|
||||
/// Addresses:
|
||||
/// - 0x000000000000000000000000000000000000080e: Council
|
||||
/// - 0x000000000000000000000000000000000000080f: Technical Committee
|
||||
/// - 0x0000000000000000000000000000000000000810: Treasury Council.
|
||||
interface Collective {
|
||||
/// @dev Execute a proposal as a single member of the collective.
|
||||
/// The sender must be a member of the collective.
|
||||
/// This will NOT revert if the Substrate proposal is dispatched but fails !
|
||||
///
|
||||
/// @param proposal SCALE-encoded Substrate call.
|
||||
///
|
||||
/// @custom:selector 09c5eabe
|
||||
function execute(bytes memory proposal) external;
|
||||
|
||||
/// @dev Make a proposal for a call.
|
||||
/// The sender must be a member of the collective.
|
||||
/// If the threshold is less than 2 then the proposal will be dispatched
|
||||
/// directly from the group of one member of the collective.
|
||||
///
|
||||
/// @param threshold Amount of members required to dispatch the proposal.
|
||||
/// @param proposal SCALE-encoded Substrate call.
|
||||
/// @return index Index of the new proposal. Meaningless if threshold < 2
|
||||
///
|
||||
/// @custom:selector c57f3260
|
||||
function propose(uint32 threshold, bytes memory proposal)
|
||||
external
|
||||
returns (uint32 index);
|
||||
|
||||
/// @dev Vote for a proposal.
|
||||
/// The sender must be a member of the collective.
|
||||
///
|
||||
/// @param proposalHash Hash of the proposal to vote for. Ensure the caller knows what they're
|
||||
/// voting in case of front-running or reorgs.
|
||||
/// @param proposalIndex Index of the proposal (returned by propose).
|
||||
/// @param approve The vote itself, is the caller approving or not the proposal.
|
||||
///
|
||||
/// @custom:selector 73e37688
|
||||
function vote(
|
||||
bytes32 proposalHash,
|
||||
uint32 proposalIndex,
|
||||
bool approve
|
||||
) external;
|
||||
|
||||
/// @dev Close a proposal.
|
||||
/// Can be called by anyone once there is enough votes.
|
||||
/// Reverts if called at a non appropriate time.
|
||||
///
|
||||
/// @param proposalHash Hash of the proposal to close.
|
||||
/// @param proposalIndex Index of the proposal.
|
||||
/// @param proposalWeightBound Maximum amount of Substrate weight the proposal can use.
|
||||
/// This call will revert if the proposal call would use more.
|
||||
/// @param lengthBound Must be a value higher or equal to the length of the SCALE-encoded
|
||||
/// proposal in bytes.
|
||||
/// @return executed Was the proposal executed or removed?
|
||||
///
|
||||
/// @custom:selector 638d9d47
|
||||
function close(
|
||||
bytes32 proposalHash,
|
||||
uint32 proposalIndex,
|
||||
uint64 proposalWeightBound,
|
||||
uint32 lengthBound
|
||||
) external returns (bool executed);
|
||||
|
||||
/// @dev Compute the hash of a proposal.
|
||||
///
|
||||
/// @param proposal SCALE-encoded Substrate call.
|
||||
/// @return proposalHash Hash of the proposal.
|
||||
///
|
||||
/// @custom:selector fc379417
|
||||
function proposalHash(bytes memory proposal)
|
||||
external
|
||||
view
|
||||
returns (bytes32 proposalHash);
|
||||
|
||||
/// @dev Get the hashes of active proposals.
|
||||
///
|
||||
/// @return proposalsHash Hashes of active proposals.
|
||||
///
|
||||
/// @custom:selector 55ef20e6
|
||||
function proposals() external view returns (bytes32[] memory proposalsHash);
|
||||
|
||||
/// @dev Get the list of members.
|
||||
///
|
||||
/// @return members List of members.
|
||||
///
|
||||
/// @custom:selector bdd4d18d
|
||||
function members() external view returns (address[] memory members);
|
||||
|
||||
/// @dev Check if the given account is a member of the collective.
|
||||
///
|
||||
/// @param account Account to check membership.
|
||||
///
|
||||
/// @custom:selector a230c524
|
||||
function isMember(address account) external view returns (bool);
|
||||
|
||||
/// @dev Get the prime account if there is one.
|
||||
///
|
||||
/// @return prime Prime account of 0x00..00 if None.
|
||||
///
|
||||
/// @custom:selector c7ee005e
|
||||
function prime() external view returns (address prime);
|
||||
|
||||
event Executed(bytes32 indexed proposalHash);
|
||||
event Proposed(
|
||||
address indexed who,
|
||||
uint32 indexed proposalIndex,
|
||||
bytes32 indexed proposalHash,
|
||||
uint32 threshold
|
||||
);
|
||||
event Voted(address indexed who, bytes32 indexed proposalHash, bool voted);
|
||||
event Closed(bytes32 indexed proposalHash);
|
||||
}
|
||||
366
operator/precompiles/collective/src/lib.rs
Normal file
366
operator/precompiles/collective/src/lib.rs
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
// Copyright 2019-2025 PureStake Inc.
|
||||
// This file is part of Moonbeam.
|
||||
|
||||
// Moonbeam is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Moonbeam is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moonbeam. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Precompile to interact with pallet_collective instances.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use fp_evm::Log;
|
||||
use frame_support::{
|
||||
dispatch::{GetDispatchInfo, Pays, PostDispatchInfo},
|
||||
sp_runtime::traits::Hash,
|
||||
traits::ConstU32,
|
||||
weights::Weight,
|
||||
};
|
||||
use pallet_evm::AddressMapping;
|
||||
use parity_scale_codec::DecodeLimit as _;
|
||||
use precompile_utils::prelude::*;
|
||||
use sp_core::{Decode, Get, H160, H256};
|
||||
use sp_runtime::traits::Dispatchable;
|
||||
use sp_std::{boxed::Box, vec::Vec};
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// System account size in bytes = Pallet_Name_Hash (16) + Storage_name_hash (16) +
|
||||
/// Blake2_128Concat (16) + AccountId (20) + AccountInfo (4 + 12 + AccountData (4* 16)) = 148
|
||||
pub const SYSTEM_ACCOUNT_SIZE: u64 = 148;
|
||||
|
||||
/// Proposal max proof size in bytes. See:
|
||||
/// moonbeam/blob/dd3e2b69f847dd74f6116b965fe7e2d97c3c7eb5/primitives/xcm/src/ethereum_xcm.rs#L27-L33
|
||||
pub const PROPOSAL_MAX_PROOF_SIZE: u64 = 256 * 1024;
|
||||
|
||||
/// Solidity selector of the Executed log.
|
||||
pub const SELECTOR_LOG_EXECUTED: [u8; 32] = keccak256!("Executed(bytes32)");
|
||||
|
||||
/// Solidity selector of the Proposed log.
|
||||
pub const SELECTOR_LOG_PROPOSED: [u8; 32] = keccak256!("Proposed(address,uint32,bytes32,uint32)");
|
||||
|
||||
/// Solidity selector of the Voted log.
|
||||
pub const SELECTOR_LOG_VOTED: [u8; 32] = keccak256!("Voted(address,bytes32,bool)");
|
||||
|
||||
/// Solidity selector of the Closed log.
|
||||
pub const SELECTOR_LOG_CLOSED: [u8; 32] = keccak256!("Closed(bytes32)");
|
||||
|
||||
pub fn log_executed(address: impl Into<H160>, hash: H256) -> Log {
|
||||
log2(address.into(), SELECTOR_LOG_EXECUTED, hash, Vec::new())
|
||||
}
|
||||
|
||||
pub fn log_proposed(
|
||||
address: impl Into<H160>,
|
||||
who: impl Into<H160>,
|
||||
index: u32,
|
||||
hash: H256,
|
||||
threshold: u32,
|
||||
) -> Log {
|
||||
log4(
|
||||
address.into(),
|
||||
SELECTOR_LOG_PROPOSED,
|
||||
who.into(),
|
||||
H256::from_slice(&solidity::encode_arguments(index)),
|
||||
hash,
|
||||
solidity::encode_arguments(threshold),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn log_voted(address: impl Into<H160>, who: impl Into<H160>, hash: H256, voted: bool) -> Log {
|
||||
log3(
|
||||
address.into(),
|
||||
SELECTOR_LOG_VOTED,
|
||||
who.into(),
|
||||
hash,
|
||||
solidity::encode_arguments(voted),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn log_closed(address: impl Into<H160>, hash: H256) -> Log {
|
||||
log2(address.into(), SELECTOR_LOG_CLOSED, hash, Vec::new())
|
||||
}
|
||||
|
||||
type GetProposalLimit = ConstU32<{ 2u32.pow(16) }>;
|
||||
type DecodeLimit = ConstU32<8>;
|
||||
|
||||
pub struct CollectivePrecompile<Runtime, Instance: 'static>(PhantomData<(Runtime, Instance)>);
|
||||
|
||||
#[precompile_utils::precompile]
|
||||
impl<Runtime, Instance> CollectivePrecompile<Runtime, Instance>
|
||||
where
|
||||
Instance: 'static,
|
||||
Runtime: pallet_collective::Config<Instance> + pallet_evm::Config,
|
||||
Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo + Decode,
|
||||
Runtime::RuntimeCall: From<pallet_collective::Call<Runtime, Instance>>,
|
||||
<Runtime as pallet_collective::Config<Instance>>::Proposal: From<Runtime::RuntimeCall>,
|
||||
<Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<Runtime::AccountId>>,
|
||||
Runtime::AccountId: Into<H160>,
|
||||
H256: From<<Runtime as frame_system::Config>::Hash>
|
||||
+ Into<<Runtime as frame_system::Config>::Hash>,
|
||||
<Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
|
||||
{
|
||||
#[precompile::public("execute(bytes)")]
|
||||
fn execute(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
proposal: BoundedBytes<GetProposalLimit>,
|
||||
) -> EvmResult {
|
||||
let proposal: Vec<_> = proposal.into();
|
||||
let proposal_hash: H256 = hash::<Runtime>(&proposal);
|
||||
|
||||
let log = log_executed(handle.context().address, proposal_hash);
|
||||
handle.record_log_costs(&[&log])?;
|
||||
|
||||
let proposal_length: u32 = proposal.len().try_into().map_err(|_| {
|
||||
RevertReason::value_is_too_large("uint32")
|
||||
.in_field("length")
|
||||
.in_field("proposal")
|
||||
})?;
|
||||
|
||||
let proposal =
|
||||
Runtime::RuntimeCall::decode_with_depth_limit(DecodeLimit::get(), &mut &*proposal)
|
||||
.map_err(|_| {
|
||||
RevertReason::custom("Failed to decode proposal").in_field("proposal")
|
||||
})?
|
||||
.into();
|
||||
let proposal = Box::new(proposal);
|
||||
|
||||
let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
|
||||
RuntimeHelper::<Runtime>::try_dispatch(
|
||||
handle,
|
||||
Some(origin).into(),
|
||||
pallet_collective::Call::<Runtime, Instance>::execute {
|
||||
proposal,
|
||||
length_bound: proposal_length,
|
||||
},
|
||||
SYSTEM_ACCOUNT_SIZE,
|
||||
)?;
|
||||
|
||||
log.record(handle)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[precompile::public("propose(uint32,bytes)")]
|
||||
fn propose(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
threshold: u32,
|
||||
proposal: BoundedBytes<GetProposalLimit>,
|
||||
) -> EvmResult<u32> {
|
||||
// ProposalCount
|
||||
handle.record_db_read::<Runtime>(4)?;
|
||||
|
||||
let proposal: Vec<_> = proposal.into();
|
||||
let proposal_length: u32 = proposal.len().try_into().map_err(|_| {
|
||||
RevertReason::value_is_too_large("uint32")
|
||||
.in_field("length")
|
||||
.in_field("proposal")
|
||||
})?;
|
||||
|
||||
let proposal_index = pallet_collective::ProposalCount::<Runtime, Instance>::get();
|
||||
let proposal_hash: H256 = hash::<Runtime>(&proposal);
|
||||
|
||||
// In pallet_collective a threshold < 2 means the proposal has been
|
||||
// executed directly.
|
||||
let log = if threshold < 2 {
|
||||
log_executed(handle.context().address, proposal_hash)
|
||||
} else {
|
||||
log_proposed(
|
||||
handle.context().address,
|
||||
handle.context().caller,
|
||||
proposal_index,
|
||||
proposal_hash,
|
||||
threshold,
|
||||
)
|
||||
};
|
||||
|
||||
handle.record_log_costs(&[&log])?;
|
||||
|
||||
let proposal =
|
||||
Runtime::RuntimeCall::decode_with_depth_limit(DecodeLimit::get(), &mut &*proposal)
|
||||
.map_err(|_| {
|
||||
RevertReason::custom("Failed to decode proposal").in_field("proposal")
|
||||
})?
|
||||
.into();
|
||||
let proposal = Box::new(proposal);
|
||||
|
||||
{
|
||||
let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
|
||||
RuntimeHelper::<Runtime>::try_dispatch(
|
||||
handle,
|
||||
Some(origin).into(),
|
||||
pallet_collective::Call::<Runtime, Instance>::propose {
|
||||
threshold,
|
||||
proposal,
|
||||
length_bound: proposal_length,
|
||||
},
|
||||
SYSTEM_ACCOUNT_SIZE,
|
||||
)?;
|
||||
}
|
||||
|
||||
log.record(handle)?;
|
||||
|
||||
Ok(proposal_index)
|
||||
}
|
||||
|
||||
#[precompile::public("vote(bytes32,uint32,bool)")]
|
||||
fn vote(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
proposal_hash: H256,
|
||||
proposal_index: u32,
|
||||
approve: bool,
|
||||
) -> EvmResult {
|
||||
// TODO: Since we cannot access ayes/nays of a proposal we cannot
|
||||
// include it in the EVM events to mirror Substrate events.
|
||||
let log = log_voted(
|
||||
handle.context().address,
|
||||
handle.context().caller,
|
||||
proposal_hash,
|
||||
approve,
|
||||
);
|
||||
handle.record_log_costs(&[&log])?;
|
||||
|
||||
let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
|
||||
RuntimeHelper::<Runtime>::try_dispatch(
|
||||
handle,
|
||||
Some(origin).into(),
|
||||
pallet_collective::Call::<Runtime, Instance>::vote {
|
||||
proposal: proposal_hash.into(),
|
||||
index: proposal_index,
|
||||
approve,
|
||||
},
|
||||
SYSTEM_ACCOUNT_SIZE,
|
||||
)?;
|
||||
|
||||
log.record(handle)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[precompile::public("close(bytes32,uint32,uint64,uint32)")]
|
||||
fn close(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
proposal_hash: H256,
|
||||
proposal_index: u32,
|
||||
proposal_weight_bound: u64,
|
||||
length_bound: u32,
|
||||
) -> EvmResult<bool> {
|
||||
// Because the actual log cannot be built before dispatch, we manually
|
||||
// record it first (`executed` and `closed` have the same cost).
|
||||
handle.record_log_costs_manual(2, 0)?;
|
||||
|
||||
let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
|
||||
let post_dispatch_info = RuntimeHelper::<Runtime>::try_dispatch(
|
||||
handle,
|
||||
Some(origin).into(),
|
||||
pallet_collective::Call::<Runtime, Instance>::close {
|
||||
proposal_hash: proposal_hash.into(),
|
||||
index: proposal_index,
|
||||
proposal_weight_bound: Weight::from_parts(
|
||||
proposal_weight_bound,
|
||||
PROPOSAL_MAX_PROOF_SIZE,
|
||||
),
|
||||
length_bound,
|
||||
},
|
||||
SYSTEM_ACCOUNT_SIZE,
|
||||
)?;
|
||||
|
||||
// We can know if the proposal was executed or not based on the `pays_fee` in
|
||||
// `PostDispatchInfo`.
|
||||
let (executed, log) = match post_dispatch_info.pays_fee {
|
||||
Pays::Yes => (true, log_executed(handle.context().address, proposal_hash)),
|
||||
Pays::No => (false, log_closed(handle.context().address, proposal_hash)),
|
||||
};
|
||||
log.record(handle)?;
|
||||
|
||||
Ok(executed)
|
||||
}
|
||||
|
||||
#[precompile::public("proposalHash(bytes)")]
|
||||
#[precompile::view]
|
||||
fn proposal_hash(
|
||||
_handle: &mut impl PrecompileHandle,
|
||||
proposal: BoundedBytes<GetProposalLimit>,
|
||||
) -> EvmResult<H256> {
|
||||
let proposal: Vec<_> = proposal.into();
|
||||
let hash = hash::<Runtime>(&proposal);
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
#[precompile::public("proposals()")]
|
||||
#[precompile::view]
|
||||
fn proposals(handle: &mut impl PrecompileHandle) -> EvmResult<Vec<H256>> {
|
||||
// Proposals: BoundedVec(32 * MaxProposals)
|
||||
handle.record_db_read::<Runtime>(
|
||||
32 * (<Runtime as pallet_collective::Config<Instance>>::MaxProposals::get() as usize),
|
||||
)?;
|
||||
|
||||
let proposals = pallet_collective::Proposals::<Runtime, Instance>::get();
|
||||
let proposals: Vec<_> = proposals.into_iter().map(|hash| hash.into()).collect();
|
||||
|
||||
Ok(proposals)
|
||||
}
|
||||
|
||||
#[precompile::public("members()")]
|
||||
#[precompile::view]
|
||||
fn members(handle: &mut impl PrecompileHandle) -> EvmResult<Vec<Address>> {
|
||||
// Members: Vec(20 * MaxMembers)
|
||||
handle.record_db_read::<Runtime>(
|
||||
20 * (<Runtime as pallet_collective::Config<Instance>>::MaxProposals::get() as usize),
|
||||
)?;
|
||||
|
||||
let members = pallet_collective::Members::<Runtime, Instance>::get();
|
||||
let members: Vec<_> = members.into_iter().map(|id| Address(id.into())).collect();
|
||||
|
||||
Ok(members)
|
||||
}
|
||||
|
||||
#[precompile::public("isMember(address)")]
|
||||
#[precompile::view]
|
||||
fn is_member(handle: &mut impl PrecompileHandle, account: Address) -> EvmResult<bool> {
|
||||
// Members: Vec(20 * MaxMembers)
|
||||
handle.record_db_read::<Runtime>(
|
||||
20 * (<Runtime as pallet_collective::Config<Instance>>::MaxProposals::get() as usize),
|
||||
)?;
|
||||
|
||||
let account = Runtime::AddressMapping::into_account_id(account.into());
|
||||
|
||||
let is_member = pallet_collective::Pallet::<Runtime, Instance>::is_member(&account);
|
||||
|
||||
Ok(is_member)
|
||||
}
|
||||
|
||||
#[precompile::public("prime()")]
|
||||
#[precompile::view]
|
||||
fn prime(handle: &mut impl PrecompileHandle) -> EvmResult<Address> {
|
||||
// Prime
|
||||
handle.record_db_read::<Runtime>(20)?;
|
||||
|
||||
let prime = pallet_collective::Prime::<Runtime, Instance>::get()
|
||||
.map(|prime| prime.into())
|
||||
.unwrap_or(H160::zero());
|
||||
|
||||
Ok(Address(prime))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash<Runtime>(data: &[u8]) -> H256
|
||||
where
|
||||
Runtime: frame_system::Config,
|
||||
H256: From<<Runtime as frame_system::Config>::Hash>,
|
||||
{
|
||||
<Runtime as frame_system::Config>::Hashing::hash(data).into()
|
||||
}
|
||||
384
operator/precompiles/collective/src/mock.rs
Normal file
384
operator/precompiles/collective/src/mock.rs
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
// Copyright 2019-2025 PureStake Inc.
|
||||
// This file is part of Moonbeam.
|
||||
|
||||
// Moonbeam is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Moonbeam is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moonbeam. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Test utilities
|
||||
use super::*;
|
||||
use frame_support::traits::tokens::{PayFromAccount, UnityAssetBalanceConversion};
|
||||
use frame_support::{
|
||||
construct_runtime, parameter_types,
|
||||
traits::{ConstU128, Everything, MapSuccess, OnFinalize, OnInitialize},
|
||||
PalletId,
|
||||
};
|
||||
use frame_system::{pallet_prelude::BlockNumberFor, EnsureRoot};
|
||||
use pallet_evm::{
|
||||
EnsureAddressNever, EnsureAddressRoot, FrameSystemAccountProvider, SubstrateBlockHashMapping,
|
||||
};
|
||||
use precompile_utils::{
|
||||
precompile_set::*,
|
||||
testing::{Bob, Charlie, MockAccount},
|
||||
};
|
||||
use sp_core::{H256, U256};
|
||||
use sp_io;
|
||||
use sp_runtime::{
|
||||
traits::{BlakeTwo256, IdentityLookup, Replace},
|
||||
BuildStorage, Permill,
|
||||
};
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
use pallet_treasury::ArgumentsFactory;
|
||||
|
||||
pub type AccountId = MockAccount;
|
||||
pub type Balance = u128;
|
||||
pub type BlockNumber = BlockNumberFor<Runtime>;
|
||||
|
||||
type Block = frame_system::mocking::MockBlockU32<Runtime>;
|
||||
|
||||
// Configure a mock runtime to test the pallet.
|
||||
construct_runtime!(
|
||||
pub enum Runtime {
|
||||
System: frame_system,
|
||||
Balances: pallet_balances,
|
||||
Evm: pallet_evm,
|
||||
Timestamp: pallet_timestamp,
|
||||
Treasury: pallet_treasury,
|
||||
CouncilCollective:
|
||||
pallet_collective::<Instance1>,
|
||||
}
|
||||
);
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: u32 = 250;
|
||||
pub const SS58Prefix: u8 = 42;
|
||||
}
|
||||
impl frame_system::Config for Runtime {
|
||||
type BaseCallFilter = Everything;
|
||||
type DbWeight = ();
|
||||
type RuntimeOrigin = RuntimeOrigin;
|
||||
type RuntimeTask = RuntimeTask;
|
||||
type Nonce = u64;
|
||||
type Block = Block;
|
||||
type RuntimeCall = RuntimeCall;
|
||||
type Hash = H256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = AccountId;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type Version = ();
|
||||
type PalletInfo = PalletInfo;
|
||||
type AccountData = pallet_balances::AccountData<Balance>;
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type SystemWeightInfo = ();
|
||||
type BlockWeights = ();
|
||||
type BlockLength = ();
|
||||
type SS58Prefix = SS58Prefix;
|
||||
type OnSetCode = ();
|
||||
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
||||
type SingleBlockMigrations = ();
|
||||
type MultiBlockMigrator = ();
|
||||
type PreInherents = ();
|
||||
type PostInherents = ();
|
||||
type PostTransactions = ();
|
||||
type ExtensionsWeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u128 = 0;
|
||||
}
|
||||
|
||||
impl pallet_balances::Config for Runtime {
|
||||
type MaxReserves = ();
|
||||
type ReserveIdentifier = ();
|
||||
type MaxLocks = ();
|
||||
type Balance = Balance;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type AccountStore = System;
|
||||
type WeightInfo = ();
|
||||
type RuntimeHoldReason = ();
|
||||
type FreezeIdentifier = ();
|
||||
type MaxFreezes = ();
|
||||
type RuntimeFreezeReason = ();
|
||||
type DoneSlashHandler = ();
|
||||
}
|
||||
|
||||
pub type Precompiles<R> = PrecompileSetBuilder<
|
||||
R,
|
||||
(PrecompileAt<AddressU64<1>, CollectivePrecompile<R, pallet_collective::Instance1>>,),
|
||||
>;
|
||||
|
||||
pub type PCall = CollectivePrecompileCall<Runtime, pallet_collective::Instance1>;
|
||||
|
||||
const MAX_POV_SIZE: u64 = 5 * 1024 * 1024;
|
||||
/// Block storage limit in bytes. Set to 40 KB.
|
||||
const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024;
|
||||
|
||||
parameter_types! {
|
||||
pub BlockGasLimit: U256 = U256::from(u64::MAX);
|
||||
pub PrecompilesValue: Precompiles<Runtime> = Precompiles::new();
|
||||
pub const WeightPerGas: Weight = Weight::from_parts(1, 0);
|
||||
pub GasLimitPovSizeRatio: u64 = {
|
||||
let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64();
|
||||
block_gas_limit.saturating_div(MAX_POV_SIZE)
|
||||
};
|
||||
pub GasLimitStorageGrowthRatio : u64 = {
|
||||
let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64();
|
||||
block_gas_limit.saturating_div(BLOCK_STORAGE_LIMIT)
|
||||
};
|
||||
}
|
||||
|
||||
impl pallet_evm::Config for Runtime {
|
||||
type FeeCalculator = ();
|
||||
type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
|
||||
type WeightPerGas = WeightPerGas;
|
||||
type CallOrigin = EnsureAddressRoot<AccountId>;
|
||||
type WithdrawOrigin = EnsureAddressNever<AccountId>;
|
||||
type AddressMapping = AccountId;
|
||||
type Currency = Balances;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Runner = pallet_evm::runner::stack::Runner<Self>;
|
||||
type PrecompilesType = Precompiles<Self>;
|
||||
type PrecompilesValue = PrecompilesValue;
|
||||
type ChainId = ();
|
||||
type OnChargeTransaction = ();
|
||||
type BlockGasLimit = BlockGasLimit;
|
||||
type BlockHashMapping = SubstrateBlockHashMapping<Self>;
|
||||
type FindAuthor = ();
|
||||
type OnCreate = ();
|
||||
type GasLimitPovSizeRatio = GasLimitPovSizeRatio;
|
||||
type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio;
|
||||
type Timestamp = Timestamp;
|
||||
type WeightInfo = pallet_evm::weights::SubstrateWeight<Runtime>;
|
||||
type AccountProvider = FrameSystemAccountProvider<Runtime>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const MinimumPeriod: u64 = 5;
|
||||
}
|
||||
impl pallet_timestamp::Config for Runtime {
|
||||
type Moment = u64;
|
||||
type OnTimestampSet = ();
|
||||
type MinimumPeriod = MinimumPeriod;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const LaunchPeriod: BlockNumber = 10;
|
||||
pub const VotingPeriod: BlockNumber = 10;
|
||||
pub const VoteLockingPeriod: BlockNumber = 10;
|
||||
pub const FastTrackVotingPeriod: BlockNumber = 5;
|
||||
pub const EnactmentPeriod: BlockNumber = 10;
|
||||
pub const CooloffPeriod: BlockNumber = 10;
|
||||
pub const MinimumDeposit: Balance = 10;
|
||||
pub const MaxVotes: u32 = 10;
|
||||
pub const MaxProposals: u32 = 10;
|
||||
pub const PreimageByteDeposit: Balance = 10;
|
||||
pub const InstantAllowed: bool = false;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ProposalBond: Permill = Permill::from_percent(5);
|
||||
pub const TreasuryId: PalletId = PalletId(*b"pc/trsry");
|
||||
pub TreasuryAccount: AccountId = Treasury::account_id();
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub struct BenchmarkHelper;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
impl ArgumentsFactory<(), AccountId> for BenchmarkHelper {
|
||||
fn create_asset_kind(_seed: u32) -> () {
|
||||
()
|
||||
}
|
||||
|
||||
fn create_beneficiary(seed: [u8; 32]) -> AccountId {
|
||||
AccountId::from(H160::from(H256::from(seed)))
|
||||
}
|
||||
}
|
||||
|
||||
impl pallet_treasury::Config for Runtime {
|
||||
type PalletId = TreasuryId;
|
||||
type Currency = Balances;
|
||||
type RejectOrigin = frame_support::traits::NeverEnsureOrigin<Balance>;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
// If spending proposal rejected, transfer proposer bond to treasury
|
||||
type SpendPeriod = ConstU32<1>;
|
||||
type Burn = ();
|
||||
type BurnDestination = ();
|
||||
type MaxApprovals = ConstU32<100>;
|
||||
type WeightInfo = pallet_treasury::weights::SubstrateWeight<Runtime>;
|
||||
type SpendFunds = ();
|
||||
type SpendOrigin = MapSuccess<
|
||||
pallet_collective::EnsureProportionMoreThan<AccountId, pallet_collective::Instance1, 1, 2>,
|
||||
Replace<ConstU128<1000>>,
|
||||
>;
|
||||
type AssetKind = ();
|
||||
type Beneficiary = AccountId;
|
||||
type BeneficiaryLookup = IdentityLookup<AccountId>;
|
||||
type Paymaster = PayFromAccount<Balances, TreasuryAccount>;
|
||||
type BalanceConverter = UnityAssetBalanceConversion;
|
||||
type PayoutPeriod = ConstU32<0>;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type BenchmarkHelper = BenchmarkHelper;
|
||||
type BlockNumberProvider = System;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub MaxProposalWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000_000);
|
||||
}
|
||||
|
||||
impl pallet_collective::Config<pallet_collective::Instance1> for Runtime {
|
||||
type RuntimeOrigin = RuntimeOrigin;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Proposal = RuntimeCall;
|
||||
/// The maximum amount of time (in blocks) for council members to vote on motions.
|
||||
/// Motions may end in fewer blocks if enough votes are cast to determine the result.
|
||||
type MotionDuration = ConstU32<2>;
|
||||
/// The maximum number of Proposlas that can be open in the council at once.
|
||||
type MaxProposals = ConstU32<100>;
|
||||
/// The maximum number of council members.
|
||||
type MaxMembers = ConstU32<100>;
|
||||
type DefaultVote = pallet_collective::MoreThanMajorityThenPrimeDefaultVote;
|
||||
type WeightInfo = pallet_collective::weights::SubstrateWeight<Runtime>;
|
||||
type SetMembersOrigin = frame_system::EnsureRoot<AccountId>;
|
||||
type MaxProposalWeight = MaxProposalWeight;
|
||||
type KillOrigin = EnsureRoot<AccountId>;
|
||||
type DisapproveOrigin = EnsureRoot<AccountId>;
|
||||
type Consideration = ();
|
||||
}
|
||||
|
||||
/// Build test externalities, prepopulated with data for testing democracy precompiles
|
||||
pub(crate) struct ExtBuilder {
|
||||
/// Endowed accounts with balances
|
||||
balances: Vec<(AccountId, Balance)>,
|
||||
/// Collective members
|
||||
collective: Vec<AccountId>,
|
||||
}
|
||||
|
||||
impl Default for ExtBuilder {
|
||||
fn default() -> ExtBuilder {
|
||||
ExtBuilder {
|
||||
balances: vec![],
|
||||
collective: vec![Bob.into(), Charlie.into()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtBuilder {
|
||||
/// Fund some accounts before starting the test
|
||||
#[allow(unused)]
|
||||
pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
|
||||
self.balances = balances;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set members of the collective
|
||||
#[allow(unused)]
|
||||
pub(crate) fn with_collective(mut self, collective: Vec<AccountId>) -> Self {
|
||||
self.collective = collective;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the test externalities for use in tests
|
||||
#[allow(unused)]
|
||||
pub(crate) fn build(self) -> sp_io::TestExternalities {
|
||||
let mut t = frame_system::GenesisConfig::<Runtime>::default()
|
||||
.build_storage()
|
||||
.expect("Frame system builds valid default genesis config");
|
||||
|
||||
pallet_balances::GenesisConfig::<Runtime> {
|
||||
balances: self.balances.clone(),
|
||||
}
|
||||
.assimilate_storage(&mut t)
|
||||
.expect("Pallet balances storage can be assimilated");
|
||||
|
||||
pallet_collective::GenesisConfig::<Runtime, pallet_collective::Instance1> {
|
||||
members: self.collective.clone(),
|
||||
phantom: Default::default(),
|
||||
}
|
||||
.assimilate_storage(&mut t)
|
||||
.expect("Pallet collective storage can be assimilated");
|
||||
|
||||
let mut ext = sp_io::TestExternalities::new(t);
|
||||
ext.execute_with(|| {
|
||||
System::set_block_number(1);
|
||||
});
|
||||
ext
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn roll_to(n: BlockNumber) {
|
||||
// We skip timestamp's on_finalize because it requires that the timestamp inherent be set
|
||||
// We may be able to simulate this by poking its storage directly, but I don't see any value
|
||||
// added from doing that.
|
||||
while System::block_number() < n {
|
||||
Treasury::on_finalize(System::block_number());
|
||||
// Times tamp::on_finalize(System::block_number());
|
||||
Evm::on_finalize(System::block_number());
|
||||
Balances::on_finalize(System::block_number());
|
||||
System::on_finalize(System::block_number());
|
||||
|
||||
System::set_block_number(System::block_number() + 1);
|
||||
|
||||
System::on_initialize(System::block_number());
|
||||
Balances::on_initialize(System::block_number());
|
||||
Evm::on_initialize(System::block_number());
|
||||
Timestamp::on_initialize(System::block_number());
|
||||
Treasury::on_initialize(System::block_number());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn events() -> Vec<RuntimeEvent> {
|
||||
System::events()
|
||||
.into_iter()
|
||||
.map(|r| r.event)
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_tail_eq {
|
||||
($tail:expr, $arr:expr) => {
|
||||
if $tail.len() != 0 {
|
||||
// 0-length always passes
|
||||
|
||||
if $tail.len() > $arr.len() {
|
||||
similar_asserts::assert_eq!($tail, $arr); // will fail
|
||||
}
|
||||
|
||||
let len_diff = $arr.len() - $tail.len();
|
||||
similar_asserts::assert_eq!($tail, $arr[len_diff..]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Panics if an event is not found in the system log of events
|
||||
#[macro_export]
|
||||
macro_rules! assert_event_emitted {
|
||||
($event:expr) => {
|
||||
match &$event {
|
||||
e => {
|
||||
assert!(
|
||||
crate::mock::events().iter().find(|x| *x == e).is_some(),
|
||||
"Event {:?} was not found in events: \n {:#?}",
|
||||
e,
|
||||
crate::mock::events()
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
685
operator/precompiles/collective/src/tests.rs
Normal file
685
operator/precompiles/collective/src/tests.rs
Normal file
|
|
@ -0,0 +1,685 @@
|
|||
// Copyright 2019-2025 PureStake Inc.
|
||||
// This file is part of Moonbeam.
|
||||
|
||||
// Moonbeam is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Moonbeam is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moonbeam. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
assert_event_emitted, hash, log_closed, log_executed, log_proposed, log_voted,
|
||||
mock::{ExtBuilder, PCall, Precompiles, PrecompilesValue, Runtime, RuntimeOrigin},
|
||||
};
|
||||
use frame_support::{assert_ok, instances::Instance1};
|
||||
use parity_scale_codec::Encode;
|
||||
use precompile_utils::{solidity::codec::Address, testing::*};
|
||||
use sp_core::{H160, H256};
|
||||
use sp_runtime::DispatchError;
|
||||
|
||||
fn precompiles() -> Precompiles<Runtime> {
|
||||
PrecompilesValue::get()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
|
||||
check_precompile_implements_solidity_interfaces(&["Collective.sol"], PCall::supports_selector)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selector_less_than_four_bytes() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
// This selector is only three bytes long when four are required.
|
||||
precompiles()
|
||||
.prepare_test(Alice, Precompile1, vec![1u8, 2u8, 3u8])
|
||||
.execute_reverts(|output| output == b"Tried to read selector out of bounds");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_selector_exists_but_length_is_right() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
precompiles()
|
||||
.prepare_test(Alice, Precompile1, vec![1u8, 2u8, 3u8, 4u8])
|
||||
.execute_reverts(|output| output == b"Unknown selector");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selectors() {
|
||||
assert!(PCall::execute_selectors().contains(&0x09c5eabe));
|
||||
assert!(PCall::propose_selectors().contains(&0xc57f3260));
|
||||
assert!(PCall::vote_selectors().contains(&0x73e37688));
|
||||
assert!(PCall::close_selectors().contains(&0x638d9d47));
|
||||
assert!(PCall::proposal_hash_selectors().contains(&0xfc379417));
|
||||
assert!(PCall::proposals_selectors().contains(&0x55ef20e6));
|
||||
assert!(PCall::members_selectors().contains(&0xbdd4d18d));
|
||||
assert!(PCall::is_member_selectors().contains(&0xa230c524));
|
||||
assert!(PCall::prime_selectors().contains(&0xc7ee005e));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modifiers() {
|
||||
ExtBuilder::default()
|
||||
.with_balances(vec![(Alice.into(), 1000)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
let mut tester = PrecompilesModifierTester::new(precompiles(), Alice, Precompile1);
|
||||
|
||||
tester.test_default_modifier(PCall::execute_selectors());
|
||||
tester.test_default_modifier(PCall::propose_selectors());
|
||||
tester.test_default_modifier(PCall::vote_selectors());
|
||||
tester.test_default_modifier(PCall::close_selectors());
|
||||
tester.test_view_modifier(PCall::proposal_hash_selectors());
|
||||
tester.test_view_modifier(PCall::proposals_selectors());
|
||||
tester.test_view_modifier(PCall::members_selectors());
|
||||
tester.test_view_modifier(PCall::is_member_selectors());
|
||||
tester.test_view_modifier(PCall::prime_selectors());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_member_cannot_propose() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let proposal = pallet_treasury::Call::<Runtime>::spend_local {
|
||||
amount: 1,
|
||||
beneficiary: Alice.into(),
|
||||
};
|
||||
let proposal: <Runtime as frame_system::Config>::RuntimeCall = proposal.into();
|
||||
let proposal = proposal.encode();
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Alice,
|
||||
Precompile1,
|
||||
PCall::propose {
|
||||
threshold: 1,
|
||||
proposal: proposal.into(),
|
||||
},
|
||||
)
|
||||
.expect_no_logs()
|
||||
.execute_reverts(|output| output.ends_with(b"NotMember\") })"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_member_cannot_vote() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Alice,
|
||||
Precompile1,
|
||||
PCall::vote {
|
||||
proposal_hash: H256::zero(),
|
||||
proposal_index: 1,
|
||||
approve: false,
|
||||
},
|
||||
)
|
||||
.expect_no_logs()
|
||||
.execute_reverts(|output| output.ends_with(b"NotMember\") })"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_member_cannot_execute() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let proposal = pallet_treasury::Call::<Runtime>::spend_local {
|
||||
amount: 1,
|
||||
beneficiary: Alice.into(),
|
||||
};
|
||||
let proposal: <Runtime as frame_system::Config>::RuntimeCall = proposal.into();
|
||||
let proposal = proposal.encode();
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Alice,
|
||||
Precompile1,
|
||||
PCall::execute {
|
||||
proposal: proposal.into(),
|
||||
},
|
||||
)
|
||||
.expect_no_logs()
|
||||
.execute_reverts(|output| output.ends_with(b"NotMember\") })"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_vote_for_unknown_proposal() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::vote {
|
||||
proposal_hash: H256::zero(),
|
||||
proposal_index: 1,
|
||||
approve: false,
|
||||
},
|
||||
)
|
||||
.expect_no_logs()
|
||||
.execute_reverts(|output| output.ends_with(b"ProposalMissing\") })"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_close_unknown_proposal() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::close {
|
||||
proposal_hash: H256::zero(),
|
||||
proposal_index: 1,
|
||||
proposal_weight_bound: 0,
|
||||
length_bound: 0,
|
||||
},
|
||||
)
|
||||
.expect_no_logs()
|
||||
.execute_reverts(|output| output.ends_with(b"ProposalMissing\") })"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn member_can_make_instant_proposal() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let proposal = pallet_treasury::Call::<Runtime>::spend_local {
|
||||
amount: 1,
|
||||
beneficiary: Alice.into(),
|
||||
};
|
||||
let proposal: <Runtime as frame_system::Config>::RuntimeCall = proposal.into();
|
||||
let proposal = proposal.encode();
|
||||
let proposal_hash: H256 = hash::<Runtime>(&proposal);
|
||||
|
||||
// Proposal is executed. The proposal call will itself fail but it
|
||||
// still counts as a success according to pallet_collective.
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::propose {
|
||||
threshold: 1,
|
||||
proposal: proposal.into(),
|
||||
},
|
||||
)
|
||||
.expect_log(log_executed(Precompile1, proposal_hash))
|
||||
.execute_returns(0u32);
|
||||
|
||||
assert_event_emitted!(pallet_collective::Event::Executed {
|
||||
proposal_hash,
|
||||
result: Err(DispatchError::BadOrigin)
|
||||
}
|
||||
.into());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn member_can_make_delayed_proposal() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let proposal = pallet_treasury::Call::<Runtime>::spend_local {
|
||||
amount: 1,
|
||||
beneficiary: Alice.into(),
|
||||
};
|
||||
let proposal: <Runtime as frame_system::Config>::RuntimeCall = proposal.into();
|
||||
let proposal = proposal.encode();
|
||||
let proposal_hash: H256 = hash::<Runtime>(&proposal);
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::propose {
|
||||
threshold: 2,
|
||||
proposal: proposal.into(),
|
||||
},
|
||||
)
|
||||
.expect_log(log_proposed(Precompile1, Bob, 0, proposal_hash, 2))
|
||||
.execute_returns(0u32);
|
||||
|
||||
assert_event_emitted!(pallet_collective::Event::Proposed {
|
||||
account: Bob.into(),
|
||||
proposal_index: 0,
|
||||
proposal_hash,
|
||||
threshold: 2,
|
||||
}
|
||||
.into());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn member_can_vote_on_proposal() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let proposal = pallet_treasury::Call::<Runtime>::spend_local {
|
||||
amount: 1,
|
||||
beneficiary: Alice.into(),
|
||||
};
|
||||
let proposal: <Runtime as frame_system::Config>::RuntimeCall = proposal.into();
|
||||
let proposal = proposal.encode();
|
||||
let proposal_hash: H256 = hash::<Runtime>(&proposal);
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::propose {
|
||||
threshold: 2,
|
||||
proposal: proposal.into(),
|
||||
},
|
||||
)
|
||||
.expect_log(log_proposed(Precompile1, Bob, 0, proposal_hash, 2))
|
||||
.execute_returns(0u32);
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Charlie,
|
||||
Precompile1,
|
||||
PCall::vote {
|
||||
proposal_hash,
|
||||
proposal_index: 0,
|
||||
approve: true,
|
||||
},
|
||||
)
|
||||
.expect_log(log_voted(Precompile1, Charlie, proposal_hash, true))
|
||||
.execute_returns(());
|
||||
|
||||
assert_event_emitted!(pallet_collective::Event::Voted {
|
||||
account: Charlie.into(),
|
||||
proposal_hash,
|
||||
voted: true,
|
||||
yes: 1,
|
||||
no: 0,
|
||||
}
|
||||
.into());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_close_if_not_enough_votes() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let proposal = pallet_treasury::Call::<Runtime>::spend_local {
|
||||
amount: 1,
|
||||
beneficiary: Alice.into(),
|
||||
};
|
||||
let proposal: <Runtime as frame_system::Config>::RuntimeCall = proposal.into();
|
||||
let proposal = proposal.encode();
|
||||
let proposal_hash: H256 = hash::<Runtime>(&proposal);
|
||||
let length_bound = proposal.len() as u32;
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::propose {
|
||||
threshold: 2,
|
||||
proposal: proposal.into(),
|
||||
},
|
||||
)
|
||||
.expect_log(log_proposed(Precompile1, Bob, 0, proposal_hash, 2))
|
||||
.execute_returns(0u32);
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Alice,
|
||||
Precompile1,
|
||||
PCall::close {
|
||||
proposal_hash,
|
||||
proposal_index: 0,
|
||||
proposal_weight_bound: 10_000_000,
|
||||
length_bound,
|
||||
},
|
||||
)
|
||||
.expect_no_logs()
|
||||
.execute_reverts(|output| output.ends_with(b"TooEarly\") })"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_close_execute_if_enough_votes() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let proposal = pallet_treasury::Call::<Runtime>::spend_local {
|
||||
amount: 1,
|
||||
beneficiary: Alice.into(),
|
||||
};
|
||||
let proposal: <Runtime as frame_system::Config>::RuntimeCall = proposal.into();
|
||||
let proposal = proposal.encode();
|
||||
let proposal_hash: H256 = hash::<Runtime>(&proposal);
|
||||
let length_bound = proposal.len() as u32;
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::propose {
|
||||
threshold: 2,
|
||||
proposal: proposal.into(),
|
||||
},
|
||||
)
|
||||
.expect_log(log_proposed(Precompile1, Bob, 0, proposal_hash, 2))
|
||||
.execute_returns(0u32);
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::vote {
|
||||
proposal_hash,
|
||||
proposal_index: 0,
|
||||
approve: true,
|
||||
},
|
||||
)
|
||||
.expect_log(log_voted(Precompile1, Bob, proposal_hash, true))
|
||||
.execute_returns(());
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Charlie,
|
||||
Precompile1,
|
||||
PCall::vote {
|
||||
proposal_hash,
|
||||
proposal_index: 0,
|
||||
approve: true,
|
||||
},
|
||||
)
|
||||
.expect_log(log_voted(Precompile1, Charlie, proposal_hash, true))
|
||||
.execute_returns(());
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Alice,
|
||||
Precompile1,
|
||||
PCall::close {
|
||||
proposal_hash,
|
||||
proposal_index: 0,
|
||||
proposal_weight_bound: 200_000_000,
|
||||
length_bound,
|
||||
},
|
||||
)
|
||||
.expect_log(log_executed(Precompile1, proposal_hash))
|
||||
.execute_returns(true);
|
||||
|
||||
assert_event_emitted!(pallet_collective::Event::Closed {
|
||||
proposal_hash,
|
||||
yes: 2,
|
||||
no: 0,
|
||||
}
|
||||
.into());
|
||||
|
||||
assert_event_emitted!(pallet_collective::Event::Approved { proposal_hash }.into());
|
||||
|
||||
assert_event_emitted!(pallet_collective::Event::Executed {
|
||||
proposal_hash,
|
||||
result: Ok(())
|
||||
}
|
||||
.into());
|
||||
|
||||
assert_event_emitted!(pallet_treasury::Event::SpendApproved {
|
||||
proposal_index: 0,
|
||||
amount: 1,
|
||||
beneficiary: Alice.into(),
|
||||
}
|
||||
.into());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_close_refuse_if_enough_votes() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let proposal = pallet_treasury::Call::<Runtime>::spend_local {
|
||||
amount: 1,
|
||||
beneficiary: Alice.into(),
|
||||
};
|
||||
let proposal: <Runtime as frame_system::Config>::RuntimeCall = proposal.into();
|
||||
let proposal = proposal.encode();
|
||||
let proposal_hash: H256 = hash::<Runtime>(&proposal);
|
||||
let length_bound = proposal.len() as u32;
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::propose {
|
||||
threshold: 2,
|
||||
proposal: proposal.into(),
|
||||
},
|
||||
)
|
||||
.expect_log(log_proposed(Precompile1, Bob, 0, proposal_hash, 2))
|
||||
.execute_returns(0u32);
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::vote {
|
||||
proposal_hash,
|
||||
proposal_index: 0,
|
||||
approve: false,
|
||||
},
|
||||
)
|
||||
.expect_log(log_voted(Precompile1, Bob, proposal_hash, false))
|
||||
.execute_returns(());
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Charlie,
|
||||
Precompile1,
|
||||
PCall::vote {
|
||||
proposal_hash,
|
||||
proposal_index: 0,
|
||||
approve: false,
|
||||
},
|
||||
)
|
||||
.expect_log(log_voted(Precompile1, Charlie, proposal_hash, false))
|
||||
.execute_returns(());
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Alice,
|
||||
Precompile1,
|
||||
PCall::close {
|
||||
proposal_hash,
|
||||
proposal_index: 0,
|
||||
proposal_weight_bound: 100_000_000,
|
||||
length_bound,
|
||||
},
|
||||
)
|
||||
.expect_log(log_closed(Precompile1, proposal_hash))
|
||||
.execute_returns(false);
|
||||
|
||||
assert_event_emitted!(pallet_collective::Event::Closed {
|
||||
proposal_hash,
|
||||
yes: 0,
|
||||
no: 2,
|
||||
}
|
||||
.into());
|
||||
|
||||
assert_event_emitted!(pallet_collective::Event::Disapproved { proposal_hash }.into());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_propose_increase_index() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
let proposal = pallet_treasury::Call::<Runtime>::spend_local {
|
||||
amount: 1,
|
||||
beneficiary: Alice.into(),
|
||||
};
|
||||
let proposal: <Runtime as frame_system::Config>::RuntimeCall = proposal.into();
|
||||
let proposal = proposal.encode();
|
||||
let proposal_hash: H256 = hash::<Runtime>(&proposal);
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::propose {
|
||||
threshold: 2,
|
||||
proposal: proposal.into(),
|
||||
},
|
||||
)
|
||||
.expect_log(log_proposed(Precompile1, Bob, 0, proposal_hash, 2))
|
||||
.execute_returns(0u32);
|
||||
|
||||
let proposal = pallet_treasury::Call::<Runtime>::spend_local {
|
||||
amount: 2,
|
||||
beneficiary: Alice.into(),
|
||||
};
|
||||
let proposal: <Runtime as frame_system::Config>::RuntimeCall = proposal.into();
|
||||
let proposal = proposal.encode();
|
||||
let proposal_hash: H256 = hash::<Runtime>(&proposal);
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::propose {
|
||||
threshold: 2,
|
||||
proposal: proposal.into(),
|
||||
},
|
||||
)
|
||||
.expect_log(log_proposed(Precompile1, Bob, 1, proposal_hash, 2))
|
||||
.execute_returns(1u32);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_members() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
precompiles()
|
||||
.prepare_test(Bob, Precompile1, PCall::members {})
|
||||
.expect_no_logs()
|
||||
.execute_returns(vec![Address(Bob.into()), Address(Charlie.into())]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_no_prime() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
precompiles()
|
||||
.prepare_test(Bob, Precompile1, PCall::prime {})
|
||||
.expect_no_logs()
|
||||
.execute_returns(Address(H160::zero()));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_some_prime() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_ok!(pallet_collective::Pallet::<
|
||||
Runtime,
|
||||
pallet_collective::Instance1,
|
||||
>::set_members(
|
||||
RuntimeOrigin::root(),
|
||||
vec![Alice.into(), Bob.into()],
|
||||
Some(Alice.into()),
|
||||
2
|
||||
));
|
||||
|
||||
precompiles()
|
||||
.prepare_test(Bob, Precompile1, PCall::prime {})
|
||||
.expect_no_logs()
|
||||
.execute_returns(Address(Alice.into()));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_is_member() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::is_member {
|
||||
account: Address(Bob.into()),
|
||||
},
|
||||
)
|
||||
.expect_no_logs()
|
||||
.execute_returns(true);
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Bob,
|
||||
Precompile1,
|
||||
PCall::is_member {
|
||||
account: Address(Alice.into()),
|
||||
},
|
||||
)
|
||||
.expect_no_logs()
|
||||
.execute_returns(false);
|
||||
});
|
||||
}
|
||||
|
||||
mod bounded_proposal_decode {
|
||||
use super::*;
|
||||
use crate::GetProposalLimit;
|
||||
use precompile_utils::prelude::BoundedBytes;
|
||||
|
||||
fn scenario<F>(nesting: usize, call: F)
|
||||
where
|
||||
F: FnOnce(BoundedBytes<GetProposalLimit>) -> PCall,
|
||||
{
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
// Some random call.
|
||||
let mut proposal = pallet_collective::Call::<Runtime, Instance1>::set_members {
|
||||
new_members: Vec::new(),
|
||||
prime: None,
|
||||
old_count: 0,
|
||||
};
|
||||
|
||||
// Nest it.
|
||||
for _ in 0..nesting {
|
||||
proposal = pallet_collective::Call::<Runtime, Instance1>::propose {
|
||||
threshold: 10,
|
||||
proposal: Box::new(proposal.into()),
|
||||
length_bound: 1,
|
||||
};
|
||||
}
|
||||
|
||||
let proposal: <Runtime as frame_system::Config>::RuntimeCall = proposal.into();
|
||||
let proposal = proposal.encode();
|
||||
|
||||
precompiles()
|
||||
.prepare_test(Alice, Precompile1, call(proposal.into()))
|
||||
.expect_no_logs()
|
||||
.execute_reverts(|output| {
|
||||
if nesting < 8 {
|
||||
output.ends_with(b"NotMember\") })")
|
||||
} else {
|
||||
output == b"proposal: Failed to decode proposal"
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proposal_above_bound() {
|
||||
scenario(8, |proposal| PCall::propose {
|
||||
threshold: 1,
|
||||
proposal,
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proposal_below_bound() {
|
||||
scenario(7, |proposal| PCall::propose {
|
||||
threshold: 1,
|
||||
proposal,
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_above_bound() {
|
||||
scenario(8, |proposal| PCall::execute { proposal });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_below_bound() {
|
||||
scenario(7, |proposal| PCall::execute { proposal });
|
||||
}
|
||||
}
|
||||
|
|
@ -126,9 +126,10 @@ strum_macros = { workspace = true }
|
|||
pallet-evm-precompile-balances-erc20 = { workspace = true }
|
||||
pallet-evm-precompile-batch = { workspace = true }
|
||||
pallet-evm-precompile-call-permit = { workspace = true }
|
||||
pallet-evm-precompile-collective = { workspace = true }
|
||||
pallet-evm-precompile-identity = { workspace = true }
|
||||
pallet-evm-precompile-proxy = { workspace = true }
|
||||
pallet-evm-precompile-registry = { workspace = true }
|
||||
pallet-evm-precompile-identity = { workspace = true }
|
||||
|
||||
# StorageHub
|
||||
pallet-bucket-nfts = { workspace = true }
|
||||
|
|
@ -199,9 +200,10 @@ std = [
|
|||
"pallet-evm-precompile-balances-erc20/std",
|
||||
"pallet-evm-precompile-batch/std",
|
||||
"pallet-evm-precompile-call-permit/std",
|
||||
"pallet-evm-precompile-collective/std",
|
||||
"pallet-evm-precompile-identity/std",
|
||||
"pallet-evm-precompile-proxy/std",
|
||||
"pallet-evm-precompile-registry/std",
|
||||
"pallet-evm-precompile-identity/std",
|
||||
"pallet-external-validators/std",
|
||||
"pallet-external-validators-rewards/std",
|
||||
"pallet-external-validators-rewards-runtime-api/std",
|
||||
|
|
|
|||
|
|
@ -739,17 +739,11 @@ impl frame_support::traits::InstanceFilter<RuntimeCall> for ProxyType {
|
|||
}
|
||||
|
||||
/// Helper function to identify governance precompiles (copied from Moonbeam)
|
||||
fn is_governance_precompile(_precompile_name: &PrecompileName) -> bool {
|
||||
// TODO: Uncomment when DataHaven implements these governance precompiles
|
||||
// matches!(
|
||||
// precompile_name,
|
||||
// PrecompileName::ConvictionVotingPrecompile
|
||||
// | PrecompileName::PreimagePrecompile
|
||||
// | PrecompileName::ReferendaPrecompile
|
||||
// | PrecompileName::OpenTechCommitteeInstance
|
||||
// | PrecompileName::TreasuryCouncilInstance
|
||||
// )
|
||||
false // Temporarily disabled until governance precompiles are added
|
||||
fn is_governance_precompile(precompile_name: &PrecompileName) -> bool {
|
||||
matches!(
|
||||
precompile_name,
|
||||
PrecompileName::TechnicalCommitteeInstance | PrecompileName::TreasuryCouncilInstance
|
||||
)
|
||||
}
|
||||
|
||||
impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType {
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@
|
|||
// along with DataHaven. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::configs::MaxAdditionalFields;
|
||||
use crate::governance::councils::{TechnicalCommitteeInstance, TreasuryCouncilInstance};
|
||||
use pallet_evm_precompile_balances_erc20::{Erc20BalancesPrecompile, Erc20Metadata};
|
||||
use pallet_evm_precompile_batch::BatchPrecompile;
|
||||
use pallet_evm_precompile_blake2::Blake2F;
|
||||
use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing};
|
||||
use pallet_evm_precompile_call_permit::CallPermitPrecompile;
|
||||
use pallet_evm_precompile_collective::CollectivePrecompile;
|
||||
use pallet_evm_precompile_file_system::FileSystemPrecompile;
|
||||
use pallet_evm_precompile_identity::IdentityPrecompile;
|
||||
use pallet_evm_precompile_modexp::Modexp;
|
||||
|
|
@ -100,6 +102,16 @@ type DataHavenPrecompilesAt<R> = (
|
|||
CallableByPrecompile<OnlyFrom<AddressU64<2056>>>,
|
||||
),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2064>,
|
||||
CollectivePrecompile<R, TreasuryCouncilInstance>,
|
||||
(CallableByContract, CallableByPrecompile),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2068>,
|
||||
CollectivePrecompile<R, TechnicalCommitteeInstance>,
|
||||
(CallableByContract, CallableByPrecompile),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2069>,
|
||||
PrecompileRegistry<R>,
|
||||
|
|
|
|||
|
|
@ -126,9 +126,10 @@ xcm-executor = { workspace = true }
|
|||
pallet-evm-precompile-balances-erc20 = { workspace = true }
|
||||
pallet-evm-precompile-batch = { workspace = true }
|
||||
pallet-evm-precompile-call-permit = { workspace = true }
|
||||
pallet-evm-precompile-collective = { workspace = true }
|
||||
pallet-evm-precompile-identity = { workspace = true }
|
||||
pallet-evm-precompile-proxy = { workspace = true }
|
||||
pallet-evm-precompile-registry = { workspace = true }
|
||||
pallet-evm-precompile-identity = { workspace = true }
|
||||
|
||||
# StorageHub
|
||||
pallet-bucket-nfts = { workspace = true }
|
||||
|
|
@ -199,9 +200,10 @@ std = [
|
|||
"pallet-evm-precompile-balances-erc20/std",
|
||||
"pallet-evm-precompile-batch/std",
|
||||
"pallet-evm-precompile-call-permit/std",
|
||||
"pallet-evm-precompile-collective/std",
|
||||
"pallet-evm-precompile-identity/std",
|
||||
"pallet-evm-precompile-proxy/std",
|
||||
"pallet-evm-precompile-registry/std",
|
||||
"pallet-evm-precompile-identity/std",
|
||||
"pallet-external-validators/std",
|
||||
"pallet-external-validators-rewards/std",
|
||||
"pallet-external-validators-rewards-runtime-api/std",
|
||||
|
|
|
|||
|
|
@ -738,17 +738,11 @@ impl frame_support::traits::InstanceFilter<RuntimeCall> for ProxyType {
|
|||
}
|
||||
|
||||
/// Helper function to identify governance precompiles (copied from Moonbeam)
|
||||
fn is_governance_precompile(_precompile_name: &PrecompileName) -> bool {
|
||||
// TODO: Uncomment when DataHaven implements these governance precompiles
|
||||
// matches!(
|
||||
// precompile_name,
|
||||
// PrecompileName::ConvictionVotingPrecompile
|
||||
// | PrecompileName::PreimagePrecompile
|
||||
// | PrecompileName::ReferendaPrecompile
|
||||
// | PrecompileName::OpenTechCommitteeInstance
|
||||
// | PrecompileName::TreasuryCouncilInstance
|
||||
// )
|
||||
false // Temporarily disabled until governance precompiles are added
|
||||
fn is_governance_precompile(precompile_name: &PrecompileName) -> bool {
|
||||
matches!(
|
||||
precompile_name,
|
||||
PrecompileName::TechnicalCommitteeInstance | PrecompileName::TreasuryCouncilInstance
|
||||
)
|
||||
}
|
||||
|
||||
impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType {
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@
|
|||
// along with DataHaven. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::configs::MaxAdditionalFields;
|
||||
use crate::governance::councils::{TechnicalCommitteeInstance, TreasuryCouncilInstance};
|
||||
use pallet_evm_precompile_balances_erc20::{Erc20BalancesPrecompile, Erc20Metadata};
|
||||
use pallet_evm_precompile_batch::BatchPrecompile;
|
||||
use pallet_evm_precompile_blake2::Blake2F;
|
||||
use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing};
|
||||
use pallet_evm_precompile_call_permit::CallPermitPrecompile;
|
||||
use pallet_evm_precompile_collective::CollectivePrecompile;
|
||||
use pallet_evm_precompile_file_system::FileSystemPrecompile;
|
||||
use pallet_evm_precompile_identity::IdentityPrecompile;
|
||||
use pallet_evm_precompile_modexp::Modexp;
|
||||
|
|
@ -100,6 +102,16 @@ type DataHavenPrecompilesAt<R> = (
|
|||
CallableByPrecompile<OnlyFrom<AddressU64<2056>>>,
|
||||
),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2064>,
|
||||
CollectivePrecompile<R, TreasuryCouncilInstance>,
|
||||
(CallableByContract, CallableByPrecompile),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2068>,
|
||||
CollectivePrecompile<R, TechnicalCommitteeInstance>,
|
||||
(CallableByContract, CallableByPrecompile),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2069>,
|
||||
PrecompileRegistry<R>,
|
||||
|
|
|
|||
|
|
@ -126,9 +126,10 @@ strum_macros = { workspace = true }
|
|||
pallet-evm-precompile-balances-erc20 = { workspace = true }
|
||||
pallet-evm-precompile-batch = { workspace = true }
|
||||
pallet-evm-precompile-call-permit = { workspace = true }
|
||||
pallet-evm-precompile-collective = { workspace = true }
|
||||
pallet-evm-precompile-identity = { workspace = true }
|
||||
pallet-evm-precompile-proxy = { workspace = true }
|
||||
pallet-evm-precompile-registry = { workspace = true }
|
||||
pallet-evm-precompile-identity = { workspace = true }
|
||||
|
||||
# StorageHub
|
||||
pallet-bucket-nfts = { workspace = true }
|
||||
|
|
@ -199,9 +200,10 @@ std = [
|
|||
"pallet-evm-precompile-balances-erc20/std",
|
||||
"pallet-evm-precompile-batch/std",
|
||||
"pallet-evm-precompile-call-permit/std",
|
||||
"pallet-evm-precompile-collective/std",
|
||||
"pallet-evm-precompile-identity/std",
|
||||
"pallet-evm-precompile-proxy/std",
|
||||
"pallet-evm-precompile-registry/std",
|
||||
"pallet-evm-precompile-identity/std",
|
||||
"pallet-evm-precompile-file-system/std",
|
||||
"pallet-grandpa/std",
|
||||
"pallet-identity/std",
|
||||
|
|
|
|||
|
|
@ -738,17 +738,11 @@ impl frame_support::traits::InstanceFilter<RuntimeCall> for ProxyType {
|
|||
}
|
||||
|
||||
/// Helper function to identify governance precompiles (copied from Moonbeam)
|
||||
fn is_governance_precompile(_precompile_name: &PrecompileName) -> bool {
|
||||
// TODO: Uncomment when DataHaven implements these governance precompiles
|
||||
// matches!(
|
||||
// precompile_name,
|
||||
// PrecompileName::ConvictionVotingPrecompile
|
||||
// | PrecompileName::PreimagePrecompile
|
||||
// | PrecompileName::ReferendaPrecompile
|
||||
// | PrecompileName::OpenTechCommitteeInstance
|
||||
// | PrecompileName::TreasuryCouncilInstance
|
||||
// )
|
||||
false // Temporarily disabled until governance precompiles are added
|
||||
fn is_governance_precompile(precompile_name: &PrecompileName) -> bool {
|
||||
matches!(
|
||||
precompile_name,
|
||||
PrecompileName::TechnicalCommitteeInstance | PrecompileName::TreasuryCouncilInstance
|
||||
)
|
||||
}
|
||||
|
||||
impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType {
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@
|
|||
// along with DataHaven. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::configs::MaxAdditionalFields;
|
||||
use crate::governance::councils::{TechnicalCommitteeInstance, TreasuryCouncilInstance};
|
||||
use pallet_evm_precompile_balances_erc20::{Erc20BalancesPrecompile, Erc20Metadata};
|
||||
use pallet_evm_precompile_batch::BatchPrecompile;
|
||||
use pallet_evm_precompile_blake2::Blake2F;
|
||||
use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing};
|
||||
use pallet_evm_precompile_call_permit::CallPermitPrecompile;
|
||||
use pallet_evm_precompile_collective::CollectivePrecompile;
|
||||
use pallet_evm_precompile_file_system::FileSystemPrecompile;
|
||||
use pallet_evm_precompile_identity::IdentityPrecompile;
|
||||
use pallet_evm_precompile_modexp::Modexp;
|
||||
|
|
@ -100,6 +102,16 @@ type DataHavenPrecompilesAt<R> = (
|
|||
CallableByPrecompile<OnlyFrom<AddressU64<2056>>>,
|
||||
),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2064>,
|
||||
CollectivePrecompile<R, TreasuryCouncilInstance>,
|
||||
(CallableByContract, CallableByPrecompile),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2068>,
|
||||
CollectivePrecompile<R, TechnicalCommitteeInstance>,
|
||||
(CallableByContract, CallableByPrecompile),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2069>,
|
||||
PrecompileRegistry<R>,
|
||||
|
|
|
|||
Loading…
Reference in a new issue