1use crate::prelude::{
6 ChiselDispatcher, ChiselResult, ChiselRunner, IntermediateOutput, SessionSource, SolidityHelper,
7};
8use alloy_dyn_abi::{DynSolType, DynSolValue};
9use alloy_json_abi::EventParam;
10use alloy_primitives::{hex, Address, B256, U256};
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
25impl SessionSource {
27 pub async fn execute(&mut self) -> Result<(Address, ChiselResult)> {
36 let compiled = self.build()?;
38 if let Some((_, contract)) =
39 compiled.clone().compiler_output.contracts_into_iter().find(|(name, _)| name == "REPL")
40 {
41 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 let run_func_statements = compiled.intermediate.run_func_body()?;
51
52 let last_yul_return = run_func_statements.iter().find_map(|statement| {
56 if let pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } = statement {
57 if let Some(statement) = block.statements.last() {
58 if let pt::YulStatement::FunctionCall(yul_call) = statement {
59 if yul_call.id.name == "return" {
60 return Some(statement.loc())
61 }
62 }
63 }
64 }
65 None
66 });
67
68 if let Some(final_statement) = run_func_statements.last() {
71 let mut source_loc = match final_statement {
78 pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } => {
79 let last_statement = block.statements.iter().rev().find(|statement| {
81 !matches!(statement, pt::YulStatement::VariableDeclaration(_, _, _))
82 });
83 if let Some(statement) = last_statement {
84 statement.loc()
85 } else {
86 run_func_statements
90 .get(run_func_statements.len().saturating_sub(2))
91 .unwrap()
92 .loc()
93 }
94 }
95 pt::Statement::Block { loc: _, unchecked: _, statements } => {
96 if let Some(statement) = statements.last() {
97 statement.loc()
98 } else {
99 run_func_statements
103 .get(run_func_statements.len().saturating_sub(2))
104 .unwrap()
105 .loc()
106 }
107 }
108 _ => final_statement.loc(),
109 };
110
111 if let Some(yul_return) = last_yul_return {
113 if yul_return.end() < source_loc.start() {
114 source_loc = yul_return;
115 }
116 }
117
118 let final_pc = {
121 let offset = source_loc.start() as u32;
122 let length = (source_loc.end() - source_loc.start()) as u32;
123 contract
124 .get_source_map_deployed()
125 .unwrap()
126 .unwrap()
127 .into_iter()
128 .zip(InstructionIter::new(&deployed_bytecode))
129 .filter(|(s, _)| s.offset() == offset && s.length() == length)
130 .map(|(_, i)| i.pc)
131 .max()
132 .unwrap_or_default()
133 };
134
135 let mut runner = self.prepare_runner(final_pc).await?;
137
138 runner.run(bytecode.into_owned())
140 } else {
141 Ok((Address::ZERO, ChiselResult::default()))
143 }
144 } else {
145 eyre::bail!("Failed to find REPL contract!")
146 }
147 }
148
149 pub async fn inspect(&self, input: &str) -> Result<(bool, Option<String>)> {
161 let line = format!("bytes memory inspectoor = abi.encode({input});");
162 let mut source = match self.clone_with_new_line(line.clone()) {
163 Ok((source, _)) => source,
164 Err(err) => {
165 debug!(%err, "failed to build new source");
166 return Ok((true, None))
167 }
168 };
169
170 let mut source_without_inspector = self.clone();
171
172 let (mut res, err) = match source.execute().await {
175 Ok((_, res)) => (res, None),
176 Err(err) => {
177 debug!(?err, %input, "execution failed");
178 match source_without_inspector.execute().await {
179 Ok((_, res)) => (res, Some(err)),
180 Err(_) => {
181 if self.config.foundry_config.verbosity >= 3 {
182 sh_err!("Could not inspect: {err}")?;
183 }
184 return Ok((true, None))
185 }
186 }
187 }
188 };
189
190 if let Some(err) = err {
192 let generated_output = source_without_inspector
193 .generated_output
194 .as_ref()
195 .ok_or_else(|| eyre::eyre!("Could not find generated output!"))?;
196
197 let intermediate_contract = generated_output
198 .intermediate
199 .intermediate_contracts
200 .get("REPL")
201 .ok_or_else(|| eyre::eyre!("Could not find intermediate contract!"))?;
202
203 if let Some(event_definition) = intermediate_contract.event_definitions.get(input) {
204 let formatted = format_event_definition(event_definition)?;
205 return Ok((false, Some(formatted)))
206 }
207
208 if self.config.foundry_config.verbosity >= 3 {
210 sh_err!("Failed eval: {err}")?;
211 }
212
213 debug!(%err, %input, "failed abi encode input");
214 return Ok((false, None))
215 }
216
217 let Some((stack, memory, _)) = &res.state else {
218 if let Ok(decoder) = ChiselDispatcher::decode_traces(&source.config, &mut res).await {
220 ChiselDispatcher::show_traces(&decoder, &mut res).await?;
221 }
222 let decoded_logs = decode_console_logs(&res.logs);
223 if !decoded_logs.is_empty() {
224 sh_println!("{}", "Logs:".green())?;
225 for log in decoded_logs {
226 sh_println!(" {log}")?;
227 }
228 }
229
230 return Err(eyre::eyre!("Failed to inspect expression"))
231 };
232
233 let generated_output = source
234 .generated_output
235 .as_ref()
236 .ok_or_else(|| eyre::eyre!("Could not find generated output!"))?;
237
238 let contract_expr = generated_output
241 .intermediate
242 .repl_contract_expressions
243 .get(input)
244 .or_else(|| source.infer_inner_expr_type());
245
246 let function_call_return_type =
249 Type::get_function_return_type(contract_expr, &generated_output.intermediate);
250
251 let (contract_expr, ty) = if let Some(function_call_return_type) = function_call_return_type
252 {
253 (function_call_return_type.0, function_call_return_type.1)
254 } else {
255 match contract_expr.and_then(|e| {
256 Type::ethabi(e, Some(&generated_output.intermediate)).map(|ty| (e, ty))
257 }) {
258 Some(res) => res,
259 None => return Ok((true, None)),
261 }
262 };
263
264 let mut offset = stack.last().unwrap().to::<usize>();
267 let mem_offset = &memory[offset..offset + 32];
268 let len = U256::try_from_be_slice(mem_offset).unwrap().to::<usize>();
269 offset += 32;
270 let data = &memory[offset..offset + len];
271 let token =
273 DynSolType::abi_decode(&ty, data).wrap_err("Could not decode inspected values")?;
274 Ok((should_continue(contract_expr), Some(format_token(token))))
275 }
276
277 fn infer_inner_expr_type(&self) -> Option<&pt::Expression> {
288 let out = self.generated_output.as_ref()?;
289 let run = out.intermediate.run_func_body().ok()?.last();
290 match run {
291 Some(pt::Statement::VariableDefinition(
292 _,
293 _,
294 Some(pt::Expression::FunctionCall(_, _, args)),
295 )) => {
296 Some(args.first().unwrap())
300 }
301 _ => None,
302 }
303 }
304
305 async fn prepare_runner(&mut self, final_pc: usize) -> Result<ChiselRunner> {
315 let env =
316 self.config.evm_opts.evm_env().await.expect("Could not instantiate fork environment");
317
318 let backend = match self.config.backend.take() {
320 Some(backend) => backend,
321 None => {
322 let fork = self.config.evm_opts.get_fork(&self.config.foundry_config, env.clone());
323 let backend = Backend::spawn(fork)?;
324 self.config.backend = Some(backend.clone());
325 backend
326 }
327 };
328
329 let executor = ExecutorBuilder::new()
331 .inspectors(|stack| {
332 stack.chisel_state(final_pc).trace_mode(TraceMode::Call).cheatcodes(
333 CheatsConfig::new(
334 &self.config.foundry_config,
335 self.config.evm_opts.clone(),
336 None,
337 None,
338 )
339 .into(),
340 )
341 })
342 .gas_limit(self.config.evm_opts.gas_limit())
343 .spec_id(self.config.foundry_config.evm_spec_id())
344 .legacy_assertions(self.config.foundry_config.legacy_assertions)
345 .build(env, backend);
346
347 Ok(ChiselRunner::new(executor, U256::MAX, Address::ZERO, self.config.calldata.clone()))
350 }
351}
352
353fn format_token(token: DynSolValue) -> String {
356 match token {
357 DynSolValue::Address(a) => {
358 format!("Type: {}\n└ Data: {}", "address".red(), a.cyan())
359 }
360 DynSolValue::FixedBytes(b, byte_len) => {
361 format!(
362 "Type: {}\n└ Data: {}",
363 format!("bytes{byte_len}").red(),
364 hex::encode_prefixed(b).cyan()
365 )
366 }
367 DynSolValue::Int(i, bit_len) => {
368 format!(
369 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
370 format!("int{bit_len}").red(),
371 format!(
372 "0x{}",
373 format!("{i:x}")
374 .char_indices()
375 .skip(64 - bit_len / 4)
376 .take(bit_len / 4)
377 .map(|(_, c)| c)
378 .collect::<String>()
379 )
380 .cyan(),
381 hex::encode_prefixed(B256::from(i)).cyan(),
382 i.cyan()
383 )
384 }
385 DynSolValue::Uint(i, bit_len) => {
386 format!(
387 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
388 format!("uint{bit_len}").red(),
389 format!(
390 "0x{}",
391 format!("{i:x}")
392 .char_indices()
393 .skip(64 - bit_len / 4)
394 .take(bit_len / 4)
395 .map(|(_, c)| c)
396 .collect::<String>()
397 )
398 .cyan(),
399 hex::encode_prefixed(B256::from(i)).cyan(),
400 i.cyan()
401 )
402 }
403 DynSolValue::Bool(b) => {
404 format!("Type: {}\n└ Value: {}", "bool".red(), b.cyan())
405 }
406 DynSolValue::String(_) | DynSolValue::Bytes(_) => {
407 let hex = hex::encode(token.abi_encode());
408 let s = token.as_str();
409 format!(
410 "Type: {}\n{}├ Hex (Memory):\n├─ Length ({}): {}\n├─ Contents ({}): {}\n├ Hex (Tuple Encoded):\n├─ Pointer ({}): {}\n├─ Length ({}): {}\n└─ Contents ({}): {}",
411 if s.is_some() { "string" } else { "dynamic bytes" }.red(),
412 if let Some(s) = s {
413 format!("├ UTF-8: {}\n", s.cyan())
414 } else {
415 String::default()
416 },
417 "[0x00:0x20]".yellow(),
418 format!("0x{}", &hex[64..128]).cyan(),
419 "[0x20:..]".yellow(),
420 format!("0x{}", &hex[128..]).cyan(),
421 "[0x00:0x20]".yellow(),
422 format!("0x{}", &hex[..64]).cyan(),
423 "[0x20:0x40]".yellow(),
424 format!("0x{}", &hex[64..128]).cyan(),
425 "[0x40:..]".yellow(),
426 format!("0x{}", &hex[128..]).cyan(),
427 )
428 }
429 DynSolValue::FixedArray(tokens) | DynSolValue::Array(tokens) => {
430 let mut out = format!(
431 "{}({}) = {}",
432 "array".red(),
433 format!("{}", tokens.len()).yellow(),
434 '['.red()
435 );
436 for token in tokens {
437 out.push_str("\n ├ ");
438 out.push_str(&format_token(token).replace('\n', "\n "));
439 out.push('\n');
440 }
441 out.push_str(&']'.red().to_string());
442 out
443 }
444 DynSolValue::Tuple(tokens) => {
445 let displayed_types = tokens
446 .iter()
447 .map(|t| t.sol_type_name().unwrap_or_default())
448 .collect::<Vec<_>>()
449 .join(", ");
450 let mut out =
451 format!("{}({}) = {}", "tuple".red(), displayed_types.yellow(), '('.red());
452 for token in tokens {
453 out.push_str("\n ├ ");
454 out.push_str(&format_token(token).replace('\n', "\n "));
455 out.push('\n');
456 }
457 out.push_str(&')'.red().to_string());
458 out
459 }
460 _ => {
461 unimplemented!()
462 }
463 }
464}
465
466fn format_event_definition(event_definition: &pt::EventDefinition) -> Result<String> {
478 let event_name = event_definition.name.as_ref().expect("Event has a name").to_string();
479 let inputs = event_definition
480 .fields
481 .iter()
482 .map(|param| {
483 let name = param
484 .name
485 .as_ref()
486 .map(ToString::to_string)
487 .unwrap_or_else(|| "<anonymous>".to_string());
488 let kind = Type::from_expression(¶m.ty)
489 .and_then(Type::into_builtin)
490 .ok_or_else(|| eyre::eyre!("Invalid type in event {event_name}"))?;
491 Ok(EventParam {
492 name,
493 ty: kind.to_string(),
494 components: vec![],
495 indexed: param.indexed,
496 internal_type: None,
497 })
498 })
499 .collect::<Result<Vec<_>>>()?;
500 let event =
501 alloy_json_abi::Event { name: event_name, inputs, anonymous: event_definition.anonymous };
502
503 Ok(format!(
504 "Type: {}\n├ Name: {}\n├ Signature: {:?}\n└ Selector: {:?}",
505 "event".red(),
506 SolidityHelper::new().highlight(&format!(
507 "{}({})",
508 &event.name,
509 &event
510 .inputs
511 .iter()
512 .map(|param| format!(
513 "{}{}{}",
514 param.ty,
515 if param.indexed { " indexed" } else { "" },
516 if param.name.is_empty() {
517 String::default()
518 } else {
519 format!(" {}", ¶m.name)
520 },
521 ))
522 .collect::<Vec<_>>()
523 .join(", ")
524 )),
525 event.signature().cyan(),
526 event.selector().cyan(),
527 ))
528}
529
530#[derive(Clone, Debug, PartialEq)]
536enum Type {
537 Builtin(DynSolType),
539
540 Array(Box<Type>),
542
543 FixedArray(Box<Type>, usize),
545
546 ArrayIndex(Box<Type>, Option<usize>),
548
549 Tuple(Vec<Option<Type>>),
551
552 Function(Box<Type>, Vec<Option<Type>>, Vec<Option<Type>>),
554
555 Access(Box<Type>, String),
557
558 Custom(Vec<String>),
560}
561
562impl Type {
563 fn from_expression(expr: &pt::Expression) -> Option<Self> {
573 match expr {
574 pt::Expression::Type(_, ty) => Self::from_type(ty),
575
576 pt::Expression::Variable(ident) => Some(Self::Custom(vec![ident.name.clone()])),
577
578 pt::Expression::ArraySubscript(_, expr, num) => {
580 Self::from_expression(expr).and_then(|ty| {
583 let boxed = Box::new(ty);
584 let num = num.as_deref().and_then(parse_number_literal).and_then(|n| {
585 if n > USIZE_MAX_AS_U256 {
587 None
588 } else {
589 Some(n.to::<usize>())
590 }
591 });
592 match expr.as_ref() {
593 pt::Expression::Type(_, _) => {
595 if let Some(num) = num {
596 Some(Self::FixedArray(boxed, num))
597 } else {
598 Some(Self::Array(boxed))
599 }
600 }
601 pt::Expression::Variable(_) => {
603 Some(Self::ArrayIndex(boxed, num))
604 }
605 _ => None
606 }
607 })
608 }
609 pt::Expression::ArrayLiteral(_, values) => {
610 values.first().and_then(Self::from_expression).map(|ty| {
611 Self::FixedArray(Box::new(ty), values.len())
612 })
613 }
614
615 pt::Expression::List(_, params) => Some(Self::Tuple(map_parameters(params))),
617
618 pt::Expression::MemberAccess(_, lhs, rhs) => {
620 Self::from_expression(lhs).map(|lhs| {
621 Self::Access(Box::new(lhs), rhs.name.clone())
622 })
623 }
624
625 pt::Expression::Parenthesis(_, inner) | pt::Expression::New(_, inner) | pt::Expression::UnaryPlus(_, inner) | pt::Expression::BitwiseNot(_, inner) | pt::Expression::ArraySlice(_, inner, _, _) | pt::Expression::PreDecrement(_, inner) | pt::Expression::PostDecrement(_, inner) | pt::Expression::PreIncrement(_, inner) | pt::Expression::PostIncrement(_, inner) | pt::Expression::Assign(_, inner, _) | pt::Expression::AssignAdd(_, inner, _) | pt::Expression::AssignSubtract(_, inner, _) | pt::Expression::AssignMultiply(_, inner, _) | pt::Expression::AssignDivide(_, inner, _) | pt::Expression::AssignModulo(_, inner, _) | pt::Expression::AssignAnd(_, inner, _) | pt::Expression::AssignOr(_, inner, _) | pt::Expression::AssignXor(_, inner, _) | pt::Expression::AssignShiftLeft(_, inner, _) | pt::Expression::AssignShiftRight(_, inner, _) => Self::from_expression(inner),
649
650 pt::Expression::ConditionalOperator(_, _, if_true, if_false) => {
652 Self::from_expression(if_true).or_else(|| Self::from_expression(if_false))
653 }
654
655 pt::Expression::AddressLiteral(_, _) => Some(Self::Builtin(DynSolType::Address)),
657 pt::Expression::HexNumberLiteral(_, s, _) => {
658 match s.parse::<Address>() {
659 Ok(addr) => {
660 if *s == addr.to_checksum(None) {
661 Some(Self::Builtin(DynSolType::Address))
662 } else {
663 Some(Self::Builtin(DynSolType::Uint(256)))
664 }
665 },
666 _ => {
667 Some(Self::Builtin(DynSolType::Uint(256)))
668 }
669 }
670 }
671
672 pt::Expression::Negate(_, inner) => Self::from_expression(inner).map(Self::invert_int),
675
676 pt::Expression::Add(_, lhs, rhs) |
680 pt::Expression::Subtract(_, lhs, rhs) |
681 pt::Expression::Multiply(_, lhs, rhs) |
682 pt::Expression::Divide(_, lhs, rhs) => {
683 match (Self::ethabi(lhs, None), Self::ethabi(rhs, None)) {
684 (Some(DynSolType::Int(_)), Some(DynSolType::Int(_))) |
685 (Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) |
686 (Some(DynSolType::Uint(_)), Some(DynSolType::Int(_))) => {
687 Some(Self::Builtin(DynSolType::Int(256)))
688 }
689 _ => {
690 Some(Self::Builtin(DynSolType::Uint(256)))
691 }
692 }
693 }
694
695 pt::Expression::Modulo(_, _, _) |
697 pt::Expression::Power(_, _, _) |
698 pt::Expression::BitwiseOr(_, _, _) |
699 pt::Expression::BitwiseAnd(_, _, _) |
700 pt::Expression::BitwiseXor(_, _, _) |
701 pt::Expression::ShiftRight(_, _, _) |
702 pt::Expression::ShiftLeft(_, _, _) |
703 pt::Expression::NumberLiteral(_, _, _, _) => Some(Self::Builtin(DynSolType::Uint(256))),
704
705 pt::Expression::RationalNumberLiteral(_, _, _, _, _) => {
707 Some(Self::Builtin(DynSolType::Uint(256)))
708 }
709
710 pt::Expression::BoolLiteral(_, _) |
712 pt::Expression::And(_, _, _) |
713 pt::Expression::Or(_, _, _) |
714 pt::Expression::Equal(_, _, _) |
715 pt::Expression::NotEqual(_, _, _) |
716 pt::Expression::Less(_, _, _) |
717 pt::Expression::LessEqual(_, _, _) |
718 pt::Expression::More(_, _, _) |
719 pt::Expression::MoreEqual(_, _, _) |
720 pt::Expression::Not(_, _) => Some(Self::Builtin(DynSolType::Bool)),
721
722 pt::Expression::StringLiteral(_) => Some(Self::Builtin(DynSolType::String)),
724
725 pt::Expression::HexLiteral(_) => Some(Self::Builtin(DynSolType::Bytes)),
727
728 pt::Expression::FunctionCall(_, name, args) => {
730 Self::from_expression(name).map(|name| {
731 let args = args.iter().map(Self::from_expression).collect();
732 Self::Function(Box::new(name), args, vec![])
733 })
734 }
735 pt::Expression::NamedFunctionCall(_, name, args) => {
736 Self::from_expression(name).map(|name| {
737 let args = args.iter().map(|arg| Self::from_expression(&arg.expr)).collect();
738 Self::Function(Box::new(name), args, vec![])
739 })
740 }
741
742 pt::Expression::Delete(_, _) | pt::Expression::FunctionCallBlock(_, _, _) => None,
744 }
745 }
746
747 fn from_type(ty: &pt::Type) -> Option<Self> {
757 let ty = match ty {
758 pt::Type::Address | pt::Type::AddressPayable | pt::Type::Payable => {
759 Self::Builtin(DynSolType::Address)
760 }
761 pt::Type::Bool => Self::Builtin(DynSolType::Bool),
762 pt::Type::String => Self::Builtin(DynSolType::String),
763 pt::Type::Int(size) => Self::Builtin(DynSolType::Int(*size as usize)),
764 pt::Type::Uint(size) => Self::Builtin(DynSolType::Uint(*size as usize)),
765 pt::Type::Bytes(size) => Self::Builtin(DynSolType::FixedBytes(*size as usize)),
766 pt::Type::DynamicBytes => Self::Builtin(DynSolType::Bytes),
767 pt::Type::Mapping { value, .. } => Self::from_expression(value)?,
768 pt::Type::Function { params, returns, .. } => {
769 let params = map_parameters(params);
770 let returns = returns
771 .as_ref()
772 .map(|(returns, _)| map_parameters(returns))
773 .unwrap_or_default();
774 Self::Function(
775 Box::new(Self::Custom(vec!["__fn_type__".to_string()])),
776 params,
777 returns,
778 )
779 }
780 pt::Type::Rational => return None,
782 };
783 Some(ty)
784 }
785
786 fn map_special(self) -> Self {
790 if !matches!(self, Self::Function(_, _, _) | Self::Access(_, _) | Self::Custom(_)) {
791 return self
792 }
793
794 let mut types = Vec::with_capacity(5);
795 let mut args = None;
796 self.recurse(&mut types, &mut args);
797
798 let len = types.len();
799 if len == 0 {
800 return self
801 }
802
803 #[expect(clippy::single_match)]
805 match &self {
806 Self::Access(inner, access) => {
807 if let Some(ty) = inner.as_ref().clone().try_as_ethabi(None) {
808 let ty = Self::Builtin(ty);
810 match access.as_str() {
811 "length" if ty.is_dynamic() || ty.is_array() || ty.is_fixed_bytes() => {
812 return Self::Builtin(DynSolType::Uint(256))
813 }
814 "pop" if ty.is_dynamic_array() => return ty,
815 _ => {}
816 }
817 }
818 }
819 _ => {}
820 }
821
822 let this = {
823 let name = types.last().unwrap().as_str();
824 match len {
825 0 => unreachable!(),
826 1 => match name {
827 "gasleft" | "addmod" | "mulmod" => Some(DynSolType::Uint(256)),
828 "keccak256" | "sha256" | "blockhash" => Some(DynSolType::FixedBytes(32)),
829 "ripemd160" => Some(DynSolType::FixedBytes(20)),
830 "ecrecover" => Some(DynSolType::Address),
831 _ => None,
832 },
833 2 => {
834 let access = types.first().unwrap().as_str();
835 match name {
836 "block" => match access {
837 "coinbase" => Some(DynSolType::Address),
838 "timestamp" | "difficulty" | "prevrandao" | "number" | "gaslimit" |
839 "chainid" | "basefee" | "blobbasefee" => Some(DynSolType::Uint(256)),
840 _ => None,
841 },
842 "msg" => match access {
843 "sender" => Some(DynSolType::Address),
844 "gas" => Some(DynSolType::Uint(256)),
845 "value" => Some(DynSolType::Uint(256)),
846 "data" => Some(DynSolType::Bytes),
847 "sig" => Some(DynSolType::FixedBytes(4)),
848 _ => None,
849 },
850 "tx" => match access {
851 "origin" => Some(DynSolType::Address),
852 "gasprice" => Some(DynSolType::Uint(256)),
853 _ => None,
854 },
855 "abi" => match access {
856 "decode" => {
857 let mut args = args.unwrap();
861 let last = args.pop().unwrap();
862 match last {
863 Some(ty) => {
864 return match ty {
865 Self::Tuple(_) => ty,
866 ty => Self::Tuple(vec![Some(ty)]),
867 }
868 }
869 None => None,
870 }
871 }
872 s if s.starts_with("encode") => Some(DynSolType::Bytes),
873 _ => None,
874 },
875 "address" => match access {
876 "balance" => Some(DynSolType::Uint(256)),
877 "code" => Some(DynSolType::Bytes),
878 "codehash" => Some(DynSolType::FixedBytes(32)),
879 "send" => Some(DynSolType::Bool),
880 _ => None,
881 },
882 "type" => match access {
883 "name" => Some(DynSolType::String),
884 "creationCode" | "runtimeCode" => Some(DynSolType::Bytes),
885 "interfaceId" => Some(DynSolType::FixedBytes(4)),
886 "min" | "max" => Some(
887 (|| args?.pop()??.into_builtin())()
889 .unwrap_or(DynSolType::Uint(256)),
890 ),
891 _ => None,
892 },
893 "string" => match access {
894 "concat" => Some(DynSolType::String),
895 _ => None,
896 },
897 "bytes" => match access {
898 "concat" => Some(DynSolType::Bytes),
899 _ => None,
900 },
901 _ => None,
902 }
903 }
904 _ => None,
905 }
906 };
907
908 this.map(Self::Builtin).unwrap_or_else(|| match types.last().unwrap().as_str() {
909 "this" | "super" => Self::Custom(types),
910 _ => match self {
911 Self::Custom(_) | Self::Access(_, _) => Self::Custom(types),
912 Self::Function(_, _, _) => self,
913 _ => unreachable!(),
914 },
915 })
916 }
917
918 fn recurse(&self, types: &mut Vec<String>, args: &mut Option<Vec<Option<Self>>>) {
921 match self {
922 Self::Builtin(ty) => types.push(ty.to_string()),
923 Self::Custom(tys) => types.extend(tys.clone()),
924 Self::Access(expr, name) => {
925 types.push(name.clone());
926 expr.recurse(types, args);
927 }
928 Self::Function(fn_name, fn_args, _fn_ret) => {
929 if args.is_none() && !fn_args.is_empty() {
930 *args = Some(fn_args.clone());
931 }
932 fn_name.recurse(types, args);
933 }
934 _ => {}
935 }
936 }
937
938 fn infer_custom_type(
952 intermediate: &IntermediateOutput,
953 custom_type: &mut Vec<String>,
954 contract_name: Option<String>,
955 ) -> Result<Option<DynSolType>> {
956 if let Some("this") | Some("super") = custom_type.last().map(String::as_str) {
957 custom_type.pop();
958 }
959 if custom_type.is_empty() {
960 return Ok(None)
961 }
962
963 if let Some(contract_name) = contract_name {
966 let intermediate_contract = intermediate
967 .intermediate_contracts
968 .get(&contract_name)
969 .ok_or_else(|| eyre::eyre!("Could not find intermediate contract!"))?;
970
971 let cur_type = custom_type.last().unwrap();
972 if let Some(func) = intermediate_contract.function_definitions.get(cur_type) {
973 if let res @ Some(_) = func_members(func, custom_type) {
975 return Ok(res)
976 }
977
978 if func.returns.is_empty() {
981 eyre::bail!(
982 "This call expression does not return any values to inspect. Insert as statement."
983 )
984 }
985
986 let (_, param) = func.returns.first().unwrap();
988 let return_ty = ¶m.as_ref().unwrap().ty;
990
991 if let pt::Expression::Variable(ident) = return_ty {
995 custom_type.push(ident.name.clone());
996 return Self::infer_custom_type(intermediate, custom_type, Some(contract_name))
997 }
998
999 if let Some(pt::FunctionAttribute::Mutability(_mut)) = func
1003 .attributes
1004 .iter()
1005 .find(|attr| matches!(attr, pt::FunctionAttribute::Mutability(_)))
1006 {
1007 if let pt::Mutability::Payable(_) = _mut {
1008 eyre::bail!("This function mutates state. Insert as a statement.")
1009 }
1010 } else {
1011 eyre::bail!("This function mutates state. Insert as a statement.")
1012 }
1013
1014 Ok(Self::ethabi(return_ty, Some(intermediate)))
1015 } else if let Some(var) = intermediate_contract.variable_definitions.get(cur_type) {
1016 Self::infer_var_expr(&var.ty, Some(intermediate), custom_type)
1017 } else if let Some(strukt) = intermediate_contract.struct_definitions.get(cur_type) {
1018 let inner_types = strukt
1019 .fields
1020 .iter()
1021 .map(|var| {
1022 Self::ethabi(&var.ty, Some(intermediate))
1023 .ok_or_else(|| eyre::eyre!("Struct `{cur_type}` has invalid fields"))
1024 })
1025 .collect::<Result<Vec<_>>>()?;
1026 Ok(Some(DynSolType::Tuple(inner_types)))
1027 } else {
1028 eyre::bail!("Could not find any definition in contract \"{contract_name}\" for type: {custom_type:?}")
1029 }
1030 } else {
1031 if let Ok(res) = Self::infer_custom_type(intermediate, custom_type, Some("REPL".into()))
1034 {
1035 return Ok(res)
1036 }
1037
1038 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 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 Ok(None)
1056 }
1057 }
1058
1059 fn infer_var_expr(
1061 expr: &pt::Expression,
1062 intermediate: Option<&IntermediateOutput>,
1063 custom_type: &mut Vec<String>,
1064 ) -> Result<Option<DynSolType>> {
1065 let res = match &expr {
1067 pt::Expression::Variable(ident) => {
1069 let name = &ident.name;
1070
1071 if let Some(intermediate) = intermediate {
1072 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 custom_type.pop();
1079 Self::infer_custom_type(intermediate, custom_type, Some(name.clone()))
1080 } else {
1081 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 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 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 Self::Access(_, _) => None,
1136 Self::Custom(mut types) => {
1137 intermediate.and_then(|intermediate| {
1139 Self::infer_custom_type(intermediate, &mut types, None).ok().flatten()
1140 })
1141 }
1142 }
1143 }
1144
1145 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 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 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 #[inline]
1201 fn into_builtin(self) -> Option<DynSolType> {
1202 match self {
1203 Self::Builtin(ty) => Some(ty),
1204 _ => None,
1205 }
1206 }
1207
1208 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 #[inline]
1228 fn is_dynamic(&self) -> bool {
1229 match self {
1230 Self::Builtin(DynSolType::Bytes | DynSolType::String | DynSolType::Array(_)) => true,
1233 Self::Array(_) => true,
1234 _ => false,
1235 }
1236 }
1237
1238 #[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 #[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#[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#[inline]
1288fn should_continue(expr: &pt::Expression) -> bool {
1289 match expr {
1290 pt::Expression::PreDecrement(_, _) | pt::Expression::PostDecrement(_, _) | pt::Expression::PreIncrement(_, _) | pt::Expression::PostIncrement(_, _) | pt::Expression::Assign(_, _, _) | pt::Expression::AssignAdd(_, _, _) | pt::Expression::AssignSubtract(_, _, _) | pt::Expression::AssignMultiply(_, _, _) | pt::Expression::AssignDivide(_, _, _) | pt::Expression::AssignModulo(_, _, _) | pt::Expression::AssignAnd(_, _, _) | pt::Expression::AssignOr(_, _, _) | pt::Expression::AssignXor(_, _, _) | pt::Expression::AssignShiftLeft(_, _, _) | pt::Expression::AssignShiftRight(_, _, _) => {
1307 true
1308 }
1309
1310 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(¶m.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 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 ("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 ("-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 ("true ? 1 : 0", Uint(256)),
1456 ("true ? -1 : 0", Int(256)),
1457 ("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(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 ("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 ("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 ]
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", Bool),
1537 ("true", Bool),
1538 ("false", Bool),
1539 ("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 ("address", Address),
1555 ("address(0)", Address),
1556 ("0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990", Address),
1557 ("payable(0)", Address),
1558 ("payable(address(0))", Address),
1559 ("string", String),
1563 ("string(\"hello world\")", String),
1564 ("\"hello world\"", String),
1565 ("unicode\"hello world 😀\"", String),
1566 ("bytes", Bytes),
1570 ("bytes(\"hello world\")", Bytes),
1571 ("bytes(unicode\"hello world 😀\")", Bytes),
1572 ("hex\"68656c6c6f20776f726c64\"", Bytes),
1573 ]
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 let global_variables = {
1601 use DynSolType::*;
1602 &[
1603 ("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 ("bytes.concat()", Bytes),
1616 ("bytes.concat(_)", Bytes),
1617 ("bytes.concat(_, _)", Bytes),
1618 ("string.concat()", String),
1619 ("string.concat(_)", String),
1620 ("string.concat(_, _)", String),
1621 ("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 ("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 ("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 ("address(_)", Address),
1662 ("address(this)", Address),
1663 ("address.balance", Uint(256)),
1666 ("address.code", Bytes),
1667 ("address.codehash", FixedBytes(32)),
1668 ("address.send(uint256)", Bool),
1669 ("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 ("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 static PRE_INSTALL_SOLC_LOCK: Mutex<bool> = Mutex::new(false);
1698
1699 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 let _ = sh_println!("found installed Solc v{v} @ {}", solc.solc.display());
1711 break
1712 }
1713 Err(e) => {
1714 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}