moonbeam_runtime/governance/
tracks.rs1use super::*;
20use crate::currency::{GLMR, KILOGLMR, SUPPLY_FACTOR};
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: 20 * KILOGLMR * 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: 2 * KILOGLMR * 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: 100 * GLMR * 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: 2 * KILOGLMR * 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(10)),
101 },
102 },
103 Track {
104 id: 4,
105 info: pallet_referenda::TrackInfo {
106 name: s("referendum_killer"),
107 max_deciding: 100,
108 decision_deposit: 4 * KILOGLMR * 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: 100 * GLMR * 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 fn tracks() -> impl Iterator<Item = Cow<'static, Track<Self::Id, Balance, BlockNumber>>> {
138 TRACKS_DATA.iter().map(Cow::Borrowed)
139 }
140 fn track_for(id: &Self::RuntimeOrigin) -> Result<Self::Id, ()> {
141 if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) {
142 match system_origin {
143 frame_system::RawOrigin::Root => {
144 if let Some(track) = Self::tracks()
145 .into_iter()
146 .find(|track| track.info.name == s("root"))
147 {
148 Ok(track.id)
149 } else {
150 Err(())
151 }
152 }
153 _ => Err(()),
154 }
155 } else if let Ok(custom_origin) = custom_origins::Origin::try_from(id.clone()) {
156 if let Some(track) = Self::tracks().into_iter().find(|track| {
157 let Ok(track_name) = from_utf8(&track.info.name) else {
158 return false;
159 };
160 let track_name = track_name.trim_end_matches('\0');
161 if let Ok(track_custom_origin) = custom_origins::Origin::from_str(track_name) {
162 track_custom_origin == custom_origin
163 } else {
164 false
165 }
166 }) {
167 Ok(track.id)
168 } else {
169 Err(())
170 }
171 } else {
172 Err(())
173 }
174 }
175}
176
177#[test]
178fn vote_locking_always_longer_than_enactment_period() {
180 for track in TRACKS_DATA {
181 assert!(
182 <Runtime as pallet_conviction_voting::Config>::VoteLockingPeriod::get()
183 >= track.info.min_enactment_period,
184 "Track {} has enactment period {} < vote locking period {}",
185 from_utf8(&track.info.name).expect("Track name is valid UTF-8"),
186 track.info.min_enactment_period,
187 <Runtime as pallet_conviction_voting::Config>::VoteLockingPeriod::get(),
188 );
189 }
190}
191
192#[test]
193fn all_tracks_have_origins() {
194 for track in TRACKS_DATA {
195 let track_is_root = track.info.name == s("root");
197 let track_name = from_utf8(&track.info.name)
198 .expect("Track name is valid UTF-8")
199 .trim_end_matches('\0');
200 let track_has_custom_origin = custom_origins::Origin::from_str(track_name).is_ok();
201 println!("{:?}", from_utf8(&track.info.name).unwrap());
202 assert!(track_is_root || track_has_custom_origin);
203 }
204}