stellar_contract_utils/pausable/
storage.rs

1use soroban_sdk::{contracttype, panic_with_error, Env};
2
3use crate::pausable::{emit_paused, emit_unpaused, PausableError};
4
5/// Storage key for the pausable state
6#[contracttype]
7pub enum PausableStorageKey {
8    /// Indicates whether the contract is in paused state.
9    Paused,
10}
11
12/// Returns true if the contract is paused, and false otherwise.
13///
14/// # Arguments
15///
16/// * `e` - Access to Soroban environment.
17pub fn paused(e: &Env) -> bool {
18    // if not paused, consider default false (unpaused)
19    e.storage().instance().get(&PausableStorageKey::Paused).unwrap_or(false)
20
21    // NOTE: We don't extend the TTL here. We don’t think utilities should
22    // have any opinion on the TTLs, contracts usually manage TTL's themselves.
23    // Extending the TTL in the utilities would be redundant in the most cases.
24}
25
26/// Triggers paused state.
27///
28/// # Arguments
29///
30/// * `e` - Access to Soroban environment.
31///
32/// # Errors
33///
34/// * refer to [`when_not_paused`] errors.
35///
36/// # Events
37///
38/// * topics - `["paused"]`
39/// * data - `[]`
40///
41/// # Security Warning
42///
43/// **IMPORTANT**: This function lacks authorization checks and should only
44/// be used in admin functions that implement their own authorization logic.
45///
46/// Example:
47///
48/// ```ignore,rust
49/// use stellar_access_control_macros::only_role;
50///
51/// #[only_role(operator, "pauser")] // `only_role` handles authorization
52/// fn emergency_pause(e: &Env, operator: Address) {
53///     pausable::pause(e);
54/// }
55/// ```
56pub fn pause(e: &Env) {
57    when_not_paused(e);
58    e.storage().instance().set(&PausableStorageKey::Paused, &true);
59    emit_paused(e);
60}
61
62/// Triggers unpaused state.
63///
64/// # Arguments
65///
66/// * `e` - Access to Soroban environment.
67///
68/// # Errors
69///
70/// * refer to [`when_paused`] errors.
71///
72/// # Events
73///
74/// * topics - `["unpaused"]`
75/// * data - `[]`
76///
77/// # Security Warning
78///
79/// **IMPORTANT**: This function lacks authorization checks and should only
80/// be used in admin functions that implement their own authorization logic.
81///
82/// Example:
83///
84/// ```ignore,rust
85/// use stellar_access_control_macros::only_role;
86///
87/// #[only_role(operator, "unpauser")] // `only_role` handles authorization
88/// fn unpause(e: &Env, operator: Address) {
89///     pausable::unpause(e);
90/// }
91/// ```
92pub fn unpause(e: &Env) {
93    when_paused(e);
94    e.storage().instance().set(&PausableStorageKey::Paused, &false);
95    emit_unpaused(e);
96}
97
98/// Helper to make a function callable only when the contract is NOT paused.
99///
100/// # Arguments
101///
102/// * `e` - Access to Soroban environment.
103///
104/// # Errors
105///
106/// * [`PausableError::EnforcedPause`] - Occurs when the contract is already in
107///   `Paused` state.
108pub fn when_not_paused(e: &Env) {
109    if paused(e) {
110        panic_with_error!(e, PausableError::EnforcedPause);
111    }
112}
113
114/// Helper to make a function callable only when the contract is paused.
115///
116/// # Arguments
117///
118/// * `e` - Access to Soroban environment.
119///
120/// # Errors
121///
122/// * [`PausableError::ExpectedPause`] - Occurs when the contract is already in
123///   `Unpaused` state.
124pub fn when_paused(e: &Env) {
125    if !paused(e) {
126        panic_with_error!(e, PausableError::ExpectedPause);
127    }
128}