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#[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}