moonbase_runtime/governance/
tracks.rs1use super::*;
20use crate::currency::{KILOUNIT, SUPPLY_FACTOR, UNIT};
21use core::str::from_utf8;
22use sp_std::str::FromStr;
23
24const fn percent(x: i32) -> sp_runtime::FixedI64 {
25 sp_runtime::FixedI64::from_rational(x as u128, 100)
26}
27const fn permill(x: i32) -> sp_runtime::FixedI64 {
28 sp_runtime::FixedI64::from_rational(x as u128, 1000)
29}
30
31use pallet_referenda::{Curve, Track};
32use sp_runtime::str_array as s;
33
34const TRACKS_DATA: [Track<u16, Balance, BlockNumber>; 6] = [
35 Track {
36 id: 0,
37 info: pallet_referenda::TrackInfo {
38 name: s("root"),
40 max_deciding: 5,
43 decision_deposit: 100 * KILOUNIT * SUPPLY_FACTOR,
45 prepare_period: 1 * DAYS,
47 decision_period: 14 * DAYS,
49 confirm_period: 1 * DAYS,
51 min_enactment_period: 1 * DAYS,
53 min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)),
56 min_support: Curve::make_linear(14, 14, permill(5), percent(25)),
59 },
60 },
61 Track {
62 id: 1,
63 info: pallet_referenda::TrackInfo {
64 name: s("whitelisted_caller"),
65 max_deciding: 100,
66 decision_deposit: 10 * KILOUNIT * SUPPLY_FACTOR,
67 prepare_period: 10 * MINUTES,
68 decision_period: 14 * DAYS,
69 confirm_period: 10 * MINUTES,
70 min_enactment_period: 30 * MINUTES,
71 min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)),
72 min_support: Curve::make_reciprocal(1, 14 * 24, percent(1), percent(0), percent(2)),
73 },
74 },
75 Track {
76 id: 2,
77 info: pallet_referenda::TrackInfo {
78 name: s("general_admin"),
79 max_deciding: 10,
80 decision_deposit: 500 * UNIT * SUPPLY_FACTOR,
81 prepare_period: 1 * HOURS,
82 decision_period: 14 * DAYS,
83 confirm_period: 1 * DAYS,
84 min_enactment_period: 1 * DAYS,
85 min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)),
86 min_support: Curve::make_reciprocal(7, 14, percent(10), percent(0), percent(50)),
87 },
88 },
89 Track {
90 id: 3,
91 info: pallet_referenda::TrackInfo {
92 name: s("referendum_canceller"),
93 max_deciding: 20,
94 decision_deposit: 10 * KILOUNIT * SUPPLY_FACTOR,
95 prepare_period: 1 * HOURS,
96 decision_period: 14 * DAYS,
97 confirm_period: 3 * HOURS,
98 min_enactment_period: 10 * MINUTES,
99 min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)),
100 min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(50)),
101 },
102 },
103 Track {
104 id: 4,
105 info: pallet_referenda::TrackInfo {
106 name: s("referendum_killer"),
107 max_deciding: 100,
108 decision_deposit: 20 * KILOUNIT * SUPPLY_FACTOR,
109 prepare_period: 1 * HOURS,
110 decision_period: 14 * DAYS,
111 confirm_period: 3 * HOURS,
112 min_enactment_period: 10 * MINUTES,
113 min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)),
114 min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)),
115 },
116 },
117 Track {
118 id: 5,
119 info: pallet_referenda::TrackInfo {
120 name: s("fast_general_admin"),
121 max_deciding: 10,
122 decision_deposit: 500 * UNIT * SUPPLY_FACTOR,
123 prepare_period: 1 * HOURS,
124 decision_period: 14 * DAYS,
125 confirm_period: 3 * HOURS,
126 min_enactment_period: 10 * MINUTES,
127 min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)),
128 min_support: Curve::make_reciprocal(5, 14, percent(1), percent(0), percent(50)),
129 },
130 },
131];
132
133pub struct TracksInfo;
134impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
135 type Id = u16;
136 type RuntimeOrigin = <RuntimeOrigin as frame_support::traits::OriginTrait>::PalletsOrigin;
137
138 fn tracks() -> impl Iterator<Item = Cow<'static, Track<Self::Id, Balance, BlockNumber>>> {
139 TRACKS_DATA.iter().map(Cow::Borrowed)
140 }
141 fn track_for(id: &Self::RuntimeOrigin) -> Result<Self::Id, ()> {
142 if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) {
143 match system_origin {
144 frame_system::RawOrigin::Root => {
145 if let Some(track) = Self::tracks()
146 .into_iter()
147 .find(|track| track.info.name == s("root"))
148 {
149 Ok(track.id)
150 } else {
151 Err(())
152 }
153 }
154 _ => Err(()),
155 }
156 } else if let Ok(custom_origin) = custom_origins::Origin::try_from(id.clone()) {
157 if let Some(track) = Self::tracks().into_iter().find(|track| {
158 let Ok(track_name) = from_utf8(&track.info.name) else {
159 return false;
160 };
161 let track_name = track_name.trim_end_matches('\0');
162 if let Ok(track_custom_origin) = custom_origins::Origin::from_str(track_name) {
163 track_custom_origin == custom_origin
164 } else {
165 false
166 }
167 }) {
168 Ok(track.id)
169 } else {
170 Err(())
171 }
172 } else {
173 Err(())
174 }
175 }
176}
177
178#[test]
179fn vote_locking_always_longer_than_enactment_period() {
181 for track in TRACKS_DATA {
182 assert!(
183 <Runtime as pallet_conviction_voting::Config>::VoteLockingPeriod::get()
184 >= track.info.min_enactment_period,
185 "Track {} has enactment period {} < vote locking period {}",
186 from_utf8(&track.info.name).expect("Track name is valid UTF-8"),
187 track.info.min_enactment_period,
188 <Runtime as pallet_conviction_voting::Config>::VoteLockingPeriod::get(),
189 );
190 }
191}
192
193#[test]
194fn all_tracks_have_origins() {
195 for track in TRACKS_DATA {
196 let track_is_root = track.info.name == s("root");
198 let track_name = from_utf8(&track.info.name)
199 .expect("Track name is valid UTF-8")
200 .trim_end_matches('\0');
201 let track_has_custom_origin = custom_origins::Origin::from_str(track_name).is_ok();
202 println!("{:?}", from_utf8(&track.info.name).unwrap());
203 assert!(track_is_root || track_has_custom_origin);
204 }
205}