mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 01:38:32 +00:00
feat: ✨ add conviction voting precompile (#202)
## Add Conviction Voting Precompile This PR introduces a new EVM precompile for conviction voting functionality, enabling smart contracts to interact with the Substrate conviction voting pallet. ### Key Changes - **New Precompile**: Added `ConvictionVotingPrecompile` at address `0x0000000000000000000000000000000000000812` (2066) - **Solidity Interface**: Complete Solidity interface (`ConvictionVoting.sol`) with all conviction voting operations - **Runtime Integration**: Integrated precompile across all runtime configurations (mainnet, testnet, stagenet) - **Comprehensive Testing**: Inherits test suite from Moonbeam ### Features - Vote casting with different conviction levels (None, Locked1x-6x) - Vote delegation and undelegation - Poll management and tallying - Support for all conviction voting pallet operations --------- Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com> Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
This commit is contained in:
parent
f7d441d9e5
commit
0988ce46d0
16 changed files with 2589 additions and 524 deletions
1004
operator/Cargo.lock
generated
1004
operator/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -36,6 +36,7 @@ pallet-evm-precompile-balances-erc20 = { path = "./precompiles/erc20-balances",
|
|||
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-conviction-voting = { path = "./precompiles/conviction-voting", 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 }
|
||||
|
|
|
|||
53
operator/precompiles/conviction-voting/Cargo.toml
Normal file
53
operator/precompiles/conviction-voting/Cargo.toml
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
[package]
|
||||
name = "pallet-evm-precompile-conviction-voting"
|
||||
authors = { workspace = true }
|
||||
description = "A Precompile to make pallet-conviction-voting calls encoding accessible to pallet-evm"
|
||||
edition = "2021"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
log = { workspace = true }
|
||||
|
||||
# Substrate
|
||||
frame-support = { workspace = true }
|
||||
frame-system = { workspace = true }
|
||||
pallet-conviction-voting = { workspace = true }
|
||||
parity-scale-codec = { workspace = true, features = ["derive"] }
|
||||
sp-core = { workspace = true }
|
||||
sp-runtime = { workspace = true }
|
||||
sp-std = { workspace = true }
|
||||
|
||||
# Frontier
|
||||
fp-evm = { workspace = true }
|
||||
pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }
|
||||
precompile-utils = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
# Frontier
|
||||
precompile-utils = { workspace = true, features = ["testing"] }
|
||||
|
||||
# Substrate
|
||||
pallet-balances = { workspace = true, features = ["insecure_zero_ed", "std"] }
|
||||
pallet-timestamp = { workspace = true, features = ["std"] }
|
||||
scale-info = { workspace = true, features = ["derive", "std"] }
|
||||
sp-io = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"fp-evm/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"pallet-conviction-voting/std",
|
||||
"pallet-evm/std",
|
||||
"parity-scale-codec/std",
|
||||
"precompile-utils/std",
|
||||
"sp-runtime/std",
|
||||
"sp-std/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"sp-runtime/runtime-benchmarks",
|
||||
]
|
||||
355
operator/precompiles/conviction-voting/ConvictionVoting.sol
Normal file
355
operator/precompiles/conviction-voting/ConvictionVoting.sol
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
pragma solidity >=0.8.3;
|
||||
|
||||
/// @dev The Conviction Voting contract's address.
|
||||
address constant CONVICTION_VOTING_ADDRESS = 0x0000000000000000000000000000000000000812;
|
||||
|
||||
/// @dev The Conviction Voting contract's instance.
|
||||
ConvictionVoting constant CONVICTION_VOTING_CONTRACT = ConvictionVoting(
|
||||
CONVICTION_VOTING_ADDRESS
|
||||
);
|
||||
|
||||
/// @author The Moonbeam Team
|
||||
/// @title Pallet Conviction Voting Interface
|
||||
/// @title The interface through which solidity contracts will interact with the Conviction Voting pallet
|
||||
/// @custom:address 0x0000000000000000000000000000000000000812
|
||||
interface ConvictionVoting {
|
||||
/// @dev Defines the conviction multiplier type.
|
||||
/// The values start at `0` and are represented as `uint8`.
|
||||
/// None => 0.1x votes, unlocked.
|
||||
/// Locked1x => 1x votes, locked for an enactment period following a successful vote.
|
||||
/// Locked2x => 2x votes, locked for 2x enactment periods following a successful vote
|
||||
/// Locked3x => 3x votes, locked for 4x...
|
||||
/// Locked4x => 4x votes, locked for 8x...,
|
||||
/// Locked5x => 5x votes, locked for 16x...
|
||||
/// Locked6x => 6x votes, locked for 32x...
|
||||
enum Conviction {
|
||||
None,
|
||||
Locked1x,
|
||||
Locked2x,
|
||||
Locked3x,
|
||||
Locked4x,
|
||||
Locked5x,
|
||||
Locked6x
|
||||
}
|
||||
|
||||
/// @dev Defines the class lock for an account.
|
||||
struct ClassLock {
|
||||
/// The track of this lock.
|
||||
uint16 trackId;
|
||||
/// The amount locked.
|
||||
uint256 amount;
|
||||
}
|
||||
|
||||
/// @dev Defines the voting information for an account and track,
|
||||
struct VotingFor {
|
||||
/// If the voting type is `Casting`, if `true` then `casting` field is significant.
|
||||
bool isCasting;
|
||||
/// If the voting type is `Delegating`, if `true` then `delegating` field is significant.
|
||||
bool isDelegating;
|
||||
/// Defines the voting information when `isCasting` is true.
|
||||
Casting casting;
|
||||
/// Defines the voting information when `isDelegating` is true.
|
||||
Delegating delegating;
|
||||
}
|
||||
|
||||
/// @dev Defines the casting vote type from an account.
|
||||
struct Casting {
|
||||
/// The votes registered.
|
||||
PollAccountVote[] votes;
|
||||
/// The delegation info.
|
||||
Delegations delegations;
|
||||
/// Any prior lock information.
|
||||
PriorLock prior;
|
||||
}
|
||||
|
||||
/// @dev Defines the delegating vote type from an account.
|
||||
struct Delegating {
|
||||
/// The delegated balance.
|
||||
uint256 balance;
|
||||
/// The deletegate account
|
||||
address target;
|
||||
/// The conviction type for the vote.
|
||||
Conviction conviction;
|
||||
/// The delegation info.
|
||||
Delegations delegations;
|
||||
/// Any prior lock information.
|
||||
PriorLock prior;
|
||||
}
|
||||
|
||||
/// @dev Defines the vote towards a poll from an account.
|
||||
struct PollAccountVote {
|
||||
/// The index of the poll.
|
||||
uint32 pollIndex;
|
||||
/// The vote registered for the poll from an account.
|
||||
AccountVote accountVote;
|
||||
}
|
||||
|
||||
/// @dev Defines the vote from an account.
|
||||
struct AccountVote {
|
||||
/// If `true` then the vote is a Standard vote and `standard` field is significant.
|
||||
bool isStandard;
|
||||
/// If `true` then the vote is a Split vote and `split` field is significant.
|
||||
bool isSplit;
|
||||
/// If `true` then the vote is a SplitAbstrain vote and `splitAbstain` field is significant.
|
||||
bool isSplitAbstain;
|
||||
/// Defines the standard vote, if `isStandard` is `true`.
|
||||
StandardVote standard;
|
||||
/// Defines the split vote, if `isSplit` is `true`.
|
||||
SplitVote split;
|
||||
/// Defines the split-abstain vote, if `isSplitAbstrain` is `true`.
|
||||
SplitAbstainVote splitAbstain;
|
||||
}
|
||||
|
||||
/// @dev Defines the standard vote.
|
||||
struct StandardVote {
|
||||
/// The vote information.
|
||||
Vote vote;
|
||||
/// The locked balance for the vote.
|
||||
uint256 balance;
|
||||
}
|
||||
|
||||
/// @dev Defines the vote parameters for a standard vote.
|
||||
struct Vote {
|
||||
/// `true` if the vote is an aye.
|
||||
bool aye;
|
||||
/// The conviction type for the vote.
|
||||
Conviction conviction;
|
||||
}
|
||||
|
||||
/// @dev Defines the standard vote.
|
||||
struct SplitVote {
|
||||
/// The amount locked towards aye.
|
||||
uint256 aye;
|
||||
/// The amount locked towards nay.
|
||||
uint256 nay;
|
||||
}
|
||||
|
||||
/// @dev Defines the standard vote.
|
||||
struct SplitAbstainVote {
|
||||
/// The amount locked towards aye.
|
||||
uint256 aye;
|
||||
/// The amount locked towards nay.
|
||||
uint256 nay;
|
||||
/// The amount locked towards abstain.
|
||||
uint256 abstain;
|
||||
}
|
||||
|
||||
/// @dev Defines the delegations for a vote.
|
||||
struct Delegations {
|
||||
/// Total number of votes.
|
||||
uint256 votes;
|
||||
/// Total capital locked.
|
||||
uint256 capital;
|
||||
}
|
||||
|
||||
/// @dev Defines any prior lock for a vote.
|
||||
struct PriorLock {
|
||||
/// Amount of balance locked.
|
||||
uint256 balance;
|
||||
}
|
||||
|
||||
/// @dev Retrieve votings for a given account and track.
|
||||
/// @custom:selector 501447ee
|
||||
/// @param who The requested account
|
||||
/// @param trackId The requested track
|
||||
function votingFor(
|
||||
address who,
|
||||
uint16 trackId
|
||||
) external view returns (VotingFor memory);
|
||||
|
||||
/// @dev Retrieve class locks for a given account.
|
||||
/// @custom:selector 7ae8ac92
|
||||
/// @param who The requested account
|
||||
function classLocksFor(
|
||||
address who
|
||||
) external view returns (ClassLock[] memory);
|
||||
|
||||
/// @dev Vote yes in a poll.
|
||||
/// @custom:selector da9df518
|
||||
/// @param pollIndex Index of poll
|
||||
/// @param voteAmount Balance locked for vote
|
||||
/// @param conviction Conviction multiplier for length of vote lock
|
||||
function voteYes(
|
||||
uint32 pollIndex,
|
||||
uint256 voteAmount,
|
||||
Conviction conviction
|
||||
) external;
|
||||
|
||||
/// @dev Vote no in a poll.
|
||||
/// @custom:selector cc600eba
|
||||
/// @param pollIndex Index of poll
|
||||
/// @param voteAmount Balance locked for vote
|
||||
/// @param conviction Conviction multiplier for length of vote lock
|
||||
function voteNo(
|
||||
uint32 pollIndex,
|
||||
uint256 voteAmount,
|
||||
Conviction conviction
|
||||
) external;
|
||||
|
||||
/// @dev Vote split in a poll.
|
||||
/// @custom:selector dd6c52a4
|
||||
/// @param pollIndex Index of poll
|
||||
/// @param aye Balance locked for aye vote
|
||||
/// @param nay Balance locked for nay vote
|
||||
function voteSplit(uint32 pollIndex, uint256 aye, uint256 nay) external;
|
||||
|
||||
/// @dev Vote split abstain in a poll.
|
||||
/// @custom:selector 52004540
|
||||
/// @param pollIndex Index of poll
|
||||
/// @param aye Balance locked for aye vote
|
||||
/// @param nay Balance locked for nay vote
|
||||
/// @param abstain Balance locked for abstain vote (support)
|
||||
function voteSplitAbstain(
|
||||
uint32 pollIndex,
|
||||
uint256 aye,
|
||||
uint256 nay,
|
||||
uint256 abstain
|
||||
) external;
|
||||
|
||||
/// @dev Remove vote in poll
|
||||
/// @custom:selector 79cae220
|
||||
/// @param pollIndex Index of the poll
|
||||
function removeVote(uint32 pollIndex) external;
|
||||
|
||||
/// @dev Remove vote in poll for track
|
||||
/// @custom:selector cc3aee1a
|
||||
/// @param pollIndex Index of the poll
|
||||
/// @param trackId Id of the track
|
||||
function removeVoteForTrack(uint32 pollIndex, uint16 trackId) external;
|
||||
|
||||
/// @dev Remove vote in poll for other voter
|
||||
/// @custom:selector cbcb9276
|
||||
//// @param target The voter to have vote removed. The removed vote must already be expired.
|
||||
/// @param trackId The trackId
|
||||
/// @param pollIndex the poll index
|
||||
function removeOtherVote(
|
||||
address target,
|
||||
uint16 trackId,
|
||||
uint32 pollIndex
|
||||
) external;
|
||||
|
||||
/// @dev Delegate to a representative for the vote trackId
|
||||
/// @custom:selector 681750e8
|
||||
/// @param trackId The trackId
|
||||
/// @param representative The representative for the trackId
|
||||
/// @param conviction The conviction multiplier
|
||||
/// @param amount delegated to representative for this vote trackId
|
||||
function delegate(
|
||||
uint16 trackId,
|
||||
address representative,
|
||||
Conviction conviction,
|
||||
uint256 amount
|
||||
) external;
|
||||
|
||||
/// @dev Undelegate for the trackId
|
||||
/// @custom:selector 98be4094
|
||||
/// @param trackId The trackId
|
||||
function undelegate(uint16 trackId) external;
|
||||
|
||||
/// @dev Unlock tokens locked for trackId
|
||||
/// @custom:selector 4259d98c
|
||||
/// @param trackId The trackId
|
||||
/// @param target The target address
|
||||
function unlock(uint16 trackId, address target) external;
|
||||
|
||||
/// @dev An account made a vote in a poll.
|
||||
/// @custom:selector 3839f7832b2a6263aa1fd5040f37d10fd4f9e9c4a9ef07ec384cb1cef9fb4c0e
|
||||
/// @param pollIndex uint32 Index of the poll.
|
||||
/// @param voter address Address of the voter.
|
||||
/// @param aye bool Is it a vote for or against the poll.
|
||||
/// @param voteAmount uint256 Amount used to vote.
|
||||
/// @param conviction uint8 Conviction of the vote.
|
||||
event Voted(
|
||||
uint32 indexed pollIndex,
|
||||
address voter,
|
||||
bool aye,
|
||||
uint256 voteAmount,
|
||||
uint8 conviction
|
||||
);
|
||||
|
||||
/// @dev An account made a split vote in a poll.
|
||||
/// @custom:selector 022787093a8aa26fe59d28969068711f73e0e78ae67d9359c71058b6a21f7ef0
|
||||
/// @param pollIndex uint32 Index of the poll.
|
||||
/// @param voter address Address of the voter.
|
||||
/// @param aye uint256 Amount for aye vote.
|
||||
/// @param nay uint256 Amount for nay vote.
|
||||
event VoteSplit(
|
||||
uint32 indexed pollIndex,
|
||||
address voter,
|
||||
uint256 aye,
|
||||
uint256 nay
|
||||
);
|
||||
|
||||
/// @dev An account made a split abstain vote in a poll.
|
||||
/// @custom:selector 476e687ab5e38fc714552f3acc083d7d83ccaa12ea11dd5f3393478d158c6fd4
|
||||
/// @param pollIndex uint32 Index of the poll.
|
||||
/// @param voter address Address of the voter.
|
||||
/// @param aye uint256 Amount for aye vote.
|
||||
/// @param nay uint256 Amount for nay vote.
|
||||
/// @param abstain uint256 Amount for abstained.
|
||||
event VoteSplitAbstained(
|
||||
uint32 indexed pollIndex,
|
||||
address voter,
|
||||
uint256 aye,
|
||||
uint256 nay,
|
||||
uint256 abstain
|
||||
);
|
||||
|
||||
/// @dev An account removed its vote from an ongoing poll.
|
||||
/// @custom:selector 49fc1dd929f126e1d88cbb9c135625e30c2deba291adeea4740e446098b9957b
|
||||
/// @param pollIndex uint32 Index of the poll.
|
||||
/// @param voter address Address of the voter.
|
||||
event VoteRemoved(uint32 indexed pollIndex, address voter);
|
||||
|
||||
/// @dev An account removed its vote from an ongoing poll.
|
||||
/// @custom:selector 49fc1dd929f126e1d88cbb9c135625e30c2deba291adeea4740e446098b9957b
|
||||
/// @param pollIndex uint32 Index of the poll.
|
||||
/// @param trackId uint32 TrackId of the poll.
|
||||
/// @param voter address Address of the voter.
|
||||
event VoteRemovedForTrack(
|
||||
uint32 indexed pollIndex,
|
||||
uint16 trackId,
|
||||
address voter
|
||||
);
|
||||
|
||||
/// @dev An account removed a vote from a poll.
|
||||
/// @custom:selector c1d068675720ab00d0c8792a0cbc7e198c0d2202111f0280f039f2c09c50491b
|
||||
/// @param pollIndex uint32 Index of the poll.
|
||||
/// @param caller address Address of the origin caller.
|
||||
/// @param target address Address of the address which's vote is being removed.
|
||||
/// @param trackId uint16 The trackId.
|
||||
event VoteRemovedOther(
|
||||
uint32 indexed pollIndex,
|
||||
address caller,
|
||||
address target,
|
||||
uint16 trackId
|
||||
);
|
||||
|
||||
/// @dev An account delegated for the given trackId.
|
||||
/// @custom:selector 6cc151d547592e227b1e85a264ac3699c6f1014112b08bb3832de1f23b9c66db
|
||||
/// @param trackId uint16 The trackId.
|
||||
/// @param from address Address of the caller.
|
||||
/// @param to address Address of the representative.
|
||||
/// @param delegatedAmount uint256 Amount being delegated.
|
||||
/// @param conviction uint8 Conviction being delegated.
|
||||
event Delegated(
|
||||
uint16 indexed trackId,
|
||||
address from,
|
||||
address to,
|
||||
uint256 delegatedAmount,
|
||||
uint8 conviction
|
||||
);
|
||||
|
||||
/// @dev An account undelegated for the given trackId.
|
||||
/// @custom:selector 1053303328f6db14014ccced6297bcad2b3897157ce46070711ab995a05dfa14
|
||||
/// @param trackId uint16 The trackId.
|
||||
/// @param caller address Address of the caller.
|
||||
event Undelegated(uint16 indexed trackId, address caller);
|
||||
|
||||
/// @dev An account called to unlock tokens for the given trackId.
|
||||
/// @custom:selector dcf72fa65ca7fb720b9ccc8ee28e0188edc3d943115124cdd4086c49f836a128
|
||||
/// @param trackId uint16 The trackId.
|
||||
/// @param caller address Address of the caller.
|
||||
event Unlocked(uint16 indexed trackId, address caller);
|
||||
}
|
||||
768
operator/precompiles/conviction-voting/src/lib.rs
Normal file
768
operator/precompiles/conviction-voting/src/lib.rs
Normal file
|
|
@ -0,0 +1,768 @@
|
|||
// 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/>.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use fp_evm::PrecompileHandle;
|
||||
use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo};
|
||||
use frame_support::traits::{Currency, Polling};
|
||||
use frame_system::pallet_prelude::BlockNumberFor;
|
||||
use pallet_conviction_voting::Call as ConvictionVotingCall;
|
||||
use pallet_conviction_voting::{
|
||||
AccountVote, Casting, ClassLocksFor, Conviction, Delegating, Tally, TallyOf, Vote, Voting,
|
||||
VotingFor,
|
||||
};
|
||||
use pallet_evm::{AddressMapping, Log};
|
||||
use precompile_utils::prelude::*;
|
||||
use sp_core::{Get, MaxEncodedLen, H160, H256, U256};
|
||||
use sp_runtime::traits::{Dispatchable, StaticLookup};
|
||||
use sp_std::marker::PhantomData;
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
type BalanceOf<Runtime> = <<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
|
||||
<Runtime as frame_system::Config>::AccountId,
|
||||
>>::Balance;
|
||||
type IndexOf<Runtime> = <<Runtime as pallet_conviction_voting::Config>::Polls as Polling<
|
||||
Tally<
|
||||
<<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
|
||||
<Runtime as frame_system::Config>::AccountId,
|
||||
>>::Balance,
|
||||
<Runtime as pallet_conviction_voting::Config>::MaxTurnout,
|
||||
>,
|
||||
>>::Index;
|
||||
type ClassOf<Runtime> = <<Runtime as pallet_conviction_voting::Config>::Polls as Polling<
|
||||
Tally<
|
||||
<<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
|
||||
<Runtime as frame_system::Config>::AccountId,
|
||||
>>::Balance,
|
||||
<Runtime as pallet_conviction_voting::Config>::MaxTurnout,
|
||||
>,
|
||||
>>::Class;
|
||||
type VotingOf<Runtime> = Voting<
|
||||
BalanceOf<Runtime>,
|
||||
<Runtime as frame_system::Config>::AccountId,
|
||||
BlockNumberFor<Runtime>,
|
||||
<<Runtime as pallet_conviction_voting::Config>::Polls as Polling<TallyOf<Runtime>>>::Index,
|
||||
<Runtime as pallet_conviction_voting::Config>::MaxVotes,
|
||||
>;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// Solidity selector of the Vote log, which is the Keccak of the Log signature.
|
||||
pub(crate) const SELECTOR_LOG_VOTED: [u8; 32] =
|
||||
keccak256!("Voted(uint32,address,bool,uint256,uint8)");
|
||||
|
||||
/// Solidity selector of the Vote Split log, which is the Keccak of the Log signature.
|
||||
pub(crate) const SELECTOR_LOG_VOTE_SPLIT: [u8; 32] =
|
||||
keccak256!("VoteSplit(uint32,address,uint256,uint256)");
|
||||
|
||||
/// Solidity selector of the Vote Split Abstained log, which is the Keccak of the Log signature.
|
||||
pub(crate) const SELECTOR_LOG_VOTE_SPLIT_ABSTAINED: [u8; 32] =
|
||||
keccak256!("VoteSplitAbstained(uint32,address,uint256,uint256,uint256)");
|
||||
|
||||
/// Solidity selector of the VoteRemove log, which is the Keccak of the Log signature.
|
||||
pub(crate) const SELECTOR_LOG_VOTE_REMOVED: [u8; 32] = keccak256!("VoteRemoved(uint32,address)");
|
||||
|
||||
/// Solidity selector of the SomeVoteRemove log, which is the Keccak of the Log signature.
|
||||
pub(crate) const SELECTOR_LOG_VOTE_REMOVED_FOR_TRACK: [u8; 32] =
|
||||
keccak256!("VoteRemovedForTrack(uint32,uint16,address)");
|
||||
|
||||
/// Solidity selector of the VoteRemoveOther log, which is the Keccak of the Log signature.
|
||||
pub(crate) const SELECTOR_LOG_VOTE_REMOVED_OTHER: [u8; 32] =
|
||||
keccak256!("VoteRemovedOther(uint32,address,address,uint16)");
|
||||
|
||||
/// Solidity selector of the Delegate log, which is the Keccak of the Log signature.
|
||||
pub(crate) const SELECTOR_LOG_DELEGATED: [u8; 32] =
|
||||
keccak256!("Delegated(uint16,address,address,uint256,uint8)");
|
||||
|
||||
/// Solidity selector of the Undelegate log, which is the Keccak of the Log signature.
|
||||
pub(crate) const SELECTOR_LOG_UNDELEGATED: [u8; 32] = keccak256!("Undelegated(uint16,address)");
|
||||
|
||||
/// Solidity selector of the Unlock log, which is the Keccak of the Log signature.
|
||||
pub(crate) const SELECTOR_LOG_UNLOCKED: [u8; 32] = keccak256!("Unlocked(uint16,address)");
|
||||
|
||||
/// A precompile to wrap the functionality from pallet-conviction-voting.
|
||||
pub struct ConvictionVotingPrecompile<Runtime>(PhantomData<Runtime>);
|
||||
|
||||
#[precompile_utils::precompile]
|
||||
impl<Runtime> ConvictionVotingPrecompile<Runtime>
|
||||
where
|
||||
Runtime: pallet_conviction_voting::Config + pallet_evm::Config + frame_system::Config,
|
||||
BalanceOf<Runtime>: TryFrom<U256> + Into<U256>,
|
||||
<Runtime as frame_system::Config>::RuntimeCall:
|
||||
Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
|
||||
<<Runtime as frame_system::Config>::RuntimeCall as Dispatchable>::RuntimeOrigin:
|
||||
From<Option<Runtime::AccountId>>,
|
||||
Runtime::AccountId: Into<H160>,
|
||||
<Runtime as frame_system::Config>::RuntimeCall: From<ConvictionVotingCall<Runtime>>,
|
||||
IndexOf<Runtime>: TryFrom<u32> + TryInto<u32>,
|
||||
ClassOf<Runtime>: TryFrom<u16> + TryInto<u16>,
|
||||
<Runtime as pallet_conviction_voting::Config>::Polls: Polling<
|
||||
Tally<
|
||||
<<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
|
||||
<Runtime as frame_system::Config>::AccountId,
|
||||
>>::Balance,
|
||||
<Runtime as pallet_conviction_voting::Config>::MaxTurnout,
|
||||
>,
|
||||
>,
|
||||
<Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
|
||||
{
|
||||
/// Internal helper function for vote* extrinsics exposed in this precompile.
|
||||
fn vote(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
poll_index: u32,
|
||||
vote: AccountVote<U256>,
|
||||
) -> EvmResult {
|
||||
let caller = handle.context().caller;
|
||||
let (poll_index, vote, event) = Self::log_vote_event(handle, poll_index, vote)?;
|
||||
|
||||
let origin = Runtime::AddressMapping::into_account_id(caller);
|
||||
let call = ConvictionVotingCall::<Runtime>::vote { poll_index, vote }.into();
|
||||
|
||||
<RuntimeHelper<Runtime>>::try_dispatch(handle, Some(origin).into(), call, 0)?;
|
||||
|
||||
event.record(handle)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Vote yes in a poll.
|
||||
///
|
||||
/// Parameters:
|
||||
/// * poll_index: Index of poll
|
||||
/// * vote_amount: Balance locked for vote
|
||||
/// * conviction: Conviction multiplier for length of vote lock
|
||||
#[precompile::public("voteYes(uint32,uint256,uint8)")]
|
||||
fn vote_yes(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
poll_index: u32,
|
||||
vote_amount: U256,
|
||||
conviction: u8,
|
||||
) -> EvmResult {
|
||||
Self::vote(
|
||||
handle,
|
||||
poll_index,
|
||||
AccountVote::Standard {
|
||||
vote: Vote {
|
||||
aye: true,
|
||||
conviction: Self::u8_to_conviction(conviction).in_field("conviction")?,
|
||||
},
|
||||
balance: vote_amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Vote no in a poll.
|
||||
///
|
||||
/// Parameters:
|
||||
/// * poll_index: Index of poll
|
||||
/// * vote_amount: Balance locked for vote
|
||||
/// * conviction: Conviction multiplier for length of vote lock
|
||||
#[precompile::public("voteNo(uint32,uint256,uint8)")]
|
||||
fn vote_no(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
poll_index: u32,
|
||||
vote_amount: U256,
|
||||
conviction: u8,
|
||||
) -> EvmResult {
|
||||
Self::vote(
|
||||
handle,
|
||||
poll_index,
|
||||
AccountVote::Standard {
|
||||
vote: Vote {
|
||||
aye: false,
|
||||
conviction: Self::u8_to_conviction(conviction).in_field("conviction")?,
|
||||
},
|
||||
balance: vote_amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Vote split in a poll.
|
||||
///
|
||||
/// Parameters:
|
||||
/// * poll_index: Index of poll
|
||||
/// * aye: Balance locked for aye vote
|
||||
/// * nay: Balance locked for nay vote
|
||||
#[precompile::public("voteSplit(uint32,uint256,uint256)")]
|
||||
fn vote_split(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
poll_index: u32,
|
||||
aye: U256,
|
||||
nay: U256,
|
||||
) -> EvmResult {
|
||||
Self::vote(handle, poll_index, AccountVote::Split { aye, nay })
|
||||
}
|
||||
|
||||
/// Vote split in a poll.
|
||||
///
|
||||
/// Parameters:
|
||||
/// * poll_index: Index of poll
|
||||
/// * aye: Balance locked for aye vote
|
||||
/// * nay: Balance locked for nay vote
|
||||
#[precompile::public("voteSplitAbstain(uint32,uint256,uint256,uint256)")]
|
||||
fn vote_split_abstain(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
poll_index: u32,
|
||||
aye: U256,
|
||||
nay: U256,
|
||||
abstain: U256,
|
||||
) -> EvmResult {
|
||||
Self::vote(
|
||||
handle,
|
||||
poll_index,
|
||||
AccountVote::SplitAbstain { aye, nay, abstain },
|
||||
)
|
||||
}
|
||||
|
||||
#[precompile::public("removeVote(uint32)")]
|
||||
fn remove_vote(handle: &mut impl PrecompileHandle, poll_index: u32) -> EvmResult {
|
||||
Self::rm_vote(handle, poll_index, None)
|
||||
}
|
||||
|
||||
#[precompile::public("removeVoteForTrack(uint32,uint16)")]
|
||||
fn remove_vote_for_track(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
poll_index: u32,
|
||||
track_id: u16,
|
||||
) -> EvmResult {
|
||||
Self::rm_vote(handle, poll_index, Some(track_id))
|
||||
}
|
||||
|
||||
/// Helper function for common code between remove_vote and remove_some_vote
|
||||
fn rm_vote(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
poll_index: u32,
|
||||
maybe_track_id: Option<u16>,
|
||||
) -> EvmResult {
|
||||
let caller = handle.context().caller;
|
||||
let index = Self::u32_to_index(poll_index).in_field("pollIndex")?;
|
||||
let (event, class) = if let Some(track_id) = maybe_track_id {
|
||||
log::trace!(
|
||||
target: "conviction-voting-precompile",
|
||||
"Removing vote from poll {:?} for track {:?}",
|
||||
index,
|
||||
track_id,
|
||||
);
|
||||
(
|
||||
log2(
|
||||
handle.context().address,
|
||||
SELECTOR_LOG_VOTE_REMOVED_FOR_TRACK,
|
||||
H256::from_low_u64_be(poll_index as u64),
|
||||
solidity::encode_event_data((track_id, Address(caller))),
|
||||
),
|
||||
Some(Self::u16_to_track_id(track_id).in_field("trackId")?),
|
||||
)
|
||||
} else {
|
||||
log::trace!(
|
||||
target: "conviction-voting-precompile",
|
||||
"Removing vote from poll {:?}",
|
||||
index,
|
||||
);
|
||||
(
|
||||
log2(
|
||||
handle.context().address,
|
||||
SELECTOR_LOG_VOTE_REMOVED,
|
||||
H256::from_low_u64_be(poll_index as u64),
|
||||
solidity::encode_event_data(Address(caller)),
|
||||
),
|
||||
None,
|
||||
)
|
||||
};
|
||||
|
||||
let origin = Runtime::AddressMapping::into_account_id(caller);
|
||||
let call = ConvictionVotingCall::<Runtime>::remove_vote { class, index };
|
||||
|
||||
RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
|
||||
|
||||
event.record(handle)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[precompile::public("removeOtherVote(address,uint16,uint32)")]
|
||||
fn remove_other_vote(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
target: Address,
|
||||
track_id: u16,
|
||||
poll_index: u32,
|
||||
) -> EvmResult {
|
||||
let caller = handle.context().caller;
|
||||
|
||||
let event = log2(
|
||||
handle.context().address,
|
||||
SELECTOR_LOG_VOTE_REMOVED_OTHER,
|
||||
H256::from_low_u64_be(poll_index as u64), // poll index,
|
||||
solidity::encode_event_data((Address(caller), target, track_id)),
|
||||
);
|
||||
handle.record_log_costs(&[&event])?;
|
||||
|
||||
let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
|
||||
let index = Self::u32_to_index(poll_index).in_field("pollIndex")?;
|
||||
|
||||
let target = Runtime::AddressMapping::into_account_id(target.into());
|
||||
let target: <Runtime::Lookup as StaticLookup>::Source =
|
||||
Runtime::Lookup::unlookup(target.clone());
|
||||
|
||||
log::trace!(
|
||||
target: "conviction-voting-precompile",
|
||||
"Removing other vote from poll {:?}",
|
||||
index
|
||||
);
|
||||
|
||||
let origin = Runtime::AddressMapping::into_account_id(caller);
|
||||
let call = ConvictionVotingCall::<Runtime>::remove_other_vote {
|
||||
target,
|
||||
class,
|
||||
index,
|
||||
};
|
||||
|
||||
RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
|
||||
|
||||
event.record(handle)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[precompile::public("delegate(uint16,address,uint8,uint256)")]
|
||||
fn delegate(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
track_id: u16,
|
||||
representative: Address,
|
||||
conviction: u8,
|
||||
amount: U256,
|
||||
) -> EvmResult {
|
||||
let caller = handle.context().caller;
|
||||
|
||||
let event = log2(
|
||||
handle.context().address,
|
||||
SELECTOR_LOG_DELEGATED,
|
||||
H256::from_low_u64_be(track_id as u64), // track id,
|
||||
solidity::encode_event_data((Address(caller), representative, amount, conviction)),
|
||||
);
|
||||
handle.record_log_costs(&[&event])?;
|
||||
|
||||
let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
|
||||
let amount = Self::u256_to_amount(amount).in_field("amount")?;
|
||||
let conviction = Self::u8_to_conviction(conviction).in_field("conviction")?;
|
||||
|
||||
log::trace!(target: "conviction-voting-precompile",
|
||||
"Delegating vote to {:?} with balance {:?} and conviction {:?}",
|
||||
representative, amount, conviction
|
||||
);
|
||||
|
||||
let representative = Runtime::AddressMapping::into_account_id(representative.into());
|
||||
let to: <Runtime::Lookup as StaticLookup>::Source =
|
||||
Runtime::Lookup::unlookup(representative.clone());
|
||||
let origin = Runtime::AddressMapping::into_account_id(caller);
|
||||
let call = ConvictionVotingCall::<Runtime>::delegate {
|
||||
class,
|
||||
to,
|
||||
conviction,
|
||||
balance: amount,
|
||||
};
|
||||
|
||||
RuntimeHelper::<Runtime>::try_dispatch(
|
||||
handle,
|
||||
Some(origin).into(),
|
||||
call,
|
||||
SYSTEM_ACCOUNT_SIZE,
|
||||
)?;
|
||||
|
||||
event.record(handle)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[precompile::public("undelegate(uint16)")]
|
||||
fn undelegate(handle: &mut impl PrecompileHandle, track_id: u16) -> EvmResult {
|
||||
let caller = handle.context().caller;
|
||||
|
||||
let event = log2(
|
||||
handle.context().address,
|
||||
SELECTOR_LOG_UNDELEGATED,
|
||||
H256::from_low_u64_be(track_id as u64), // track id,
|
||||
solidity::encode_event_data(Address(caller)),
|
||||
);
|
||||
handle.record_log_costs(&[&event])?;
|
||||
|
||||
let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
|
||||
let origin = Runtime::AddressMapping::into_account_id(caller);
|
||||
let call = ConvictionVotingCall::<Runtime>::undelegate { class };
|
||||
|
||||
RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
|
||||
|
||||
event.record(handle)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[precompile::public("unlock(uint16,address)")]
|
||||
fn unlock(handle: &mut impl PrecompileHandle, track_id: u16, target: Address) -> EvmResult {
|
||||
let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
|
||||
|
||||
let event = log2(
|
||||
handle.context().address,
|
||||
SELECTOR_LOG_UNLOCKED,
|
||||
H256::from_low_u64_be(track_id as u64), // track id,
|
||||
solidity::encode_event_data(target),
|
||||
);
|
||||
handle.record_log_costs(&[&event])?;
|
||||
|
||||
let target: H160 = target.into();
|
||||
let target = Runtime::AddressMapping::into_account_id(target);
|
||||
let target: <Runtime::Lookup as StaticLookup>::Source =
|
||||
Runtime::Lookup::unlookup(target.clone());
|
||||
|
||||
log::trace!(
|
||||
target: "conviction-voting-precompile",
|
||||
"Unlocking conviction-voting tokens for {:?}", target
|
||||
);
|
||||
|
||||
let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
|
||||
let call = ConvictionVotingCall::<Runtime>::unlock { class, target };
|
||||
|
||||
RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
|
||||
|
||||
event.record(handle)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[precompile::public("votingFor(address,uint16)")]
|
||||
#[precompile::view]
|
||||
fn voting_for(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
who: Address,
|
||||
track_id: u16,
|
||||
) -> EvmResult<OutputVotingFor> {
|
||||
// VotingFor: Twox64Concat(8) + 20 + Twox64Concat(8) + TransInfo::Id(2) + VotingOf
|
||||
handle.record_db_read::<Runtime>(38 + VotingOf::<Runtime>::max_encoded_len())?;
|
||||
|
||||
let who = Runtime::AddressMapping::into_account_id(who.into());
|
||||
let class = Self::u16_to_track_id(track_id).in_field("trackId")?;
|
||||
|
||||
let voting = <VotingFor<Runtime>>::get(&who, &class);
|
||||
|
||||
Ok(Self::voting_to_output(voting)?)
|
||||
}
|
||||
|
||||
#[precompile::public("classLocksFor(address)")]
|
||||
#[precompile::view]
|
||||
fn class_locks_for(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
who: Address,
|
||||
) -> EvmResult<Vec<OutputClassLock>> {
|
||||
// ClassLocksFor: Twox64Concat(8) + 20 + BoundedVec(TransInfo::Id(2) * ClassCountOf)
|
||||
handle.record_db_read::<Runtime>(
|
||||
28 + ((2 * frame_support::traits::ClassCountOf::<
|
||||
<Runtime as pallet_conviction_voting::Config>::Polls,
|
||||
Tally<
|
||||
<<Runtime as pallet_conviction_voting::Config>::Currency as Currency<
|
||||
<Runtime as frame_system::Config>::AccountId,
|
||||
>>::Balance,
|
||||
<Runtime as pallet_conviction_voting::Config>::MaxTurnout,
|
||||
>,
|
||||
>::get()) as usize),
|
||||
)?;
|
||||
|
||||
let who = Runtime::AddressMapping::into_account_id(who.into());
|
||||
|
||||
let class_locks_for = <ClassLocksFor<Runtime>>::get(&who);
|
||||
let mut output = Vec::new();
|
||||
for (track_id, amount) in class_locks_for {
|
||||
output.push(OutputClassLock {
|
||||
track: Self::track_id_to_u16(track_id)?,
|
||||
amount: amount.into(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn u8_to_conviction(conviction: u8) -> MayRevert<Conviction> {
|
||||
conviction
|
||||
.try_into()
|
||||
.map_err(|_| RevertReason::custom("Must be an integer between 0 and 6 included").into())
|
||||
}
|
||||
|
||||
fn u32_to_index(index: u32) -> MayRevert<IndexOf<Runtime>> {
|
||||
index
|
||||
.try_into()
|
||||
.map_err(|_| RevertReason::value_is_too_large("index type").into())
|
||||
}
|
||||
|
||||
fn u16_to_track_id(class: u16) -> MayRevert<ClassOf<Runtime>> {
|
||||
class
|
||||
.try_into()
|
||||
.map_err(|_| RevertReason::value_is_too_large("trackId type").into())
|
||||
}
|
||||
|
||||
fn track_id_to_u16(class: ClassOf<Runtime>) -> MayRevert<u16> {
|
||||
class
|
||||
.try_into()
|
||||
.map_err(|_| RevertReason::value_is_too_large("trackId type").into())
|
||||
}
|
||||
|
||||
fn u256_to_amount(value: U256) -> MayRevert<BalanceOf<Runtime>> {
|
||||
value
|
||||
.try_into()
|
||||
.map_err(|_| RevertReason::value_is_too_large("balance type").into())
|
||||
}
|
||||
|
||||
fn log_vote_event(
|
||||
handle: &mut impl PrecompileHandle,
|
||||
poll_index: u32,
|
||||
vote: AccountVote<U256>,
|
||||
) -> EvmResult<(IndexOf<Runtime>, AccountVote<BalanceOf<Runtime>>, Log)> {
|
||||
let (contract_addr, caller) = (handle.context().address, handle.context().caller);
|
||||
let (vote, event) = match vote {
|
||||
AccountVote::Standard { vote, balance } => {
|
||||
let event = log2(
|
||||
contract_addr,
|
||||
SELECTOR_LOG_VOTED,
|
||||
H256::from_low_u64_be(poll_index as u64),
|
||||
solidity::encode_event_data((
|
||||
Address(caller),
|
||||
vote.aye,
|
||||
balance,
|
||||
u8::from(vote.conviction),
|
||||
)),
|
||||
);
|
||||
(
|
||||
AccountVote::Standard {
|
||||
vote,
|
||||
balance: Self::u256_to_amount(balance).in_field("voteAmount")?,
|
||||
},
|
||||
event,
|
||||
)
|
||||
}
|
||||
AccountVote::Split { aye, nay } => {
|
||||
let event = log2(
|
||||
contract_addr,
|
||||
SELECTOR_LOG_VOTE_SPLIT,
|
||||
H256::from_low_u64_be(poll_index as u64),
|
||||
solidity::encode_event_data((Address(caller), aye, nay)),
|
||||
);
|
||||
(
|
||||
AccountVote::Split {
|
||||
aye: Self::u256_to_amount(aye).in_field("aye")?,
|
||||
nay: Self::u256_to_amount(nay).in_field("nay")?,
|
||||
},
|
||||
event,
|
||||
)
|
||||
}
|
||||
AccountVote::SplitAbstain { aye, nay, abstain } => {
|
||||
let event = log2(
|
||||
contract_addr,
|
||||
SELECTOR_LOG_VOTE_SPLIT_ABSTAINED,
|
||||
H256::from_low_u64_be(poll_index as u64),
|
||||
solidity::encode_event_data((Address(caller), aye, nay, abstain)),
|
||||
);
|
||||
(
|
||||
AccountVote::SplitAbstain {
|
||||
aye: Self::u256_to_amount(aye).in_field("aye")?,
|
||||
nay: Self::u256_to_amount(nay).in_field("nay")?,
|
||||
abstain: Self::u256_to_amount(abstain).in_field("abstain")?,
|
||||
},
|
||||
event,
|
||||
)
|
||||
}
|
||||
};
|
||||
handle.record_log_costs(&[&event])?;
|
||||
Ok((Self::u32_to_index(poll_index)?, vote, event))
|
||||
}
|
||||
|
||||
fn voting_to_output(voting: VotingOf<Runtime>) -> MayRevert<OutputVotingFor> {
|
||||
let output = match voting {
|
||||
Voting::Casting(Casting {
|
||||
votes,
|
||||
delegations,
|
||||
prior,
|
||||
}) => {
|
||||
let mut output_votes = Vec::new();
|
||||
for (poll_index, account_vote) in votes {
|
||||
let poll_index: u32 = poll_index
|
||||
.try_into()
|
||||
.map_err(|_| RevertReason::value_is_too_large("index type"))?;
|
||||
let account_vote = match account_vote {
|
||||
AccountVote::Standard { vote, balance } => OutputAccountVote {
|
||||
is_standard: true,
|
||||
standard: StandardVote {
|
||||
vote: OutputVote {
|
||||
aye: vote.aye,
|
||||
conviction: vote.conviction.into(),
|
||||
},
|
||||
balance: balance.into(),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
AccountVote::Split { aye, nay } => OutputAccountVote {
|
||||
is_split: true,
|
||||
split: SplitVote {
|
||||
aye: aye.into(),
|
||||
nay: nay.into(),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
AccountVote::SplitAbstain { aye, nay, abstain } => OutputAccountVote {
|
||||
is_split_abstain: true,
|
||||
split_abstain: SplitAbstainVote {
|
||||
aye: aye.into(),
|
||||
nay: nay.into(),
|
||||
abstain: abstain.into(),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
|
||||
output_votes.push(PollAccountVote {
|
||||
poll_index,
|
||||
account_vote,
|
||||
});
|
||||
}
|
||||
|
||||
OutputVotingFor {
|
||||
is_casting: true,
|
||||
casting: OutputCasting {
|
||||
votes: output_votes,
|
||||
delegations: Delegations {
|
||||
votes: delegations.votes.into(),
|
||||
capital: delegations.capital.into(),
|
||||
},
|
||||
prior: PriorLock {
|
||||
balance: prior.locked().into(),
|
||||
},
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
Voting::Delegating(Delegating {
|
||||
balance,
|
||||
target,
|
||||
conviction,
|
||||
delegations,
|
||||
prior,
|
||||
}) => OutputVotingFor {
|
||||
is_delegating: true,
|
||||
delegating: OutputDelegating {
|
||||
balance: balance.into(),
|
||||
target: Address(target.into()),
|
||||
conviction: conviction.into(),
|
||||
delegations: Delegations {
|
||||
votes: delegations.votes.into(),
|
||||
capital: delegations.capital.into(),
|
||||
},
|
||||
prior: PriorLock {
|
||||
balance: prior.locked().into(),
|
||||
},
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, solidity::Codec)]
|
||||
pub struct OutputClassLock {
|
||||
track: u16,
|
||||
amount: U256,
|
||||
}
|
||||
|
||||
#[derive(Default, solidity::Codec)]
|
||||
pub struct OutputVotingFor {
|
||||
is_casting: bool,
|
||||
is_delegating: bool,
|
||||
casting: OutputCasting,
|
||||
delegating: OutputDelegating,
|
||||
}
|
||||
|
||||
#[derive(Default, solidity::Codec)]
|
||||
pub struct OutputCasting {
|
||||
votes: Vec<PollAccountVote>,
|
||||
delegations: Delegations,
|
||||
prior: PriorLock,
|
||||
}
|
||||
|
||||
#[derive(Default, solidity::Codec)]
|
||||
pub struct PollAccountVote {
|
||||
poll_index: u32,
|
||||
account_vote: OutputAccountVote,
|
||||
}
|
||||
|
||||
#[derive(Default, solidity::Codec)]
|
||||
pub struct OutputDelegating {
|
||||
balance: U256,
|
||||
target: Address,
|
||||
conviction: u8,
|
||||
delegations: Delegations,
|
||||
prior: PriorLock,
|
||||
}
|
||||
|
||||
#[derive(Default, solidity::Codec)]
|
||||
pub struct OutputAccountVote {
|
||||
is_standard: bool,
|
||||
is_split: bool,
|
||||
is_split_abstain: bool,
|
||||
standard: StandardVote,
|
||||
split: SplitVote,
|
||||
split_abstain: SplitAbstainVote,
|
||||
}
|
||||
|
||||
#[derive(Default, solidity::Codec)]
|
||||
pub struct StandardVote {
|
||||
vote: OutputVote,
|
||||
balance: U256,
|
||||
}
|
||||
|
||||
#[derive(Default, solidity::Codec)]
|
||||
pub struct OutputVote {
|
||||
aye: bool,
|
||||
conviction: u8,
|
||||
}
|
||||
|
||||
#[derive(Default, solidity::Codec)]
|
||||
pub struct SplitVote {
|
||||
aye: U256,
|
||||
nay: U256,
|
||||
}
|
||||
|
||||
#[derive(Default, solidity::Codec)]
|
||||
pub struct SplitAbstainVote {
|
||||
aye: U256,
|
||||
nay: U256,
|
||||
abstain: U256,
|
||||
}
|
||||
|
||||
#[derive(Default, solidity::Codec)]
|
||||
pub struct Delegations {
|
||||
votes: U256,
|
||||
capital: U256,
|
||||
}
|
||||
|
||||
#[derive(Default, solidity::Codec)]
|
||||
pub struct PriorLock {
|
||||
balance: U256,
|
||||
}
|
||||
318
operator/precompiles/conviction-voting/src/mock.rs
Normal file
318
operator/precompiles/conviction-voting/src/mock.rs
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
// 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::{
|
||||
construct_runtime, parameter_types,
|
||||
traits::{Everything, PollStatus, Polling, TotalIssuanceOf},
|
||||
weights::Weight,
|
||||
};
|
||||
use pallet_conviction_voting::TallyOf;
|
||||
use pallet_evm::{EnsureAddressNever, EnsureAddressRoot, FrameSystemAccountProvider};
|
||||
use precompile_utils::{precompile_set::*, testing::MockAccount};
|
||||
use sp_core::{H256, U256};
|
||||
use sp_runtime::{
|
||||
traits::{BlakeTwo256, ConstU32, ConstU64, IdentityLookup},
|
||||
BuildStorage, DispatchError, Perbill,
|
||||
};
|
||||
use sp_std::collections::btree_map::BTreeMap;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
use frame_support::traits::VoteTally;
|
||||
|
||||
pub type AccountId = MockAccount;
|
||||
pub type Balance = u128;
|
||||
|
||||
type Block = frame_system::mocking::MockBlock<Runtime>;
|
||||
|
||||
construct_runtime!(
|
||||
pub enum Runtime {
|
||||
System: frame_system,
|
||||
Balances: pallet_balances,
|
||||
Evm: pallet_evm,
|
||||
Timestamp: pallet_timestamp,
|
||||
ConvictionVoting: pallet_conviction_voting,
|
||||
}
|
||||
);
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: u32 = 250;
|
||||
pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 1);
|
||||
pub const MaximumBlockLength: u32 = 2 * 1024;
|
||||
pub const AvailableBlockRatio: Perbill = Perbill::one();
|
||||
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<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 = [u8; 4];
|
||||
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 = ();
|
||||
}
|
||||
|
||||
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)
|
||||
};
|
||||
}
|
||||
|
||||
pub type Precompiles<R> =
|
||||
PrecompileSetBuilder<R, (PrecompileAt<AddressU64<1>, ConvictionVotingPrecompile<R>>,)>;
|
||||
|
||||
pub type PCall = ConvictionVotingPrecompileCall<Runtime>;
|
||||
|
||||
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<Runtime>;
|
||||
type PrecompilesValue = PrecompilesValue;
|
||||
type ChainId = ();
|
||||
type OnChargeTransaction = ();
|
||||
type BlockGasLimit = BlockGasLimit;
|
||||
type BlockHashMapping = pallet_evm::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 = ();
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum TestPollState {
|
||||
Ongoing(TallyOf<Runtime>, u8),
|
||||
Completed(u64, bool),
|
||||
}
|
||||
use TestPollState::*;
|
||||
|
||||
parameter_types! {
|
||||
pub static Polls: BTreeMap<u8, TestPollState> = vec![
|
||||
(1, Completed(1, true)),
|
||||
(2, Completed(2, false)),
|
||||
(3, Ongoing(Tally::from_parts(0, 0, 0), 0)),
|
||||
].into_iter().collect();
|
||||
}
|
||||
|
||||
pub struct TestPolls;
|
||||
impl Polling<TallyOf<Runtime>> for TestPolls {
|
||||
type Index = u8;
|
||||
type Votes = u128;
|
||||
type Moment = u64;
|
||||
type Class = u8;
|
||||
fn classes() -> Vec<u8> {
|
||||
vec![0, 1, 2]
|
||||
}
|
||||
fn as_ongoing(index: u8) -> Option<(TallyOf<Runtime>, Self::Class)> {
|
||||
Polls::get().remove(&index).and_then(|x| {
|
||||
if let TestPollState::Ongoing(t, c) = x {
|
||||
Some((t, c))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
fn access_poll<R>(
|
||||
index: Self::Index,
|
||||
f: impl FnOnce(PollStatus<&mut TallyOf<Runtime>, u64, u8>) -> R,
|
||||
) -> R {
|
||||
let mut polls = Polls::get();
|
||||
let entry = polls.get_mut(&index);
|
||||
let r = match entry {
|
||||
Some(Ongoing(ref mut tally_mut_ref, class)) => {
|
||||
f(PollStatus::Ongoing(tally_mut_ref, *class))
|
||||
}
|
||||
Some(Completed(when, succeeded)) => f(PollStatus::Completed(
|
||||
(*when).try_into().unwrap(),
|
||||
*succeeded,
|
||||
)),
|
||||
None => f(PollStatus::None),
|
||||
};
|
||||
Polls::set(polls);
|
||||
r
|
||||
}
|
||||
fn try_access_poll<R>(
|
||||
index: Self::Index,
|
||||
f: impl FnOnce(PollStatus<&mut TallyOf<Runtime>, u64, u8>) -> Result<R, DispatchError>,
|
||||
) -> Result<R, DispatchError> {
|
||||
let mut polls = Polls::get();
|
||||
let entry = polls.get_mut(&index);
|
||||
let r = match entry {
|
||||
Some(Ongoing(ref mut tally_mut_ref, class)) => {
|
||||
f(PollStatus::Ongoing(tally_mut_ref, *class))
|
||||
}
|
||||
Some(Completed(when, succeeded)) => f(PollStatus::Completed(
|
||||
(*when).try_into().unwrap(),
|
||||
*succeeded,
|
||||
)),
|
||||
None => f(PollStatus::None),
|
||||
}?;
|
||||
Polls::set(polls);
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn create_ongoing(class: Self::Class) -> Result<Self::Index, ()> {
|
||||
let mut polls = Polls::get();
|
||||
let i = polls.keys().rev().next().map_or(0, |x| x + 1);
|
||||
polls.insert(i, Ongoing(Tally::new(0), class));
|
||||
Polls::set(polls);
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn end_ongoing(index: Self::Index, approved: bool) -> Result<(), ()> {
|
||||
let mut polls = Polls::get();
|
||||
match polls.get(&index) {
|
||||
Some(Ongoing(..)) => {}
|
||||
_ => return Err(()),
|
||||
}
|
||||
let now = frame_system::Pallet::<Runtime>::block_number();
|
||||
polls.insert(index, Completed(now, approved));
|
||||
Polls::set(polls);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl pallet_conviction_voting::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Currency = pallet_balances::Pallet<Self>;
|
||||
type VoteLockingPeriod = ConstU64<3>;
|
||||
type MaxVotes = ConstU32<3>;
|
||||
type WeightInfo = ();
|
||||
type MaxTurnout = TotalIssuanceOf<Balances, Self::AccountId>;
|
||||
type Polls = TestPolls;
|
||||
}
|
||||
|
||||
pub(crate) struct ExtBuilder {
|
||||
/// Endowed accounts with balances
|
||||
balances: Vec<(AccountId, Balance)>,
|
||||
}
|
||||
|
||||
impl Default for ExtBuilder {
|
||||
fn default() -> ExtBuilder {
|
||||
ExtBuilder { balances: vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtBuilder {
|
||||
/// Fund some accounts before starting the test
|
||||
pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
|
||||
self.balances = balances;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the test externalities for use in tests
|
||||
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");
|
||||
|
||||
let mut ext = sp_io::TestExternalities::new(t);
|
||||
ext.execute_with(|| {
|
||||
System::set_block_number(1);
|
||||
});
|
||||
ext
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn events() -> Vec<RuntimeEvent> {
|
||||
System::events()
|
||||
.into_iter()
|
||||
.map(|r| r.event)
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
578
operator/precompiles/conviction-voting/src/tests.rs
Normal file
578
operator/precompiles/conviction-voting/src/tests.rs
Normal file
|
|
@ -0,0 +1,578 @@
|
|||
// 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::{
|
||||
mock::*, SELECTOR_LOG_DELEGATED, SELECTOR_LOG_UNDELEGATED, SELECTOR_LOG_UNLOCKED,
|
||||
SELECTOR_LOG_VOTED, SELECTOR_LOG_VOTE_REMOVED, SELECTOR_LOG_VOTE_REMOVED_FOR_TRACK,
|
||||
SELECTOR_LOG_VOTE_REMOVED_OTHER, SELECTOR_LOG_VOTE_SPLIT, SELECTOR_LOG_VOTE_SPLIT_ABSTAINED,
|
||||
};
|
||||
use precompile_utils::{prelude::*, testing::*};
|
||||
|
||||
use frame_support::assert_ok;
|
||||
use pallet_evm::{Call as EvmCall, Event as EvmEvent};
|
||||
use sp_core::{H160, H256, U256};
|
||||
use sp_runtime::{
|
||||
traits::{Dispatchable, PostDispatchInfoOf},
|
||||
DispatchResultWithInfo,
|
||||
};
|
||||
|
||||
const ONGOING_POLL_INDEX: u32 = 3;
|
||||
|
||||
fn precompiles() -> Precompiles<Runtime> {
|
||||
PrecompilesValue::get()
|
||||
}
|
||||
|
||||
fn evm_call(input: Vec<u8>) -> EvmCall<Runtime> {
|
||||
EvmCall::call {
|
||||
source: Alice.into(),
|
||||
target: Precompile1.into(),
|
||||
input,
|
||||
value: U256::zero(),
|
||||
gas_limit: u64::max_value(),
|
||||
max_fee_per_gas: 0.into(),
|
||||
max_priority_fee_per_gas: Some(U256::zero()),
|
||||
nonce: None,
|
||||
access_list: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
|
||||
check_precompile_implements_solidity_interfaces(
|
||||
&["ConvictionVoting.sol"],
|
||||
PCall::supports_selector,
|
||||
)
|
||||
}
|
||||
|
||||
fn standard_vote(
|
||||
direction: bool,
|
||||
vote_amount: U256,
|
||||
conviction: u8,
|
||||
) -> DispatchResultWithInfo<PostDispatchInfoOf<RuntimeCall>> {
|
||||
let input = match direction {
|
||||
// Vote Yes
|
||||
true => PCall::vote_yes {
|
||||
poll_index: ONGOING_POLL_INDEX,
|
||||
vote_amount,
|
||||
conviction,
|
||||
}
|
||||
.into(),
|
||||
// Vote No
|
||||
false => PCall::vote_no {
|
||||
poll_index: ONGOING_POLL_INDEX,
|
||||
vote_amount,
|
||||
conviction,
|
||||
}
|
||||
.into(),
|
||||
};
|
||||
RuntimeCall::Evm(evm_call(input)).dispatch(RuntimeOrigin::root())
|
||||
}
|
||||
|
||||
fn split_vote(
|
||||
aye: U256,
|
||||
nay: U256,
|
||||
maybe_abstain: Option<U256>,
|
||||
) -> DispatchResultWithInfo<PostDispatchInfoOf<RuntimeCall>> {
|
||||
let input = if let Some(abstain) = maybe_abstain {
|
||||
// Vote SplitAbstain
|
||||
PCall::vote_split_abstain {
|
||||
poll_index: ONGOING_POLL_INDEX,
|
||||
aye,
|
||||
nay,
|
||||
abstain,
|
||||
}
|
||||
.into()
|
||||
} else {
|
||||
// Vote Split
|
||||
PCall::vote_split {
|
||||
poll_index: ONGOING_POLL_INDEX,
|
||||
aye,
|
||||
nay,
|
||||
}
|
||||
.into()
|
||||
};
|
||||
RuntimeCall::Evm(evm_call(input)).dispatch(RuntimeOrigin::root())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standard_vote_logs_work() {
|
||||
ExtBuilder::default()
|
||||
.with_balances(vec![(Alice.into(), 100_000)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// Vote Yes
|
||||
assert_ok!(standard_vote(true, 100_000.into(), 0.into()));
|
||||
|
||||
// Vote No
|
||||
assert_ok!(standard_vote(false, 99_000.into(), 1.into()));
|
||||
|
||||
// Assert vote events are emitted.
|
||||
let expected_events = vec![
|
||||
EvmEvent::Log {
|
||||
log: log2(
|
||||
Precompile1,
|
||||
SELECTOR_LOG_VOTED,
|
||||
H256::from_low_u64_be(ONGOING_POLL_INDEX as u64),
|
||||
solidity::encode_event_data((
|
||||
Address(Alice.into()), // caller,
|
||||
true, // vote
|
||||
U256::from(100_000), // amount
|
||||
0u8, // conviction
|
||||
)),
|
||||
),
|
||||
}
|
||||
.into(),
|
||||
EvmEvent::Log {
|
||||
log: log2(
|
||||
Precompile1,
|
||||
SELECTOR_LOG_VOTED,
|
||||
H256::from_low_u64_be(ONGOING_POLL_INDEX as u64),
|
||||
solidity::encode_event_data((
|
||||
Address(Alice.into()), // caller
|
||||
false, // vote,
|
||||
U256::from(99_000), // amount
|
||||
1u8, // conviction
|
||||
)),
|
||||
),
|
||||
}
|
||||
.into(),
|
||||
];
|
||||
for log in expected_events {
|
||||
assert!(
|
||||
events().contains(&log),
|
||||
"Expected event not found: {:?}\nAll events:\n{:?}",
|
||||
log,
|
||||
events()
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_vote_logs_work() {
|
||||
ExtBuilder::default()
|
||||
.with_balances(vec![(Alice.into(), 100_000)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// Vote split
|
||||
assert_ok!(split_vote(20_000.into(), 30_000.into(), None));
|
||||
|
||||
// Vote split abstain
|
||||
assert_ok!(split_vote(
|
||||
20_000.into(),
|
||||
20_000.into(),
|
||||
Some(10_000.into())
|
||||
));
|
||||
|
||||
// Assert vote events are emitted.
|
||||
let expected_events = vec![
|
||||
EvmEvent::Log {
|
||||
log: log2(
|
||||
Precompile1,
|
||||
SELECTOR_LOG_VOTE_SPLIT,
|
||||
H256::from_low_u64_be(ONGOING_POLL_INDEX as u64),
|
||||
solidity::encode_event_data((
|
||||
Address(Alice.into()), // caller
|
||||
U256::from(20_000), // aye vote
|
||||
U256::from(30_000), // nay vote
|
||||
)),
|
||||
),
|
||||
}
|
||||
.into(),
|
||||
EvmEvent::Log {
|
||||
log: log2(
|
||||
Precompile1,
|
||||
SELECTOR_LOG_VOTE_SPLIT_ABSTAINED,
|
||||
H256::from_low_u64_be(ONGOING_POLL_INDEX as u64),
|
||||
solidity::encode_event_data((
|
||||
Address(Alice.into()), // caller,
|
||||
U256::from(20_000), // aye vote
|
||||
U256::from(20_000), // nay vote
|
||||
U256::from(10_000), // abstain vote
|
||||
)),
|
||||
),
|
||||
}
|
||||
.into(),
|
||||
];
|
||||
for log in expected_events {
|
||||
assert!(
|
||||
events().contains(&log),
|
||||
"Expected event not found: {:?}\nAll events:\n{:?}",
|
||||
log,
|
||||
events()
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_vote_logs_work() {
|
||||
ExtBuilder::default()
|
||||
.with_balances(vec![(Alice.into(), 100_000)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// Vote..
|
||||
assert_ok!(standard_vote(true, 100_000.into(), 0.into()));
|
||||
|
||||
// ..and remove
|
||||
let input = PCall::remove_vote {
|
||||
poll_index: ONGOING_POLL_INDEX,
|
||||
}
|
||||
.into();
|
||||
assert_ok!(RuntimeCall::Evm(evm_call(input)).dispatch(RuntimeOrigin::root()));
|
||||
|
||||
// Assert remove vote event is emitted.
|
||||
assert!(events().contains(
|
||||
&EvmEvent::Log {
|
||||
log: log2(
|
||||
Precompile1,
|
||||
SELECTOR_LOG_VOTE_REMOVED,
|
||||
H256::from_low_u64_be(ONGOING_POLL_INDEX as u64),
|
||||
solidity::encode_event_data(Address(Alice.into())) // caller
|
||||
),
|
||||
}
|
||||
.into()
|
||||
));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_vote_for_track_logs_work() {
|
||||
ExtBuilder::default()
|
||||
.with_balances(vec![(Alice.into(), 100_000)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// Vote..
|
||||
assert_ok!(standard_vote(true, 100_000.into(), 0.into()));
|
||||
|
||||
// ..and remove
|
||||
let input = PCall::remove_vote_for_track {
|
||||
poll_index: ONGOING_POLL_INDEX,
|
||||
track_id: 0u16,
|
||||
}
|
||||
.into();
|
||||
assert_ok!(RuntimeCall::Evm(evm_call(input)).dispatch(RuntimeOrigin::root()));
|
||||
|
||||
// Assert remove vote event is emitted.
|
||||
assert!(events().contains(
|
||||
&EvmEvent::Log {
|
||||
log: log2(
|
||||
Precompile1,
|
||||
SELECTOR_LOG_VOTE_REMOVED_FOR_TRACK,
|
||||
H256::from_low_u64_be(ONGOING_POLL_INDEX as u64),
|
||||
solidity::encode_event_data((
|
||||
0u16,
|
||||
Address(Alice.into()) // caller
|
||||
))
|
||||
),
|
||||
}
|
||||
.into()
|
||||
));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_other_vote_logs_work() {
|
||||
ExtBuilder::default()
|
||||
.with_balances(vec![(Alice.into(), 100_000)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// Vote..
|
||||
assert_ok!(standard_vote(true, 100_000.into(), 0.into()));
|
||||
|
||||
// ..and remove other
|
||||
let input = PCall::remove_other_vote {
|
||||
target: H160::from(Alice).into(),
|
||||
track_id: 0u16,
|
||||
poll_index: ONGOING_POLL_INDEX,
|
||||
}
|
||||
.into();
|
||||
assert_ok!(RuntimeCall::Evm(evm_call(input)).dispatch(RuntimeOrigin::root()));
|
||||
|
||||
// Assert remove other vote event is emitted.
|
||||
assert!(events().contains(
|
||||
&EvmEvent::Log {
|
||||
log: log2(
|
||||
Precompile1,
|
||||
SELECTOR_LOG_VOTE_REMOVED_OTHER,
|
||||
H256::from_low_u64_be(ONGOING_POLL_INDEX as u64),
|
||||
solidity::encode_event_data((
|
||||
Address(Alice.into()), // caller
|
||||
Address(Alice.into()), // target
|
||||
0u16, // track id
|
||||
))
|
||||
),
|
||||
}
|
||||
.into()
|
||||
));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delegate_undelegate_logs_work() {
|
||||
ExtBuilder::default()
|
||||
.with_balances(vec![(Alice.into(), 100_000)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// Delegate
|
||||
let input = PCall::delegate {
|
||||
track_id: 0u16,
|
||||
representative: H160::from(Bob).into(),
|
||||
conviction: 0.into(),
|
||||
amount: 100_000.into(),
|
||||
}
|
||||
.into();
|
||||
assert_ok!(RuntimeCall::Evm(evm_call(input)).dispatch(RuntimeOrigin::root()));
|
||||
|
||||
// Assert delegate event is emitted.
|
||||
assert!(events().contains(
|
||||
&EvmEvent::Log {
|
||||
log: log2(
|
||||
Precompile1,
|
||||
SELECTOR_LOG_DELEGATED,
|
||||
H256::from_low_u64_be(0 as u64), // track id
|
||||
solidity::encode_event_data((
|
||||
Address(Alice.into()), // from
|
||||
Address(Bob.into()), // to
|
||||
U256::from(100_000), // amount
|
||||
0u8 // conviction
|
||||
))
|
||||
),
|
||||
}
|
||||
.into()
|
||||
));
|
||||
|
||||
// Undelegate
|
||||
let input = PCall::undelegate { track_id: 0u16 }.into();
|
||||
assert_ok!(RuntimeCall::Evm(evm_call(input)).dispatch(RuntimeOrigin::root()));
|
||||
|
||||
// Assert undelegate event is emitted.
|
||||
assert!(events().contains(
|
||||
&EvmEvent::Log {
|
||||
log: log2(
|
||||
Precompile1,
|
||||
SELECTOR_LOG_UNDELEGATED,
|
||||
H256::from_low_u64_be(0 as u64), // track id
|
||||
solidity::encode_event_data(Address(Alice.into())) // caller
|
||||
),
|
||||
}
|
||||
.into()
|
||||
));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unlock_logs_work() {
|
||||
ExtBuilder::default()
|
||||
.with_balances(vec![(Alice.into(), 100_000)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// Vote
|
||||
assert_ok!(standard_vote(true, 100_000.into(), 0.into()));
|
||||
|
||||
// Remove
|
||||
let input = PCall::remove_vote {
|
||||
poll_index: ONGOING_POLL_INDEX,
|
||||
}
|
||||
.into();
|
||||
assert_ok!(RuntimeCall::Evm(evm_call(input)).dispatch(RuntimeOrigin::root()));
|
||||
|
||||
// Unlock
|
||||
let input = PCall::unlock {
|
||||
track_id: 0u16,
|
||||
target: H160::from(Alice).into(),
|
||||
}
|
||||
.into();
|
||||
assert_ok!(RuntimeCall::Evm(evm_call(input)).dispatch(RuntimeOrigin::root()));
|
||||
|
||||
// Assert unlock event is emitted.
|
||||
assert!(events().contains(
|
||||
&EvmEvent::Log {
|
||||
log: log2(
|
||||
Precompile1,
|
||||
SELECTOR_LOG_UNLOCKED,
|
||||
H256::from_low_u64_be(0 as u64), // track id
|
||||
solidity::encode_event_data(Address(Alice.into()))
|
||||
),
|
||||
}
|
||||
.into()
|
||||
));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_voting_for_returns_correct_value_for_standard_vote() {
|
||||
ExtBuilder::default()
|
||||
.with_balances(vec![(Alice.into(), 100_000)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// Vote Yes
|
||||
assert_ok!(standard_vote(true, 100_000.into(), 1.into()));
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Alice,
|
||||
Precompile1,
|
||||
PCall::voting_for {
|
||||
who: H160::from(Alice).into(),
|
||||
track_id: 0u16,
|
||||
},
|
||||
)
|
||||
.expect_no_logs()
|
||||
.execute_returns(crate::OutputVotingFor {
|
||||
is_casting: true,
|
||||
casting: crate::OutputCasting {
|
||||
votes: vec![crate::PollAccountVote {
|
||||
poll_index: 3,
|
||||
account_vote: crate::OutputAccountVote {
|
||||
is_standard: true,
|
||||
standard: crate::StandardVote {
|
||||
vote: crate::OutputVote {
|
||||
aye: true,
|
||||
conviction: 1,
|
||||
},
|
||||
balance: 100_000.into(),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
}],
|
||||
delegations: crate::Delegations {
|
||||
votes: 0.into(),
|
||||
capital: 0.into(),
|
||||
},
|
||||
prior: crate::PriorLock { balance: 0.into() },
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_voting_for_returns_correct_value_for_split_vote() {
|
||||
ExtBuilder::default()
|
||||
.with_balances(vec![(Alice.into(), 100_000)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// Vote Yes
|
||||
assert_ok!(split_vote(20_000.into(), 30_000.into(), None));
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Alice,
|
||||
Precompile1,
|
||||
PCall::voting_for {
|
||||
who: H160::from(Alice).into(),
|
||||
track_id: 0u16,
|
||||
},
|
||||
)
|
||||
.expect_no_logs()
|
||||
.execute_returns(crate::OutputVotingFor {
|
||||
is_casting: true,
|
||||
casting: crate::OutputCasting {
|
||||
votes: vec![crate::PollAccountVote {
|
||||
poll_index: 3,
|
||||
account_vote: crate::OutputAccountVote {
|
||||
is_split: true,
|
||||
split: crate::SplitVote {
|
||||
aye: 20_000.into(),
|
||||
nay: 30_000.into(),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
}],
|
||||
delegations: crate::Delegations {
|
||||
votes: 0.into(),
|
||||
capital: 0.into(),
|
||||
},
|
||||
prior: crate::PriorLock { balance: 0.into() },
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_voting_for_returns_correct_value_for_split_abstain_vote() {
|
||||
ExtBuilder::default()
|
||||
.with_balances(vec![(Alice.into(), 100_000)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// Vote Yes
|
||||
assert_ok!(split_vote(
|
||||
20_000.into(),
|
||||
30_000.into(),
|
||||
Some(10_000.into())
|
||||
));
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Alice,
|
||||
Precompile1,
|
||||
PCall::voting_for {
|
||||
who: H160::from(Alice).into(),
|
||||
track_id: 0u16,
|
||||
},
|
||||
)
|
||||
.expect_no_logs()
|
||||
.execute_returns(crate::OutputVotingFor {
|
||||
is_casting: true,
|
||||
casting: crate::OutputCasting {
|
||||
votes: vec![crate::PollAccountVote {
|
||||
poll_index: 3,
|
||||
account_vote: crate::OutputAccountVote {
|
||||
is_split_abstain: true,
|
||||
split_abstain: crate::SplitAbstainVote {
|
||||
aye: 20_000.into(),
|
||||
nay: 30_000.into(),
|
||||
abstain: 10_000.into(),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
}],
|
||||
delegations: crate::Delegations {
|
||||
votes: 0.into(),
|
||||
capital: 0.into(),
|
||||
},
|
||||
prior: crate::PriorLock { balance: 0.into() },
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_class_locks_for_returns_correct_value() {
|
||||
ExtBuilder::default()
|
||||
.with_balances(vec![(Alice.into(), 100_000)])
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
// Vote Yes
|
||||
assert_ok!(standard_vote(true, 100_000.into(), 1.into()));
|
||||
|
||||
precompiles()
|
||||
.prepare_test(
|
||||
Alice,
|
||||
Precompile1,
|
||||
PCall::class_locks_for {
|
||||
who: H160::from(Alice).into(),
|
||||
},
|
||||
)
|
||||
.expect_no_logs()
|
||||
.execute_returns(vec![crate::OutputClassLock {
|
||||
track: 0u16,
|
||||
amount: U256::from(100_000),
|
||||
}]);
|
||||
})
|
||||
}
|
||||
|
|
@ -127,6 +127,7 @@ 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-conviction-voting = { workspace = true }
|
||||
pallet-evm-precompile-identity = { workspace = true }
|
||||
pallet-evm-precompile-proxy = { workspace = true }
|
||||
pallet-evm-precompile-registry = { workspace = true }
|
||||
|
|
@ -201,6 +202,7 @@ std = [
|
|||
"pallet-evm-precompile-batch/std",
|
||||
"pallet-evm-precompile-call-permit/std",
|
||||
"pallet-evm-precompile-collective/std",
|
||||
"pallet-evm-precompile-conviction-voting/std",
|
||||
"pallet-evm-precompile-identity/std",
|
||||
"pallet-evm-precompile-proxy/std",
|
||||
"pallet-evm-precompile-registry/std",
|
||||
|
|
|
|||
|
|
@ -742,7 +742,9 @@ impl frame_support::traits::InstanceFilter<RuntimeCall> for ProxyType {
|
|||
fn is_governance_precompile(precompile_name: &PrecompileName) -> bool {
|
||||
matches!(
|
||||
precompile_name,
|
||||
PrecompileName::TechnicalCommitteeInstance | PrecompileName::TreasuryCouncilInstance
|
||||
PrecompileName::ConvictionVotingPrecompile
|
||||
| PrecompileName::TechnicalCommitteeInstance
|
||||
| PrecompileName::TreasuryCouncilInstance
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ 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_conviction_voting::ConvictionVotingPrecompile;
|
||||
use pallet_evm_precompile_file_system::FileSystemPrecompile;
|
||||
use pallet_evm_precompile_identity::IdentityPrecompile;
|
||||
use pallet_evm_precompile_modexp::Modexp;
|
||||
|
|
@ -107,6 +108,11 @@ type DataHavenPrecompilesAt<R> = (
|
|||
CollectivePrecompile<R, TreasuryCouncilInstance>,
|
||||
(CallableByContract, CallableByPrecompile),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2066>,
|
||||
ConvictionVotingPrecompile<R>,
|
||||
(CallableByContract, CallableByPrecompile),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2068>,
|
||||
CollectivePrecompile<R, TechnicalCommitteeInstance>,
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ 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-conviction-voting = { workspace = true }
|
||||
pallet-evm-precompile-identity = { workspace = true }
|
||||
pallet-evm-precompile-proxy = { workspace = true }
|
||||
pallet-evm-precompile-registry = { workspace = true }
|
||||
|
|
@ -201,6 +202,7 @@ std = [
|
|||
"pallet-evm-precompile-batch/std",
|
||||
"pallet-evm-precompile-call-permit/std",
|
||||
"pallet-evm-precompile-collective/std",
|
||||
"pallet-evm-precompile-conviction-voting/std",
|
||||
"pallet-evm-precompile-identity/std",
|
||||
"pallet-evm-precompile-proxy/std",
|
||||
"pallet-evm-precompile-registry/std",
|
||||
|
|
|
|||
|
|
@ -741,7 +741,9 @@ impl frame_support::traits::InstanceFilter<RuntimeCall> for ProxyType {
|
|||
fn is_governance_precompile(precompile_name: &PrecompileName) -> bool {
|
||||
matches!(
|
||||
precompile_name,
|
||||
PrecompileName::TechnicalCommitteeInstance | PrecompileName::TreasuryCouncilInstance
|
||||
PrecompileName::ConvictionVotingPrecompile
|
||||
| PrecompileName::TechnicalCommitteeInstance
|
||||
| PrecompileName::TreasuryCouncilInstance
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ 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_conviction_voting::ConvictionVotingPrecompile;
|
||||
use pallet_evm_precompile_file_system::FileSystemPrecompile;
|
||||
use pallet_evm_precompile_identity::IdentityPrecompile;
|
||||
use pallet_evm_precompile_modexp::Modexp;
|
||||
|
|
@ -107,6 +108,11 @@ type DataHavenPrecompilesAt<R> = (
|
|||
CollectivePrecompile<R, TreasuryCouncilInstance>,
|
||||
(CallableByContract, CallableByPrecompile),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2066>,
|
||||
ConvictionVotingPrecompile<R>,
|
||||
(CallableByContract, CallableByPrecompile),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2068>,
|
||||
CollectivePrecompile<R, TechnicalCommitteeInstance>,
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }
|
|||
pallet-evm-chain-id = { workspace = true }
|
||||
pallet-evm-precompile-blake2 = { workspace = true }
|
||||
pallet-evm-precompile-bn128 = { workspace = true }
|
||||
pallet-evm-precompile-conviction-voting = { workspace = true }
|
||||
pallet-evm-precompile-modexp = { workspace = true }
|
||||
pallet-evm-precompile-sha3fips = { workspace = true }
|
||||
pallet-evm-precompile-simple = { workspace = true }
|
||||
|
|
@ -201,6 +202,7 @@ std = [
|
|||
"pallet-evm-precompile-batch/std",
|
||||
"pallet-evm-precompile-call-permit/std",
|
||||
"pallet-evm-precompile-collective/std",
|
||||
"pallet-evm-precompile-conviction-voting/std",
|
||||
"pallet-evm-precompile-identity/std",
|
||||
"pallet-evm-precompile-proxy/std",
|
||||
"pallet-evm-precompile-registry/std",
|
||||
|
|
|
|||
|
|
@ -741,7 +741,9 @@ impl frame_support::traits::InstanceFilter<RuntimeCall> for ProxyType {
|
|||
fn is_governance_precompile(precompile_name: &PrecompileName) -> bool {
|
||||
matches!(
|
||||
precompile_name,
|
||||
PrecompileName::TechnicalCommitteeInstance | PrecompileName::TreasuryCouncilInstance
|
||||
PrecompileName::ConvictionVotingPrecompile
|
||||
| PrecompileName::TechnicalCommitteeInstance
|
||||
| PrecompileName::TreasuryCouncilInstance
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ 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_conviction_voting::ConvictionVotingPrecompile;
|
||||
use pallet_evm_precompile_file_system::FileSystemPrecompile;
|
||||
use pallet_evm_precompile_identity::IdentityPrecompile;
|
||||
use pallet_evm_precompile_modexp::Modexp;
|
||||
|
|
@ -107,6 +108,11 @@ type DataHavenPrecompilesAt<R> = (
|
|||
CollectivePrecompile<R, TreasuryCouncilInstance>,
|
||||
(CallableByContract, CallableByPrecompile),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2066>,
|
||||
ConvictionVotingPrecompile<R>,
|
||||
(CallableByContract, CallableByPrecompile),
|
||||
>,
|
||||
PrecompileAt<
|
||||
AddressU64<2068>,
|
||||
CollectivePrecompile<R, TechnicalCommitteeInstance>,
|
||||
|
|
|
|||
Loading…
Reference in a new issue