Skip to main content

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