Skip to main content

cougr_core/standards/
execution_guard.rs

1use soroban_sdk::{contracttype, Env, Symbol};
2
3use super::error::StandardsError;
4
5const EXECUTION_LOCK_PREFIX: &str = "std_exec";
6
7/// Storage-backed execution lock for sensitive state transitions.
8#[derive(Clone, Debug)]
9pub struct ExecutionGuard {
10    id: Symbol,
11}
12
13#[contracttype]
14#[derive(Clone, Debug, Eq, PartialEq)]
15pub struct ExecutionGuardEnteredEvent {
16    pub guard_id: Symbol,
17}
18
19#[contracttype]
20#[derive(Clone, Debug, Eq, PartialEq)]
21pub struct ExecutionGuardExitedEvent {
22    pub guard_id: Symbol,
23}
24
25impl ExecutionGuard {
26    pub fn new(id: Symbol) -> Self {
27        Self { id }
28    }
29
30    pub fn is_locked(&self, env: &Env) -> bool {
31        env.storage()
32            .persistent()
33            .get(&self.lock_key(env))
34            .unwrap_or(false)
35    }
36
37    pub fn require_unlocked(&self, env: &Env) -> Result<(), StandardsError> {
38        if self.is_locked(env) {
39            return Err(StandardsError::ExecutionLocked);
40        }
41        Ok(())
42    }
43
44    pub fn enter(&self, env: &Env) -> Result<ExecutionGuardEnteredEvent, StandardsError> {
45        self.require_unlocked(env)?;
46        env.storage().persistent().set(&self.lock_key(env), &true);
47        Ok(ExecutionGuardEnteredEvent {
48            guard_id: self.id.clone(),
49        })
50    }
51
52    pub fn exit(&self, env: &Env) -> Result<ExecutionGuardExitedEvent, StandardsError> {
53        if !self.is_locked(env) {
54            return Err(StandardsError::ExecutionNotLocked);
55        }
56        env.storage().persistent().set(&self.lock_key(env), &false);
57        Ok(ExecutionGuardExitedEvent {
58            guard_id: self.id.clone(),
59        })
60    }
61
62    pub fn execute<T>(&self, env: &Env, f: impl FnOnce() -> T) -> Result<T, StandardsError> {
63        self.enter(env)?;
64        let result = f();
65        self.exit(env)?;
66        Ok(result)
67    }
68
69    fn lock_key(&self, env: &Env) -> (Symbol, Symbol) {
70        (Symbol::new(env, EXECUTION_LOCK_PREFIX), self.id.clone())
71    }
72}