1use std::fmt::Debug;
2
3use cosmwasm_schema::cw_serde;
4use cosmwasm_std::{
5 Addr, Api, CustomQuery, DepsMut, MessageInfo, Response, StdError, StdResult, Storage,
6};
7use cw_storage_plus::Item;
8use schemars::JsonSchema;
9use thiserror::Error;
10
11#[cw_serde]
13pub struct OwnerResponse {
14 pub owner: Option<String>,
15 pub proposed: Option<String>,
16 pub initialized: bool,
17 pub abolished: bool,
18 #[cfg(feature = "emergency-owner")]
19 pub emergency_owner: Option<String>,
20}
21
22#[derive(Error, Debug, PartialEq)]
24pub enum OwnerError {
25 #[error("{0}")]
26 Std(#[from] StdError),
27
28 #[error("Caller is not owner")]
29 NotOwner {},
30
31 #[error("Caller is not the proposed owner")]
32 NotProposedOwner {},
33
34 #[error("Owner state transition was not valid")]
35 StateTransitionError {},
36
37 #[cfg(feature = "emergency-owner")]
38 #[error("Caller is not the emergency owner")]
39 NotEmergencyOwner {},
40}
41
42type OwnerResult<T> = Result<T, OwnerError>;
43
44#[cw_serde]
46enum OwnerState {
47 Uninitialized,
48 Base {
49 owner: Addr,
50 #[cfg(feature = "emergency-owner")]
51 emergency_owner: Option<Addr>,
52 },
53 Proposed {
54 owner: Addr,
55 proposed: Addr,
56 #[cfg(feature = "emergency-owner")]
57 emergency_owner: Option<Addr>,
58 },
59 Abolished,
60}
61
62#[cw_serde]
63pub enum OwnerUpdate {
64 ProposeNewOwner { proposed: String },
66 ClearProposed,
68 AcceptProposed,
70 AbolishOwnerRole,
72 #[cfg(feature = "emergency-owner")]
73 SetEmergencyOwner { emergency_owner: String },
75 #[cfg(feature = "emergency-owner")]
76 ClearEmergencyOwner,
78}
79
80#[cw_serde]
81pub enum OwnerInit {
82 SetInitialOwner { owner: String },
84 AbolishOwnerRole,
86}
87
88pub struct Owner<'a>(Item<'a, OwnerState>);
92
93impl<'a> Owner<'a> {
94 pub const fn new(namespace: &'a str) -> Self {
95 Self(Item::new(namespace))
96 }
97
98 fn state(&self, storage: &'a dyn Storage) -> StdResult<OwnerState> {
99 Ok(self
100 .0
101 .may_load(storage)?
102 .unwrap_or(OwnerState::Uninitialized))
103 }
104
105 pub fn current(&self, storage: &'a dyn Storage) -> StdResult<Option<Addr>> {
109 Ok(match self.state(storage)? {
110 OwnerState::Base { owner, .. } => Some(owner),
111 OwnerState::Proposed { owner, .. } => Some(owner),
112 _ => None,
113 })
114 }
115
116 pub fn is_owner(&self, storage: &'a dyn Storage, addr: &Addr) -> StdResult<bool> {
117 match self.current(storage)? {
118 Some(owner) if owner == addr => Ok(true),
119 _ => Ok(false),
120 }
121 }
122
123 pub fn proposed(&self, storage: &'a dyn Storage) -> StdResult<Option<Addr>> {
124 Ok(match self.state(storage)? {
125 OwnerState::Proposed { proposed, .. } => Some(proposed),
126 _ => None,
127 })
128 }
129
130 pub fn is_proposed(&self, storage: &'a dyn Storage, addr: &Addr) -> StdResult<bool> {
131 match self.proposed(storage)? {
132 Some(proposed) if proposed == addr => Ok(true),
133 _ => Ok(false),
134 }
135 }
136
137 #[cfg(feature = "emergency-owner")]
138 pub fn emergency_owner(&self, storage: &'a dyn Storage) -> StdResult<Option<Addr>> {
139 Ok(match self.state(storage)? {
140 OwnerState::Base {
141 emergency_owner, ..
142 } => emergency_owner,
143 OwnerState::Proposed {
144 emergency_owner, ..
145 } => emergency_owner,
146 _ => None,
147 })
148 }
149
150 #[cfg(feature = "emergency-owner")]
151 pub fn is_emergency_owner(&self, storage: &'a dyn Storage, addr: &Addr) -> StdResult<bool> {
152 match self.emergency_owner(storage)? {
153 Some(em_owner) if em_owner == addr => Ok(true),
154 _ => Ok(false),
155 }
156 }
157
158 pub fn query(&self, storage: &'a dyn Storage) -> StdResult<OwnerResponse> {
159 Ok(OwnerResponse {
160 owner: self.current(storage)?.map(Into::into),
161 proposed: self.proposed(storage)?.map(Into::into),
162 initialized: !matches!(self.state(storage)?, OwnerState::Uninitialized),
163 abolished: matches!(self.state(storage)?, OwnerState::Abolished),
164 #[cfg(feature = "emergency-owner")]
165 emergency_owner: self.emergency_owner(storage)?.map(Into::into),
166 })
167 }
168
169 pub fn initialize(
174 &self,
175 storage: &'a mut dyn Storage,
176 api: &'a dyn Api,
177 init_action: OwnerInit,
178 ) -> OwnerResult<()> {
179 let initial_state = self.state(storage)?;
180 match initial_state {
181 OwnerState::Uninitialized => {
182 let new_state = match init_action {
183 OwnerInit::SetInitialOwner { owner } => {
184 let validated = api.addr_validate(&owner)?;
185 OwnerState::Base {
186 owner: validated,
187 #[cfg(feature = "emergency-owner")]
188 emergency_owner: None,
189 }
190 }
191 OwnerInit::AbolishOwnerRole => OwnerState::Abolished,
192 };
193 self.0.save(storage, &new_state)?;
194 Ok(())
195 }
196 _ => Err(OwnerError::StateTransitionError {}),
198 }
199 }
200
201 pub fn update<C, Q: CustomQuery>(
203 &self,
204 deps: DepsMut<Q>,
205 info: MessageInfo,
206 update: OwnerUpdate,
207 ) -> OwnerResult<Response<C>>
208 where
209 C: Clone + Debug + PartialEq + JsonSchema,
210 {
211 let new_state = self.transition_state(deps.storage, deps.api, &info.sender, update)?;
212 self.0.save(deps.storage, &new_state)?;
213
214 let res = self.query(deps.storage)?;
215 Ok(Response::new()
216 .add_attribute("action", "update_owner")
217 .add_attribute("owner", res.owner.unwrap_or_else(|| "None".to_string()))
218 .add_attribute(
219 "proposed",
220 res.proposed.unwrap_or_else(|| "None".to_string()),
221 )
222 .add_attribute("sender", info.sender))
223 }
224
225 fn transition_state(
227 &self,
228 storage: &'a mut dyn Storage,
229 api: &'a dyn Api,
230 sender: &Addr,
231 event: OwnerUpdate,
232 ) -> OwnerResult<OwnerState> {
233 let state = self.state(storage)?;
234
235 let new_state = match (state, event) {
236 (
237 OwnerState::Base {
238 owner,
239 #[cfg(feature = "emergency-owner")]
240 emergency_owner,
241 ..
242 },
243 OwnerUpdate::ProposeNewOwner { proposed },
244 ) => {
245 self.assert_owner(storage, sender)?;
246 let validated = api.addr_validate(&proposed)?;
247 OwnerState::Proposed {
248 owner,
249 proposed: validated,
250 #[cfg(feature = "emergency-owner")]
251 emergency_owner,
252 }
253 }
254 #[cfg(feature = "emergency-owner")]
255 (
256 OwnerState::Base { owner, .. },
257 OwnerUpdate::SetEmergencyOwner { emergency_owner },
258 ) => {
259 self.assert_owner(storage, sender)?;
260 let validated = api.addr_validate(&emergency_owner)?;
261 OwnerState::Base {
262 owner,
263 emergency_owner: Some(validated),
264 }
265 }
266 #[cfg(feature = "emergency-owner")]
267 (OwnerState::Base { owner, .. }, OwnerUpdate::ClearEmergencyOwner) => {
268 self.assert_owner(storage, sender)?;
269 OwnerState::Base {
270 owner,
271 emergency_owner: None,
272 }
273 }
274 (OwnerState::Base { .. }, OwnerUpdate::AbolishOwnerRole) => {
275 self.assert_owner(storage, sender)?;
276 OwnerState::Abolished
277 }
278 (
279 OwnerState::Proposed {
280 proposed,
281 #[cfg(feature = "emergency-owner")]
282 emergency_owner,
283 ..
284 },
285 OwnerUpdate::AcceptProposed,
286 ) => {
287 self.assert_proposed(storage, sender)?;
288 OwnerState::Base {
289 owner: proposed,
290 #[cfg(feature = "emergency-owner")]
291 emergency_owner,
292 }
293 }
294 (
295 OwnerState::Proposed {
296 owner,
297 #[cfg(feature = "emergency-owner")]
298 emergency_owner,
299 ..
300 },
301 OwnerUpdate::ClearProposed,
302 ) => {
303 self.assert_owner(storage, sender)?;
304 OwnerState::Base {
305 owner,
306 #[cfg(feature = "emergency-owner")]
307 emergency_owner,
308 }
309 }
310 (_, _) => return Err(OwnerError::StateTransitionError {}),
311 };
312 Ok(new_state)
313 }
314
315 pub fn assert_owner(&self, storage: &'a dyn Storage, caller: &Addr) -> OwnerResult<()> {
320 if !self.is_owner(storage, caller)? {
321 Err(OwnerError::NotOwner {})
322 } else {
323 Ok(())
324 }
325 }
326
327 pub fn assert_proposed(&self, storage: &'a dyn Storage, caller: &Addr) -> OwnerResult<()> {
328 if !self.is_proposed(storage, caller)? {
329 Err(OwnerError::NotProposedOwner {})
330 } else {
331 Ok(())
332 }
333 }
334
335 #[cfg(feature = "emergency-owner")]
336 pub fn assert_emergency_owner(
337 &self,
338 storage: &'a dyn Storage,
339 caller: &Addr,
340 ) -> OwnerResult<()> {
341 if !self.is_emergency_owner(storage, caller)? {
342 Err(OwnerError::NotEmergencyOwner {})
343 } else {
344 Ok(())
345 }
346 }
347}
348
349#[cfg(test)]
350mod tests {
351
352 use crate::owner::OwnerState;
357 use crate::OwnerUpdate::{AbolishOwnerRole, AcceptProposed, ClearProposed, ProposeNewOwner};
358 #[cfg(feature = "emergency-owner")]
359 use crate::OwnerUpdate::{ClearEmergencyOwner, SetEmergencyOwner};
360 use crate::{Owner, OwnerError, OwnerInit, OwnerResponse};
361 use cosmwasm_std::testing::{mock_dependencies, mock_info};
362 use cosmwasm_std::{Addr, Empty, Storage};
363
364 #[test]
365 fn invalid_uninitialized_state_transitions() {
366 let mut deps = mock_dependencies();
367 let sender = Addr::unchecked("peter_parker");
368 let info = mock_info(sender.as_ref(), &[]);
369 let owner = Owner::new("xyz");
370
371 let err = owner
372 .update::<Empty, Empty>(
373 deps.as_mut(),
374 info.clone(),
375 ProposeNewOwner {
376 proposed: "abc".to_string(),
377 },
378 )
379 .unwrap_err();
380 assert_eq!(err, OwnerError::StateTransitionError {});
381
382 let err = owner
383 .update::<Empty, Empty>(deps.as_mut(), info.clone(), ClearProposed)
384 .unwrap_err();
385 assert_eq!(err, OwnerError::StateTransitionError {});
386
387 let err = owner
388 .update::<Empty, Empty>(deps.as_mut(), info.clone(), AcceptProposed)
389 .unwrap_err();
390 assert_eq!(err, OwnerError::StateTransitionError {});
391
392 let err = owner
393 .update::<Empty, Empty>(deps.as_mut(), info.clone(), AbolishOwnerRole)
394 .unwrap_err();
395 assert_eq!(err, OwnerError::StateTransitionError {});
396
397 #[cfg(feature = "emergency-owner")]
398 {
399 let err = owner
400 .update::<Empty, Empty>(
401 deps.as_mut(),
402 info.clone(),
403 SetEmergencyOwner {
404 emergency_owner: "xyz".to_string(),
405 },
406 )
407 .unwrap_err();
408
409 assert_eq!(err, OwnerError::StateTransitionError {});
410 let err = owner
411 .update::<Empty, Empty>(deps.as_mut(), info, ClearEmergencyOwner)
412 .unwrap_err();
413 assert_eq!(err, OwnerError::StateTransitionError {});
414 }
415 }
416
417 #[test]
418 fn invalid_owner_set_no_proposed_state_transitions() {
419 let mut deps = mock_dependencies();
420 let sender = Addr::unchecked("peter_parker");
421 let info = mock_info(sender.as_ref(), &[]);
422 let owner = Owner::new("xyz");
423
424 let mut_deps = deps.as_mut();
425
426 owner
427 .initialize(
428 mut_deps.storage,
429 mut_deps.api,
430 OwnerInit::SetInitialOwner {
431 owner: sender.to_string(),
432 },
433 )
434 .unwrap();
435
436 let err = owner
437 .initialize(
438 mut_deps.storage,
439 mut_deps.api,
440 OwnerInit::SetInitialOwner {
441 owner: "abc".to_string(),
442 },
443 )
444 .unwrap_err();
445 assert_eq!(err, OwnerError::StateTransitionError {});
446
447 let err = owner
448 .update::<Empty, Empty>(deps.as_mut(), info.clone(), ClearProposed)
449 .unwrap_err();
450 assert_eq!(err, OwnerError::StateTransitionError {});
451
452 let err = owner
453 .update::<Empty, Empty>(deps.as_mut(), info, AcceptProposed)
454 .unwrap_err();
455 assert_eq!(err, OwnerError::StateTransitionError {});
456 }
457
458 #[test]
459 fn invalid_owner_set_with_proposed_state_transitions() {
460 let mut deps = mock_dependencies();
461 let sender = Addr::unchecked("peter_parker");
462 let info = mock_info(sender.as_ref(), &[]);
463 let owner = Owner::new("xyz");
464
465 let mut_deps = deps.as_mut();
466
467 owner
468 .initialize(
469 mut_deps.storage,
470 mut_deps.api,
471 OwnerInit::SetInitialOwner {
472 owner: sender.to_string(),
473 },
474 )
475 .unwrap();
476
477 owner
478 .update::<Empty, Empty>(
479 mut_deps,
480 info.clone(),
481 ProposeNewOwner {
482 proposed: "abc".to_string(),
483 },
484 )
485 .unwrap();
486
487 let mut_deps = deps.as_mut();
488
489 let err = owner
490 .initialize(
491 mut_deps.storage,
492 mut_deps.api,
493 OwnerInit::SetInitialOwner {
494 owner: "abc".to_string(),
495 },
496 )
497 .unwrap_err();
498 assert_eq!(err, OwnerError::StateTransitionError {});
499
500 let err = owner
501 .update::<Empty, Empty>(
502 deps.as_mut(),
503 info.clone(),
504 ProposeNewOwner {
505 proposed: "efg".to_string(),
506 },
507 )
508 .unwrap_err();
509 assert_eq!(err, OwnerError::StateTransitionError {});
510
511 #[cfg(feature = "emergency-owner")]
512 {
513 let err = owner
514 .update::<Empty, Empty>(
515 deps.as_mut(),
516 info.clone(),
517 SetEmergencyOwner {
518 emergency_owner: "xyz".to_string(),
519 },
520 )
521 .unwrap_err();
522 assert_eq!(err, OwnerError::StateTransitionError {});
523
524 let err = owner
525 .update::<Empty, Empty>(deps.as_mut(), info, ClearEmergencyOwner)
526 .unwrap_err();
527 assert_eq!(err, OwnerError::StateTransitionError {});
528 }
529 }
530
531 #[test]
532 fn invalid_owner_role_abolished_state_transitions() {
533 let mut deps = mock_dependencies();
534 let sender = Addr::unchecked("peter_parker");
535 let info = mock_info(sender.as_ref(), &[]);
536 let owner = Owner::new("xyz");
537
538 let mut_deps = deps.as_mut();
539
540 owner
541 .initialize(mut_deps.storage, mut_deps.api, OwnerInit::AbolishOwnerRole)
542 .unwrap();
543
544 let err = owner
545 .initialize(
546 mut_deps.storage,
547 mut_deps.api,
548 OwnerInit::SetInitialOwner {
549 owner: "abc".to_string(),
550 },
551 )
552 .unwrap_err();
553 assert_eq!(err, OwnerError::StateTransitionError {});
554
555 let err = owner
556 .update::<Empty, Empty>(
557 deps.as_mut(),
558 info.clone(),
559 ProposeNewOwner {
560 proposed: "efg".to_string(),
561 },
562 )
563 .unwrap_err();
564 assert_eq!(err, OwnerError::StateTransitionError {});
565
566 let err = owner
567 .update::<Empty, Empty>(deps.as_mut(), info.clone(), ClearProposed)
568 .unwrap_err();
569 assert_eq!(err, OwnerError::StateTransitionError {});
570
571 let err = owner
572 .update::<Empty, Empty>(deps.as_mut(), info.clone(), AcceptProposed)
573 .unwrap_err();
574 assert_eq!(err, OwnerError::StateTransitionError {});
575
576 let err = owner
577 .update::<Empty, Empty>(deps.as_mut(), info.clone(), AbolishOwnerRole)
578 .unwrap_err();
579 assert_eq!(err, OwnerError::StateTransitionError {});
580
581 #[cfg(feature = "emergency-owner")]
582 {
583 let err = owner
584 .update::<Empty, Empty>(
585 deps.as_mut(),
586 info.clone(),
587 SetEmergencyOwner {
588 emergency_owner: "xyz".to_string(),
589 },
590 )
591 .unwrap_err();
592 assert_eq!(err, OwnerError::StateTransitionError {});
593
594 let err = owner
595 .update::<Empty, Empty>(deps.as_mut(), info, ClearEmergencyOwner)
596 .unwrap_err();
597 assert_eq!(err, OwnerError::StateTransitionError {});
598 }
599 }
600
601 #[test]
606 fn initialize_owner_permissions() {
607 let mut deps = mock_dependencies();
608 let mut_deps = deps.as_mut();
609 let owner = Owner::new("xyz");
610
611 owner
613 .initialize(mut_deps.storage, mut_deps.api, OwnerInit::AbolishOwnerRole)
614 .unwrap();
615
616 let mut deps = mock_dependencies();
617 let mut_deps = deps.as_mut();
618
619 owner
620 .initialize(
621 mut_deps.storage,
622 mut_deps.api,
623 OwnerInit::SetInitialOwner {
624 owner: "xyz".to_string(),
625 },
626 )
627 .unwrap();
628 }
629
630 #[test]
631 fn propose_new_owner_permissions() {
632 let mut deps = mock_dependencies();
633 let sender = Addr::unchecked("peter_parker");
634 let owner = Owner::new("xyz");
635
636 let mut_deps = deps.as_mut();
637 owner
638 .initialize(
639 mut_deps.storage,
640 mut_deps.api,
641 OwnerInit::SetInitialOwner {
642 owner: sender.to_string(),
643 },
644 )
645 .unwrap();
646
647 let bad_guy = Addr::unchecked("doc_oc");
648 let info = mock_info(bad_guy.as_ref(), &[]);
649 let err = owner
650 .update::<Empty, Empty>(
651 mut_deps,
652 info,
653 ProposeNewOwner {
654 proposed: bad_guy.to_string(),
655 },
656 )
657 .unwrap_err();
658
659 assert_eq!(err, OwnerError::NotOwner {})
660 }
661
662 #[test]
663 fn clear_proposed_permissions() {
664 let mut deps = mock_dependencies();
665 let sender = Addr::unchecked("peter_parker");
666 let info = mock_info(sender.as_ref(), &[]);
667 let owner = Owner::new("xyz");
668
669 let mut_deps = deps.as_mut();
670 owner
671 .initialize(
672 mut_deps.storage,
673 mut_deps.api,
674 OwnerInit::SetInitialOwner {
675 owner: sender.to_string(),
676 },
677 )
678 .unwrap();
679 owner
680 .update::<Empty, Empty>(
681 mut_deps,
682 info,
683 ProposeNewOwner {
684 proposed: "miles_morales".to_string(),
685 },
686 )
687 .unwrap();
688
689 let bad_guy = Addr::unchecked("doc_oc");
690 let info = mock_info(bad_guy.as_ref(), &[]);
691 let err = owner
692 .update::<Empty, Empty>(deps.as_mut(), info, ClearProposed)
693 .unwrap_err();
694
695 assert_eq!(err, OwnerError::NotOwner {})
696 }
697
698 #[test]
699 fn accept_proposed_permissions() {
700 let mut deps = mock_dependencies();
701 let sender = Addr::unchecked("peter_parker");
702 let info = mock_info(sender.as_ref(), &[]);
703 let owner = Owner::new("xyz");
704
705 let mut_deps = deps.as_mut();
706 owner
707 .initialize(
708 mut_deps.storage,
709 mut_deps.api,
710 OwnerInit::SetInitialOwner {
711 owner: sender.to_string(),
712 },
713 )
714 .unwrap();
715 owner
716 .update::<Empty, Empty>(
717 mut_deps,
718 info,
719 ProposeNewOwner {
720 proposed: "miles_morales".to_string(),
721 },
722 )
723 .unwrap();
724
725 let bad_guy = Addr::unchecked("doc_oc");
726 let info = mock_info(bad_guy.as_ref(), &[]);
727 let err = owner
728 .update::<Empty, Empty>(deps.as_mut(), info, AcceptProposed)
729 .unwrap_err();
730
731 assert_eq!(err, OwnerError::NotProposedOwner {})
732 }
733
734 #[test]
735 fn abolish_owner_role_permissions() {
736 let mut deps = mock_dependencies();
737 let sender = Addr::unchecked("peter_parker");
738 let owner = Owner::new("xyz");
739
740 let mut_deps = deps.as_mut();
741 owner
742 .initialize(
743 mut_deps.storage,
744 mut_deps.api,
745 OwnerInit::SetInitialOwner {
746 owner: sender.to_string(),
747 },
748 )
749 .unwrap();
750
751 let bad_guy = Addr::unchecked("doc_oc");
752 let info = mock_info(bad_guy.as_ref(), &[]);
753 let err = owner
754 .update::<Empty, Empty>(deps.as_mut(), info, AbolishOwnerRole)
755 .unwrap_err();
756
757 assert_eq!(err, OwnerError::NotOwner {})
758 }
759
760 #[cfg(feature = "emergency-owner")]
761 #[test]
762 fn set_emergency_owner_role_permissions() {
763 let mut deps = mock_dependencies();
764 let sender = Addr::unchecked("peter_parker");
765 let owner = Owner::new("xyz");
766
767 let mut_deps = deps.as_mut();
768 owner
769 .initialize(
770 mut_deps.storage,
771 mut_deps.api,
772 OwnerInit::SetInitialOwner {
773 owner: sender.to_string(),
774 },
775 )
776 .unwrap();
777
778 let bad_guy = Addr::unchecked("doc_oc");
779 let info = mock_info(bad_guy.as_ref(), &[]);
780 let err = owner
781 .update::<Empty, Empty>(
782 deps.as_mut(),
783 info,
784 SetEmergencyOwner {
785 emergency_owner: bad_guy.to_string(),
786 },
787 )
788 .unwrap_err();
789
790 assert_eq!(err, OwnerError::NotOwner {})
791 }
792
793 #[cfg(feature = "emergency-owner")]
794 #[test]
795 fn clear_emergency_owner_role_permissions() {
796 let mut deps = mock_dependencies();
797 let sender = Addr::unchecked("peter_parker");
798 let owner = Owner::new("xyz");
799
800 let mut_deps = deps.as_mut();
801 owner
802 .initialize(
803 mut_deps.storage,
804 mut_deps.api,
805 OwnerInit::SetInitialOwner {
806 owner: sender.to_string(),
807 },
808 )
809 .unwrap();
810
811 let bad_guy = Addr::unchecked("doc_oc");
812 let info = mock_info(bad_guy.as_ref(), &[]);
813 let err = owner
814 .update::<Empty, Empty>(deps.as_mut(), info, ClearEmergencyOwner)
815 .unwrap_err();
816
817 assert_eq!(err, OwnerError::NotOwner {})
818 }
819
820 fn assert_uninitialized(storage: &dyn Storage, owner: &Owner) {
825 let state = owner.state(storage).unwrap();
826 match state {
827 OwnerState::Uninitialized => {}
828 _ => panic!("Should be in the Uninitialized state"),
829 }
830
831 let current = owner.current(storage).unwrap();
832 assert_eq!(current, None);
833
834 let proposed = owner.proposed(storage).unwrap();
835 assert_eq!(proposed, None);
836
837 let res = owner.query(storage).unwrap();
838 assert_eq!(
839 res,
840 OwnerResponse {
841 owner: None,
842 proposed: None,
843 initialized: false,
844 abolished: false,
845 #[cfg(feature = "emergency-owner")]
846 emergency_owner: None,
847 }
848 );
849 }
850
851 #[test]
852 fn uninitialized_state() {
853 let deps = mock_dependencies();
854 let owner = Owner::new("xyz");
855 assert_uninitialized(deps.as_ref().storage, &owner);
856 }
857
858 #[test]
859 fn initialize_owner() {
860 let mut deps = mock_dependencies();
861 let original_owner = Addr::unchecked("peter_parker");
862 let owner = Owner::new("xyz");
863
864 let mut_deps = deps.as_mut();
865 owner
866 .initialize(
867 mut_deps.storage,
868 mut_deps.api,
869 OwnerInit::SetInitialOwner {
870 owner: original_owner.to_string(),
871 },
872 )
873 .unwrap();
874
875 let state = owner.state(mut_deps.storage).unwrap();
876 match state {
877 OwnerState::Base { .. } => {}
878 _ => panic!("Should be in the Base state"),
879 }
880
881 let current = owner.current(mut_deps.storage).unwrap();
882 assert_eq!(current, Some(original_owner.clone()));
883 assert!(owner.is_owner(mut_deps.storage, &original_owner).unwrap());
884
885 let proposed = owner.proposed(mut_deps.storage).unwrap();
886 assert_eq!(proposed, None);
887
888 let res = owner.query(mut_deps.storage).unwrap();
889 assert_eq!(
890 res,
891 OwnerResponse {
892 owner: Some(original_owner.to_string()),
893 proposed: None,
894 initialized: true,
895 abolished: false,
896 #[cfg(feature = "emergency-owner")]
897 emergency_owner: None,
898 }
899 );
900 }
901
902 #[test]
903 fn propose_new_owner() {
904 let mut deps = mock_dependencies();
905 let original_owner = Addr::unchecked("peter_parker");
906 let proposed_owner = Addr::unchecked("miles_morales");
907 let info = mock_info(original_owner.as_ref(), &[]);
908 let owner = Owner::new("xyz");
909
910 let mut_deps = deps.as_mut();
911 owner
912 .initialize(
913 mut_deps.storage,
914 mut_deps.api,
915 OwnerInit::SetInitialOwner {
916 owner: original_owner.to_string(),
917 },
918 )
919 .unwrap();
920
921 owner
922 .update::<Empty, Empty>(
923 mut_deps,
924 info,
925 ProposeNewOwner {
926 proposed: "miles_morales".to_string(),
927 },
928 )
929 .unwrap();
930
931 let storage = deps.as_mut().storage;
932
933 let state = owner.state(storage).unwrap();
934 match state {
935 OwnerState::Proposed { .. } => {}
936 _ => panic!("Should be in the Proposed state"),
937 }
938
939 let current = owner.current(storage).unwrap();
940 assert_eq!(current, Some(original_owner.clone()));
941 assert!(owner.is_owner(storage, &original_owner).unwrap());
942
943 let proposed = owner.proposed(storage).unwrap();
944 assert_eq!(proposed, Some(proposed_owner.clone()));
945 assert!(owner.is_proposed(storage, &proposed_owner).unwrap());
946
947 let res = owner.query(storage).unwrap();
948 assert_eq!(
949 res,
950 OwnerResponse {
951 owner: Some(original_owner.to_string()),
952 proposed: Some(proposed_owner.to_string()),
953 initialized: true,
954 abolished: false,
955 #[cfg(feature = "emergency-owner")]
956 emergency_owner: None,
957 }
958 );
959 }
960
961 #[test]
962 fn clear_proposed() {
963 let mut deps = mock_dependencies();
964 let original_owner = Addr::unchecked("peter_parker");
965 let proposed_owner = Addr::unchecked("miles_morales");
966 let info = mock_info(original_owner.as_ref(), &[]);
967 let owner = Owner::new("xyz");
968
969 let mut_deps = deps.as_mut();
970 owner
971 .initialize(
972 mut_deps.storage,
973 mut_deps.api,
974 OwnerInit::SetInitialOwner {
975 owner: original_owner.to_string(),
976 },
977 )
978 .unwrap();
979
980 let mut_deps = deps.as_mut();
981 owner
982 .update::<Empty, Empty>(
983 mut_deps,
984 info.clone(),
985 ProposeNewOwner {
986 proposed: "miles_morales".to_string(),
987 },
988 )
989 .unwrap();
990
991 let mut_deps = deps.as_mut();
992 owner
993 .update::<Empty, Empty>(mut_deps, info, ClearProposed)
994 .unwrap();
995
996 let storage = deps.as_mut().storage;
997
998 let state = owner.state(storage).unwrap();
999 match state {
1000 OwnerState::Base { .. } => {}
1001 _ => panic!("Should be in the Base state"),
1002 }
1003
1004 let current = owner.current(storage).unwrap();
1005 assert_eq!(current, Some(original_owner.clone()));
1006 assert!(owner.is_owner(storage, &original_owner).unwrap());
1007
1008 let proposed = owner.proposed(storage).unwrap();
1009 assert_eq!(proposed, None);
1010 assert!(!owner.is_proposed(storage, &proposed_owner).unwrap());
1011
1012 let res = owner.query(storage).unwrap();
1013 assert_eq!(
1014 res,
1015 OwnerResponse {
1016 owner: Some(original_owner.to_string()),
1017 proposed: None,
1018 initialized: true,
1019 abolished: false,
1020 #[cfg(feature = "emergency-owner")]
1021 emergency_owner: None,
1022 }
1023 );
1024 }
1025
1026 #[test]
1027 fn accept_proposed() {
1028 let mut deps = mock_dependencies();
1029 let original_owner = Addr::unchecked("peter_parker");
1030 let proposed_owner = Addr::unchecked("miles_morales");
1031 let info = mock_info(original_owner.as_ref(), &[]);
1032 let owner = Owner::new("xyz");
1033
1034 let mut_deps = deps.as_mut();
1035 owner
1036 .initialize(
1037 mut_deps.storage,
1038 mut_deps.api,
1039 OwnerInit::SetInitialOwner {
1040 owner: original_owner.to_string(),
1041 },
1042 )
1043 .unwrap();
1044
1045 let mut_deps = deps.as_mut();
1046 owner
1047 .update::<Empty, Empty>(
1048 mut_deps,
1049 info,
1050 ProposeNewOwner {
1051 proposed: "miles_morales".to_string(),
1052 },
1053 )
1054 .unwrap();
1055
1056 let info = mock_info(proposed_owner.as_ref(), &[]);
1057 let mut_deps = deps.as_mut();
1058 owner
1059 .update::<Empty, Empty>(mut_deps, info, AcceptProposed)
1060 .unwrap();
1061
1062 let storage = deps.as_mut().storage;
1063
1064 let state = owner.state(storage).unwrap();
1065 match state {
1066 OwnerState::Base { .. } => {}
1067 _ => panic!("Should be in the Base state"),
1068 }
1069
1070 let current = owner.current(storage).unwrap();
1071 assert_eq!(current, Some(proposed_owner.clone()));
1072 assert!(owner.is_owner(storage, &proposed_owner).unwrap());
1073
1074 let proposed = owner.proposed(storage).unwrap();
1075 assert_eq!(proposed, None);
1076 assert!(!owner.is_proposed(storage, &proposed_owner).unwrap());
1077
1078 let res = owner.query(storage).unwrap();
1079 assert_eq!(
1080 res,
1081 OwnerResponse {
1082 owner: Some(proposed_owner.to_string()),
1083 proposed: None,
1084 initialized: true,
1085 abolished: false,
1086 #[cfg(feature = "emergency-owner")]
1087 emergency_owner: None,
1088 }
1089 );
1090 }
1091
1092 #[test]
1093 fn abolish_owner_role() {
1094 let mut deps = mock_dependencies();
1095 let original_owner = Addr::unchecked("peter_parker");
1096 let info = mock_info(original_owner.as_ref(), &[]);
1097 let owner = Owner::new("xyz");
1098
1099 let mut_deps = deps.as_mut();
1100 owner
1101 .initialize(
1102 mut_deps.storage,
1103 mut_deps.api,
1104 OwnerInit::SetInitialOwner {
1105 owner: original_owner.to_string(),
1106 },
1107 )
1108 .unwrap();
1109
1110 let mut_deps = deps.as_mut();
1111 owner
1112 .update::<Empty, Empty>(mut_deps, info, AbolishOwnerRole)
1113 .unwrap();
1114
1115 let storage = deps.as_mut().storage;
1116
1117 let state = owner.state(storage).unwrap();
1118 match state {
1119 OwnerState::Abolished => {}
1120 _ => panic!("Should be in the Abolished state"),
1121 }
1122
1123 let current = owner.current(storage).unwrap();
1124 assert_eq!(current, None);
1125 assert!(!owner.is_owner(storage, &original_owner).unwrap());
1126
1127 let proposed = owner.proposed(storage).unwrap();
1128 assert_eq!(proposed, None);
1129 assert!(!owner.is_proposed(storage, &original_owner).unwrap());
1130
1131 let res = owner.query(storage).unwrap();
1132 assert_eq!(
1133 res,
1134 OwnerResponse {
1135 owner: None,
1136 proposed: None,
1137 initialized: true,
1138 abolished: true,
1139 #[cfg(feature = "emergency-owner")]
1140 emergency_owner: None,
1141 }
1142 );
1143 }
1144
1145 #[cfg(feature = "emergency-owner")]
1146 #[test]
1147 fn set_emergency_owner() {
1148 let mut deps = mock_dependencies();
1149 let original_owner = Addr::unchecked("peter_parker");
1150 let emergency_owner = Addr::unchecked("miles_morales");
1151 let info = mock_info(original_owner.as_ref(), &[]);
1152 let owner = Owner::new("xyz");
1153
1154 let mut_deps = deps.as_mut();
1155
1156 owner
1157 .initialize(
1158 mut_deps.storage,
1159 mut_deps.api,
1160 OwnerInit::SetInitialOwner {
1161 owner: original_owner.to_string(),
1162 },
1163 )
1164 .unwrap();
1165
1166 let current = owner.current(mut_deps.storage).unwrap();
1167 assert_eq!(current, Some(original_owner.clone()));
1168 assert!(owner.is_owner(mut_deps.storage, &original_owner).unwrap());
1169
1170 let em_owner = owner.emergency_owner(mut_deps.storage).unwrap();
1171 assert_eq!(em_owner, None);
1172 assert!(!owner
1173 .is_emergency_owner(mut_deps.storage, &emergency_owner)
1174 .unwrap());
1175
1176 let res = owner.query(mut_deps.storage).unwrap();
1177 assert_eq!(
1178 res,
1179 OwnerResponse {
1180 owner: Some(original_owner.to_string()),
1181 proposed: None,
1182 initialized: true,
1183 abolished: false,
1184 emergency_owner: None,
1185 }
1186 );
1187
1188 owner
1189 .update::<Empty, Empty>(
1190 mut_deps,
1191 info,
1192 SetEmergencyOwner {
1193 emergency_owner: emergency_owner.to_string(),
1194 },
1195 )
1196 .unwrap();
1197
1198 let storage = deps.as_ref().storage;
1199
1200 let current = owner.current(storage).unwrap();
1201 assert_eq!(current, Some(original_owner.clone()));
1202 assert!(owner.is_owner(storage, &original_owner).unwrap());
1203
1204 let em_owner = owner.emergency_owner(storage).unwrap();
1205 assert_eq!(em_owner, Some(emergency_owner.clone()));
1206 assert!(owner.is_emergency_owner(storage, &emergency_owner).unwrap());
1207
1208 let state = owner.state(storage).unwrap();
1209 match state {
1210 OwnerState::Base { .. } => {}
1211 _ => panic!("Should be in the Base state"),
1212 }
1213
1214 let res = owner.query(storage).unwrap();
1215 assert_eq!(
1216 res,
1217 OwnerResponse {
1218 owner: Some(original_owner.to_string()),
1219 proposed: None,
1220 emergency_owner: Some(emergency_owner.to_string()),
1221 initialized: true,
1222 abolished: false,
1223 }
1224 );
1225 }
1226
1227 #[cfg(feature = "emergency-owner")]
1228 #[test]
1229 fn clear_emergency_owner() {
1230 let mut deps = mock_dependencies();
1231 let original_owner = Addr::unchecked("peter_parker");
1232 let emergency_owner = Addr::unchecked("miles_morales");
1233 let info = mock_info(original_owner.as_ref(), &[]);
1234 let owner = Owner::new("xyz");
1235
1236 let mut_deps = deps.as_mut();
1237
1238 owner
1239 .initialize(
1240 mut_deps.storage,
1241 mut_deps.api,
1242 OwnerInit::SetInitialOwner {
1243 owner: original_owner.to_string(),
1244 },
1245 )
1246 .unwrap();
1247
1248 owner
1249 .update::<Empty, Empty>(
1250 mut_deps,
1251 info.clone(),
1252 SetEmergencyOwner {
1253 emergency_owner: emergency_owner.to_string(),
1254 },
1255 )
1256 .unwrap();
1257
1258 owner
1259 .update::<Empty, Empty>(deps.as_mut(), info, ClearEmergencyOwner)
1260 .unwrap();
1261
1262 let storage = deps.as_ref().storage;
1263
1264 let current = owner.current(storage).unwrap();
1265 assert_eq!(current, Some(original_owner.clone()));
1266 assert!(owner.is_owner(storage, &original_owner).unwrap());
1267
1268 let em_owner = owner.emergency_owner(storage).unwrap();
1269 assert_eq!(em_owner, None);
1270 assert!(!owner.is_emergency_owner(storage, &emergency_owner).unwrap());
1271
1272 let state = owner.state(storage).unwrap();
1273 match state {
1274 OwnerState::Base { .. } => {}
1275 _ => panic!("Should be in the Base state"),
1276 }
1277
1278 let res = owner.query(storage).unwrap();
1279 assert_eq!(
1280 res,
1281 OwnerResponse {
1282 owner: Some(original_owner.to_string()),
1283 proposed: None,
1284 initialized: true,
1285 abolished: false,
1286 emergency_owner: None,
1287 }
1288 );
1289 }
1290}