Skip to main content

cougr_core/standards/
delayed_execution.rs

1use soroban_sdk::{contracttype, Bytes, Env, Symbol, Vec};
2
3use super::error::StandardsError;
4
5const OPERATION_PREFIX: &str = "std_delay";
6const OPERATION_IDS_PREFIX: &str = "std_delay_ids";
7const NONCE_PREFIX: &str = "std_delay_n";
8
9/// Storage-backed delayed execution queue.
10#[derive(Clone, Debug)]
11pub struct DelayedExecutionPolicy {
12    id: Symbol,
13}
14
15#[contracttype]
16#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct DelayedOperation {
18    pub operation_id: u64,
19    pub action: Symbol,
20    pub payload: Bytes,
21    pub scheduled_at: u64,
22    pub not_before: u64,
23    pub expires_at: u64,
24    pub executed: bool,
25}
26
27#[contracttype]
28#[derive(Clone, Debug, Eq, PartialEq)]
29pub struct DelayedExecutionScheduledEvent {
30    pub operation_id: u64,
31    pub action: Symbol,
32    pub not_before: u64,
33    pub expires_at: u64,
34}
35
36#[contracttype]
37#[derive(Clone, Debug, Eq, PartialEq)]
38pub struct DelayedExecutionCancelledEvent {
39    pub operation_id: u64,
40    pub action: Symbol,
41}
42
43#[contracttype]
44#[derive(Clone, Debug, Eq, PartialEq)]
45pub struct DelayedExecutionExecutedEvent {
46    pub operation_id: u64,
47    pub action: Symbol,
48    pub executed_at: u64,
49}
50
51impl DelayedExecutionPolicy {
52    pub fn new(id: Symbol) -> Self {
53        Self { id }
54    }
55
56    pub fn schedule(
57        &self,
58        env: &Env,
59        action: Symbol,
60        payload: Bytes,
61        delay: u64,
62        ttl: u64,
63    ) -> Result<DelayedExecutionScheduledEvent, StandardsError> {
64        let now = env.ledger().timestamp();
65        let operation_id = self.next_operation_id(env);
66        let operation = DelayedOperation {
67            operation_id,
68            action: action.clone(),
69            payload,
70            scheduled_at: now,
71            not_before: now + delay,
72            expires_at: now + delay + ttl,
73            executed: false,
74        };
75
76        env.storage()
77            .persistent()
78            .set(&self.operation_key(env, operation_id), &operation);
79
80        let mut ids = self.operation_ids(env);
81        ids.push_back(operation_id);
82        env.storage()
83            .persistent()
84            .set(&self.operation_ids_key(env), &ids);
85
86        Ok(DelayedExecutionScheduledEvent {
87            operation_id,
88            action,
89            not_before: operation.not_before,
90            expires_at: operation.expires_at,
91        })
92    }
93
94    pub fn operation(&self, env: &Env, operation_id: u64) -> Option<DelayedOperation> {
95        env.storage()
96            .persistent()
97            .get(&self.operation_key(env, operation_id))
98    }
99
100    pub fn pending_operations(&self, env: &Env) -> Vec<DelayedOperation> {
101        let ids = self.operation_ids(env);
102        let mut operations = Vec::new(env);
103
104        for i in 0..ids.len() {
105            if let Some(operation_id) = ids.get(i) {
106                if let Some(operation) = self.operation(env, operation_id) {
107                    if !operation.executed {
108                        operations.push_back(operation);
109                    }
110                }
111            }
112        }
113
114        operations
115    }
116
117    pub fn cancel(
118        &self,
119        env: &Env,
120        operation_id: u64,
121    ) -> Result<DelayedExecutionCancelledEvent, StandardsError> {
122        let operation = self
123            .operation(env, operation_id)
124            .ok_or(StandardsError::OperationNotFound)?;
125
126        self.remove_operation(env, operation_id);
127
128        Ok(DelayedExecutionCancelledEvent {
129            operation_id,
130            action: operation.action,
131        })
132    }
133
134    pub fn execute_ready(
135        &self,
136        env: &Env,
137        operation_id: u64,
138    ) -> Result<DelayedExecutionExecutedEvent, StandardsError> {
139        let mut operation = self
140            .operation(env, operation_id)
141            .ok_or(StandardsError::OperationNotFound)?;
142
143        if operation.executed {
144            return Err(StandardsError::OperationAlreadyExecuted);
145        }
146
147        let now = env.ledger().timestamp();
148        if now < operation.not_before {
149            return Err(StandardsError::OperationNotReady);
150        }
151        if now > operation.expires_at {
152            return Err(StandardsError::OperationExpired);
153        }
154
155        operation.executed = true;
156        env.storage()
157            .persistent()
158            .set(&self.operation_key(env, operation_id), &operation);
159        self.remove_operation_id(env, operation_id);
160
161        Ok(DelayedExecutionExecutedEvent {
162            operation_id,
163            action: operation.action,
164            executed_at: now,
165        })
166    }
167
168    fn next_operation_id(&self, env: &Env) -> u64 {
169        let key = self.nonce_key(env);
170        let next = env.storage().persistent().get::<_, u64>(&key).unwrap_or(0) + 1;
171        env.storage().persistent().set(&key, &next);
172        next
173    }
174
175    fn remove_operation(&self, env: &Env, operation_id: u64) {
176        env.storage()
177            .persistent()
178            .remove(&self.operation_key(env, operation_id));
179        self.remove_operation_id(env, operation_id);
180    }
181
182    fn remove_operation_id(&self, env: &Env, operation_id: u64) {
183        let ids = self.operation_ids(env);
184        let mut retained = Vec::new(env);
185
186        for i in 0..ids.len() {
187            if let Some(candidate) = ids.get(i) {
188                if candidate != operation_id {
189                    retained.push_back(candidate);
190                }
191            }
192        }
193
194        env.storage()
195            .persistent()
196            .set(&self.operation_ids_key(env), &retained);
197    }
198
199    fn operation_ids(&self, env: &Env) -> Vec<u64> {
200        env.storage()
201            .persistent()
202            .get(&self.operation_ids_key(env))
203            .unwrap_or_else(|| Vec::new(env))
204    }
205
206    fn operation_key(&self, env: &Env, operation_id: u64) -> (Symbol, Symbol, u64) {
207        (
208            Symbol::new(env, OPERATION_PREFIX),
209            self.id.clone(),
210            operation_id,
211        )
212    }
213
214    fn operation_ids_key(&self, env: &Env) -> (Symbol, Symbol) {
215        (Symbol::new(env, OPERATION_IDS_PREFIX), self.id.clone())
216    }
217
218    fn nonce_key(&self, env: &Env) -> (Symbol, Symbol) {
219        (Symbol::new(env, NONCE_PREFIX), self.id.clone())
220    }
221}