1use cosmwasm_schema::cw_serde;
2use cosmwasm_std::{Decimal, Uint128};
3
4use thiserror::Error;
5
6#[cw_serde]
12pub enum ActiveThreshold {
13 AbsoluteCount { count: Uint128 },
16 Percentage { percent: Decimal },
19}
20
21#[cw_serde]
22pub struct ActiveThresholdResponse {
23 pub active_threshold: Option<ActiveThreshold>,
24}
25
26#[derive(Error, Debug, PartialEq, Eq)]
27pub enum ActiveThresholdError {
28 #[error("Absolute count threshold cannot be greater than the total token supply")]
29 InvalidAbsoluteCount {},
30
31 #[error("Active threshold percentage must be greater than 0 and not greater than 1")]
32 InvalidActivePercentage {},
33
34 #[error("Active threshold count must be greater than zero")]
35 ZeroActiveCount {},
36}
37
38pub fn assert_valid_absolute_count_threshold(
39 count: Uint128,
40 supply: Uint128,
41) -> Result<(), ActiveThresholdError> {
42 if count.is_zero() {
43 return Err(ActiveThresholdError::ZeroActiveCount {});
44 }
45 if count > supply {
46 return Err(ActiveThresholdError::InvalidAbsoluteCount {});
47 }
48 Ok(())
49}
50
51pub fn assert_valid_percentage_threshold(percent: Decimal) -> Result<(), ActiveThresholdError> {
52 if percent.is_zero() || percent > Decimal::one() {
53 return Err(ActiveThresholdError::InvalidActivePercentage {});
54 }
55 Ok(())
56}
57
58#[derive(Error, Debug, PartialEq, Eq)]
59pub enum ThresholdError {
60 #[error("Required threshold cannot be zero")]
61 ZeroThreshold {},
62
63 #[error("Not possible to reach required (passing) threshold")]
64 UnreachableThreshold {},
65}
66
67#[cw_serde]
91#[derive(Copy)]
92pub enum PercentageThreshold {
93 Majority {},
95 Percent(Decimal),
98}
99
100#[cw_serde]
102pub enum Threshold {
103 AbsolutePercentage { percentage: PercentageThreshold },
108
109 ThresholdQuorum {
114 threshold: PercentageThreshold,
115 quorum: PercentageThreshold,
116 },
117
118 AbsoluteCount { threshold: Uint128 },
121}
122
123fn validate_percentage(percent: &PercentageThreshold) -> Result<(), ThresholdError> {
125 if let PercentageThreshold::Percent(percent) = percent {
126 if percent.is_zero() {
127 Err(ThresholdError::ZeroThreshold {})
128 } else if *percent > Decimal::one() {
129 Err(ThresholdError::UnreachableThreshold {})
130 } else {
131 Ok(())
132 }
133 } else {
134 Ok(())
135 }
136}
137
138pub fn validate_quorum(quorum: &PercentageThreshold) -> Result<(), ThresholdError> {
140 match quorum {
141 PercentageThreshold::Majority {} => Ok(()),
142 PercentageThreshold::Percent(quorum) => {
143 if *quorum > Decimal::one() {
144 Err(ThresholdError::UnreachableThreshold {})
145 } else {
146 Ok(())
147 }
148 }
149 }
150}
151
152impl Threshold {
153 pub fn validate(&self) -> Result<(), ThresholdError> {
159 match self {
160 Threshold::AbsolutePercentage {
161 percentage: percentage_needed,
162 } => validate_percentage(percentage_needed),
163 Threshold::ThresholdQuorum { threshold, quorum } => {
164 validate_percentage(threshold)?;
165 validate_quorum(quorum)
166 }
167 Threshold::AbsoluteCount { threshold } => {
168 if threshold.is_zero() {
169 Err(ThresholdError::ZeroThreshold {})
170 } else {
171 Ok(())
172 }
173 }
174 }
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 macro_rules! p {
183 ($x:expr ) => {
184 PercentageThreshold::Percent(Decimal::percent($x))
185 };
186 }
187
188 #[test]
189 fn test_threshold_validation() {
190 let t = Threshold::AbsoluteCount {
191 threshold: Uint128::zero(),
192 };
193 assert_eq!(t.validate().unwrap_err(), ThresholdError::ZeroThreshold {});
194
195 let t = Threshold::AbsolutePercentage { percentage: p!(0) };
196 assert_eq!(t.validate().unwrap_err(), ThresholdError::ZeroThreshold {});
197
198 let t = Threshold::AbsolutePercentage {
199 percentage: p!(101),
200 };
201 assert_eq!(
202 t.validate().unwrap_err(),
203 ThresholdError::UnreachableThreshold {}
204 );
205
206 let t = Threshold::AbsolutePercentage {
207 percentage: p!(100),
208 };
209 t.validate().unwrap();
210
211 let t = Threshold::ThresholdQuorum {
212 threshold: p!(101),
213 quorum: p!(0),
214 };
215 assert_eq!(
216 t.validate().unwrap_err(),
217 ThresholdError::UnreachableThreshold {}
218 );
219
220 let t = Threshold::ThresholdQuorum {
221 threshold: p!(100),
222 quorum: p!(0),
223 };
224 t.validate().unwrap();
225
226 let t = Threshold::ThresholdQuorum {
227 threshold: p!(100),
228 quorum: p!(101),
229 };
230 assert_eq!(
231 t.validate().unwrap_err(),
232 ThresholdError::UnreachableThreshold {}
233 );
234 }
235}