Skip to main content

safe_rs/types/
batch_result.rs

1//! Unified batch result types for Safe and EOA transactions
2
3use alloy::primitives::TxHash;
4
5use crate::eoa::EoaBatchResult;
6use crate::safe::ExecutionResult;
7use crate::simulation::SimulationResult;
8
9/// Unified result of executing a batch of transactions
10///
11/// This provides a common interface for both Safe (atomic multicall) and
12/// EOA (sequential transactions) execution results.
13#[derive(Debug, Clone)]
14pub struct BatchResult {
15    /// Transaction hashes (Safe has 1, EOA has N)
16    pub tx_hashes: Vec<TxHash>,
17    /// Whether all transactions succeeded
18    pub success: bool,
19    /// Number of successful transactions
20    pub success_count: usize,
21    /// Number of failed transactions
22    pub failure_count: usize,
23    /// Whether the batch was executed atomically (Safe=true, EOA=false)
24    pub atomic: bool,
25}
26
27impl BatchResult {
28    /// Creates a BatchResult from a Safe execution result
29    pub fn from_safe(result: ExecutionResult) -> Self {
30        BatchResult {
31            tx_hashes: vec![result.tx_hash],
32            success: result.success,
33            success_count: usize::from(result.success),
34            failure_count: usize::from(!result.success),
35            atomic: true,
36        }
37    }
38
39    /// Creates a BatchResult from an EOA batch result
40    pub fn from_eoa(result: EoaBatchResult) -> Self {
41        BatchResult {
42            tx_hashes: result.tx_hashes(),
43            success: result.all_succeeded(),
44            success_count: result.success_count,
45            failure_count: result.failure_count,
46            atomic: false,
47        }
48    }
49
50    /// Returns true if all transactions succeeded
51    pub fn all_succeeded(&self) -> bool {
52        self.success
53    }
54}
55
56/// Unified result of simulating a batch of transactions
57///
58/// This provides a common interface for simulation results from both
59/// Safe (single multicall simulation) and EOA (multiple individual simulations).
60#[derive(Debug, Clone)]
61pub struct BatchSimulationResult {
62    /// Individual simulation results for each call
63    pub results: Vec<SimulationResult>,
64    /// Total gas used across all simulated calls
65    pub total_gas_used: u64,
66    /// Whether the batch will execute atomically (Safe=true, EOA=false)
67    pub atomic: bool,
68}
69
70impl BatchSimulationResult {
71    /// Creates a BatchSimulationResult from a Safe simulation result
72    pub fn from_safe(result: SimulationResult) -> Self {
73        let gas_used = result.gas_used;
74        BatchSimulationResult {
75            results: vec![result],
76            total_gas_used: gas_used,
77            atomic: true,
78        }
79    }
80
81    /// Creates a BatchSimulationResult from EOA simulation results
82    pub fn from_eoa(results: Vec<SimulationResult>) -> Self {
83        let total_gas_used = results.iter().map(|r| r.gas_used).sum();
84        BatchSimulationResult {
85            results,
86            total_gas_used,
87            atomic: false,
88        }
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    use crate::eoa::EoaTxResult;
96
97    #[test]
98    fn test_batch_result_from_safe_success() {
99        let safe_result = ExecutionResult {
100            tx_hash: TxHash::ZERO,
101            success: true,
102        };
103
104        let result = BatchResult::from_safe(safe_result);
105
106        assert_eq!(result.tx_hashes.len(), 1);
107        assert!(result.success);
108        assert_eq!(result.success_count, 1);
109        assert_eq!(result.failure_count, 0);
110        assert!(result.atomic);
111    }
112
113    #[test]
114    fn test_batch_result_from_safe_failure() {
115        let safe_result = ExecutionResult {
116            tx_hash: TxHash::ZERO,
117            success: false,
118        };
119
120        let result = BatchResult::from_safe(safe_result);
121
122        assert!(!result.success);
123        assert_eq!(result.success_count, 0);
124        assert_eq!(result.failure_count, 1);
125        assert!(result.atomic);
126    }
127
128    #[test]
129    fn test_batch_result_from_eoa() {
130        let eoa_result = EoaBatchResult {
131            results: vec![
132                EoaTxResult {
133                    tx_hash: TxHash::ZERO,
134                    success: true,
135                    index: 0,
136                },
137                EoaTxResult {
138                    tx_hash: TxHash::ZERO,
139                    success: true,
140                    index: 1,
141                },
142            ],
143            success_count: 2,
144            failure_count: 0,
145            first_failure: None,
146        };
147
148        let result = BatchResult::from_eoa(eoa_result);
149
150        assert_eq!(result.tx_hashes.len(), 2);
151        assert!(result.success);
152        assert_eq!(result.success_count, 2);
153        assert_eq!(result.failure_count, 0);
154        assert!(!result.atomic);
155    }
156
157    #[test]
158    fn test_batch_simulation_result_from_safe() {
159        let sim_result = SimulationResult {
160            success: true,
161            gas_used: 50000,
162            return_data: Default::default(),
163            revert_reason: None,
164            logs: vec![],
165            state_diff: Default::default(),
166            traces: None,
167        };
168
169        let result = BatchSimulationResult::from_safe(sim_result);
170
171        assert_eq!(result.results.len(), 1);
172        assert_eq!(result.total_gas_used, 50000);
173        assert!(result.atomic);
174    }
175
176    #[test]
177    fn test_batch_simulation_result_from_eoa() {
178        let sim_results = vec![
179            SimulationResult {
180                success: true,
181                gas_used: 21000,
182                return_data: Default::default(),
183                revert_reason: None,
184                logs: vec![],
185                state_diff: Default::default(),
186                traces: None,
187            },
188            SimulationResult {
189                success: true,
190                gas_used: 30000,
191                return_data: Default::default(),
192                revert_reason: None,
193                logs: vec![],
194                state_diff: Default::default(),
195                traces: None,
196            },
197        ];
198
199        let result = BatchSimulationResult::from_eoa(sim_results);
200
201        assert_eq!(result.results.len(), 2);
202        assert_eq!(result.total_gas_used, 51000);
203        assert!(!result.atomic);
204    }
205}