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