1use cosmwasm_std::{Addr, Api, StdError, StdResult, Storage};
5use cw_storage_plus::Item;
6use thiserror::Error;
7
8#[cfg(test)]
9mod tests;
10
11#[derive(Error, Debug, PartialEq)]
12pub enum PauseError {
13 #[error(transparent)]
14 Std(#[from] StdError),
15
16 #[error("contract is paused pending governance intervention")]
17 Paused {},
18
19 #[error("unauthorized pauser ({sender})")]
20 Unauthorized { sender: Addr },
21}
22
23pub struct PauseOrchestrator<'a> {
24 pub pauser: Item<'a, Option<Addr>>,
25 pub paused: Item<'a, bool>,
26}
27
28impl<'a> PauseOrchestrator<'a> {
29 pub const fn new(pauser_key: &'a str, paused_key: &'a str) -> Self {
32 Self {
33 pauser: Item::new(pauser_key),
34 paused: Item::new(paused_key),
35 }
36 }
37
38 pub fn set_pauser(
41 &self,
42 storage: &mut dyn Storage,
43 api: &dyn Api,
44 pauser: Option<&str>,
45 ) -> StdResult<()> {
46 self.pauser
47 .save(storage, &pauser.map(|h| api.addr_validate(h)).transpose()?)?;
48 self.paused.save(storage, &false)
49 }
50
51 pub fn error_if_paused(&self, storage: &dyn Storage) -> Result<(), PauseError> {
53 if self.paused.load(storage)? {
54 Err(PauseError::Paused {})
55 } else {
56 Ok(())
57 }
58 }
59
60 pub fn pause(&self, storage: &mut dyn Storage, sender: &Addr) -> Result<(), PauseError> {
63 self.error_if_paused(storage)?;
64
65 let pauser = self.pauser.load(storage)?;
66 if pauser.as_ref().map_or(true, |pauser| sender != pauser) {
67 Err(PauseError::Unauthorized {
68 sender: sender.clone(),
69 })
70 } else {
71 self.pauser.save(storage, &None)?;
72 self.paused.save(storage, &true)?;
73 Ok(())
74 }
75 }
76
77 pub fn query_pauser(&self, storage: &dyn Storage) -> StdResult<Option<Addr>> {
81 self.pauser.load(storage)
82 }
83
84 pub fn query_paused(&self, storage: &dyn Storage) -> StdResult<bool> {
87 self.paused.load(storage)
88 }
89}