1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//! Contract method pausing/unpausing

use crate::{event::Event, near_contract_tools};
use near_contract_tools_macros::Event;
use near_sdk::require;
use serde::Serialize;

/// Events emitted when contract pause state is changed
#[derive(Event, Serialize)]
#[event(standard = "x-paus", version = "1.0.0", rename_all = "snake_case")]
#[serde(untagged)]
pub enum PausableEvent {
    /// Emitted when the contract is paused
    Pause,
    /// Emitted when the contract is unpaused
    Unpause,
}

/// Externally-accessible interface for a pausable contract
pub trait Pausable {
    /// Returns `true` if the contract is paused, `false` otherwise
    fn paus_is_paused(&self) -> bool;
}

/// Internal-only interactions for a pausable contract
///
/// # Examples
///
/// ```
/// use near_sdk::near_bindgen;
/// use near_contract_tools::{
///     pausable::{Pausable, PausableController},
///     Pausable,
/// };
///
/// #[derive(Pausable)]
/// #[near_bindgen]
/// struct Contract {
///     // ...
/// }
///
/// #[near_bindgen]
/// impl Contract {
///     pub fn only_when_unpaused(&self) {
///         self.require_unpaused();
///     }
///
///     pub fn only_when_paused(&self) {
///         self.require_paused();
///     }
///
///     pub fn emergency_shutdown(&self) {
///         self.pause();
///     }
///
///     pub fn emergency_shutdown_end(&self) {
///         self.unpause();
///     }
/// }
/// ```
pub trait PausableController {
    /// Force the contract pause state in a particular direction.
    /// Does not emit events or check the current pause state.
    fn set_is_paused(&self, is_paused: bool);
    /// Returns `true` if the contract is paused, `false` otherwise
    fn is_paused(&self) -> bool;

    /// Pauses the contract if it is currently unpaused, panics otherwise.
    /// Emits a `PausableEvent::Pause` event.
    fn pause(&self) {
        self.require_unpaused();
        self.set_is_paused(true);
        PausableEvent::Pause.emit();
    }

    /// Unpauses the contract if it is currently paused, panics otherwise.
    /// Emits a `PausableEvent::Unpause` event.
    fn unpause(&self) {
        self.require_paused();
        self.set_is_paused(false);
        PausableEvent::Unpause.emit();
    }

    /// Rejects if the contract is unpaused
    fn require_paused(&self) {
        require!(self.is_paused(), "Disallowed while contract is unpaused");
    }

    /// Rejects if the contract is paused
    fn require_unpaused(&self) {
        require!(!self.is_paused(), "Disallowed while contract is paused");
    }
}