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