revm_trace/utils/
multicall_utils.rs

1//! Multicall utilities for batch contract calls
2//! by dynamically deploying a Multicall contract and executing batch calls.
3//!
4//! Key features:
5//! - Works on any EVM chain (no need for pre-deployed Multicall contracts)
6//! - Dynamically deploys Multicall contract in simulation
7//! - Supports batch calls with individual error handling
8//! - Lightweight implementation without complex inspectors
9//!
10//! This module provides a universal Multicall solution that works on any EVM chain.
11
12use alloy::{
13    hex,
14    primitives::{Address, Bytes, TxKind},
15    sol_types::SolCall,
16};
17use anyhow::Result;
18use revm::{
19    context::TxEnv,
20    context_interface::result::ExecutionResult,
21    database::{CacheDB, Database, DatabaseCommit, DatabaseRef},
22    ExecuteCommitEvm, ExecuteEvm,
23};
24
25use crate::{
26    errors::{EvmError, RuntimeError},
27    evm::TraceEvm,
28    traits::ResetDB,
29};
30
31// Multicall3 interface - standard and widely supported
32mod multicall3 {
33    use alloy::sol;
34
35    sol! {
36        #[derive(Debug)]
37        struct MulticallCall {
38            address target;
39            bytes callData;
40        }
41
42        #[derive(Debug)]
43        struct MulticallResult {
44            bool success;
45            bytes returnData;
46        }
47
48        contract Multicall3 {
49            function aggregate(MulticallCall[] calldata calls)
50                public payable
51                returns (uint256 blockNumber, bytes[] memory returnData);
52
53            function tryAggregate(bool requireSuccess, MulticallCall[] calldata calls)
54                public payable
55                returns (MulticallResult[] memory returnData);
56        }
57    }
58}
59
60use multicall3::Multicall3::tryAggregateCall;
61pub use multicall3::{MulticallCall, MulticallResult};
62
63/// Multicall manager for batch contract calls
64///
65/// Manages the deployment and execution of Multicall contracts for batch operations.
66/// This allows executing multiple contract calls in a single transaction, improving
67/// efficiency and reducing the number of required RPC calls.
68pub struct MulticallManager {
69    /// Multicall3 contract bytecode for deployment
70    multicall_bytecode: Bytes,
71}
72
73impl MulticallManager {
74    /// Create a new MulticallManager with default Multicall3 bytecode
75    ///
76    /// Initializes the manager with a simplified Multicall contract that can handle
77    /// basic multi-call operations. In production environments, you may want to
78    /// use the full Multicall3 contract bytecode.
79    ///
80    /// # Returns
81    /// A new `MulticallManager` instance ready for deployment and execution
82    ///
83    /// # Example
84    /// ```no_run
85    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
86    /// use revm_trace::{create_evm, utils::multicall_utils::MulticallManager};
87    ///
88    /// let evm = create_evm("https://eth.llamarpc.com").await?;
89    /// let manager = MulticallManager::new();
90    /// // Use manager.deploy_and_batch_call() to execute multiple calls
91    /// # Ok(())
92    /// # }
93    /// ```
94    pub fn new() -> Self {
95        // Multicall3 bytecode - this is a simplified version for testing
96        // In production, you'd want to use the full Multicall3 contract
97        // This contract provides basic multicall functionality with tryAggregate support
98        const SIMPLE_MULTICALL_BYTECODE: &str = "0x608060405234801561001057600080fd5b50610ee0806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c0033";
99
100        Self {
101            multicall_bytecode: Bytes::from(hex::decode(SIMPLE_MULTICALL_BYTECODE).unwrap()),
102        }
103    }
104
105    /// Deploy a Multicall contract to the EVM state
106    ///
107    /// This method deploys a simple Multicall3-compatible contract that can execute
108    /// multiple contract calls in a single transaction. The deployment uses a CREATE
109    /// transaction and returns the deployed contract address.
110    ///
111    /// # Arguments
112    /// * `evm` - Mutable reference to the EVM instance
113    ///
114    /// # Returns
115    /// * `Ok(Address)` - Address of the deployed Multicall contract
116    /// * `Err(EvmError)` - If deployment fails or contract creation is invalid
117    ///
118    /// # Implementation Details
119    /// - Uses CREATE transaction type to deploy the contract
120    /// - Returns the contract address from the execution output
121    /// - Handles revert and halt scenarios appropriately
122    ///
123    /// # Example
124    /// ```no_run
125    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
126    /// use revm_trace::{create_evm, utils::multicall_utils::MulticallManager};
127    ///
128    /// let mut evm = create_evm("https://eth.llamarpc.com").await?;
129    /// let manager = MulticallManager::new();
130    /// // The deploy_multicall method is internal - use deploy_and_batch_call instead
131    /// let calls = vec![/* your multicall data */];
132    /// let _results = manager.deploy_and_batch_call(&mut evm, calls, false)?;
133    /// # Ok(())
134    /// # }
135    /// ```
136    fn deploy_multicall<DB, INSP>(&self, evm: &mut TraceEvm<DB, INSP>) -> Result<Address, EvmError>
137    where
138        DB: Database + DatabaseCommit,
139    {
140        // Deploy the Multicall contract using CREATE transaction
141        let tx = TxEnv {
142            kind: TxKind::Create,
143            data: self.multicall_bytecode.clone(),
144            chain_id: Some(evm.cfg.chain_id),
145            ..Default::default()
146        };
147
148        // Execute the deployment transaction and commit to state
149        let result = evm.transact_commit(tx).map_err(|e| {
150            RuntimeError::ExecutionFailed(format!("Multicall deployment failed: {e}"))
151        })?;
152
153        // Process deployment result and extract contract address
154        match result {
155            ExecutionResult::Success { output, .. } => {
156                match output.address() {
157                    Some(address) => {
158                        // Successfully deployed, return the contract address
159                        Ok(*address)
160                    }
161                    None => {
162                        // No address returned, deployment failed
163                        Err(RuntimeError::Revert(
164                            "Multicall deployment did not return a contract address".to_string(),
165                        )
166                        .into())
167                    }
168                }
169            }
170            ExecutionResult::Revert { output, .. } => Err(RuntimeError::Revert(format!(
171                "Multicall deployment reverted: {}",
172                String::from_utf8_lossy(&output)
173            ))
174            .into()),
175            ExecutionResult::Halt { reason, .. } => {
176                Err(RuntimeError::Revert(format!("Multicall deployment halted: {reason:?}")).into())
177            }
178        }
179    }
180
181    /// Deploy Multicall contract and execute batch calls in a single operation
182    ///
183    /// This is the main entry point for batch contract calls. It deploys a fresh
184    /// Multicall contract and executes all provided calls through it, returning
185    /// individual results for each call.
186    ///
187    /// # Arguments
188    /// * `evm` - EVM instance for execution (must support database reset)
189    /// * `calls` - Vector of MulticallCall structs defining target contracts and call data
190    /// * `require_success` - Whether all calls must succeed (passed to tryAggregate)
191    ///
192    /// # Returns
193    /// * `Ok(Vec<MulticallResult>)` - Results for each call, including success status and return data
194    /// * `Err(EvmError)` - If deployment or batch execution fails
195    ///
196    /// # Implementation Details
197    /// 1. Resets database to ensure clean state for deployment
198    /// 2. Deploys Multicall contract using CREATE transaction
199    /// 3. Encodes batch call data using tryAggregate function
200    /// 4. Executes batch call transaction with appropriate nonce
201    /// 5. Decodes and returns individual call results
202    ///
203    /// # Example
204    /// ```no_run
205    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
206    /// use revm_trace::{create_evm, utils::multicall_utils::{MulticallManager, MulticallCall}};
207    /// use alloy::primitives::{address, bytes};
208    ///
209    /// let mut evm = create_evm("https://eth.llamarpc.com").await?;
210    /// let manager = MulticallManager::new();
211    ///
212    /// let calls = vec![
213    ///     MulticallCall {
214    ///         target: address!("A0b86a33E6417c9f36C6d2bAC7Cf26b6e7b2c3Ad"),
215    ///         callData: bytes!("0x70a08231000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045"),
216    ///     },
217    /// ];
218    ///
219    /// let results = manager.deploy_and_batch_call(&mut evm, calls, false)?;
220    /// for (i, result) in results.iter().enumerate() {
221    ///     println!("Call {}: success={}, data={:?}", i, result.success, result.returnData);
222    /// }
223    /// # Ok(())
224    /// # }
225    /// ```
226    pub fn deploy_and_batch_call<DB, INSP>(
227        &self,
228        evm: &mut TraceEvm<CacheDB<DB>, INSP>,
229        calls: Vec<MulticallCall>,
230        require_success: bool,
231    ) -> Result<Vec<MulticallResult>, EvmError>
232    where
233        DB: DatabaseRef,
234    {
235        // Handle empty calls case
236        if calls.is_empty() {
237            return Ok(Vec::new());
238        }
239
240        // Reset database to ensure clean state for deployment
241        evm.reset_db();
242
243        // Deploy Multicall contract and get its address
244        let multicall_address = self.deploy_multicall(evm)?;
245
246        // Encode the batch call data using tryAggregate function
247        let multicall_data = tryAggregateCall {
248            requireSuccess: require_success,
249            calls,
250        }
251        .abi_encode();
252
253        // Create transaction to call the deployed Multicall contract
254        let tx = TxEnv {
255            kind: TxKind::Call(multicall_address),
256            data: multicall_data.into(),
257            chain_id: Some(evm.cfg.chain_id),
258            nonce: 1, // After deployment, nonce should start from 1
259            ..Default::default()
260        };
261
262        // Execute the multicall transaction
263        let execution_result = evm.transact(tx);
264
265        match execution_result {
266            Ok(execution_result) => {
267                // Process the execution result
268                match execution_result.result {
269                    ExecutionResult::Success { output, .. } => {
270                        // Decode the multicall results from the output data
271                        let results: Vec<MulticallResult> = tryAggregateCall::abi_decode_returns(
272                            &output.into_data(),
273                        )
274                        .map_err(|e| {
275                            RuntimeError::DecodeError(format!(
276                                "Failed to decode Multicall result: {e}"
277                            ))
278                        })?;
279
280                        Ok(results)
281                    }
282                    ExecutionResult::Revert { output, .. } => Err(RuntimeError::Revert(format!(
283                        "Multicall execution reverted: {}",
284                        String::from_utf8_lossy(&output)
285                    ))
286                    .into()),
287                    ExecutionResult::Halt { reason, .. } => Err(RuntimeError::Revert(format!(
288                        "Multicall execution halted: {reason:?}",
289                    ))
290                    .into()),
291                }
292            }
293            Err(e) => {
294                // Handle execution error
295                Err(
296                    RuntimeError::ExecutionFailed(format!("Multicall execution failed: {e}"))
297                        .into(),
298                )
299            }
300        }
301    }
302}
303
304impl Default for MulticallManager {
305    /// Create a default MulticallManager instance
306    ///
307    /// This is equivalent to calling `MulticallManager::new()`.
308    fn default() -> Self {
309        Self::new()
310    }
311}