Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pausable inline docs #9

Merged
merged 5 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion contracts/utils/pausable/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,60 @@
//! Pausable Contract Module.
//!
//! This contract module allows implementing a configurable stop mechanism for
//! your contract.
//!
//! By implementing the trait [`Pausable`] for your contract,
//! you can safely integrate the Pausable functionality.
//! The trait [`Pausable`] has the following methods:
//! - [`paused()`]
//! - [`pause()`]
//! - [`unpause()`]
//!
//! The trait ensures all the required methods are implemented for your
//! contract, and nothing is forgotten. Additionally, if you are to implement
//! multiple extensions or utilities for your contract, the code will be better
//! organized.
//!
//! We also provide two macros `when_paused` and `when_not_paused` (will be
//! implemented later). These macros act as guards for your functions. For
//! example:
//!
//! ```ignore
//! #[when_not_paused]
//! fn transfer(e: &env, from: Address, to: Address) {
//! /* this body will execute ONLY when NOT_PAUSED */
//! }
//! ```
//!
//! For a safe pause/unpause implementation, we expose the underlying
//! functions required for the pausing. These functions work with the Soroban
//! environment required for the Smart Contracts `e: &Env`, and take advantage
//! of the storage by storing a flag for the pause mechanism.
//!
//! We expect you to utilize these functions (`storage::*`) for implementing
//! the methods of the `Pausable` trait, along with your custom business logic
//! (authentication, etc.)
//!
//! For god knows why, if you want to opt-out of [`Pausable`] trait, and use
//! `storage::*` functions directly in your contract, well... you can!
//! But we encourage the use of [`Pausable`] trait instead, due to:
//! - there is no additional cost
//! - standardization
//! - you cannot mistakenly forget one of the methods
//! - your code will be better organized, especially if you implement multiple
//! extensions/utils
//!
//! TL;DR
//! to see it all in action, check out the `examples/pausable/src/contract.rs`
//! file.

#![no_std]

mod pausable;
mod storage;

pub use crate::{
pausable::{emit_paused, emit_unpaused, Pausable, PausableClient},
pausable::{emit_paused, emit_unpaused, Pausable, PausableClient, PausableError},
storage::{pause, paused, unpause, when_not_paused, when_paused},
};

Expand Down
27 changes: 15 additions & 12 deletions contracts/utils/pausable/src/pausable.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
//! Pausable Contract Module.
//!
//! Contract module which allows implementing an emergency stop mechanism
//! that can be triggered by an authorized account.
//!
//! It provides functions [`pausable::when_not_paused`]
//! and [`pausable::when_paused`],
//! which can be added to the functions of your contract.
//!
//! Note that your contract will NOT be pausable by simply including this
//! module only once and where you use [`pausable::when_not_paused`].

use soroban_sdk::{contractclient, contracterror, symbol_short, Address, Env};

#[contractclient(name = "PausableClient")]
Expand All @@ -19,6 +7,11 @@ pub trait Pausable {
/// # Arguments
///
/// * `e` - Access to Soroban environment.
///
/// # Notes
///
/// We expect you to use the [`crate::storage::paused()`] function from
/// the `storage` module when implementing this function.
fn paused(e: Env) -> bool;

/// Triggers `Paused` state.
Expand All @@ -37,6 +30,11 @@ pub trait Pausable {
///
/// * topics - `["paused"]`
/// * data - `[caller: Address]`
///
/// # Notes
///
/// We expect you to use the [`crate::storage::pause()`] function from
/// the `storage` module.
fn pause(e: Env, caller: Address);

/// Triggers `Unpaused` state.
Expand All @@ -55,6 +53,11 @@ pub trait Pausable {
///
/// * topics - `["unpaused"]`
/// * data - `[caller: Address]`
///
/// # Notes
///
/// We expect you to use the [`crate::storage::unpause()`] function
/// from the `storage` module.
fn unpause(e: Env, caller: Address);
}

Expand Down
20 changes: 20 additions & 0 deletions contracts/utils/pausable/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ pub(crate) const PAUSED: Symbol = symbol_short!("PAUSED");
/// # Arguments
///
/// * `e` - Access to Soroban environment.
///
/// # Notes
///
/// no authentication is required for this function.
pub fn paused(e: &Env) -> bool {
// if not paused, consider default false (unpaused)
e.storage().instance().get(&PAUSED).unwrap_or(false)
Expand All @@ -31,6 +35,10 @@ pub fn paused(e: &Env) -> bool {
///
/// * topics - `["paused"]`
/// * data - `[caller: Address]`
///
/// # Notes
///
/// Authentication is required for this function.
pub fn pause(e: &Env, caller: &Address) {
caller.require_auth();
when_not_paused(e);
Expand All @@ -54,6 +62,10 @@ pub fn pause(e: &Env, caller: &Address) {
///
/// * topics - `["unpaused"]`
/// * data - `[caller: Address]`
///
/// # Notes
///
/// Authentication is required for this function.
pub fn unpause(e: &Env, caller: &Address) {
caller.require_auth();
when_paused(e);
Expand All @@ -72,6 +84,10 @@ pub fn unpause(e: &Env, caller: &Address) {
///
/// If the contract is in the `Paused` state, then the error
/// [`PausableError::EnforcedPause`] is thrown.
///
/// # Notes
///
/// No authentication is required for this function.
pub fn when_not_paused(e: &Env) {
if paused(e) {
panic_with_error!(e, PausableError::EnforcedPause)
Expand All @@ -89,6 +105,10 @@ pub fn when_not_paused(e: &Env) {
///
/// If the contract is in `Unpaused` state, then the error
/// [`PausableError::ExpectedPause`] is thrown.
///
/// # Notes
///
/// No authentication is required for this function.
pub fn when_paused(e: &Env) {
if !paused(e) {
panic_with_error!(e, PausableError::ExpectedPause)
Expand Down
8 changes: 5 additions & 3 deletions examples/pausable/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//! Pausable Example Contract.
//!
//! Demonstrates an example usage of `openzeppelin_pausable` moddule by implementing
//! an emergency stop mechanism that can be triggered only by the owner account.
//! Demonstrates an example usage of `openzeppelin_pausable` moddule by
//! implementing an emergency stop mechanism that can be triggered only by the
//! owner account.
//!
//! Counter can be incremented only when `unpaused` and reset only when `paused`.
//! Counter can be incremented only when `unpaused` and reset only when
//! `paused`.

use openzeppelin_pausable::{self as pausable, Pausable};
use soroban_sdk::{
Expand Down
2 changes: 2 additions & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ reorder_impl_items = true
group_imports = "StdExternalCrate"
use_field_init_shorthand = true
use_small_heuristics = "Max"
wrap_comments = true
format_code_in_doc_comments = true

# most of these are unstable, so we enable them
unstable_features = true
Expand Down
Loading