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