stellar_contract_utils/pausable/
mod.rs

1//! Pausable Contract Module.
2//!
3//! This contract module allows implementing a configurable stop mechanism for
4//! your contract.
5//!
6//! By implementing the trait [`Pausable`] for your contract, you can safely
7//! integrate the Pausable functionality. The trait [`Pausable`] has the
8//! following methods:
9//! - [`paused()`]
10//! - [`pause()`]
11//! - [`unpause()`]
12//!
13//! The trait ensures all the required methods are implemented for your
14//! contract, and nothing is forgotten. Additionally, if you are to implement
15//! multiple extensions or utilities for your contract, the code will be better
16//! organized.
17//!
18//! We also provide two macros `when_paused` and `when_not_paused`. These macros
19//! act as guards for your functions. For example:
20//!
21//! ```ignore
22//! #[when_not_paused]
23//! fn transfer(e: &env, from: Address, to: MuxedAddress) {
24//!     /* this body will execute ONLY when NOT_PAUSED */
25//! }
26//! ```
27//!
28//! For a safe pause/unpause implementation, we expose the underlying functions
29//! required for the pausing. These functions work with the Soroban environment
30//! required for the Smart Contracts `e: &Env`, and take advantage of the
31//! storage by storing a flag for the pause mechanism.
32//!
33//! We expect you to utilize these functions (`storage::*`) for implementing the
34//! methods of the `Pausable` trait, along with your custom business logic
35//! (authentication, etc.)
36//!
37//! You can opt-out of [`Pausable`] trait, and use `storage::*` functions
38//! directly in your contract if you want more customizability. But we encourage
39//! the use of [`Pausable`] trait instead, due to the following reasons:
40//! - there is no additional cost
41//! - standardization
42//! - you cannot mistakenly forget one of the methods
43//! - your code will be better organized, especially if you implement multiple
44//!   extensions/utils
45//!
46//! TL;DR
47//! to see it all in action, check out the `examples/pausable/src/contract.rs`
48//! file.
49
50mod storage;
51
52#[cfg(test)]
53mod test;
54
55use soroban_sdk::{contracterror, contractevent, Address, Env};
56
57pub use crate::pausable::storage::{pause, paused, unpause, when_not_paused, when_paused};
58
59pub trait Pausable {
60    /// Returns true if the contract is paused, and false otherwise.
61    ///
62    /// # Arguments
63    ///
64    /// * `e` - Access to Soroban environment.
65    fn paused(e: &Env) -> bool {
66        crate::pausable::paused(e)
67    }
68
69    /// Triggers `Paused` state.
70    ///
71    /// # Arguments
72    ///
73    /// * `e` - Access to Soroban environment.
74    /// * `caller` - The address of the caller.
75    ///
76    /// # Errors
77    ///
78    /// * [`PausableError::EnforcedPause`] - Occurs when the contract is already
79    ///   in `Paused` state.
80    ///
81    /// # Events
82    ///
83    /// * topics - `["paused"]`
84    /// * data - `[]`
85    ///
86    /// # Notes
87    ///
88    /// We recommend using [`pause`] when implementing this function.
89    ///
90    /// # Security Warning
91    ///
92    /// **IMPORTANT**: The base implementation of [`pause`]
93    /// intentionally lacks authorization controls. If you want to restrict
94    /// who can `pause` the contract, you MUST implement proper
95    /// authorization in your contract.
96    fn pause(e: &Env, caller: Address);
97
98    /// Triggers `Unpaused` state.
99    ///
100    /// # Arguments
101    ///
102    /// * `e` - Access to Soroban environment.
103    /// * `caller` - The address of the caller.
104    ///
105    /// # Errors
106    ///
107    /// * [`PausableError::ExpectedPause`] - Occurs when the contract is already
108    ///   in `Unpaused` state.
109    ///
110    /// # Events
111    ///
112    /// * topics - `["unpaused"]`
113    /// * data - `[]`
114    ///
115    /// # Notes
116    ///
117    /// We recommend using [`unpause`] when implementing this function.
118    ///
119    /// # Security Warning
120    ///
121    /// **IMPORTANT**: The base implementation of [`unpause`]
122    /// intentionally lacks authorization controls. If you want to restrict
123    /// who can `unpause` the contract, you MUST implement proper
124    /// authorization in your contract.
125    fn unpause(e: &Env, caller: Address);
126}
127
128// ################## ERRORS ##################
129
130#[contracterror]
131#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
132#[repr(u32)]
133pub enum PausableError {
134    /// The operation failed because the contract is paused.
135    EnforcedPause = 1000,
136    /// The operation failed because the contract is not paused.
137    ExpectedPause = 1001,
138}
139
140// ################## EVENTS ##################
141
142/// Event emitted when the contract is paused.
143#[contractevent]
144#[derive(Clone, Debug, Eq, PartialEq)]
145pub struct Paused {}
146
147/// Emits an event when `Paused` state is triggered.
148///
149/// # Arguments
150///
151/// * `e` - The Soroban environment.
152pub fn emit_paused(e: &Env) {
153    Paused {}.publish(e);
154}
155
156/// Event emitted when the contract is unpaused.
157#[contractevent]
158#[derive(Clone, Debug, Eq, PartialEq)]
159pub struct Unpaused {}
160
161/// Emits an event when `Unpaused` state is triggered.
162///
163/// # Arguments
164///
165/// * `e` - The Soroban environment.
166pub fn emit_unpaused(e: &Env) {
167    Unpaused {}.publish(e);
168}