Skip to main content

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