Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4e2e06e
upgrade to v0.9.37
nbaztec Feb 3, 2023
1359267
fix tests
nbaztec Feb 3, 2023
a42bab5
fix tests, update frontier
nbaztec Feb 3, 2023
dbd06b0
update frontier
nbaztec Feb 3, 2023
8e02406
update frontier
nbaztec Feb 3, 2023
acdfe12
fix batch precompile tests
nbaztec Feb 6, 2023
2091dd4
fix lint
nbaztec Feb 6, 2023
1a25606
fix tests
nbaztec Feb 6, 2023
cf5d088
add root-testing pallet
nbaztec Feb 7, 2023
e7d7887
fix length fees test
nbaztec Feb 7, 2023
99f29f3
fix length fees test
nbaztec Feb 7, 2023
d472156
fix ts tests
nbaztec Feb 9, 2023
3a9e01a
fmt
nbaztec Feb 9, 2023
352320a
fix typo
nbaztec Feb 9, 2023
bcb7622
fix statemine tests
nbaztec Feb 9, 2023
8b18f5b
fix ts test
nbaztec Feb 9, 2023
c36d9be
fix tests
nbaztec Feb 9, 2023
8f84984
fix test
nbaztec Feb 9, 2023
1820b9d
change test name
nbaztec Feb 10, 2023
e7fe53e
remove dev_mode, add call_index
nbaztec Feb 10, 2023
e8bb693
bump
nbaztec Feb 10, 2023
363bf1d
init
amarsinghcodes Feb 11, 2023
9edecb6
into master
amarsinghcodes Feb 18, 2023
99db641
closed referendum info getter
amarsinghcodes Feb 20, 2023
50b1c46
make closed referendum info into a struct
amarsinghcodes Feb 20, 2023
da9df04
into master
amarsinghcodes Feb 20, 2023
79c2ffb
init ongoing referendum info
amarsinghcodes Feb 20, 2023
90f7cce
everything but schedule address works
amarsinghcodes Feb 20, 2023
b812fc2
fix schedule address which does implement encode even if it is not tr…
amarsinghcodes Feb 20, 2023
e23d35c
fix comment
amarsinghcodes Feb 20, 2023
907a954
into master
amarsinghcodes Feb 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions precompiles/referenda/Referenda.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ Referenda constant REFERENDA_CONTRACT = Referenda(REFERENDA_ADDRESS);
/// @title The interface through which solidity contracts will interact with the Referenda pallet
/// @custom:address 0x0000000000000000000000000000000000000811
interface Referenda {
/// @dev Defines the referendum status.
/// The values start at `0` (most permissive) and are represented as `uint8`
enum ReferendumStatus {
Ongoing,
Approved,
Rejected,
Cancelled,
TimedOut,
Killed
}
struct TrackInfo {
string name;
uint256 maxDeciding;
Expand All @@ -23,6 +33,50 @@ interface Referenda {
bytes minApproval;
bytes minSupport;
}
struct OngoingReferendumInfo {
/// The track of this referendum.
uint16 trackId;
/// The origin for this referendum.
bytes origin;
/// The hash of the proposal up for referendum.
bytes proposal;
/// Whether proposal is scheduled for enactment at or after `enactment_time`.
bool enactmentType;
/// The time the proposal should be scheduled for enactment.
uint256 enactmentTime;
/// The time of submission. Once `UndecidingTimeout` passes, it may be closed by anyone if
/// `deciding` is `None`.
uint256 submissionTime;
address submissionDepositor;
uint256 submissionDeposit;
address decisionDepositor;
uint256 decisionDeposit;
/// When this referendum began being "decided". If confirming, then the
/// end will actually be delayed until the end of the confirmation period.
uint256 decidingSince;
/// If nonzero, then the referendum has entered confirmation stage and will end at
/// the block number as long as it doesn't lose its approval in the meantime.
uint256 decidingConfirmingEnd;
/// The number of aye votes, expressed in terms of post-conviction lock-vote.
uint256 ayes;
/// Percent of aye votes, expressed pre-conviction, over total votes in the class.
uint32 support;
/// Percent of aye votes over aye + nay votes.
uint32 approval;
/// Whether we have been placed in the queue for being decided or not.
bool inQueue;
/// The next scheduled wake-up
uint256 alarmTime;
address alarmScheduleAddress;
}
struct ClosedReferendumInfo {
ReferendumStatus status;
uint256 end;
address submissionDepositor;
uint256 submissionDeposit;
address decisionDepositor;
uint256 decisionDeposit;
}

/// Return the total referendum count
/// @custom:selector 3a42ee31
Expand All @@ -47,6 +101,38 @@ interface Referenda {
/// @custom:selector 34038146
function trackInfo(uint16 trackId) external view returns (TrackInfo memory);

/// Return the ReferendumStatus for the input referendumIndex
/// @param referendumIndex The index of the referendum
/// @custom:selector 8d407c0b
function referendumStatus(uint32 referendumIndex)
external
view
returns (ReferendumStatus);

/// Return the referendumInfo for an ongoing referendum
/// @param referendumIndex The index of the referendum
/// @custom:selector 14febfbf
function ongoingReferendumInfo(uint32 referendumIndex)
external
view
returns (OngoingReferendumInfo memory);

/// Return the referendumInfo for a closed referendum
/// @param referendumIndex The index of the referendum
/// @custom:selector 14febfbf
function closedReferendumInfo(uint32 referendumIndex)
external
view
returns (ClosedReferendumInfo memory);

/// Return the block the referendum was killed
/// @param referendumIndex The index of the referendum
/// @custom:selector 6414ddc5
function killedReferendumBlock(uint32 referendumIndex)
external
view
returns (uint256);

/// @dev Submit a referenda
/// @custom:selector 131f3468
/// @param trackId The trackId corresponding to the origin from which the proposal is to be
Expand Down
235 changes: 232 additions & 3 deletions precompiles/referenda/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@

use fp_evm::PrecompileHandle;
use frame_support::dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo};
use frame_support::traits::{schedule::DispatchTime, Bounded, Currency, Get, OriginTrait};
use frame_support::traits::{
schedule::DispatchTime, Bounded, Currency, Get, OriginTrait, VoteTally,
};
use pallet_evm::AddressMapping;
use pallet_referenda::{Call as ReferendaCall, DecidingCount, ReferendumCount, TracksInfo};
use pallet_referenda::{
Call as ReferendaCall, DecidingCount, Deposit, ReferendumCount, ReferendumInfo,
ReferendumInfoFor, TracksInfo,
};
use parity_scale_codec::Encode;
use precompile_utils::{data::String, prelude::*};
use sp_core::{H256, U256};
use sp_core::{H160, H256, U256};
use sp_std::{boxed::Box, marker::PhantomData, str::FromStr, vec::Vec};

#[cfg(test)]
Expand Down Expand Up @@ -75,6 +80,54 @@ pub struct TrackInfo {
min_support: UnboundedBytes,
}

#[derive(EvmData)]
pub struct OngoingReferendumInfo {
/// The track of this referendum.
track_id: u16,
/// The origin for this referendum.
origin: UnboundedBytes,
/// The hash of the proposal up for referendum.
proposal: UnboundedBytes,
/// Whether proposal is scheduled for enactment at or after `enactment_time`.
enactment_type: bool,
/// The time the proposal should be scheduled for enactment.
enactment_time: U256,
/// The time of submission. Once `UndecidingTimeout` passes, it may be closed by anyone if
/// `deciding` is `None`.
submission_time: U256,
submission_depositor: Address,
submission_deposit: U256,
decision_depositor: Address,
decision_deposit: U256,
/// When this referendum began being "decided". If confirming, then the
/// end will actually be delayed until the end of the confirmation period.
deciding_since: U256,
/// If nonzero, then the referendum has entered confirmation stage and will end at
/// the block number as long as it doesn't lose its approval in the meantime.
deciding_confirming_end: U256,
/// The number of aye votes, expressed in terms of post-conviction lock-vote.
ayes: U256,
/// Percent aye votes, expressed pre-conviction, over the total votes in the class.
support: u32,
/// Percent of aye votes over aye + nay votes.
approval: u32,
/// Whether we have been placed in the queue for being decided or not.
in_queue: bool,
/// The next scheduled wake-up
alarm_time: U256,
alarm_schedule_address: Address,
}

#[derive(EvmData)]
pub struct ClosedReferendumInfo {
status: u8,
end: U256,
submission_depositor: Address,
submission_deposit: U256,
decision_depositor: Address,
decision_deposit: U256,
}

/// A precompile to wrap the functionality from pallet-referenda.
pub struct ReferendaPrecompile<Runtime, GovOrigin>(PhantomData<(Runtime, GovOrigin)>);

Expand All @@ -83,6 +136,7 @@ impl<Runtime, GovOrigin> ReferendaPrecompile<Runtime, GovOrigin>
where
Runtime: pallet_referenda::Config + pallet_evm::Config + frame_system::Config,
OriginOf<Runtime>: From<GovOrigin>,
Runtime::AccountId: Into<H160>,
<Runtime as frame_system::Config>::RuntimeCall:
Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
<<Runtime as frame_system::Config>::RuntimeCall as Dispatchable>::RuntimeOrigin:
Expand All @@ -92,6 +146,12 @@ where
Runtime::BlockNumber: Into<U256>,
TrackIdOf<Runtime>: TryFrom<u16> + TryInto<u16>,
BalanceOf<Runtime>: Into<U256>,
Runtime::Votes: Into<U256>,
<<Runtime as pallet_referenda::Config>::Scheduler as frame_support::traits::schedule::v3::Anon<
<Runtime as frame_system::Config>::BlockNumber,
<Runtime as pallet_referenda::Config>::RuntimeCall,
<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::PalletsOrigin>
>::Address: Into<H160>,
GovOrigin: FromStr,
H256: From<<Runtime as frame_system::Config>::Hash>
+ Into<<Runtime as frame_system::Config>::Hash>,
Expand Down Expand Up @@ -243,6 +303,175 @@ where
Ok(referendum_index)
}

#[precompile::public("referendumStatus(uint32)")]
#[precompile::view]
fn referendum_status(
handle: &mut impl PrecompileHandle,
referendum_index: u32,
) -> EvmResult<u8> {
// Fetch data from pallet
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

let status = match ReferendumInfoFor::<Runtime>::get(referendum_index).ok_or(
RevertReason::custom("Referendum does not exist for index")
.in_field("referendum_index"),
)? {
ReferendumInfo::Ongoing(..) => 0,
ReferendumInfo::Approved(..) => 1,
ReferendumInfo::Rejected(..) => 2,
ReferendumInfo::Cancelled(..) => 3,
ReferendumInfo::TimedOut(..) => 4,
ReferendumInfo::Killed(..) => 5,
};

Ok(status)
}

#[precompile::public("ongoingReferendumInfo(uint32)")]
#[precompile::view]
fn ongoing_referendum_info(
handle: &mut impl PrecompileHandle,
referendum_index: u32,
) -> EvmResult<OngoingReferendumInfo> {
// Fetch data from pallet
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

match ReferendumInfoFor::<Runtime>::get(referendum_index).ok_or(
RevertReason::custom("Referendum does not exist for index")
.in_field("referendum_index"),
)? {
ReferendumInfo::Ongoing(info) => {
let track_id = info.track.try_into().map_err(|_| {
RevertReason::value_is_too_large("Track id type not u16")
})?;
let (enactment_type, enactment_time) = match info.enactment {
DispatchTime::At(x) => (true, x.into()),
DispatchTime::After(x) => (false, x.into()),
};
let (decision_depositor, decision_deposit) =
if let Some(deposit) = info.decision_deposit {
(Address(deposit.who.into()), deposit.amount.into())
} else {
(Address(H160::zero()), U256::zero())
};
let (deciding_since, deciding_confirming_end) =
if let Some(deciding_status) = info.deciding {
(
deciding_status.since.into(),
deciding_status.confirming.unwrap_or_default().into(),
)
} else {
(U256::zero(), U256::zero())
};
let (alarm_time, alarm_schedule_address) = if let Some((time, address)) = info.alarm
{
(time.into(), Address(address.into()))
} else {
(U256::zero(), Address(H160::zero()))
};

Ok(OngoingReferendumInfo {
track_id,
origin: info.origin.encode().into(),
proposal: info.proposal.encode().into(),
enactment_type,
enactment_time,
submission_time: info.submitted.into(),
submission_depositor: Address(info.submission_deposit.who.into()),
submission_deposit: info.submission_deposit.amount.into(),
decision_depositor,
decision_deposit,
deciding_since,
deciding_confirming_end,
ayes: info.tally.ayes(info.track).into(),
support: info.tally.support(info.track).deconstruct(),
approval: info.tally.approval(info.track).deconstruct(),
in_queue: info.in_queue,
alarm_time,
alarm_schedule_address,
})
}
_ => Err(RevertReason::custom("Referendum not ongoing").into()),
}
}

#[precompile::public("closedReferendumInfo(uint32)")]
#[precompile::view]
fn closed_referendum_info(
handle: &mut impl PrecompileHandle,
referendum_index: u32,
) -> EvmResult<ClosedReferendumInfo> {
// Fetch data from pallet
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

let get_closed_ref_info =
|status,
moment: Runtime::BlockNumber,
submission_deposit: Option<Deposit<Runtime::AccountId, BalanceOf<Runtime>>>,
decision_deposit: Option<Deposit<Runtime::AccountId, BalanceOf<Runtime>>>|
-> ClosedReferendumInfo {
let (submission_depositor, submission_deposit_amount): (Address, U256) =
if let Some(Deposit { who, amount }) = submission_deposit {
(Address(who.into()), amount.into())
} else {
(Address(H160::zero()), U256::zero())
};
let (decision_depositor, decision_deposit_amount) =
if let Some(Deposit { who, amount }) = decision_deposit {
(Address(who.into()), amount.into())
} else {
(Address(H160::zero()), U256::zero())
};
ClosedReferendumInfo {
status,
end: moment.into(),
submission_depositor,
submission_deposit: submission_deposit_amount,
decision_depositor,
decision_deposit: decision_deposit_amount,
}
};

match ReferendumInfoFor::<Runtime>::get(referendum_index).ok_or(
RevertReason::custom("Referendum does not exist for index")
.in_field("referendum_index"),
)? {
ReferendumInfo::Approved(moment, submission_deposit, decision_deposit) => Ok(
get_closed_ref_info(1, moment, submission_deposit, decision_deposit),
),
ReferendumInfo::Rejected(moment, submission_deposit, decision_deposit) => Ok(
get_closed_ref_info(2, moment, submission_deposit, decision_deposit),
),
ReferendumInfo::Cancelled(moment, submission_deposit, decision_deposit) => Ok(
get_closed_ref_info(3, moment, submission_deposit, decision_deposit),
),
ReferendumInfo::TimedOut(moment, submission_deposit, decision_deposit) => Ok(
get_closed_ref_info(4, moment, submission_deposit, decision_deposit),
),
_ => Err(RevertReason::custom("Referendum not closed").into()),
}
}

#[precompile::public("killedReferendumBlock(uint32)")]
#[precompile::view]
fn killed_referendum_block(
handle: &mut impl PrecompileHandle,
referendum_index: u32,
) -> EvmResult<U256> {
// Fetch data from pallet
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

let block = match ReferendumInfoFor::<Runtime>::get(referendum_index).ok_or(
RevertReason::custom("Referendum does not exist for index")
.in_field("referendum_index"),
)? {
ReferendumInfo::Killed(b) => b,
_ => return Err(RevertReason::custom("Referendum not killed").into()),
};

Ok(block.into())
}

/// Propose a referendum on a privileged action.
///
/// Parameters:
Expand Down