1use serde::{Deserialize, Serialize};
9use std::collections::BTreeMap;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
13pub enum ExecutionPhase {
14 Validation,
16 Execution,
18 Settlement,
20}
21
22impl ExecutionPhase {
23 pub fn as_str(&self) -> &'static str {
25 match self {
26 Self::Validation => "validation",
27 Self::Execution => "execution",
28 Self::Settlement => "settlement",
29 }
30 }
31}
32
33impl std::str::FromStr for ExecutionPhase {
34 type Err = ();
35
36 fn from_str(s: &str) -> Result<Self, Self::Err> {
37 match s {
38 "validation" => Ok(Self::Validation),
39 "execution" => Ok(Self::Execution),
40 "settlement" => Ok(Self::Settlement),
41 _ => Err(()),
42 }
43 }
44}
45
46impl std::fmt::Display for ExecutionPhase {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 write!(f, "{}", self.as_str())
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
54pub enum AALayer {
55 Bundler,
57 Account,
59 Paymaster,
61 Protocol,
63 EntryPoint,
65}
66
67impl AALayer {
68 pub fn as_str(&self) -> &'static str {
70 match self {
71 Self::Bundler => "bundler",
72 Self::Account => "account",
73 Self::Paymaster => "paymaster",
74 Self::Protocol => "protocol",
75 Self::EntryPoint => "entrypoint",
76 }
77 }
78}
79
80impl std::str::FromStr for AALayer {
81 type Err = ();
82
83 fn from_str(s: &str) -> Result<Self, Self::Err> {
84 match s {
85 "bundler" => Ok(Self::Bundler),
86 "account" => Ok(Self::Account),
87 "paymaster" => Ok(Self::Paymaster),
88 "protocol" => Ok(Self::Protocol),
89 "entrypoint" => Ok(Self::EntryPoint),
90 _ => Err(()),
91 }
92 }
93}
94
95impl std::fmt::Display for AALayer {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 write!(f, "{}", self.as_str())
98 }
99}
100
101#[derive(Debug, Clone, Default, Serialize, Deserialize)]
103pub struct AAContext {
104 pub current_phase: Option<ExecutionPhase>,
106
107 pub layer_state: BTreeMap<String, BTreeMap<String, serde_json::Value>>,
109
110 pub phase_snapshots: BTreeMap<String, BTreeMap<String, BTreeMap<String, serde_json::Value>>>,
112
113 pub user_op: Option<UserOpData>,
115
116 pub account_state: Option<AccountState>,
118
119 pub paymaster_state: Option<PaymasterState>,
121
122 pub entry_point_state: Option<EntryPointState>,
124}
125
126impl AAContext {
127 pub fn set_phase(&mut self, phase: ExecutionPhase) {
129 self.current_phase = Some(phase);
130 }
131
132 pub fn get_phase(&self) -> Option<ExecutionPhase> {
134 self.current_phase
135 }
136
137 pub fn in_phase(&self, phase: ExecutionPhase) -> bool {
139 self.current_phase == Some(phase)
140 }
141
142 pub fn snapshot_phase(&mut self, phase: ExecutionPhase) {
144 self.phase_snapshots
145 .insert(phase.to_string(), self.layer_state.clone());
146 }
147
148 pub fn get_phase_snapshot(
150 &self,
151 phase: ExecutionPhase,
152 ) -> Option<&BTreeMap<String, BTreeMap<String, serde_json::Value>>> {
153 self.phase_snapshots.get(phase.as_str())
154 }
155
156 pub fn get_layer_var(&self, layer: &str, var: &str) -> Option<&serde_json::Value> {
158 self.layer_state.get(layer)?.get(var)
159 }
160
161 pub fn set_layer_var(&mut self, layer: String, var: String, value: serde_json::Value) {
163 self.layer_state
164 .entry(layer)
165 .or_default()
166 .insert(var, value);
167 }
168
169 pub fn get_layer_var_at_phase(
171 &self,
172 phase: ExecutionPhase,
173 layer: &str,
174 var: &str,
175 ) -> Option<&serde_json::Value> {
176 self.phase_snapshots
177 .get(phase.as_str())?
178 .get(layer)?
179 .get(var)
180 }
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct UserOpData {
186 pub sender: String,
188 pub nonce: u128,
190 pub init_code: Vec<u8>,
192 pub call_data: Vec<u8>,
194 pub call_gas_limit: u128,
196 pub verification_gas_limit: u128,
198 pub pre_op_gas: u128,
200 pub max_gas_price: u128,
202 pub max_priority_fee_per_gas: u128,
204 pub paymaster_and_data: Vec<u8>,
206 pub signature: Vec<u8>,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct AccountState {
213 pub nonce: u128,
215 pub balance: u128,
217 pub expected_signer: String,
219 pub signature_valid: bool,
221 pub reentrancy_locked: bool,
223 pub execution_failed: bool,
225 pub state_hash_before: String,
227 pub state_hash_after: String,
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct PaymasterState {
234 pub address: String,
236 pub deposit: u128,
238 pub nonce: u128,
240 pub status: String,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct EntryPointState {
247 pub address: String,
249 pub block_number: u128,
251 pub block_timestamp: u128,
253 pub authenticated_caller: String,
255}
256
257#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct CrossLayerCheckResult {
260 pub invariant_name: String,
262 pub layers_involved: Vec<String>,
264 pub holds: bool,
266 pub failure_reason: Option<String>,
268 pub variables_used: BTreeMap<String, serde_json::Value>,
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275 use std::str::FromStr;
276
277 #[test]
278 fn test_execution_phase_from_str() {
279 assert_eq!(
280 ExecutionPhase::from_str("validation"),
281 Ok(ExecutionPhase::Validation)
282 );
283 assert_eq!(
284 ExecutionPhase::from_str("execution"),
285 Ok(ExecutionPhase::Execution)
286 );
287 assert_eq!(
288 ExecutionPhase::from_str("settlement"),
289 Ok(ExecutionPhase::Settlement)
290 );
291 assert_eq!(ExecutionPhase::from_str("invalid"), Err(()));
292 }
293
294 #[test]
295 fn test_aa_layer_from_str() {
296 assert_eq!(AALayer::from_str("bundler"), Ok(AALayer::Bundler));
297 assert_eq!(AALayer::from_str("account"), Ok(AALayer::Account));
298 assert_eq!(AALayer::from_str("paymaster"), Ok(AALayer::Paymaster));
299 assert_eq!(AALayer::from_str("protocol"), Ok(AALayer::Protocol));
300 assert_eq!(AALayer::from_str("invalid"), Err(()));
301 }
302
303 #[test]
304 fn test_aa_context_layer_vars() {
305 let mut ctx = AAContext::default();
306 ctx.set_layer_var(
307 "bundler".to_string(),
308 "nonce".to_string(),
309 serde_json::json!(42),
310 );
311
312 let value = ctx.get_layer_var("bundler", "nonce");
313 assert_eq!(value, Some(&serde_json::json!(42)));
314 }
315
316 #[test]
317 fn test_phase_tracking() {
318 let mut ctx = AAContext::default();
319 assert_eq!(ctx.get_phase(), None);
320
321 ctx.set_phase(ExecutionPhase::Validation);
322 assert!(ctx.in_phase(ExecutionPhase::Validation));
323 assert!(!ctx.in_phase(ExecutionPhase::Execution));
324
325 ctx.set_layer_var(
326 "account".to_string(),
327 "balance".to_string(),
328 serde_json::json!(1000),
329 );
330 ctx.snapshot_phase(ExecutionPhase::Validation);
331
332 ctx.set_phase(ExecutionPhase::Execution);
333 ctx.set_layer_var(
334 "account".to_string(),
335 "balance".to_string(),
336 serde_json::json!(500),
337 );
338
339 let pre_exec_balance =
340 ctx.get_layer_var_at_phase(ExecutionPhase::Validation, "account", "balance");
341 assert_eq!(pre_exec_balance, Some(&serde_json::json!(1000)));
342 }
343}