chisel/
executor.rs

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