chisel/
executor.rs

1//! Executor
2//!
3//! This module contains the execution logic for the [SessionSource].
4
5use crate::{
6    prelude::{ChiselDispatcher, ChiselResult, ChiselRunner, SessionSource, SolidityHelper},
7    source::IntermediateOutput,
8};
9use alloy_dyn_abi::{DynSolType, DynSolValue};
10use alloy_json_abi::EventParam;
11use alloy_primitives::{Address, B256, U256, hex};
12use eyre::{Result, WrapErr};
13use foundry_compilers::Artifact;
14use foundry_evm::{
15    backend::Backend, decode::decode_console_logs, executors::ExecutorBuilder,
16    inspectors::CheatsConfig, traces::TraceMode,
17};
18use solang_parser::pt;
19use std::ops::ControlFlow;
20use yansi::Paint;
21
22/// Executor implementation for [SessionSource]
23impl SessionSource {
24    /// Runs the source with the [ChiselRunner]
25    pub async fn execute(&mut self) -> Result<ChiselResult> {
26        // Recompile the project and ensure no errors occurred.
27        let output = self.build()?;
28
29        let (bytecode, final_pc) = output.enter(|output| -> Result<_> {
30            let contract = output
31                .repl_contract()
32                .ok_or_else(|| eyre::eyre!("failed to find REPL contract"))?;
33            trace!(?contract, "REPL contract");
34            let bytecode = contract
35                .get_bytecode_bytes()
36                .ok_or_else(|| eyre::eyre!("No bytecode found for `REPL` contract"))?;
37            Ok((bytecode.into_owned(), output.final_pc(contract)?))
38        })?;
39
40        let Some(final_pc) = final_pc else { return Ok(Default::default()) };
41
42        let mut runner = self.build_runner(final_pc).await?;
43        runner.run(bytecode)
44    }
45
46    /// Inspect a contract element inside of the current session
47    ///
48    /// ### Takes
49    ///
50    /// A solidity snippet
51    ///
52    /// ### Returns
53    ///
54    /// If the input is valid `Ok((continue, formatted_output))` where:
55    /// - `continue` is true if the input should be appended to the source
56    /// - `formatted_output` is the formatted value, if any
57    pub async fn inspect(&self, input: &str) -> Result<(ControlFlow<()>, Option<String>)> {
58        let line = format!("bytes memory inspectoor = abi.encode({input});");
59        let mut source = match self.clone_with_new_line(line) {
60            Ok((source, _)) => source,
61            Err(err) => {
62                debug!(%err, "failed to build new source");
63                return Ok((ControlFlow::Continue(()), None));
64            }
65        };
66
67        let mut source_without_inspector = self.clone();
68
69        // Events and tuples fails compilation due to it not being able to be encoded in
70        // `inspectoor`. If that happens, try executing without the inspector.
71        let (mut res, err) = match source.execute().await {
72            Ok(res) => (res, None),
73            Err(err) => {
74                debug!(?err, %input, "execution failed");
75                match source_without_inspector.execute().await {
76                    Ok(res) => (res, Some(err)),
77                    Err(_) => {
78                        if self.config.foundry_config.verbosity >= 3 {
79                            sh_err!("Could not inspect: {err}")?;
80                        }
81                        return Ok((ControlFlow::Continue(()), None));
82                    }
83                }
84            }
85        };
86
87        // If abi-encoding the input failed, check whether it is an event
88        if let Some(err) = err {
89            let output = source_without_inspector.build()?;
90
91            let formatted_event =
92                output.enter(|output| output.get_event(input).map(format_event_definition));
93            if let Some(formatted_event) = formatted_event {
94                return Ok((ControlFlow::Break(()), Some(formatted_event?)));
95            }
96
97            // we were unable to check the event
98            if self.config.foundry_config.verbosity >= 3 {
99                sh_err!("Failed eval: {err}")?;
100            }
101
102            debug!(%err, %input, "failed abi encode input");
103            return Ok((ControlFlow::Break(()), None));
104        }
105        drop(source_without_inspector);
106
107        let Some((stack, memory)) = &res.state else {
108            // Show traces and logs, if there are any, and return an error
109            if let Ok(decoder) = ChiselDispatcher::decode_traces(&source.config, &mut res).await {
110                ChiselDispatcher::show_traces(&decoder, &mut res).await?;
111            }
112            let decoded_logs = decode_console_logs(&res.logs);
113            if !decoded_logs.is_empty() {
114                sh_println!("{}", "Logs:".green())?;
115                for log in decoded_logs {
116                    sh_println!("  {log}")?;
117                }
118            }
119
120            return Err(eyre::eyre!("Failed to inspect expression"));
121        };
122
123        // Either the expression referred to by `input`, or the last expression,
124        // which was wrapped in `abi.encode`.
125        let generated_output = source.build()?;
126
127        // If the expression is a variable declaration within the REPL contract, use its type;
128        // otherwise, attempt to infer the type.
129        let contract_expr = generated_output
130            .intermediate
131            .repl_contract_expressions
132            .get(input)
133            .or_else(|| source.infer_inner_expr_type());
134
135        // If the current action is a function call, we get its return type
136        // otherwise it returns None
137        let function_call_return_type =
138            Type::get_function_return_type(contract_expr, &generated_output.intermediate);
139
140        let (contract_expr, ty) = if let Some(function_call_return_type) = function_call_return_type
141        {
142            (function_call_return_type.0, function_call_return_type.1)
143        } else {
144            match contract_expr.and_then(|e| {
145                Type::ethabi(e, Some(&generated_output.intermediate)).map(|ty| (e, ty))
146            }) {
147                Some(res) => res,
148                // this type was denied for inspection, continue
149                None => return Ok((ControlFlow::Continue(()), None)),
150            }
151        };
152
153        // the file compiled correctly, thus the last stack item must be the memory offset of
154        // the `bytes memory inspectoor` value
155        let data = (|| -> Option<_> {
156            let mut offset: usize = stack.last()?.try_into().ok()?;
157            debug!("inspect memory @ {offset}: {}", hex::encode(memory));
158            let mem_offset = memory.get(offset..offset + 32)?;
159            let len: usize = U256::try_from_be_slice(mem_offset)?.try_into().ok()?;
160            offset += 32;
161            memory.get(offset..offset + len)
162        })();
163        let Some(data) = data else {
164            eyre::bail!("Failed to inspect last expression: could not retrieve data from memory")
165        };
166        let token = ty.abi_decode(data).wrap_err("Could not decode inspected values")?;
167        let c = if should_continue(contract_expr) {
168            ControlFlow::Continue(())
169        } else {
170            ControlFlow::Break(())
171        };
172        Ok((c, Some(format_token(token))))
173    }
174
175    /// Gracefully attempts to extract the type of the expression within the `abi.encode(...)`
176    /// call inserted by the inspect function.
177    ///
178    /// ### Takes
179    ///
180    /// A reference to a [SessionSource]
181    ///
182    /// ### Returns
183    ///
184    /// Optionally, a [Type]
185    fn infer_inner_expr_type(&self) -> Option<&pt::Expression> {
186        let out = self.build().ok()?;
187        let run = out.run_func_body().ok()?.last();
188        match run {
189            Some(pt::Statement::VariableDefinition(
190                _,
191                _,
192                Some(pt::Expression::FunctionCall(_, _, args)),
193            )) => {
194                // We can safely unwrap the first expression because this function
195                // will only be called on a session source that has just had an
196                // `inspectoor` variable appended to it.
197                Some(args.first().unwrap())
198            }
199            _ => None,
200        }
201    }
202
203    async fn build_runner(&mut self, final_pc: usize) -> Result<ChiselRunner> {
204        let env = self.config.evm_opts.evm_env().await?;
205
206        let backend = match self.config.backend.clone() {
207            Some(backend) => backend,
208            None => {
209                let fork = self.config.evm_opts.get_fork(&self.config.foundry_config, env.clone());
210                let backend = Backend::spawn(fork)?;
211                self.config.backend = Some(backend.clone());
212                backend
213            }
214        };
215
216        let executor = ExecutorBuilder::new()
217            .inspectors(|stack| {
218                stack.chisel_state(final_pc).trace_mode(TraceMode::Call).cheatcodes(
219                    CheatsConfig::new(
220                        &self.config.foundry_config,
221                        self.config.evm_opts.clone(),
222                        None,
223                        None,
224                    )
225                    .into(),
226                )
227            })
228            .gas_limit(self.config.evm_opts.gas_limit())
229            .spec_id(self.config.foundry_config.evm_spec_id())
230            .legacy_assertions(self.config.foundry_config.legacy_assertions)
231            .build(env, backend);
232
233        Ok(ChiselRunner::new(executor, U256::MAX, Address::ZERO, self.config.calldata.clone()))
234    }
235}
236
237/// Formats a value into an inspection message
238// TODO: Verbosity option
239fn format_token(token: DynSolValue) -> String {
240    match token {
241        DynSolValue::Address(a) => {
242            format!("Type: {}\n└ Data: {}", "address".red(), a.cyan())
243        }
244        DynSolValue::FixedBytes(b, byte_len) => {
245            format!(
246                "Type: {}\n└ Data: {}",
247                format!("bytes{byte_len}").red(),
248                hex::encode_prefixed(b).cyan()
249            )
250        }
251        DynSolValue::Int(i, bit_len) => {
252            format!(
253                "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
254                format!("int{bit_len}").red(),
255                format!(
256                    "0x{}",
257                    format!("{i:x}")
258                        .chars()
259                        .skip(if i.is_negative() { 64 - bit_len / 4 } else { 0 })
260                        .collect::<String>()
261                )
262                .cyan(),
263                hex::encode_prefixed(B256::from(i)).cyan(),
264                i.cyan()
265            )
266        }
267        DynSolValue::Uint(i, bit_len) => {
268            format!(
269                "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
270                format!("uint{bit_len}").red(),
271                format!("0x{i:x}").cyan(),
272                hex::encode_prefixed(B256::from(i)).cyan(),
273                i.cyan()
274            )
275        }
276        DynSolValue::Bool(b) => {
277            format!("Type: {}\n└ Value: {}", "bool".red(), b.cyan())
278        }
279        DynSolValue::String(_) | DynSolValue::Bytes(_) => {
280            let hex = hex::encode(token.abi_encode());
281            let s = token.as_str();
282            format!(
283                "Type: {}\n{}├ Hex (Memory):\n├─ Length ({}): {}\n├─ Contents ({}): {}\n├ Hex (Tuple Encoded):\n├─ Pointer ({}): {}\n├─ Length ({}): {}\n└─ Contents ({}): {}",
284                if s.is_some() { "string" } else { "dynamic bytes" }.red(),
285                if let Some(s) = s {
286                    format!("├ UTF-8: {}\n", s.cyan())
287                } else {
288                    String::default()
289                },
290                "[0x00:0x20]".yellow(),
291                format!("0x{}", &hex[64..128]).cyan(),
292                "[0x20:..]".yellow(),
293                format!("0x{}", &hex[128..]).cyan(),
294                "[0x00:0x20]".yellow(),
295                format!("0x{}", &hex[..64]).cyan(),
296                "[0x20:0x40]".yellow(),
297                format!("0x{}", &hex[64..128]).cyan(),
298                "[0x40:..]".yellow(),
299                format!("0x{}", &hex[128..]).cyan(),
300            )
301        }
302        DynSolValue::FixedArray(tokens) | DynSolValue::Array(tokens) => {
303            let mut out = format!(
304                "{}({}) = {}",
305                "array".red(),
306                format!("{}", tokens.len()).yellow(),
307                '['.red()
308            );
309            for token in tokens {
310                out.push_str("\n  ├ ");
311                out.push_str(&format_token(token).replace('\n', "\n  "));
312                out.push('\n');
313            }
314            out.push_str(&']'.red().to_string());
315            out
316        }
317        DynSolValue::Tuple(tokens) => {
318            let displayed_types = tokens
319                .iter()
320                .map(|t| t.sol_type_name().unwrap_or_default())
321                .collect::<Vec<_>>()
322                .join(", ");
323            let mut out =
324                format!("{}({}) = {}", "tuple".red(), displayed_types.yellow(), '('.red());
325            for token in tokens {
326                out.push_str("\n  ├ ");
327                out.push_str(&format_token(token).replace('\n', "\n  "));
328                out.push('\n');
329            }
330            out.push_str(&')'.red().to_string());
331            out
332        }
333        _ => {
334            unimplemented!()
335        }
336    }
337}
338
339/// Formats a [pt::EventDefinition] into an inspection message
340///
341/// ### Takes
342///
343/// An borrowed [pt::EventDefinition]
344///
345/// ### Returns
346///
347/// A formatted [pt::EventDefinition] for use in inspection output.
348// TODO: Verbosity option
349fn format_event_definition(event_definition: &pt::EventDefinition) -> Result<String> {
350    let event_name = event_definition.name.as_ref().expect("Event has a name").to_string();
351    let inputs = event_definition
352        .fields
353        .iter()
354        .map(|param| {
355            let name = param
356                .name
357                .as_ref()
358                .map(ToString::to_string)
359                .unwrap_or_else(|| "<anonymous>".to_string());
360            let kind = Type::from_expression(&param.ty)
361                .and_then(Type::into_builtin)
362                .ok_or_else(|| eyre::eyre!("Invalid type in event {event_name}"))?;
363            Ok(EventParam {
364                name,
365                ty: kind.to_string(),
366                components: vec![],
367                indexed: param.indexed,
368                internal_type: None,
369            })
370        })
371        .collect::<Result<Vec<_>>>()?;
372    let event =
373        alloy_json_abi::Event { name: event_name, inputs, anonymous: event_definition.anonymous };
374
375    Ok(format!(
376        "Type: {}\n├ Name: {}\n├ Signature: {:?}\n└ Selector: {:?}",
377        "event".red(),
378        SolidityHelper::new().highlight(&format!(
379            "{}({})",
380            &event.name,
381            &event
382                .inputs
383                .iter()
384                .map(|param| format!(
385                    "{}{}{}",
386                    param.ty,
387                    if param.indexed { " indexed" } else { "" },
388                    if param.name.is_empty() {
389                        String::default()
390                    } else {
391                        format!(" {}", &param.name)
392                    },
393                ))
394                .collect::<Vec<_>>()
395                .join(", ")
396        )),
397        event.signature().cyan(),
398        event.selector().cyan(),
399    ))
400}
401
402// =============================================
403// Modified from
404// [soli](https://github.com/jpopesculian/soli)
405// =============================================
406
407#[derive(Clone, Debug, PartialEq)]
408enum Type {
409    /// (type)
410    Builtin(DynSolType),
411
412    /// (type)
413    Array(Box<Self>),
414
415    /// (type, length)
416    FixedArray(Box<Self>, usize),
417
418    /// (type, index)
419    ArrayIndex(Box<Self>, Option<usize>),
420
421    /// (types)
422    Tuple(Vec<Option<Self>>),
423
424    /// (name, params, returns)
425    Function(Box<Self>, Vec<Option<Self>>, Vec<Option<Self>>),
426
427    /// (lhs, rhs)
428    Access(Box<Self>, String),
429
430    /// (types)
431    Custom(Vec<String>),
432}
433
434impl Type {
435    /// Convert a [pt::Expression] to a [Type]
436    ///
437    /// ### Takes
438    ///
439    /// A reference to a [pt::Expression] to convert.
440    ///
441    /// ### Returns
442    ///
443    /// Optionally, an owned [Type]
444    fn from_expression(expr: &pt::Expression) -> Option<Self> {
445        match expr {
446            pt::Expression::Type(_, ty) => Self::from_type(ty),
447
448            pt::Expression::Variable(ident) => Some(Self::Custom(vec![ident.name.clone()])),
449
450            // array
451            pt::Expression::ArraySubscript(_, expr, num) => {
452                // if num is Some then this is either an index operation (arr[<num>])
453                // or a FixedArray statement (new uint256[<num>])
454                Self::from_expression(expr).and_then(|ty| {
455                    let boxed = Box::new(ty);
456                    let num = num.as_deref().and_then(parse_number_literal).and_then(|n| {
457                        usize::try_from(n).ok()
458                    });
459                    match expr.as_ref() {
460                        // statement
461                        pt::Expression::Type(_, _) => {
462                            if let Some(num) = num {
463                                Some(Self::FixedArray(boxed, num))
464                            } else {
465                                Some(Self::Array(boxed))
466                            }
467                        }
468                        // index
469                        pt::Expression::Variable(_) => {
470                            Some(Self::ArrayIndex(boxed, num))
471                        }
472                        _ => None
473                    }
474                })
475            }
476            pt::Expression::ArrayLiteral(_, values) => {
477                values.first().and_then(Self::from_expression).map(|ty| {
478                    Self::FixedArray(Box::new(ty), values.len())
479                })
480            }
481
482            // tuple
483            pt::Expression::List(_, params) => Some(Self::Tuple(map_parameters(params))),
484
485            // <lhs>.<rhs>
486            pt::Expression::MemberAccess(_, lhs, rhs) => {
487                Self::from_expression(lhs).map(|lhs| {
488                    Self::Access(Box::new(lhs), rhs.name.clone())
489                })
490            }
491
492            // <inner>
493            pt::Expression::Parenthesis(_, inner) |         // (<inner>)
494            pt::Expression::New(_, inner) |                 // new <inner>
495            pt::Expression::UnaryPlus(_, inner) |           // +<inner>
496            // ops
497            pt::Expression::BitwiseNot(_, inner) |          // ~<inner>
498            pt::Expression::ArraySlice(_, inner, _, _) |    // <inner>[*start*:*end*]
499            // assign ops
500            pt::Expression::PreDecrement(_, inner) |        // --<inner>
501            pt::Expression::PostDecrement(_, inner) |       // <inner>--
502            pt::Expression::PreIncrement(_, inner) |        // ++<inner>
503            pt::Expression::PostIncrement(_, inner) |       // <inner>++
504            pt::Expression::Assign(_, inner, _) |           // <inner>   = ...
505            pt::Expression::AssignAdd(_, inner, _) |        // <inner>  += ...
506            pt::Expression::AssignSubtract(_, inner, _) |   // <inner>  -= ...
507            pt::Expression::AssignMultiply(_, inner, _) |   // <inner>  *= ...
508            pt::Expression::AssignDivide(_, inner, _) |     // <inner>  /= ...
509            pt::Expression::AssignModulo(_, inner, _) |     // <inner>  %= ...
510            pt::Expression::AssignAnd(_, inner, _) |        // <inner>  &= ...
511            pt::Expression::AssignOr(_, inner, _) |         // <inner>  |= ...
512            pt::Expression::AssignXor(_, inner, _) |        // <inner>  ^= ...
513            pt::Expression::AssignShiftLeft(_, inner, _) |  // <inner> <<= ...
514            pt::Expression::AssignShiftRight(_, inner, _)   // <inner> >>= ...
515            => Self::from_expression(inner),
516
517            // *condition* ? <if_true> : <if_false>
518            pt::Expression::ConditionalOperator(_, _, if_true, if_false) => {
519                Self::from_expression(if_true).or_else(|| Self::from_expression(if_false))
520            }
521
522            // address
523            pt::Expression::AddressLiteral(_, _) => Some(Self::Builtin(DynSolType::Address)),
524            pt::Expression::HexNumberLiteral(_, s, _) => {
525                match s.parse::<Address>() {
526                    Ok(addr) => {
527                        if *s == addr.to_checksum(None) {
528                            Some(Self::Builtin(DynSolType::Address))
529                        } else {
530                            Some(Self::Builtin(DynSolType::Uint(256)))
531                        }
532                    },
533                    _ => {
534                        Some(Self::Builtin(DynSolType::Uint(256)))
535                    }
536                }
537            }
538
539            // uint and int
540            // invert
541            pt::Expression::Negate(_, inner) => Self::from_expression(inner).map(Self::invert_int),
542
543            // int if either operand is int
544            // TODO: will need an update for Solidity v0.8.18 user defined operators:
545            // https://github.com/ethereum/solidity/issues/13718#issuecomment-1341058649
546            pt::Expression::Add(_, lhs, rhs) |
547            pt::Expression::Subtract(_, lhs, rhs) |
548            pt::Expression::Multiply(_, lhs, rhs) |
549            pt::Expression::Divide(_, lhs, rhs) => {
550                match (Self::ethabi(lhs, None), Self::ethabi(rhs, None)) {
551                    (Some(DynSolType::Int(_)), Some(DynSolType::Int(_))) |
552                    (Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) |
553                    (Some(DynSolType::Uint(_)), Some(DynSolType::Int(_))) => {
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        match &self {
673            Self::Access(inner, access) => {
674                if let Some(ty) = inner.as_ref().clone().try_as_ethabi(None) {
675                    // Array / bytes members
676                    let ty = Self::Builtin(ty);
677                    match access.as_str() {
678                        "length" if ty.is_dynamic() || ty.is_array() || ty.is_fixed_bytes() => {
679                            return Self::Builtin(DynSolType::Uint(256));
680                        }
681                        "pop" if ty.is_dynamic_array() => return ty,
682                        _ => {}
683                    }
684                }
685            }
686            _ => {}
687        }
688
689        let this = {
690            let name = types.last().unwrap().as_str();
691            match len {
692                0 => unreachable!(),
693                1 => match name {
694                    "gasleft" | "addmod" | "mulmod" => Some(DynSolType::Uint(256)),
695                    "keccak256" | "sha256" | "blockhash" => Some(DynSolType::FixedBytes(32)),
696                    "ripemd160" => Some(DynSolType::FixedBytes(20)),
697                    "ecrecover" => Some(DynSolType::Address),
698                    _ => None,
699                },
700                2 => {
701                    let access = types.first().unwrap().as_str();
702                    match name {
703                        "block" => match access {
704                            "coinbase" => Some(DynSolType::Address),
705                            "timestamp" | "difficulty" | "prevrandao" | "number" | "gaslimit"
706                            | "chainid" | "basefee" | "blobbasefee" => Some(DynSolType::Uint(256)),
707                            _ => None,
708                        },
709                        "msg" => match access {
710                            "sender" => Some(DynSolType::Address),
711                            "gas" => Some(DynSolType::Uint(256)),
712                            "value" => Some(DynSolType::Uint(256)),
713                            "data" => Some(DynSolType::Bytes),
714                            "sig" => Some(DynSolType::FixedBytes(4)),
715                            _ => None,
716                        },
717                        "tx" => match access {
718                            "origin" => Some(DynSolType::Address),
719                            "gasprice" => Some(DynSolType::Uint(256)),
720                            _ => None,
721                        },
722                        "abi" => match access {
723                            "decode" => {
724                                // args = Some([Bytes(_), Tuple(args)])
725                                // unwrapping is safe because this is first compiled by solc so
726                                // it is guaranteed to be a valid call
727                                let mut args = args.unwrap();
728                                let last = args.pop().unwrap();
729                                match last {
730                                    Some(ty) => {
731                                        return match ty {
732                                            Self::Tuple(_) => ty,
733                                            ty => Self::Tuple(vec![Some(ty)]),
734                                        };
735                                    }
736                                    None => None,
737                                }
738                            }
739                            s if s.starts_with("encode") => Some(DynSolType::Bytes),
740                            _ => None,
741                        },
742                        "address" => match access {
743                            "balance" => Some(DynSolType::Uint(256)),
744                            "code" => Some(DynSolType::Bytes),
745                            "codehash" => Some(DynSolType::FixedBytes(32)),
746                            "send" => Some(DynSolType::Bool),
747                            _ => None,
748                        },
749                        "type" => match access {
750                            "name" => Some(DynSolType::String),
751                            "creationCode" | "runtimeCode" => Some(DynSolType::Bytes),
752                            "interfaceId" => Some(DynSolType::FixedBytes(4)),
753                            "min" | "max" => Some(
754                                // Either a builtin or an enum
755                                (|| args?.pop()??.into_builtin())()
756                                    .unwrap_or(DynSolType::Uint(256)),
757                            ),
758                            _ => None,
759                        },
760                        "string" => match access {
761                            "concat" => Some(DynSolType::String),
762                            _ => None,
763                        },
764                        "bytes" => match access {
765                            "concat" => Some(DynSolType::Bytes),
766                            _ => None,
767                        },
768                        _ => None,
769                    }
770                }
771                _ => None,
772            }
773        };
774
775        this.map(Self::Builtin).unwrap_or_else(|| match types.last().unwrap().as_str() {
776            "this" | "super" => Self::Custom(types),
777            _ => match self {
778                Self::Custom(_) | Self::Access(_, _) => Self::Custom(types),
779                Self::Function(_, _, _) => self,
780                _ => unreachable!(),
781            },
782        })
783    }
784
785    /// Recurses over itself, appending all the idents and function arguments in the order that they
786    /// are found
787    fn recurse(&self, types: &mut Vec<String>, args: &mut Option<Vec<Option<Self>>>) {
788        match self {
789            Self::Builtin(ty) => types.push(ty.to_string()),
790            Self::Custom(tys) => types.extend(tys.clone()),
791            Self::Access(expr, name) => {
792                types.push(name.clone());
793                expr.recurse(types, args);
794            }
795            Self::Function(fn_name, fn_args, _fn_ret) => {
796                if args.is_none() && !fn_args.is_empty() {
797                    *args = Some(fn_args.clone());
798                }
799                fn_name.recurse(types, args);
800            }
801            _ => {}
802        }
803    }
804
805    /// Infers a custom type's true type by recursing up the parse tree
806    ///
807    /// ### Takes
808    /// - A reference to the [IntermediateOutput]
809    /// - An array of custom types generated by the `MemberAccess` arm of [Self::from_expression]
810    /// - An optional contract name. This should always be `None` when this function is first
811    ///   called.
812    ///
813    /// ### Returns
814    ///
815    /// If successful, an `Ok(Some(DynSolType))` variant.
816    /// If gracefully failed, an `Ok(None)` variant.
817    /// If failed, an `Err(e)` variant.
818    fn infer_custom_type(
819        intermediate: &IntermediateOutput,
820        custom_type: &mut Vec<String>,
821        contract_name: Option<String>,
822    ) -> Result<Option<DynSolType>> {
823        if let Some("this") | Some("super") = custom_type.last().map(String::as_str) {
824            custom_type.pop();
825        }
826        if custom_type.is_empty() {
827            return Ok(None);
828        }
829
830        // If a contract exists with the given name, check its definitions for a match.
831        // Otherwise look in the `run`
832        if let Some(contract_name) = contract_name {
833            let intermediate_contract = intermediate
834                .intermediate_contracts
835                .get(&contract_name)
836                .ok_or_else(|| eyre::eyre!("Could not find intermediate contract!"))?;
837
838            let cur_type = custom_type.last().unwrap();
839            if let Some(func) = intermediate_contract.function_definitions.get(cur_type) {
840                // Check if the custom type is a function pointer member access
841                if let res @ Some(_) = func_members(func, custom_type) {
842                    return Ok(res);
843                }
844
845                // Because tuple types cannot be passed to `abi.encode`, we will only be
846                // receiving functions that have 0 or 1 return parameters here.
847                if func.returns.is_empty() {
848                    eyre::bail!(
849                        "This call expression does not return any values to inspect. Insert as statement."
850                    )
851                }
852
853                // Empty return types check is done above
854                let (_, param) = func.returns.first().unwrap();
855                // Return type should always be present
856                let return_ty = &param.as_ref().unwrap().ty;
857
858                // If the return type is a variable (not a type expression), re-enter the recursion
859                // on the same contract for a variable / struct search. It could be a contract,
860                // struct, array, etc.
861                if let pt::Expression::Variable(ident) = return_ty {
862                    custom_type.push(ident.name.clone());
863                    return Self::infer_custom_type(intermediate, custom_type, Some(contract_name));
864                }
865
866                // Check if our final function call alters the state. If it does, we bail so that it
867                // will be inserted normally without inspecting. If the state mutability was not
868                // expressly set, the function is inferred to alter state.
869                if let Some(pt::FunctionAttribute::Mutability(_mut)) = func
870                    .attributes
871                    .iter()
872                    .find(|attr| matches!(attr, pt::FunctionAttribute::Mutability(_)))
873                {
874                    if let pt::Mutability::Payable(_) = _mut {
875                        eyre::bail!("This function mutates state. Insert as a statement.")
876                    }
877                } else {
878                    eyre::bail!("This function mutates state. Insert as a statement.")
879                }
880
881                Ok(Self::ethabi(return_ty, Some(intermediate)))
882            } else if let Some(var) = intermediate_contract.variable_definitions.get(cur_type) {
883                Self::infer_var_expr(&var.ty, Some(intermediate), custom_type)
884            } else if let Some(strukt) = intermediate_contract.struct_definitions.get(cur_type) {
885                let inner_types = strukt
886                    .fields
887                    .iter()
888                    .map(|var| {
889                        Self::ethabi(&var.ty, Some(intermediate))
890                            .ok_or_else(|| eyre::eyre!("Struct `{cur_type}` has invalid fields"))
891                    })
892                    .collect::<Result<Vec<_>>>()?;
893                Ok(Some(DynSolType::Tuple(inner_types)))
894            } else {
895                eyre::bail!(
896                    "Could not find any definition in contract \"{contract_name}\" for type: {custom_type:?}"
897                )
898            }
899        } else {
900            // Check if the custom type is a variable or function within the REPL contract before
901            // anything. If it is, we can stop here.
902            if let Ok(res) = Self::infer_custom_type(intermediate, custom_type, Some("REPL".into()))
903            {
904                return Ok(res);
905            }
906
907            // Check if the first element of the custom type is a known contract. If it is, begin
908            // our recursion on that contract's definitions.
909            let name = custom_type.last().unwrap();
910            let contract = intermediate.intermediate_contracts.get(name);
911            if contract.is_some() {
912                let contract_name = custom_type.pop();
913                return Self::infer_custom_type(intermediate, custom_type, contract_name);
914            }
915
916            // See [`Type::infer_var_expr`]
917            let name = custom_type.last().unwrap();
918            if let Some(expr) = intermediate.repl_contract_expressions.get(name) {
919                return Self::infer_var_expr(expr, Some(intermediate), custom_type);
920            }
921
922            // The first element of our custom type was neither a variable or a function within the
923            // REPL contract, move on to globally available types gracefully.
924            Ok(None)
925        }
926    }
927
928    /// Infers the type from a variable's type
929    fn infer_var_expr(
930        expr: &pt::Expression,
931        intermediate: Option<&IntermediateOutput>,
932        custom_type: &mut Vec<String>,
933    ) -> Result<Option<DynSolType>> {
934        // Resolve local (in `run` function) or global (in the `REPL` or other contract) variable
935        let res = match &expr {
936            // Custom variable handling
937            pt::Expression::Variable(ident) => {
938                let name = &ident.name;
939
940                if let Some(intermediate) = intermediate {
941                    // expression in `run`
942                    if let Some(expr) = intermediate.repl_contract_expressions.get(name) {
943                        Self::infer_var_expr(expr, Some(intermediate), custom_type)
944                    } else if intermediate.intermediate_contracts.contains_key(name) {
945                        if custom_type.len() > 1 {
946                            // There is still some recursing left to do: jump into the contract.
947                            custom_type.pop();
948                            Self::infer_custom_type(intermediate, custom_type, Some(name.clone()))
949                        } else {
950                            // We have no types left to recurse: return the address of the contract.
951                            Ok(Some(DynSolType::Address))
952                        }
953                    } else {
954                        Err(eyre::eyre!("Could not infer variable type"))
955                    }
956                } else {
957                    Ok(None)
958                }
959            }
960            ty => Ok(Self::ethabi(ty, intermediate)),
961        };
962        // re-run everything with the resolved variable in case we're accessing a builtin member
963        // for example array or bytes length etc
964        match res {
965            Ok(Some(ty)) => {
966                let box_ty = Box::new(Self::Builtin(ty.clone()));
967                let access = Self::Access(box_ty, custom_type.drain(..).next().unwrap_or_default());
968                if let Some(mapped) = access.map_special().try_as_ethabi(intermediate) {
969                    Ok(Some(mapped))
970                } else {
971                    Ok(Some(ty))
972                }
973            }
974            res => res,
975        }
976    }
977
978    /// Attempt to convert this type into a [DynSolType]
979    ///
980    /// ### Takes
981    /// An immutable reference to an [IntermediateOutput]
982    ///
983    /// ### Returns
984    /// Optionally, a [DynSolType]
985    fn try_as_ethabi(self, intermediate: Option<&IntermediateOutput>) -> Option<DynSolType> {
986        match self {
987            Self::Builtin(ty) => Some(ty),
988            Self::Tuple(types) => Some(DynSolType::Tuple(types_to_parameters(types, intermediate))),
989            Self::Array(inner) => match *inner {
990                ty @ Self::Custom(_) => ty.try_as_ethabi(intermediate),
991                _ => inner
992                    .try_as_ethabi(intermediate)
993                    .map(|inner| DynSolType::Array(Box::new(inner))),
994            },
995            Self::FixedArray(inner, size) => match *inner {
996                ty @ Self::Custom(_) => ty.try_as_ethabi(intermediate),
997                _ => inner
998                    .try_as_ethabi(intermediate)
999                    .map(|inner| DynSolType::FixedArray(Box::new(inner), size)),
1000            },
1001            ty @ Self::ArrayIndex(_, _) => ty.into_array_index(intermediate),
1002            Self::Function(ty, _, _) => ty.try_as_ethabi(intermediate),
1003            // should have been mapped to `Custom` in previous steps
1004            Self::Access(_, _) => None,
1005            Self::Custom(mut types) => {
1006                // Cover any local non-state-modifying function call expressions
1007                intermediate.and_then(|intermediate| {
1008                    Self::infer_custom_type(intermediate, &mut types, None).ok().flatten()
1009                })
1010            }
1011        }
1012    }
1013
1014    /// Equivalent to `Type::from_expression` + `Type::map_special` + `Type::try_as_ethabi`
1015    fn ethabi(
1016        expr: &pt::Expression,
1017        intermediate: Option<&IntermediateOutput>,
1018    ) -> Option<DynSolType> {
1019        Self::from_expression(expr)
1020            .map(Self::map_special)
1021            .and_then(|ty| ty.try_as_ethabi(intermediate))
1022    }
1023
1024    /// Get the return type of a function call expression.
1025    fn get_function_return_type<'a>(
1026        contract_expr: Option<&'a pt::Expression>,
1027        intermediate: &IntermediateOutput,
1028    ) -> Option<(&'a pt::Expression, DynSolType)> {
1029        let function_call = match contract_expr? {
1030            pt::Expression::FunctionCall(_, function_call, _) => function_call,
1031            _ => return None,
1032        };
1033        let (contract_name, function_name) = match function_call.as_ref() {
1034            pt::Expression::MemberAccess(_, contract_name, function_name) => {
1035                (contract_name, function_name)
1036            }
1037            _ => return None,
1038        };
1039        let contract_name = match contract_name.as_ref() {
1040            pt::Expression::Variable(contract_name) => contract_name.to_owned(),
1041            _ => return None,
1042        };
1043
1044        let pt::Expression::Variable(contract_name) =
1045            intermediate.repl_contract_expressions.get(&contract_name.name)?
1046        else {
1047            return None;
1048        };
1049
1050        let contract = intermediate
1051            .intermediate_contracts
1052            .get(&contract_name.name)?
1053            .function_definitions
1054            .get(&function_name.name)?;
1055        let return_parameter = contract.as_ref().returns.first()?.to_owned().1?;
1056        Self::ethabi(&return_parameter.ty, Some(intermediate)).map(|p| (contract_expr.unwrap(), p))
1057    }
1058
1059    /// Inverts Int to Uint and vice-versa.
1060    fn invert_int(self) -> Self {
1061        match self {
1062            Self::Builtin(DynSolType::Uint(n)) => Self::Builtin(DynSolType::Int(n)),
1063            Self::Builtin(DynSolType::Int(n)) => Self::Builtin(DynSolType::Uint(n)),
1064            x => x,
1065        }
1066    }
1067
1068    /// Returns the `DynSolType` contained by `Type::Builtin`
1069    #[inline]
1070    fn into_builtin(self) -> Option<DynSolType> {
1071        match self {
1072            Self::Builtin(ty) => Some(ty),
1073            _ => None,
1074        }
1075    }
1076
1077    /// Returns the resulting `DynSolType` of indexing self
1078    fn into_array_index(self, intermediate: Option<&IntermediateOutput>) -> Option<DynSolType> {
1079        match self {
1080            Self::Array(inner) | Self::FixedArray(inner, _) | Self::ArrayIndex(inner, _) => {
1081                match inner.try_as_ethabi(intermediate) {
1082                    Some(DynSolType::Array(inner)) | Some(DynSolType::FixedArray(inner, _)) => {
1083                        Some(*inner)
1084                    }
1085                    Some(DynSolType::Bytes)
1086                    | Some(DynSolType::String)
1087                    | Some(DynSolType::FixedBytes(_)) => Some(DynSolType::FixedBytes(1)),
1088                    ty => ty,
1089                }
1090            }
1091            _ => None,
1092        }
1093    }
1094
1095    /// Returns whether this type is dynamic
1096    #[inline]
1097    fn is_dynamic(&self) -> bool {
1098        match self {
1099            // TODO: Note, this is not entirely correct. Fixed arrays of non-dynamic types are
1100            // not dynamic, nor are tuples of non-dynamic types.
1101            Self::Builtin(DynSolType::Bytes | DynSolType::String | DynSolType::Array(_)) => true,
1102            Self::Array(_) => true,
1103            _ => false,
1104        }
1105    }
1106
1107    /// Returns whether this type is an array
1108    #[inline]
1109    fn is_array(&self) -> bool {
1110        matches!(
1111            self,
1112            Self::Array(_)
1113                | Self::FixedArray(_, _)
1114                | Self::Builtin(DynSolType::Array(_))
1115                | Self::Builtin(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(_)) | Some(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.into_iter() {
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}