cw_pause_once/
lib.rs

1//! This provides a simple type, `PauseOrchestrator`, that allows a
2//! specified address to execute a pause a single time.
3
4use 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    /// Creates a new pause orchestrator using the provided storage
30    /// keys.
31    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    /// Sets a new pauser who may pause the contract. If the contract
39    /// is paused, it is unpaused.
40    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    /// Errors if the module is paused, does nothing otherwise.
52    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    /// Pauses the module and removes the previous pauser's ability to
61    /// pause.
62    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    /// Gets the pause policy for this orchestrator. If there is no
78    /// pause policy (the orchestrator may not be paused), returns
79    /// None.
80    pub fn query_pauser(&self, storage: &dyn Storage) -> StdResult<Option<Addr>> {
81        self.pauser.load(storage)
82    }
83
84    /// Gets when this orchestrator will unpause. If the orchestrator
85    /// is not paused, returns None.
86    pub fn query_paused(&self, storage: &dyn Storage) -> StdResult<bool> {
87        self.paused.load(storage)
88    }
89}