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