11use schemars:: JsonSchema ;
22use serde:: { Deserialize , Serialize } ;
33
4- use crate :: error:: ContractError ;
5- use cosmwasm_std:: { CosmosMsg , Decimal , Empty } ;
6- use cw3:: { ThresholdResponse , Vote } ;
4+ use cosmwasm_std:: { CosmosMsg , Empty } ;
5+ use cw3:: Vote ;
76use cw4:: MemberChangedHookMsg ;
8- use utils:: { Duration , Expiration } ;
7+ use utils:: { Duration , Expiration , Threshold } ;
98
109#[ derive( Serialize , Deserialize , Clone , PartialEq , JsonSchema , Debug ) ]
1110pub struct InstantiateMsg {
@@ -15,102 +14,6 @@ pub struct InstantiateMsg {
1514 pub max_voting_period : Duration ,
1615}
1716
18- /// This defines the different ways tallies can happen.
19- ///
20- /// The total_weight used for calculating success as well as the weights of each
21- /// individual voter used in tallying should be snapshotted at the beginning of
22- /// the block at which the proposal starts (this is likely the responsibility of a
23- /// correct cw4 implementation).
24- /// See also `ThresholdResponse` in the cw3 spec.
25- #[ derive( Serialize , Deserialize , Clone , PartialEq , JsonSchema , Debug ) ]
26- #[ serde( rename_all = "snake_case" ) ]
27- pub enum Threshold {
28- /// Declares that a fixed weight of Yes votes is needed to pass.
29- /// See `ThresholdResponse.AbsoluteCount` in the cw3 spec for details.
30- AbsoluteCount { weight : u64 } ,
31-
32- /// Declares a percentage of the total weight that must cast Yes votes in order for
33- /// a proposal to pass.
34- /// See `ThresholdResponse.AbsolutePercentage` in the cw3 spec for details.
35- AbsolutePercentage { percentage : Decimal } ,
36-
37- /// Declares a `quorum` of the total votes that must participate in the election in order
38- /// for the vote to be considered at all.
39- /// See `ThresholdResponse.ThresholdQuorum` in the cw3 spec for details.
40- ThresholdQuorum { threshold : Decimal , quorum : Decimal } ,
41- }
42-
43- impl Threshold {
44- /// returns error if this is an unreachable value,
45- /// given a total weight of all members in the group
46- pub fn validate ( & self , total_weight : u64 ) -> Result < ( ) , ContractError > {
47- match self {
48- Threshold :: AbsoluteCount {
49- weight : weight_needed,
50- } => {
51- if * weight_needed == 0 {
52- Err ( ContractError :: ZeroWeight { } )
53- } else if * weight_needed > total_weight {
54- Err ( ContractError :: UnreachableWeight { } )
55- } else {
56- Ok ( ( ) )
57- }
58- }
59- Threshold :: AbsolutePercentage {
60- percentage : percentage_needed,
61- } => valid_threshold ( percentage_needed) ,
62- Threshold :: ThresholdQuorum {
63- threshold,
64- quorum : quroum,
65- } => {
66- valid_threshold ( threshold) ?;
67- valid_quorum ( quroum)
68- }
69- }
70- }
71-
72- /// Creates a response from the saved data, just missing the total_weight info
73- pub fn to_response ( & self , total_weight : u64 ) -> ThresholdResponse {
74- match self . clone ( ) {
75- Threshold :: AbsoluteCount { weight } => ThresholdResponse :: AbsoluteCount {
76- weight,
77- total_weight,
78- } ,
79- Threshold :: AbsolutePercentage { percentage } => ThresholdResponse :: AbsolutePercentage {
80- percentage,
81- total_weight,
82- } ,
83- Threshold :: ThresholdQuorum { threshold, quorum } => {
84- ThresholdResponse :: ThresholdQuorum {
85- threshold,
86- quorum,
87- total_weight,
88- }
89- }
90- }
91- }
92- }
93-
94- /// Asserts that the 0.5 < percent <= 1.0
95- fn valid_threshold ( percent : & Decimal ) -> Result < ( ) , ContractError > {
96- if * percent > Decimal :: percent ( 100 ) || * percent < Decimal :: percent ( 50 ) {
97- Err ( ContractError :: InvalidThreshold { } )
98- } else {
99- Ok ( ( ) )
100- }
101- }
102-
103- /// Asserts that the 0.5 < percent <= 1.0
104- fn valid_quorum ( percent : & Decimal ) -> Result < ( ) , ContractError > {
105- if percent. is_zero ( ) {
106- Err ( ContractError :: ZeroQuorumThreshold { } )
107- } else if * percent > Decimal :: one ( ) {
108- Err ( ContractError :: UnreachableQuorumThreshold { } )
109- } else {
110- Ok ( ( ) )
111- }
112- }
113-
11417// TODO: add some T variants? Maybe good enough as fixed Empty for now
11518#[ derive( Serialize , Deserialize , Clone , Debug , PartialEq , JsonSchema ) ]
11619#[ serde( rename_all = "snake_case" ) ]
@@ -170,153 +73,3 @@ pub enum QueryMsg {
17073 limit : Option < u32 > ,
17174 } ,
17275}
173-
174- #[ cfg( test) ]
175- mod tests {
176- use super :: * ;
177-
178- #[ test]
179- fn validate_quorum_percentage ( ) {
180- // TODO: test the error messages
181-
182- // 0 is never a valid percentage
183- let err = valid_quorum ( & Decimal :: zero ( ) ) . unwrap_err ( ) ;
184- assert_eq ! (
185- err. to_string( ) ,
186- ContractError :: ZeroQuorumThreshold { } . to_string( )
187- ) ;
188-
189- // 100% is
190- valid_quorum ( & Decimal :: one ( ) ) . unwrap ( ) ;
191-
192- // 101% is not
193- let err = valid_quorum ( & Decimal :: percent ( 101 ) ) . unwrap_err ( ) ;
194- assert_eq ! (
195- err. to_string( ) ,
196- ContractError :: UnreachableQuorumThreshold { } . to_string( )
197- ) ;
198- // not 100.1%
199- let err = valid_quorum ( & Decimal :: permille ( 1001 ) ) . unwrap_err ( ) ;
200- assert_eq ! (
201- err. to_string( ) ,
202- ContractError :: UnreachableQuorumThreshold { } . to_string( )
203- ) ;
204- }
205-
206- #[ test]
207- fn validate_threshold_percentage ( ) {
208- // other values in between 0.5 and 1 are valid
209- valid_threshold ( & Decimal :: percent ( 51 ) ) . unwrap ( ) ;
210- valid_threshold ( & Decimal :: percent ( 67 ) ) . unwrap ( ) ;
211- valid_threshold ( & Decimal :: percent ( 99 ) ) . unwrap ( ) ;
212- let err = valid_threshold ( & Decimal :: percent ( 101 ) ) . unwrap_err ( ) ;
213- assert_eq ! (
214- err. to_string( ) ,
215- ContractError :: InvalidThreshold { } . to_string( )
216- ) ;
217- }
218-
219- #[ test]
220- fn validate_threshold ( ) {
221- // absolute count ensures 0 < required <= total_weight
222- let err = Threshold :: AbsoluteCount { weight : 0 }
223- . validate ( 5 )
224- . unwrap_err ( ) ;
225- // TODO: remove to_string() when PartialEq implemented
226- assert_eq ! ( err. to_string( ) , ContractError :: ZeroWeight { } . to_string( ) ) ;
227- let err = Threshold :: AbsoluteCount { weight : 6 }
228- . validate ( 5 )
229- . unwrap_err ( ) ;
230- assert_eq ! (
231- err. to_string( ) ,
232- ContractError :: UnreachableWeight { } . to_string( )
233- ) ;
234-
235- Threshold :: AbsoluteCount { weight : 1 } . validate ( 5 ) . unwrap ( ) ;
236- Threshold :: AbsoluteCount { weight : 5 } . validate ( 5 ) . unwrap ( ) ;
237-
238- // AbsolutePercentage just enforces valid_percentage (tested above)
239- let err = Threshold :: AbsolutePercentage {
240- percentage : Decimal :: zero ( ) ,
241- }
242- . validate ( 5 )
243- . unwrap_err ( ) ;
244- assert_eq ! (
245- err. to_string( ) ,
246- ContractError :: InvalidThreshold { } . to_string( )
247- ) ;
248- Threshold :: AbsolutePercentage {
249- percentage : Decimal :: percent ( 51 ) ,
250- }
251- . validate ( 5 )
252- . unwrap ( ) ;
253-
254- // Quorum enforces both valid just enforces valid_percentage (tested above)
255- Threshold :: ThresholdQuorum {
256- threshold : Decimal :: percent ( 51 ) ,
257- quorum : Decimal :: percent ( 40 ) ,
258- }
259- . validate ( 5 )
260- . unwrap ( ) ;
261- let err = Threshold :: ThresholdQuorum {
262- threshold : Decimal :: percent ( 101 ) ,
263- quorum : Decimal :: percent ( 40 ) ,
264- }
265- . validate ( 5 )
266- . unwrap_err ( ) ;
267- assert_eq ! (
268- err. to_string( ) ,
269- ContractError :: InvalidThreshold { } . to_string( )
270- ) ;
271- let err = Threshold :: ThresholdQuorum {
272- threshold : Decimal :: percent ( 51 ) ,
273- quorum : Decimal :: percent ( 0 ) ,
274- }
275- . validate ( 5 )
276- . unwrap_err ( ) ;
277- assert_eq ! (
278- err. to_string( ) ,
279- ContractError :: ZeroQuorumThreshold { } . to_string( )
280- ) ;
281- }
282-
283- #[ test]
284- fn threshold_response ( ) {
285- let total_weight: u64 = 100 ;
286-
287- let res = Threshold :: AbsoluteCount { weight : 42 } . to_response ( total_weight) ;
288- assert_eq ! (
289- res,
290- ThresholdResponse :: AbsoluteCount {
291- weight: 42 ,
292- total_weight
293- }
294- ) ;
295-
296- let res = Threshold :: AbsolutePercentage {
297- percentage : Decimal :: percent ( 51 ) ,
298- }
299- . to_response ( total_weight) ;
300- assert_eq ! (
301- res,
302- ThresholdResponse :: AbsolutePercentage {
303- percentage: Decimal :: percent( 51 ) ,
304- total_weight
305- }
306- ) ;
307-
308- let res = Threshold :: ThresholdQuorum {
309- threshold : Decimal :: percent ( 66 ) ,
310- quorum : Decimal :: percent ( 50 ) ,
311- }
312- . to_response ( total_weight) ;
313- assert_eq ! (
314- res,
315- ThresholdResponse :: ThresholdQuorum {
316- threshold: Decimal :: percent( 66 ) ,
317- quorum: Decimal :: percent( 50 ) ,
318- total_weight
319- }
320- ) ;
321- }
322- }
0 commit comments