chisel/
executor.rs

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