Skip to content
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
19 changes: 19 additions & 0 deletions contracts/geev-core/src/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,22 @@ pub fn check_admin(env: &Env) -> Address {

admin
}

/// Guard for value-moving entrypoints: panics when the contract is paused.
///
/// The paused flag is stored as `DataKey::Paused` in instance storage and
/// defaults to `false` (not paused) when the key is absent.
///
/// # Panics
/// Panics with [`Error::ContractPaused`] if `DataKey::Paused` is `true`.
pub fn require_not_paused(env: &Env) {
let paused: bool = env
.storage()
.instance()
.get(&DataKey::Paused)
.unwrap_or(false);

if paused {
panic_with_error!(env, Error::ContractPaused);
}
}
39 changes: 39 additions & 0 deletions contracts/geev-core/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ pub struct AdminTransferred {
new_admin: Address,
}

/// Emitted when the contract is paused by an admin.
#[contractevent]
pub struct ContractPaused {
admin: Address,
}

/// Emitted when the contract is unpaused by an admin.
#[contractevent]
pub struct ContractUnpaused {
admin: Address,
}

#[contractimpl]
impl AdminContract {
/// Emergency withdraw function - callable only by Admin
Expand Down Expand Up @@ -256,4 +268,31 @@ impl AdminContract {
.instance()
.set(&DataKey::TokenFee(token), &fee_bps);
}

/// Pause the contract — blocks all value-moving entrypoints.
///
/// Callable only by the admin. Emits [`ContractPaused`] on each
/// transition. `admin_withdraw` and `unpause` remain callable while
/// paused.
///
/// # Panics
/// Panics if called by a non-admin address.
pub fn pause(env: Env) {
let admin = check_admin(&env);
env.storage().instance().set(&DataKey::Paused, &true);
ContractPaused { admin }.publish(&env);
}

/// Unpause the contract — restores all value-moving entrypoints.
///
/// Callable only by the admin, including while the contract is paused.
/// Emits [`ContractUnpaused`] on each transition.
///
/// # Panics
/// Panics if called by a non-admin address.
pub fn unpause(env: Env) {
let admin = check_admin(&env);
env.storage().instance().set(&DataKey::Paused, &false);
ContractUnpaused { admin }.publish(&env);
}
}
4 changes: 4 additions & 0 deletions contracts/geev-core/src/giveaway.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![allow(clippy::too_many_arguments)]
use crate::access::require_not_paused;
use crate::profile::ProfileContract;
use crate::types::{
DataKey, Error, Giveaway, GiveawayStatus, ParticipantVerification, SelectionMethod,
Expand Down Expand Up @@ -90,6 +91,7 @@ impl GiveawayContract {
selection_method: SelectionMethod,
fee_bps: Option<u32>,
) -> u64 {
require_not_paused(&env);
creator.require_auth();

if winner_count == 0 {
Expand Down Expand Up @@ -168,6 +170,7 @@ impl GiveawayContract {
}

pub fn enter_giveaway(env: Env, participant: Address, giveaway_id: u64) {
require_not_paused(&env);
participant.require_auth();

let giveaway_key = DataKey::Giveaway(giveaway_id);
Expand Down Expand Up @@ -369,6 +372,7 @@ impl GiveawayContract {
/// Called by an individual winner to claim their share of the prize
/// while the giveaway is `Claimable` and before `claim_deadline`.
pub fn claim_prize(env: Env, giveaway_id: u64, winner: Address) {
require_not_paused(&env);
winner.require_auth();

with_reentrancy_guard(&env, || {
Expand Down
3 changes: 3 additions & 0 deletions contracts/geev-core/src/mutual_aid.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::access::require_not_paused;
use crate::types::{DataKey, Error, HelpRequest, HelpRequestStatus};
use crate::utils::with_reentrancy_guard;
use soroban_sdk::{contract, contractevent, contractimpl, panic_with_error, token, Address, Env};
Expand Down Expand Up @@ -100,6 +101,7 @@ impl MutualAidContract {
}

pub fn donate(env: Env, donor: Address, request_id: u64, amount: i128) {
require_not_paused(&env);
donor.require_auth();

if amount <= 0 {
Expand Down Expand Up @@ -210,6 +212,7 @@ impl MutualAidContract {
/// The request moves to `Closed` and a one-shot claim record is written, so the
/// payout cannot be repeated.
pub fn claim_help_request_funds(env: Env, creator: Address, request_id: u64) {
require_not_paused(&env);
creator.require_auth();

with_reentrancy_guard(&env, || {
Expand Down
Loading
Loading