chisel/
executor.rs

1//! Executor
2//!
3//! This module contains the execution logic for the [SessionSource].
4
5use crate::prelude::{
6    ChiselDispatcher, ChiselResult, ChiselRunner, IntermediateOutput, SessionSource, SolidityHelper,
7};
8use alloy_dyn_abi::{DynSolType, DynSolValue};
9use alloy_json_abi::EventParam;
10use alloy_primitives::{hex, Address, B256, U256};
11use core::fmt::Debug;
12use eyre::{Result, WrapErr};
13use foundry_compilers::Artifact;
14use foundry_evm::{
15    backend::Backend, decode::decode_console_logs, executors::ExecutorBuilder,
16    inspectors::CheatsConfig, traces::TraceMode,
17};
18use solang_parser::pt::{self, CodeLocation};
19use std::str::FromStr;
20use tracing::debug;
21use yansi::Paint;
22
23const USIZE_MAX_AS_U256: U256 = U256::from_limbs([usize::MAX as u64, 0, 0, 0]);
24
25/// Executor implementation for [SessionSource]
26impl SessionSource {
27    /// Runs the source with the [ChiselRunner]
28    ///
29    /// ### Returns
30    ///
31    /// Optionally, a tuple containing the [Address] of the deployed REPL contract as well as
32    /// the [ChiselResult].
33    ///
34    /// Returns an error if compilation fails.
35    pub async fn execute(&mut self) -> Result<(Address, ChiselResult)> {
36        // Recompile the project and ensure no errors occurred.
37        let compiled = self.build()?;
38        if let Some((_, contract)) =
39            compiled.clone().compiler_output.contracts_into_iter().find(|(name, _)| name == "REPL")
40        {
41            // These *should* never panic after a successful compilation.
42            let bytecode = contract
43                .get_bytecode_bytes()
44                .ok_or_else(|| eyre::eyre!("No bytecode found for `REPL` contract"))?;
45            let deployed_bytecode = contract
46                .get_deployed_bytecode_bytes()
47                .ok_or_else(|| eyre::eyre!("No deployed bytecode found for `REPL` contract"))?;
48
49            // Fetch the run function's body statement
50            let run_func_statements = compiled.intermediate.run_func_body()?;
51
52            // Record loc of first yul block return statement (if any).
53            // This is used to decide which is the final statement within the `run()` method.
54            // see <https://github.com/foundry-rs/foundry/issues/4617>.
55            let last_yul_return = run_func_statements.iter().find_map(|statement| {
56                if let pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } = statement {
57                    if let Some(statement) = block.statements.last() {
58                        if let pt::YulStatement::FunctionCall(yul_call) = statement {
59                            if yul_call.id.name == "return" {
60                                return Some(statement.loc())
61                            }
62                        }
63                    }
64                }
65                None
66            });
67
68            // Find the last statement within the "run()" method and get the program
69            // counter via the source map.
70            if let Some(final_statement) = run_func_statements.last() {
71                // If the final statement is some type of block (assembly, unchecked, or regular),
72                // we need to find the final statement within that block. Otherwise, default to
73                // the source loc of the final statement of the `run()` function's block.
74                //
75                // There is some code duplication within the arms due to the difference between
76                // the [pt::Statement] type and the [pt::YulStatement] types.
77                let mut source_loc = match final_statement {
78                    pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } => {
79                        // Select last non variable declaration statement, see <https://github.com/foundry-rs/foundry/issues/4938>.
80                        let last_statement = block.statements.iter().rev().find(|statement| {
81                            !matches!(statement, pt::YulStatement::VariableDeclaration(_, _, _))
82                        });
83                        if let Some(statement) = last_statement {
84                            statement.loc()
85                        } else {
86                            // In the case where the block is empty, attempt to grab the statement
87                            // before the asm block. Because we use saturating sub to get the second
88                            // to last index, this can always be safely unwrapped.
89                            run_func_statements
90                                .get(run_func_statements.len().saturating_sub(2))
91                                .unwrap()
92                                .loc()
93                        }
94                    }
95                    pt::Statement::Block { loc: _, unchecked: _, statements } => {
96                        if let Some(statement) = statements.last() {
97                            statement.loc()
98                        } else {
99                            // In the case where the block is empty, attempt to grab the statement
100                            // before the block. Because we use saturating sub to get the second to
101                            // last index, this can always be safely unwrapped.
102                            run_func_statements
103                                .get(run_func_statements.len().saturating_sub(2))
104                                .unwrap()
105                                .loc()
106                        }
107                    }
108                    _ => final_statement.loc(),
109                };
110
111                // Consider yul return statement as final statement (if it's loc is lower) .
112                if let Some(yul_return) = last_yul_return {
113                    if yul_return.end() < source_loc.start() {
114                        source_loc = yul_return;
115                    }
116                }
117
118                // Map the source location of the final statement of the `run()` function to its
119                // corresponding runtime program counter
120                let final_pc = {
121                    let offset = source_loc.start() as u32;
122                    let length = (source_loc.end() - source_loc.start()) as u32;
123                    contract
124                        .get_source_map_deployed()
125                        .unwrap()
126                        .unwrap()
127                        .into_iter()
128                        .zip(InstructionIter::new(&deployed_bytecode))
129                        .filter(|(s, _)| s.offset() == offset && s.length() == length)
130                        .map(|(_, i)| i.pc)
131                        .max()
132                        .unwrap_or_default()
133                };
134
135                // Create a new runner
136                let mut runner = self.prepare_runner(final_pc).await?;
137
138                // Return [ChiselResult] or bubble up error
139                runner.run(bytecode.into_owned())
140            } else {
141                // Return a default result if no statements are present.
142                Ok((Address::ZERO, ChiselResult::default()))
143            }
144        } else {
145            eyre::bail!("Failed to find REPL contract!")
146        }
147    }
148
149    /// Inspect a contract element inside of the current session
150    ///
151    /// ### Takes
152    ///
153    /// A solidity snippet
154    ///
155    /// ### Returns
156    ///
157    /// If the input is valid `Ok((continue, formatted_output))` where:
158    /// - `continue` is true if the input should be appended to the source
159    /// - `formatted_output` is the formatted value, if any
160    pub async fn inspect(&self, input: &str) -> Result<(bool, Option<String>)> {
161        let line = format!("bytes memory inspectoor = abi.encode({input});");
162        let mut source = match self.clone_with_new_line(line.clone()) {
163            Ok((source, _)) => source,
164            Err(err) => {
165                debug!(%err, "failed to build new source");
166                return Ok((true, None))
167            }
168        };
169
170        let mut source_without_inspector = self.clone();
171
172        // Events and tuples fails compilation due to it not being able to be encoded in
173        // `inspectoor`. If that happens, try executing without the inspector.
174        let (mut res, err) = match source.execute().await {
175            Ok((_, res)) => (res, None),
176            Err(err) => {
177                debug!(?err, %input, "execution failed");
178                match source_without_inspector.execute().await {
179                    Ok((_, res)) => (res, Some(err)),
180                    Err(_) => {
181                        if self.config.foundry_config.verbosity >= 3 {
182                            sh_err!("Could not inspect: {err}")?;
183                        }
184                        return Ok((true, None))
185                    }
186                }
187            }
188        };
189
190        // If abi-encoding the input failed, check whether it is an event
191        if let Some(err) = err {
192            let generated_output = source_without_inspector
193                .generated_output
194                .as_ref()
195                .ok_or_else(|| eyre::eyre!("Could not find generated output!"))?;
196
197            let intermediate_contract = generated_output
198                .intermediate
199                .intermediate_contracts
200                .get("REPL")
201                .ok_or_else(|| eyre::eyre!("Could not find intermediate contract!"))?;
202
203            if let Some(event_definition) = intermediate_contract.event_definitions.get(input) {
204                let formatted = format_event_definition(event_definition)?;
205                return Ok((false, Some(formatted)))
206            }
207
208            // we were unable to check the event
209            if self.config.foundry_config.verbosity >= 3 {
210                sh_err!("Failed eval: {err}")?;
211            }
212
213            debug!(%err, %input, "failed abi encode input");
214            return Ok((false, None))
215        }
216
217        let Some((stack, memory, _)) = &res.state else {
218            // Show traces and logs, if there are any, and return an error
219            if let Ok(decoder) = ChiselDispatcher::decode_traces(&source.config, &mut res).await {
220                ChiselDispatcher::show_traces(&decoder, &mut res).await?;
221            }
222            let decoded_logs = decode_console_logs(&res.logs);
223            if !decoded_logs.is_empty() {
224                sh_println!("{}", "Logs:".green())?;
225                for log in decoded_logs {
226                    sh_println!("  {log}")?;
227                }
228            }
229
230            return Err(eyre::eyre!("Failed to inspect expression"))
231        };
232
233        let generated_output = source
234            .generated_output
235            .as_ref()
236            .ok_or_else(|| eyre::eyre!("Could not find generated output!"))?;
237
238        // If the expression is a variable declaration within the REPL contract, use its type;
239        // otherwise, attempt to infer the type.
240        let contract_expr = generated_output
241            .intermediate
242            .repl_contract_expressions
243            .get(input)
244            .or_else(|| source.infer_inner_expr_type());
245
246        // If the current action is a function call, we get its return type
247        // otherwise it returns None
248        let function_call_return_type =
249            Type::get_function_return_type(contract_expr, &generated_output.intermediate);
250
251        let (contract_expr, ty) = if let Some(function_call_return_type) = function_call_return_type
252        {
253            (function_call_return_type.0, function_call_return_type.1)
254        } else {
255            match contract_expr.and_then(|e| {
256                Type::ethabi(e, Some(&generated_output.intermediate)).map(|ty| (e, ty))
257            }) {
258                Some(res) => res,
259                // this type was denied for inspection, continue
260                None => return Ok((true, None)),
261            }
262        };
263
264        // the file compiled correctly, thus the last stack item must be the memory offset of
265        // the `bytes memory inspectoor` value
266        let mut offset = stack.last().unwrap().to::<usize>();
267        let mem_offset = &memory[offset..offset + 32];
268        let len = U256::try_from_be_slice(mem_offset).unwrap().to::<usize>();
269        offset += 32;
270        let data = &memory[offset..offset + len];
271        // `tokens` is guaranteed to have the same length as the provided types
272        let token =
273            DynSolType::abi_decode(&ty, data).wrap_err("Could not decode inspected values")?;
274        Ok((should_continue(contract_expr), Some(format_token(token))))
275    }
276
277    /// Gracefully attempts to extract the type of the expression within the `abi.encode(...)`
278    /// call inserted by the inspect function.
279    ///
280    /// ### Takes
281    ///
282    /// A reference to a [SessionSource]
283    ///
284    /// ### Returns
285    ///
286    /// Optionally, a [Type]
287    fn infer_inner_expr_type(&self) -> Option<&pt::Expression> {
288        let out = self.generated_output.as_ref()?;
289        let run = out.intermediate.run_func_body().ok()?.last();
290        match run {
291            Some(pt::Statement::VariableDefinition(
292                _,
293                _,
294                Some(pt::Expression::FunctionCall(_, _, args)),
295            )) => {
296                // We can safely unwrap the first expression because this function
297                // will only be called on a session source that has just had an
298                // `inspectoor` variable appended to it.
299                Some(args.first().unwrap())
300            }
301            _ => None,
302        }
303    }
304
305    /// Prepare a runner for the Chisel REPL environment
306    ///
307    /// ### Takes
308    ///
309    /// The final statement's program counter for the ChiselInspector
310    ///
311    /// ### Returns
312    ///
313    /// A configured [ChiselRunner]
314    async fn prepare_runner(&mut self, final_pc: usize) -> Result<ChiselRunner> {
315        let env =
316            self.config.evm_opts.evm_env().await.expect("Could not instantiate fork environment");
317
318        // Create an in-memory backend
319        let backend = match self.config.backend.take() {
320            Some(backend) => backend,
321            None => {
322                let fork = self.config.evm_opts.get_fork(&self.config.foundry_config, env.clone());
323                let backend = Backend::spawn(fork)?;
324                self.config.backend = Some(backend.clone());
325                backend
326            }
327        };
328
329        // Build a new executor
330        let executor = ExecutorBuilder::new()
331            .inspectors(|stack| {
332                stack.chisel_state(final_pc).trace_mode(TraceMode::Call).cheatcodes(
333                    CheatsConfig::new(
334                        &self.config.foundry_config,
335                        self.config.evm_opts.clone(),
336                        None,
337                        None,
338                    )
339                    .into(),
340                )
341            })
342            .gas_limit(self.config.evm_opts.gas_limit())
343            .spec_id(self.config.foundry_config.evm_spec_id())
344            .legacy_assertions(self.config.foundry_config.legacy_assertions)
345            .build(env, backend);
346
347        // Create a [ChiselRunner] with a default balance of [U256::MAX] and
348        // the sender [Address::zero].
349        Ok(ChiselRunner::new(executor, U256::MAX, Address::ZERO, self.config.calldata.clone()))
350    }
351}
352
353/// Formats a value into an inspection message
354// TODO: Verbosity option
355fn format_token(token: DynSolValue) -> String {
356    match token {
357        DynSolValue::Address(a) => {
358            format!("Type: {}\n└ Data: {}", "address".red(), a.cyan())
359        }
360        DynSolValue::FixedBytes(b, byte_len) => {
361            format!(
362                "Type: {}\n└ Data: {}",
363                format!("bytes{byte_len}").red(),
364                hex::encode_prefixed(b).cyan()
365            )
366        }
367        DynSolValue::Int(i, bit_len) => {
368            format!(
369                "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
370                format!("int{bit_len}").red(),
371                format!(
372                    "0x{}",
373                    format!("{i:x}")
374                        .char_indices()
375                        .skip(64 - bit_len / 4)
376                        .take(bit_len / 4)
377                        .map(|(_, c)| c)
378                        .collect::<String>()
379                )
380                .cyan(),
381                hex::encode_prefixed(B256::from(i)).cyan(),
382                i.cyan()
383            )
384        }
385        DynSolValue::Uint(i, bit_len) => {
386            format!(
387                "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
388                format!("uint{bit_len}").red(),
389                format!(
390                    "0x{}",
391                    format!("{i:x}")
392                        .char_indices()
393                        .skip(64 - bit_len / 4)
394                        .take(bit_len / 4)
395                        .map(|(_, c)| c)
396                        .collect::<String>()
397                )
398                .cyan(),
399                hex::encode_prefixed(B256::from(i)).cyan(),
400                i.cyan()
401            )
402        }
403        DynSolValue::Bool(b) => {
404            format!("Type: {}\n└ Value: {}", "bool".red(), b.cyan())
405        }
406        DynSolValue::String(_) | DynSolValue::Bytes(_) => {
407            let hex = hex::encode(token.abi_encode());
408            let s = token.as_str();
409            format!(
410                "Type: {}\n{}├ Hex (Memory):\n├─ Length ({}): {}\n├─ Contents ({}): {}\n├ Hex (Tuple Encoded):\n├─ Pointer ({}): {}\n├─ Length ({}): {}\n└─ Contents ({}): {}",
411                if s.is_some() { "string" } else { "dynamic bytes" }.red(),
412                if let Some(s) = s {
413                    format!("├ UTF-8: {}\n", s.cyan())
414                } else {
415                    String::default()
416                },
417                "[0x00:0x20]".yellow(),
418                format!("0x{}", &hex[64..128]).cyan(),
419                "[0x20:..]".yellow(),
420                format!("0x{}", &hex[128..]).cyan(),
421                "[0x00:0x20]".yellow(),
422                format!("0x{}", &hex[..64]).cyan(),
423                "[0x20:0x40]".yellow(),
424                format!("0x{}", &hex[64..128]).cyan(),
425                "[0x40:..]".yellow(),
426                format!("0x{}", &hex[128..]).cyan(),
427            )
428        }
429        DynSolValue::FixedArray(tokens) | DynSolValue::Array(tokens) => {
430            let mut out = format!(
431                "{}({}) = {}",
432                "array".red(),
433                format!("{}", tokens.len()).yellow(),
434                '['.red()
435            );
436            for token in tokens {
437                out.push_str("\n  ├ ");
438                out.push_str(&format_token(token).replace('\n', "\n  "));
439                out.push('\n');
440            }
441            out.push_str(&']'.red().to_string());
442            out
443        }
444        DynSolValue::Tuple(tokens) => {
445            let displayed_types = tokens
446                .iter()
447                .map(|t| t.sol_type_name().unwrap_or_default())
448                .collect::<Vec<_>>()
449                .join(", ");
450            let mut out =
451                format!("{}({}) = {}", "tuple".red(), displayed_types.yellow(), '('.red());
452            for token in tokens {
453                out.push_str("\n  ├ ");
454                out.push_str(&format_token(token).replace('\n', "\n  "));
455                out.push('\n');
456            }
457            out.push_str(&')'.red().to_string());
458            out
459        }
460        _ => {
461            unimplemented!()
462        }
463    }
464}
465
466/// Formats a [pt::EventDefinition] into an inspection message
467///
468/// ### Takes
469///
470/// An borrowed [pt::EventDefinition]
471///
472/// ### Returns
473///
474/// A formatted [pt::EventDefinition] for use in inspection output.
475///
476/// TODO: Verbosity option
477fn format_event_definition(event_definition: &pt::EventDefinition) -> Result<String> {
478    let event_name = event_definition.name.as_ref().expect("Event has a name").to_string();
479    let inputs = event_definition
480        .fields
481        .iter()
482        .map(|param| {
483            let name = param
484                .name
485                .as_ref()
486                .map(ToString::to_string)
487                .unwrap_or_else(|| "<anonymous>".to_string());
488            let kind = Type::from_expression(&param.ty)
489                .and_then(Type::into_builtin)
490                .ok_or_else(|| eyre::eyre!("Invalid type in event {event_name}"))?;
491            Ok(EventParam {
492                name,
493                ty: kind.to_string(),
494                components: vec![],
495                indexed: param.indexed,
496                internal_type: None,
497            })
498        })
499        .collect::<Result<Vec<_>>>()?;
500    let event =
501        alloy_json_abi::Event { name: event_name, inputs, anonymous: event_definition.anonymous };
502
503    Ok(format!(
504        "Type: {}\n├ Name: {}\n├ Signature: {:?}\n└ Selector: {:?}",
505        "event".red(),
506        SolidityHelper::new().highlight(&format!(
507            "{}({})",
508            &event.name,
509            &event
510                .inputs
511                .iter()
512                .map(|param| format!(
513                    "{}{}{}",
514                    param.ty,
515                    if param.indexed { " indexed" } else { "" },
516                    if param.name.is_empty() {
517                        String::default()
518                    } else {
519                        format!(" {}", &param.name)
520                    },
521                ))
522                .collect::<Vec<_>>()
523                .join(", ")
524        )),
525        event.signature().cyan(),
526        event.selector().cyan(),
527    ))
528}
529
530// =============================================
531// Modified from
532// [soli](https://github.com/jpopesculian/soli)
533// =============================================
534
535#[derive(Clone, Debug, PartialEq)]
536enum Type {
537    /// (type)
538    Builtin(DynSolType),
539
540    /// (type)
541    Array(Box<Type>),
542
543    /// (type, length)
544    FixedArray(Box<Type>, usize),
545
546    /// (type, index)
547    ArrayIndex(Box<Type>, Option<usize>),
548
549    /// (types)
550    Tuple(Vec<Option<Type>>),
551
552    /// (name, params, returns)
553    Function(Box<Type>, Vec<Option<Type>>, Vec<Option<Type>>),
554
555    /// (lhs, rhs)
556    Access(Box<Type>, String),
557
558    /// (types)
559    Custom(Vec<String>),
560}
561
562impl Type {
563    /// Convert a [pt::Expression] to a [Type]
564    ///
565    /// ### Takes
566    ///
567    /// A reference to a [pt::Expression] to convert.
568    ///
569    /// ### Returns
570    ///
571    /// Optionally, an owned [Type]
572    fn from_expression(expr: &pt::Expression) -> Option<Self> {
573        match expr {
574            pt::Expression::Type(_, ty) => Self::from_type(ty),
575
576            pt::Expression::Variable(ident) => Some(Self::Custom(vec![ident.name.clone()])),
577
578            // array
579            pt::Expression::ArraySubscript(_, expr, num) => {
580                // if num is Some then this is either an index operation (arr[<num>])
581                // or a FixedArray statement (new uint256[<num>])
582                Self::from_expression(expr).and_then(|ty| {
583                    let boxed = Box::new(ty);
584                    let num = num.as_deref().and_then(parse_number_literal).and_then(|n| {
585                        // overflow check
586                        if n > USIZE_MAX_AS_U256 {
587                            None
588                        } else {
589                            Some(n.to::<usize>())
590                        }
591                    });
592                    match expr.as_ref() {
593                        // statement
594                        pt::Expression::Type(_, _) => {
595                            if let Some(num) = num {
596                                Some(Self::FixedArray(boxed, num))
597                            } else {
598                                Some(Self::Array(boxed))
599                            }
600                        }
601                        // index
602                        pt::Expression::Variable(_) => {
603                            Some(Self::ArrayIndex(boxed, num))
604                        }
605                        _ => None
606                    }
607                })
608            }
609            pt::Expression::ArrayLiteral(_, values) => {
610                values.first().and_then(Self::from_expression).map(|ty| {
611                    Self::FixedArray(Box::new(ty), values.len())
612                })
613            }
614
615            // tuple
616            pt::Expression::List(_, params) => Some(Self::Tuple(map_parameters(params))),
617
618            // <lhs>.<rhs>
619            pt::Expression::MemberAccess(_, lhs, rhs) => {
620                Self::from_expression(lhs).map(|lhs| {
621                    Self::Access(Box::new(lhs), rhs.name.clone())
622                })
623            }
624
625            // <inner>
626            pt::Expression::Parenthesis(_, inner) |         // (<inner>)
627            pt::Expression::New(_, inner) |                 // new <inner>
628            pt::Expression::UnaryPlus(_, inner) |           // +<inner>
629            // ops
630            pt::Expression::BitwiseNot(_, inner) |          // ~<inner>
631            pt::Expression::ArraySlice(_, inner, _, _) |    // <inner>[*start*:*end*]
632            // assign ops
633            pt::Expression::PreDecrement(_, inner) |        // --<inner>
634            pt::Expression::PostDecrement(_, inner) |       // <inner>--
635            pt::Expression::PreIncrement(_, inner) |        // ++<inner>
636            pt::Expression::PostIncrement(_, inner) |       // <inner>++
637            pt::Expression::Assign(_, inner, _) |           // <inner>   = ...
638            pt::Expression::AssignAdd(_, inner, _) |        // <inner>  += ...
639            pt::Expression::AssignSubtract(_, inner, _) |   // <inner>  -= ...
640            pt::Expression::AssignMultiply(_, inner, _) |   // <inner>  *= ...
641            pt::Expression::AssignDivide(_, inner, _) |     // <inner>  /= ...
642            pt::Expression::AssignModulo(_, inner, _) |     // <inner>  %= ...
643            pt::Expression::AssignAnd(_, inner, _) |        // <inner>  &= ...
644            pt::Expression::AssignOr(_, inner, _) |         // <inner>  |= ...
645            pt::Expression::AssignXor(_, inner, _) |        // <inner>  ^= ...
646            pt::Expression::AssignShiftLeft(_, inner, _) |  // <inner> <<= ...
647            pt::Expression::AssignShiftRight(_, inner, _)   // <inner> >>= ...
648            => Self::from_expression(inner),
649
650            // *condition* ? <if_true> : <if_false>
651            pt::Expression::ConditionalOperator(_, _, if_true, if_false) => {
652                Self::from_expression(if_true).or_else(|| Self::from_expression(if_false))
653            }
654
655            // address
656            pt::Expression::AddressLiteral(_, _) => Some(Self::Builtin(DynSolType::Address)),
657            pt::Expression::HexNumberLiteral(_, s, _) => {
658                match s.parse::<Address>() {
659                    Ok(addr) => {
660                        if *s == addr.to_checksum(None) {
661                            Some(Self::Builtin(DynSolType::Address))
662                        } else {
663                            Some(Self::Builtin(DynSolType::Uint(256)))
664                        }
665                    },
666                    _ => {
667                        Some(Self::Builtin(DynSolType::Uint(256)))
668                    }
669                }
670            }
671
672            // uint and int
673            // invert
674            pt::Expression::Negate(_, inner) => Self::from_expression(inner).map(Self::invert_int),
675
676            // int if either operand is int
677            // TODO: will need an update for Solidity v0.8.18 user defined operators:
678            // https://github.com/ethereum/solidity/issues/13718#issuecomment-1341058649
679            pt::Expression::Add(_, lhs, rhs) |
680            pt::Expression::Subtract(_, lhs, rhs) |
681            pt::Expression::Multiply(_, lhs, rhs) |
682            pt::Expression::Divide(_, lhs, rhs) => {
683                match (Self::ethabi(lhs, None), Self::ethabi(rhs, None)) {
684                    (Some(DynSolType::Int(_)), Some(DynSolType::Int(_))) |
685                    (Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) |
686                    (Some(DynSolType::Uint(_)), Some(DynSolType::Int(_))) => {
687                        Some(Self::Builtin(DynSolType::Int(256)))
688                    }
689                    _ => {
690                        Some(Self::Builtin(DynSolType::Uint(256)))
691                    }
692                }
693            }
694
695            // always assume uint
696            pt::Expression::Modulo(_, _, _) |
697            pt::Expression::Power(_, _, _) |
698            pt::Expression::BitwiseOr(_, _, _) |
699            pt::Expression::BitwiseAnd(_, _, _) |
700            pt::Expression::BitwiseXor(_, _, _) |
701            pt::Expression::ShiftRight(_, _, _) |
702            pt::Expression::ShiftLeft(_, _, _) |
703            pt::Expression::NumberLiteral(_, _, _, _) => Some(Self::Builtin(DynSolType::Uint(256))),
704
705            // TODO: Rational numbers
706            pt::Expression::RationalNumberLiteral(_, _, _, _, _) => {
707                Some(Self::Builtin(DynSolType::Uint(256)))
708            }
709
710            // bool
711            pt::Expression::BoolLiteral(_, _) |
712            pt::Expression::And(_, _, _) |
713            pt::Expression::Or(_, _, _) |
714            pt::Expression::Equal(_, _, _) |
715            pt::Expression::NotEqual(_, _, _) |
716            pt::Expression::Less(_, _, _) |
717            pt::Expression::LessEqual(_, _, _) |
718            pt::Expression::More(_, _, _) |
719            pt::Expression::MoreEqual(_, _, _) |
720            pt::Expression::Not(_, _) => Some(Self::Builtin(DynSolType::Bool)),
721
722            // string
723            pt::Expression::StringLiteral(_) => Some(Self::Builtin(DynSolType::String)),
724
725            // bytes
726            pt::Expression::HexLiteral(_) => Some(Self::Builtin(DynSolType::Bytes)),
727
728            // function
729            pt::Expression::FunctionCall(_, name, args) => {
730                Self::from_expression(name).map(|name| {
731                    let args = args.iter().map(Self::from_expression).collect();
732                    Self::Function(Box::new(name), args, vec![])
733                })
734            }
735            pt::Expression::NamedFunctionCall(_, name, args) => {
736                Self::from_expression(name).map(|name| {
737                    let args = args.iter().map(|arg| Self::from_expression(&arg.expr)).collect();
738                    Self::Function(Box::new(name), args, vec![])
739                })
740            }
741
742            // explicitly None
743            pt::Expression::Delete(_, _) | pt::Expression::FunctionCallBlock(_, _, _) => None,
744        }
745    }
746
747    /// Convert a [pt::Type] to a [Type]
748    ///
749    /// ### Takes
750    ///
751    /// A reference to a [pt::Type] to convert.
752    ///
753    /// ### Returns
754    ///
755    /// Optionally, an owned [Type]
756    fn from_type(ty: &pt::Type) -> Option<Self> {
757        let ty = match ty {
758            pt::Type::Address | pt::Type::AddressPayable | pt::Type::Payable => {
759                Self::Builtin(DynSolType::Address)
760            }
761            pt::Type::Bool => Self::Builtin(DynSolType::Bool),
762            pt::Type::String => Self::Builtin(DynSolType::String),
763            pt::Type::Int(size) => Self::Builtin(DynSolType::Int(*size as usize)),
764            pt::Type::Uint(size) => Self::Builtin(DynSolType::Uint(*size as usize)),
765            pt::Type::Bytes(size) => Self::Builtin(DynSolType::FixedBytes(*size as usize)),
766            pt::Type::DynamicBytes => Self::Builtin(DynSolType::Bytes),
767            pt::Type::Mapping { value, .. } => Self::from_expression(value)?,
768            pt::Type::Function { params, returns, .. } => {
769                let params = map_parameters(params);
770                let returns = returns
771                    .as_ref()
772                    .map(|(returns, _)| map_parameters(returns))
773                    .unwrap_or_default();
774                Self::Function(
775                    Box::new(Self::Custom(vec!["__fn_type__".to_string()])),
776                    params,
777                    returns,
778                )
779            }
780            // TODO: Rational numbers
781            pt::Type::Rational => return None,
782        };
783        Some(ty)
784    }
785
786    /// Handle special expressions like [global variables](https://docs.soliditylang.org/en/latest/cheatsheet.html#global-variables)
787    ///
788    /// See: <https://github.com/ethereum/solidity/blob/81268e336573721819e39fbb3fefbc9344ad176c/libsolidity/ast/Types.cpp#L4106>
789    fn map_special(self) -> Self {
790        if !matches!(self, Self::Function(_, _, _) | Self::Access(_, _) | Self::Custom(_)) {
791            return self
792        }
793
794        let mut types = Vec::with_capacity(5);
795        let mut args = None;
796        self.recurse(&mut types, &mut args);
797
798        let len = types.len();
799        if len == 0 {
800            return self
801        }
802
803        // Type members, like array, bytes etc
804        #[expect(clippy::single_match)]
805        match &self {
806            Self::Access(inner, access) => {
807                if let Some(ty) = inner.as_ref().clone().try_as_ethabi(None) {
808                    // Array / bytes members
809                    let ty = Self::Builtin(ty);
810                    match access.as_str() {
811                        "length" if ty.is_dynamic() || ty.is_array() || ty.is_fixed_bytes() => {
812                            return Self::Builtin(DynSolType::Uint(256))
813                        }
814                        "pop" if ty.is_dynamic_array() => return ty,
815                        _ => {}
816                    }
817                }
818            }
819            _ => {}
820        }
821
822        let this = {
823            let name = types.last().unwrap().as_str();
824            match len {
825                0 => unreachable!(),
826                1 => match name {
827                    "gasleft" | "addmod" | "mulmod" => Some(DynSolType::Uint(256)),
828                    "keccak256" | "sha256" | "blockhash" => Some(DynSolType::FixedBytes(32)),
829                    "ripemd160" => Some(DynSolType::FixedBytes(20)),
830                    "ecrecover" => Some(DynSolType::Address),
831                    _ => None,
832                },
833                2 => {
834                    let access = types.first().unwrap().as_str();
835                    match name {
836                        "block" => match access {
837                            "coinbase" => Some(DynSolType::Address),
838                            "timestamp" | "difficulty" | "prevrandao" | "number" | "gaslimit" |
839                            "chainid" | "basefee" | "blobbasefee" => Some(DynSolType::Uint(256)),
840                            _ => None,
841                        },
842                        "msg" => match access {
843                            "sender" => Some(DynSolType::Address),
844                            "gas" => Some(DynSolType::Uint(256)),
845                            "value" => Some(DynSolType::Uint(256)),
846                            "data" => Some(DynSolType::Bytes),
847                            "sig" => Some(DynSolType::FixedBytes(4)),
848                            _ => None,
849                        },
850                        "tx" => match access {
851                            "origin" => Some(DynSolType::Address),
852                            "gasprice" => Some(DynSolType::Uint(256)),
853                            _ => None,
854                        },
855                        "abi" => match access {
856                            "decode" => {
857                                // args = Some([Bytes(_), Tuple(args)])
858                                // unwrapping is safe because this is first compiled by solc so
859                                // it is guaranteed to be a valid call
860                                let mut args = args.unwrap();
861                                let last = args.pop().unwrap();
862                                match last {
863                                    Some(ty) => {
864                                        return match ty {
865                                            Self::Tuple(_) => ty,
866                                            ty => Self::Tuple(vec![Some(ty)]),
867                                        }
868                                    }
869                                    None => None,
870                                }
871                            }
872                            s if s.starts_with("encode") => Some(DynSolType::Bytes),
873                            _ => None,
874                        },
875                        "address" => match access {
876                            "balance" => Some(DynSolType::Uint(256)),
877                            "code" => Some(DynSolType::Bytes),
878                            "codehash" => Some(DynSolType::FixedBytes(32)),
879                            "send" => Some(DynSolType::Bool),
880                            _ => None,
881                        },
882                        "type" => match access {
883                            "name" => Some(DynSolType::String),
884                            "creationCode" | "runtimeCode" => Some(DynSolType::Bytes),
885                            "interfaceId" => Some(DynSolType::FixedBytes(4)),
886                            "min" | "max" => Some(
887                                // Either a builtin or an enum
888                                (|| args?.pop()??.into_builtin())()
889                                    .unwrap_or(DynSolType::Uint(256)),
890                            ),
891                            _ => None,
892                        },
893                        "string" => match access {
894                            "concat" => Some(DynSolType::String),
895                            _ => None,
896                        },
897                        "bytes" => match access {
898                            "concat" => Some(DynSolType::Bytes),
899                            _ => None,
900                        },
901                        _ => None,
902                    }
903                }
904                _ => None,
905            }
906        };
907
908        this.map(Self::Builtin).unwrap_or_else(|| match types.last().unwrap().as_str() {
909            "this" | "super" => Self::Custom(types),
910            _ => match self {
911                Self::Custom(_) | Self::Access(_, _) => Self::Custom(types),
912                Self::Function(_, _, _) => self,
913                _ => unreachable!(),
914            },
915        })
916    }
917
918    /// Recurses over itself, appending all the idents and function arguments in the order that they
919    /// are found
920    fn recurse(&self, types: &mut Vec<String>, args: &mut Option<Vec<Option<Self>>>) {
921        match self {
922            Self::Builtin(ty) => types.push(ty.to_string()),
923            Self::Custom(tys) => types.extend(tys.clone()),
924            Self::Access(expr, name) => {
925                types.push(name.clone());
926                expr.recurse(types, args);
927            }
928            Self::Function(fn_name, fn_args, _fn_ret) => {
929                if args.is_none() && !fn_args.is_empty() {
930                    *args = Some(fn_args.clone());
931                }
932                fn_name.recurse(types, args);
933            }
934            _ => {}
935        }
936    }
937
938    /// Infers a custom type's true type by recursing up the parse tree
939    ///
940    /// ### Takes
941    /// - A reference to the [IntermediateOutput]
942    /// - An array of custom types generated by the `MemberAccess` arm of [Self::from_expression]
943    /// - An optional contract name. This should always be `None` when this function is first
944    ///   called.
945    ///
946    /// ### Returns
947    ///
948    /// If successful, an `Ok(Some(DynSolType))` variant.
949    /// If gracefully failed, an `Ok(None)` variant.
950    /// If failed, an `Err(e)` variant.
951    fn infer_custom_type(
952        intermediate: &IntermediateOutput,
953        custom_type: &mut Vec<String>,
954        contract_name: Option<String>,
955    ) -> Result<Option<DynSolType>> {
956        if let Some("this") | Some("super") = custom_type.last().map(String::as_str) {
957            custom_type.pop();
958        }
959        if custom_type.is_empty() {
960            return Ok(None)
961        }
962
963        // If a contract exists with the given name, check its definitions for a match.
964        // Otherwise look in the `run`
965        if let Some(contract_name) = contract_name {
966            let intermediate_contract = intermediate
967                .intermediate_contracts
968                .get(&contract_name)
969                .ok_or_else(|| eyre::eyre!("Could not find intermediate contract!"))?;
970
971            let cur_type = custom_type.last().unwrap();
972            if let Some(func) = intermediate_contract.function_definitions.get(cur_type) {
973                // Check if the custom type is a function pointer member access
974                if let res @ Some(_) = func_members(func, custom_type) {
975                    return Ok(res)
976                }
977
978                // Because tuple types cannot be passed to `abi.encode`, we will only be
979                // receiving functions that have 0 or 1 return parameters here.
980                if func.returns.is_empty() {
981                    eyre::bail!(
982                        "This call expression does not return any values to inspect. Insert as statement."
983                    )
984                }
985
986                // Empty return types check is done above
987                let (_, param) = func.returns.first().unwrap();
988                // Return type should always be present
989                let return_ty = &param.as_ref().unwrap().ty;
990
991                // If the return type is a variable (not a type expression), re-enter the recursion
992                // on the same contract for a variable / struct search. It could be a contract,
993                // struct, array, etc.
994                if let pt::Expression::Variable(ident) = return_ty {
995                    custom_type.push(ident.name.clone());
996                    return Self::infer_custom_type(intermediate, custom_type, Some(contract_name))
997                }
998
999                // Check if our final function call alters the state. If it does, we bail so that it
1000                // will be inserted normally without inspecting. If the state mutability was not
1001                // expressly set, the function is inferred to alter state.
1002                if let Some(pt::FunctionAttribute::Mutability(_mut)) = func
1003                    .attributes
1004                    .iter()
1005                    .find(|attr| matches!(attr, pt::FunctionAttribute::Mutability(_)))
1006                {
1007                    if let pt::Mutability::Payable(_) = _mut {
1008                        eyre::bail!("This function mutates state. Insert as a statement.")
1009                    }
1010                } else {
1011                    eyre::bail!("This function mutates state. Insert as a statement.")
1012                }
1013
1014                Ok(Self::ethabi(return_ty, Some(intermediate)))
1015            } else if let Some(var) = intermediate_contract.variable_definitions.get(cur_type) {
1016                Self::infer_var_expr(&var.ty, Some(intermediate), custom_type)
1017            } else if let Some(strukt) = intermediate_contract.struct_definitions.get(cur_type) {
1018                let inner_types = strukt
1019                    .fields
1020                    .iter()
1021                    .map(|var| {
1022                        Self::ethabi(&var.ty, Some(intermediate))
1023                            .ok_or_else(|| eyre::eyre!("Struct `{cur_type}` has invalid fields"))
1024                    })
1025                    .collect::<Result<Vec<_>>>()?;
1026                Ok(Some(DynSolType::Tuple(inner_types)))
1027            } else {
1028                eyre::bail!("Could not find any definition in contract \"{contract_name}\" for type: {custom_type:?}")
1029            }
1030        } else {
1031            // Check if the custom type is a variable or function within the REPL contract before
1032            // anything. If it is, we can stop here.
1033            if let Ok(res) = Self::infer_custom_type(intermediate, custom_type, Some("REPL".into()))
1034            {
1035                return Ok(res)
1036            }
1037
1038            // Check if the first element of the custom type is a known contract. If it is, begin
1039            // our recursion on on that contract's definitions.
1040            let name = custom_type.last().unwrap();
1041            let contract = intermediate.intermediate_contracts.get(name);
1042            if contract.is_some() {
1043                let contract_name = custom_type.pop();
1044                return Self::infer_custom_type(intermediate, custom_type, contract_name)
1045            }
1046
1047            // See [`Type::infer_var_expr`]
1048            let name = custom_type.last().unwrap();
1049            if let Some(expr) = intermediate.repl_contract_expressions.get(name) {
1050                return Self::infer_var_expr(expr, Some(intermediate), custom_type)
1051            }
1052
1053            // The first element of our custom type was neither a variable or a function within the
1054            // REPL contract, move on to globally available types gracefully.
1055            Ok(None)
1056        }
1057    }
1058
1059    /// Infers the type from a variable's type
1060    fn infer_var_expr(
1061        expr: &pt::Expression,
1062        intermediate: Option<&IntermediateOutput>,
1063        custom_type: &mut Vec<String>,
1064    ) -> Result<Option<DynSolType>> {
1065        // Resolve local (in `run` function) or global (in the `REPL` or other contract) variable
1066        let res = match &expr {
1067            // Custom variable handling
1068            pt::Expression::Variable(ident) => {
1069                let name = &ident.name;
1070
1071                if let Some(intermediate) = intermediate {
1072                    // expression in `run`
1073                    if let Some(expr) = intermediate.repl_contract_expressions.get(name) {
1074                        Self::infer_var_expr(expr, Some(intermediate), custom_type)
1075                    } else if intermediate.intermediate_contracts.contains_key(name) {
1076                        if custom_type.len() > 1 {
1077                            // There is still some recursing left to do: jump into the contract.
1078                            custom_type.pop();
1079                            Self::infer_custom_type(intermediate, custom_type, Some(name.clone()))
1080                        } else {
1081                            // We have no types left to recurse: return the address of the contract.
1082                            Ok(Some(DynSolType::Address))
1083                        }
1084                    } else {
1085                        Err(eyre::eyre!("Could not infer variable type"))
1086                    }
1087                } else {
1088                    Ok(None)
1089                }
1090            }
1091            ty => Ok(Self::ethabi(ty, intermediate)),
1092        };
1093        // re-run everything with the resolved variable in case we're accessing a builtin member
1094        // for example array or bytes length etc
1095        match res {
1096            Ok(Some(ty)) => {
1097                let box_ty = Box::new(Self::Builtin(ty.clone()));
1098                let access = Self::Access(box_ty, custom_type.drain(..).next().unwrap_or_default());
1099                if let Some(mapped) = access.map_special().try_as_ethabi(intermediate) {
1100                    Ok(Some(mapped))
1101                } else {
1102                    Ok(Some(ty))
1103                }
1104            }
1105            res => res,
1106        }
1107    }
1108
1109    /// Attempt to convert this type into a [DynSolType]
1110    ///
1111    /// ### Takes
1112    /// An immutable reference to an [IntermediateOutput]
1113    ///
1114    /// ### Returns
1115    /// Optionally, a [DynSolType]
1116    fn try_as_ethabi(self, intermediate: Option<&IntermediateOutput>) -> Option<DynSolType> {
1117        match self {
1118            Self::Builtin(ty) => Some(ty),
1119            Self::Tuple(types) => Some(DynSolType::Tuple(types_to_parameters(types, intermediate))),
1120            Self::Array(inner) => match *inner {
1121                ty @ Self::Custom(_) => ty.try_as_ethabi(intermediate),
1122                _ => inner
1123                    .try_as_ethabi(intermediate)
1124                    .map(|inner| DynSolType::Array(Box::new(inner))),
1125            },
1126            Self::FixedArray(inner, size) => match *inner {
1127                ty @ Self::Custom(_) => ty.try_as_ethabi(intermediate),
1128                _ => inner
1129                    .try_as_ethabi(intermediate)
1130                    .map(|inner| DynSolType::FixedArray(Box::new(inner), size)),
1131            },
1132            ty @ Self::ArrayIndex(_, _) => ty.into_array_index(intermediate),
1133            Self::Function(ty, _, _) => ty.try_as_ethabi(intermediate),
1134            // should have been mapped to `Custom` in previous steps
1135            Self::Access(_, _) => None,
1136            Self::Custom(mut types) => {
1137                // Cover any local non-state-modifying function call expressions
1138                intermediate.and_then(|intermediate| {
1139                    Self::infer_custom_type(intermediate, &mut types, None).ok().flatten()
1140                })
1141            }
1142        }
1143    }
1144
1145    /// Equivalent to `Type::from_expression` + `Type::map_special` + `Type::try_as_ethabi`
1146    fn ethabi(
1147        expr: &pt::Expression,
1148        intermediate: Option<&IntermediateOutput>,
1149    ) -> Option<DynSolType> {
1150        Self::from_expression(expr)
1151            .map(Self::map_special)
1152            .and_then(|ty| ty.try_as_ethabi(intermediate))
1153    }
1154
1155    /// Get the return type of a function call expression.
1156    fn get_function_return_type<'a>(
1157        contract_expr: Option<&'a pt::Expression>,
1158        intermediate: &IntermediateOutput,
1159    ) -> Option<(&'a pt::Expression, DynSolType)> {
1160        let function_call = match contract_expr? {
1161            pt::Expression::FunctionCall(_, function_call, _) => function_call,
1162            _ => return None,
1163        };
1164        let (contract_name, function_name) = match function_call.as_ref() {
1165            pt::Expression::MemberAccess(_, contract_name, function_name) => {
1166                (contract_name, function_name)
1167            }
1168            _ => return None,
1169        };
1170        let contract_name = match contract_name.as_ref() {
1171            pt::Expression::Variable(contract_name) => contract_name.to_owned(),
1172            _ => return None,
1173        };
1174
1175        let pt::Expression::Variable(contract_name) =
1176            intermediate.repl_contract_expressions.get(&contract_name.name)?
1177        else {
1178            return None
1179        };
1180
1181        let contract = intermediate
1182            .intermediate_contracts
1183            .get(&contract_name.name)?
1184            .function_definitions
1185            .get(&function_name.name)?;
1186        let return_parameter = contract.as_ref().returns.first()?.to_owned().1?;
1187        Self::ethabi(&return_parameter.ty, Some(intermediate)).map(|p| (contract_expr.unwrap(), p))
1188    }
1189
1190    /// Inverts Int to Uint and vice-versa.
1191    fn invert_int(self) -> Self {
1192        match self {
1193            Self::Builtin(DynSolType::Uint(n)) => Self::Builtin(DynSolType::Int(n)),
1194            Self::Builtin(DynSolType::Int(n)) => Self::Builtin(DynSolType::Uint(n)),
1195            x => x,
1196        }
1197    }
1198
1199    /// Returns the `DynSolType` contained by `Type::Builtin`
1200    #[inline]
1201    fn into_builtin(self) -> Option<DynSolType> {
1202        match self {
1203            Self::Builtin(ty) => Some(ty),
1204            _ => None,
1205        }
1206    }
1207
1208    /// Returns the resulting `DynSolType` of indexing self
1209    fn into_array_index(self, intermediate: Option<&IntermediateOutput>) -> Option<DynSolType> {
1210        match self {
1211            Self::Array(inner) | Self::FixedArray(inner, _) | Self::ArrayIndex(inner, _) => {
1212                match inner.try_as_ethabi(intermediate) {
1213                    Some(DynSolType::Array(inner)) | Some(DynSolType::FixedArray(inner, _)) => {
1214                        Some(*inner)
1215                    }
1216                    Some(DynSolType::Bytes) |
1217                    Some(DynSolType::String) |
1218                    Some(DynSolType::FixedBytes(_)) => Some(DynSolType::FixedBytes(1)),
1219                    ty => ty,
1220                }
1221            }
1222            _ => None,
1223        }
1224    }
1225
1226    /// Returns whether this type is dynamic
1227    #[inline]
1228    fn is_dynamic(&self) -> bool {
1229        match self {
1230            // TODO: Note, this is not entirely correct. Fixed arrays of non-dynamic types are
1231            // not dynamic, nor are tuples of non-dynamic types.
1232            Self::Builtin(DynSolType::Bytes | DynSolType::String | DynSolType::Array(_)) => true,
1233            Self::Array(_) => true,
1234            _ => false,
1235        }
1236    }
1237
1238    /// Returns whether this type is an array
1239    #[inline]
1240    fn is_array(&self) -> bool {
1241        matches!(
1242            self,
1243            Self::Array(_) |
1244                Self::FixedArray(_, _) |
1245                Self::Builtin(DynSolType::Array(_)) |
1246                Self::Builtin(DynSolType::FixedArray(_, _))
1247        )
1248    }
1249
1250    /// Returns whether this type is a dynamic array (can call push, pop)
1251    #[inline]
1252    fn is_dynamic_array(&self) -> bool {
1253        matches!(self, Self::Array(_) | Self::Builtin(DynSolType::Array(_)))
1254    }
1255
1256    fn is_fixed_bytes(&self) -> bool {
1257        matches!(self, Self::Builtin(DynSolType::FixedBytes(_)))
1258    }
1259}
1260
1261/// Returns Some if the custom type is a function member access
1262///
1263/// Ref: <https://docs.soliditylang.org/en/latest/types.html#function-types>
1264#[inline]
1265fn func_members(func: &pt::FunctionDefinition, custom_type: &[String]) -> Option<DynSolType> {
1266    if !matches!(func.ty, pt::FunctionTy::Function) {
1267        return None
1268    }
1269
1270    let vis = func.attributes.iter().find_map(|attr| match attr {
1271        pt::FunctionAttribute::Visibility(vis) => Some(vis),
1272        _ => None,
1273    });
1274    match vis {
1275        Some(pt::Visibility::External(_)) | Some(pt::Visibility::Public(_)) => {
1276            match custom_type.first().unwrap().as_str() {
1277                "address" => Some(DynSolType::Address),
1278                "selector" => Some(DynSolType::FixedBytes(4)),
1279                _ => None,
1280            }
1281        }
1282        _ => None,
1283    }
1284}
1285
1286/// Whether execution should continue after inspecting this expression
1287#[inline]
1288fn should_continue(expr: &pt::Expression) -> bool {
1289    match expr {
1290        // assignments
1291        pt::Expression::PreDecrement(_, _) |       // --<inner>
1292        pt::Expression::PostDecrement(_, _) |      // <inner>--
1293        pt::Expression::PreIncrement(_, _) |       // ++<inner>
1294        pt::Expression::PostIncrement(_, _) |      // <inner>++
1295        pt::Expression::Assign(_, _, _) |          // <inner>   = ...
1296        pt::Expression::AssignAdd(_, _, _) |       // <inner>  += ...
1297        pt::Expression::AssignSubtract(_, _, _) |  // <inner>  -= ...
1298        pt::Expression::AssignMultiply(_, _, _) |  // <inner>  *= ...
1299        pt::Expression::AssignDivide(_, _, _) |    // <inner>  /= ...
1300        pt::Expression::AssignModulo(_, _, _) |    // <inner>  %= ...
1301        pt::Expression::AssignAnd(_, _, _) |       // <inner>  &= ...
1302        pt::Expression::AssignOr(_, _, _) |        // <inner>  |= ...
1303        pt::Expression::AssignXor(_, _, _) |       // <inner>  ^= ...
1304        pt::Expression::AssignShiftLeft(_, _, _) | // <inner> <<= ...
1305        pt::Expression::AssignShiftRight(_, _, _)  // <inner> >>= ...
1306        => {
1307            true
1308        }
1309
1310        // Array.pop()
1311        pt::Expression::FunctionCall(_, lhs, _) => {
1312            match lhs.as_ref() {
1313                pt::Expression::MemberAccess(_, _inner, access) => access.name == "pop",
1314                _ => false
1315            }
1316        }
1317
1318        _ => false
1319    }
1320}
1321
1322fn map_parameters(params: &[(pt::Loc, Option<pt::Parameter>)]) -> Vec<Option<Type>> {
1323    params
1324        .iter()
1325        .map(|(_, param)| param.as_ref().and_then(|param| Type::from_expression(&param.ty)))
1326        .collect()
1327}
1328
1329fn types_to_parameters(
1330    types: Vec<Option<Type>>,
1331    intermediate: Option<&IntermediateOutput>,
1332) -> Vec<DynSolType> {
1333    types.into_iter().filter_map(|ty| ty.and_then(|ty| ty.try_as_ethabi(intermediate))).collect()
1334}
1335
1336fn parse_number_literal(expr: &pt::Expression) -> Option<U256> {
1337    match expr {
1338        pt::Expression::NumberLiteral(_, num, exp, unit) => {
1339            let num = U256::from_str(num).unwrap_or(U256::ZERO);
1340            let exp = exp.parse().unwrap_or(0u32);
1341            if exp > 77 {
1342                None
1343            } else {
1344                let exp = U256::from(10usize.pow(exp));
1345                let unit_mul = unit_multiplier(unit).ok()?;
1346                Some(num * exp * unit_mul)
1347            }
1348        }
1349        pt::Expression::HexNumberLiteral(_, num, unit) => {
1350            let unit_mul = unit_multiplier(unit).ok()?;
1351            num.parse::<U256>().map(|num| num * unit_mul).ok()
1352        }
1353        // TODO: Rational numbers
1354        pt::Expression::RationalNumberLiteral(..) => None,
1355        _ => None,
1356    }
1357}
1358
1359#[inline]
1360fn unit_multiplier(unit: &Option<pt::Identifier>) -> Result<U256> {
1361    if let Some(unit) = unit {
1362        let mul = match unit.name.as_str() {
1363            "seconds" => 1,
1364            "minutes" => 60,
1365            "hours" => 60 * 60,
1366            "days" => 60 * 60 * 24,
1367            "weeks" => 60 * 60 * 24 * 7,
1368            "wei" => 1,
1369            "gwei" => 10_usize.pow(9),
1370            "ether" => 10_usize.pow(18),
1371            other => eyre::bail!("unknown unit: {other}"),
1372        };
1373        Ok(U256::from(mul))
1374    } else {
1375        Ok(U256::from(1))
1376    }
1377}
1378
1379#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1380struct Instruction {
1381    pub pc: usize,
1382    pub opcode: u8,
1383    pub data: [u8; 32],
1384    pub data_len: u8,
1385}
1386
1387struct InstructionIter<'a> {
1388    bytes: &'a [u8],
1389    offset: usize,
1390}
1391
1392impl<'a> InstructionIter<'a> {
1393    pub fn new(bytes: &'a [u8]) -> Self {
1394        Self { bytes, offset: 0 }
1395    }
1396}
1397
1398impl Iterator for InstructionIter<'_> {
1399    type Item = Instruction;
1400    fn next(&mut self) -> Option<Self::Item> {
1401        let pc = self.offset;
1402        self.offset += 1;
1403        let opcode = *self.bytes.get(pc)?;
1404        let (data, data_len) = if matches!(opcode, 0x60..=0x7F) {
1405            let mut data = [0; 32];
1406            let data_len = (opcode - 0x60 + 1) as usize;
1407            data[..data_len].copy_from_slice(&self.bytes[self.offset..self.offset + data_len]);
1408            self.offset += data_len;
1409            (data, data_len as u8)
1410        } else {
1411            ([0; 32], 0)
1412        };
1413        Some(Instruction { pc, opcode, data, data_len })
1414    }
1415}
1416
1417#[cfg(test)]
1418mod tests {
1419    use super::*;
1420    use foundry_compilers::{error::SolcError, solc::Solc};
1421    use semver::Version;
1422    use std::sync::Mutex;
1423
1424    #[test]
1425    fn test_const() {
1426        assert_eq!(USIZE_MAX_AS_U256.to::<u64>(), usize::MAX as u64);
1427        assert_eq!(USIZE_MAX_AS_U256.to::<u64>(), usize::MAX as u64);
1428    }
1429
1430    #[test]
1431    fn test_expressions() {
1432        static EXPRESSIONS: &[(&str, DynSolType)] = {
1433            use DynSolType::*;
1434            &[
1435                // units
1436                // uint
1437                ("1 seconds", Uint(256)),
1438                ("1 minutes", Uint(256)),
1439                ("1 hours", Uint(256)),
1440                ("1 days", Uint(256)),
1441                ("1 weeks", Uint(256)),
1442                ("1 wei", Uint(256)),
1443                ("1 gwei", Uint(256)),
1444                ("1 ether", Uint(256)),
1445                // int
1446                ("-1 seconds", Int(256)),
1447                ("-1 minutes", Int(256)),
1448                ("-1 hours", Int(256)),
1449                ("-1 days", Int(256)),
1450                ("-1 weeks", Int(256)),
1451                ("-1 wei", Int(256)),
1452                ("-1 gwei", Int(256)),
1453                ("-1 ether", Int(256)),
1454                //
1455                ("true ? 1 : 0", Uint(256)),
1456                ("true ? -1 : 0", Int(256)),
1457                // misc
1458                //
1459
1460                // ops
1461                // uint
1462                ("1 + 1", Uint(256)),
1463                ("1 - 1", Uint(256)),
1464                ("1 * 1", Uint(256)),
1465                ("1 / 1", Uint(256)),
1466                ("1 % 1", Uint(256)),
1467                ("1 ** 1", Uint(256)),
1468                ("1 | 1", Uint(256)),
1469                ("1 & 1", Uint(256)),
1470                ("1 ^ 1", Uint(256)),
1471                ("1 >> 1", Uint(256)),
1472                ("1 << 1", Uint(256)),
1473                // int
1474                ("int(1) + 1", Int(256)),
1475                ("int(1) - 1", Int(256)),
1476                ("int(1) * 1", Int(256)),
1477                ("int(1) / 1", Int(256)),
1478                ("1 + int(1)", Int(256)),
1479                ("1 - int(1)", Int(256)),
1480                ("1 * int(1)", Int(256)),
1481                ("1 / int(1)", Int(256)),
1482                //
1483
1484                // assign
1485                ("uint256 a; a--", Uint(256)),
1486                ("uint256 a; --a", Uint(256)),
1487                ("uint256 a; a++", Uint(256)),
1488                ("uint256 a; ++a", Uint(256)),
1489                ("uint256 a; a   = 1", Uint(256)),
1490                ("uint256 a; a  += 1", Uint(256)),
1491                ("uint256 a; a  -= 1", Uint(256)),
1492                ("uint256 a; a  *= 1", Uint(256)),
1493                ("uint256 a; a  /= 1", Uint(256)),
1494                ("uint256 a; a  %= 1", Uint(256)),
1495                ("uint256 a; a  &= 1", Uint(256)),
1496                ("uint256 a; a  |= 1", Uint(256)),
1497                ("uint256 a; a  ^= 1", Uint(256)),
1498                ("uint256 a; a <<= 1", Uint(256)),
1499                ("uint256 a; a >>= 1", Uint(256)),
1500                //
1501
1502                // bool
1503                ("true && true", Bool),
1504                ("true || true", Bool),
1505                ("true == true", Bool),
1506                ("true != true", Bool),
1507                ("true < true", Bool),
1508                ("true <= true", Bool),
1509                ("true > true", Bool),
1510                ("true >= true", Bool),
1511                ("!true", Bool),
1512                //
1513            ]
1514        };
1515
1516        let source = &mut source();
1517
1518        let array_expressions: &[(&str, DynSolType)] = &[
1519            ("[1, 2, 3]", fixed_array(DynSolType::Uint(256), 3)),
1520            ("[uint8(1), 2, 3]", fixed_array(DynSolType::Uint(8), 3)),
1521            ("[int8(1), 2, 3]", fixed_array(DynSolType::Int(8), 3)),
1522            ("new uint256[](3)", array(DynSolType::Uint(256))),
1523            ("uint256[] memory a = new uint256[](3);\na[0]", DynSolType::Uint(256)),
1524            ("uint256[] memory a = new uint256[](3);\na[0:3]", array(DynSolType::Uint(256))),
1525        ];
1526        generic_type_test(source, array_expressions);
1527        generic_type_test(source, EXPRESSIONS);
1528    }
1529
1530    #[test]
1531    fn test_types() {
1532        static TYPES: &[(&str, DynSolType)] = {
1533            use DynSolType::*;
1534            &[
1535                // bool
1536                ("bool", Bool),
1537                ("true", Bool),
1538                ("false", Bool),
1539                //
1540
1541                // int and uint
1542                ("uint", Uint(256)),
1543                ("uint(1)", Uint(256)),
1544                ("1", Uint(256)),
1545                ("0x01", Uint(256)),
1546                ("int", Int(256)),
1547                ("int(1)", Int(256)),
1548                ("int(-1)", Int(256)),
1549                ("-1", Int(256)),
1550                ("-0x01", Int(256)),
1551                //
1552
1553                // address
1554                ("address", Address),
1555                ("address(0)", Address),
1556                ("0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990", Address),
1557                ("payable(0)", Address),
1558                ("payable(address(0))", Address),
1559                //
1560
1561                // string
1562                ("string", String),
1563                ("string(\"hello world\")", String),
1564                ("\"hello world\"", String),
1565                ("unicode\"hello world 😀\"", String),
1566                //
1567
1568                // bytes
1569                ("bytes", Bytes),
1570                ("bytes(\"hello world\")", Bytes),
1571                ("bytes(unicode\"hello world 😀\")", Bytes),
1572                ("hex\"68656c6c6f20776f726c64\"", Bytes),
1573                //
1574            ]
1575        };
1576
1577        let mut types: Vec<(String, DynSolType)> = Vec::with_capacity(96 + 32 + 100);
1578        for (n, b) in (8..=256).step_by(8).zip(1..=32) {
1579            types.push((format!("uint{n}(0)"), DynSolType::Uint(n)));
1580            types.push((format!("int{n}(0)"), DynSolType::Int(n)));
1581            types.push((format!("bytes{b}(0x00)"), DynSolType::FixedBytes(b)));
1582        }
1583
1584        for n in 0..=32 {
1585            types.push((
1586                format!("uint256[{n}]"),
1587                DynSolType::FixedArray(Box::new(DynSolType::Uint(256)), n),
1588            ));
1589        }
1590
1591        generic_type_test(&mut source(), TYPES);
1592        generic_type_test(&mut source(), &types);
1593    }
1594
1595    #[test]
1596    fn test_global_vars() {
1597        init_tracing();
1598
1599        // https://docs.soliditylang.org/en/latest/cheatsheet.html#global-variables
1600        let global_variables = {
1601            use DynSolType::*;
1602            &[
1603                // abi
1604                ("abi.decode(bytes, (uint8[13]))", Tuple(vec![FixedArray(Box::new(Uint(8)), 13)])),
1605                ("abi.decode(bytes, (address, bytes))", Tuple(vec![Address, Bytes])),
1606                ("abi.decode(bytes, (uint112, uint48))", Tuple(vec![Uint(112), Uint(48)])),
1607                ("abi.encode(_, _)", Bytes),
1608                ("abi.encodePacked(_, _)", Bytes),
1609                ("abi.encodeWithSelector(bytes4, _, _)", Bytes),
1610                ("abi.encodeCall(function(), (_, _))", Bytes),
1611                ("abi.encodeWithSignature(string, _, _)", Bytes),
1612                //
1613
1614                //
1615                ("bytes.concat()", Bytes),
1616                ("bytes.concat(_)", Bytes),
1617                ("bytes.concat(_, _)", Bytes),
1618                ("string.concat()", String),
1619                ("string.concat(_)", String),
1620                ("string.concat(_, _)", String),
1621                //
1622
1623                // block
1624                ("block.basefee", Uint(256)),
1625                ("block.chainid", Uint(256)),
1626                ("block.coinbase", Address),
1627                ("block.difficulty", Uint(256)),
1628                ("block.gaslimit", Uint(256)),
1629                ("block.number", Uint(256)),
1630                ("block.timestamp", Uint(256)),
1631                //
1632
1633                // tx
1634                ("gasleft()", Uint(256)),
1635                ("msg.data", Bytes),
1636                ("msg.sender", Address),
1637                ("msg.sig", FixedBytes(4)),
1638                ("msg.value", Uint(256)),
1639                ("tx.gasprice", Uint(256)),
1640                ("tx.origin", Address),
1641                //
1642
1643                // assertions
1644                // assert(bool)
1645                // require(bool)
1646                // revert()
1647                // revert(string)
1648                //
1649
1650                //
1651                ("blockhash(uint)", FixedBytes(32)),
1652                ("keccak256(bytes)", FixedBytes(32)),
1653                ("sha256(bytes)", FixedBytes(32)),
1654                ("ripemd160(bytes)", FixedBytes(20)),
1655                ("ecrecover(bytes32, uint8, bytes32, bytes32)", Address),
1656                ("addmod(uint, uint, uint)", Uint(256)),
1657                ("mulmod(uint, uint, uint)", Uint(256)),
1658                //
1659
1660                // address
1661                ("address(_)", Address),
1662                ("address(this)", Address),
1663                // ("super", Type::Custom("super".to_string))
1664                // (selfdestruct(address payable), None)
1665                ("address.balance", Uint(256)),
1666                ("address.code", Bytes),
1667                ("address.codehash", FixedBytes(32)),
1668                ("address.send(uint256)", Bool),
1669                // (address.transfer(uint256), None)
1670                //
1671
1672                // type
1673                ("type(C).name", String),
1674                ("type(C).creationCode", Bytes),
1675                ("type(C).runtimeCode", Bytes),
1676                ("type(I).interfaceId", FixedBytes(4)),
1677                ("type(uint256).min", Uint(256)),
1678                ("type(int128).min", Int(128)),
1679                ("type(int256).min", Int(256)),
1680                ("type(uint256).max", Uint(256)),
1681                ("type(int128).max", Int(128)),
1682                ("type(int256).max", Int(256)),
1683                ("type(Enum1).min", Uint(256)),
1684                ("type(Enum1).max", Uint(256)),
1685                // function
1686                ("this.run.address", Address),
1687                ("this.run.selector", FixedBytes(4)),
1688            ]
1689        };
1690
1691        generic_type_test(&mut source(), global_variables);
1692    }
1693
1694    #[track_caller]
1695    fn source() -> SessionSource {
1696        // synchronize solc install
1697        static PRE_INSTALL_SOLC_LOCK: Mutex<bool> = Mutex::new(false);
1698
1699        // on some CI targets installing results in weird malformed solc files, we try installing it
1700        // multiple times
1701        let version = "0.8.20";
1702        for _ in 0..3 {
1703            let mut is_preinstalled = PRE_INSTALL_SOLC_LOCK.lock().unwrap();
1704            if !*is_preinstalled {
1705                let solc = Solc::find_or_install(&version.parse().unwrap())
1706                    .map(|solc| (solc.version.clone(), solc));
1707                match solc {
1708                    Ok((v, solc)) => {
1709                        // successfully installed
1710                        let _ = sh_println!("found installed Solc v{v} @ {}", solc.solc.display());
1711                        break
1712                    }
1713                    Err(e) => {
1714                        // try reinstalling
1715                        let _ = sh_err!("error while trying to re-install Solc v{version}: {e}");
1716                        let solc = Solc::blocking_install(&version.parse().unwrap());
1717                        if solc.map_err(SolcError::from).is_ok() {
1718                            *is_preinstalled = true;
1719                            break
1720                        }
1721                    }
1722                }
1723            }
1724        }
1725
1726        let solc = Solc::find_or_install(&Version::new(0, 8, 19)).expect("could not install solc");
1727        SessionSource::new(solc, Default::default())
1728    }
1729
1730    fn array(ty: DynSolType) -> DynSolType {
1731        DynSolType::Array(Box::new(ty))
1732    }
1733
1734    fn fixed_array(ty: DynSolType, len: usize) -> DynSolType {
1735        DynSolType::FixedArray(Box::new(ty), len)
1736    }
1737
1738    fn parse(s: &mut SessionSource, input: &str, clear: bool) -> IntermediateOutput {
1739        if clear {
1740            s.drain_run();
1741            s.drain_top_level_code();
1742            s.drain_global_code();
1743        }
1744
1745        *s = s.clone_with_new_line("enum Enum1 { A }".into()).unwrap().0;
1746
1747        let input = format!("{};", input.trim_end().trim_end_matches(';'));
1748        let (mut _s, _) = s.clone_with_new_line(input).unwrap();
1749        *s = _s.clone();
1750        let s = &mut _s;
1751
1752        if let Err(e) = s.parse() {
1753            for err in e {
1754                let _ = sh_eprintln!("{}:{}: {}", err.loc.start(), err.loc.end(), err.message);
1755            }
1756            let source = s.to_repl_source();
1757            panic!("could not parse input:\n{source}")
1758        }
1759        s.generate_intermediate_output().expect("could not generate intermediate output")
1760    }
1761
1762    fn expr(stmts: &[pt::Statement]) -> pt::Expression {
1763        match stmts.last().expect("no statements") {
1764            pt::Statement::Expression(_, e) => e.clone(),
1765            s => panic!("Not an expression: {s:?}"),
1766        }
1767    }
1768
1769    fn get_type(
1770        s: &mut SessionSource,
1771        input: &str,
1772        clear: bool,
1773    ) -> (Option<Type>, IntermediateOutput) {
1774        let intermediate = parse(s, input, clear);
1775        let run_func_body = intermediate.run_func_body().expect("no run func body");
1776        let expr = expr(run_func_body);
1777        (Type::from_expression(&expr).map(Type::map_special), intermediate)
1778    }
1779
1780    fn get_type_ethabi(s: &mut SessionSource, input: &str, clear: bool) -> Option<DynSolType> {
1781        let (ty, intermediate) = get_type(s, input, clear);
1782        ty.and_then(|ty| ty.try_as_ethabi(Some(&intermediate)))
1783    }
1784
1785    fn generic_type_test<'a, T, I>(s: &mut SessionSource, input: I)
1786    where
1787        T: AsRef<str> + std::fmt::Display + 'a,
1788        I: IntoIterator<Item = &'a (T, DynSolType)> + 'a,
1789    {
1790        for (input, expected) in input.into_iter() {
1791            let input = input.as_ref();
1792            let ty = get_type_ethabi(s, input, true);
1793            assert_eq!(ty.as_ref(), Some(expected), "\n{input}");
1794        }
1795    }
1796
1797    fn init_tracing() {
1798        let _ = tracing_subscriber::FmtSubscriber::builder()
1799            .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
1800            .try_init();
1801    }
1802}