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::{Address, B256, U256, hex};
11use core::fmt::Debug;
12use eyre::{Result, WrapErr};
13use foundry_cli::utils;
14use foundry_compilers::Artifact;
15use foundry_evm::{
16 backend::Backend, decode::decode_console_logs, executors::ExecutorBuilder,
17 inspectors::CheatsConfig, traces::TraceMode,
18};
19use solang_parser::pt::{self, CodeLocation};
20use std::str::FromStr;
21use tracing::debug;
22use yansi::Paint;
23
24const USIZE_MAX_AS_U256: U256 = U256::from_limbs([usize::MAX as u64, 0, 0, 0]);
25
26impl SessionSource {
28 pub async fn execute(&mut self) -> Result<(Address, ChiselResult)> {
37 let compiled = self.build()?;
39 if let Some((_, contract)) =
40 compiled.clone().compiler_output.contracts_into_iter().find(|(name, _)| name == "REPL")
41 {
42 let bytecode = contract
44 .get_bytecode_bytes()
45 .ok_or_else(|| eyre::eyre!("No bytecode found for `REPL` contract"))?;
46 let deployed_bytecode = contract
47 .get_deployed_bytecode_bytes()
48 .ok_or_else(|| eyre::eyre!("No deployed bytecode found for `REPL` contract"))?;
49
50 let run_func_statements = compiled.intermediate.run_func_body()?;
52
53 let last_yul_return = run_func_statements.iter().find_map(|statement| {
57 if let pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } = statement
58 && let Some(statement) = block.statements.last()
59 && let pt::YulStatement::FunctionCall(yul_call) = statement
60 && yul_call.id.name == "return"
61 {
62 return Some(statement.loc());
63 }
64 None
65 });
66
67 if let Some(final_statement) = run_func_statements.last() {
70 let mut source_loc = match final_statement {
77 pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } => {
78 let last_statement = block.statements.iter().rev().find(|statement| {
80 !matches!(statement, pt::YulStatement::VariableDeclaration(_, _, _))
81 });
82 if let Some(statement) = last_statement {
83 statement.loc()
84 } else {
85 run_func_statements
89 .get(run_func_statements.len().saturating_sub(2))
90 .unwrap()
91 .loc()
92 }
93 }
94 pt::Statement::Block { loc: _, unchecked: _, statements } => {
95 if let Some(statement) = statements.last() {
96 statement.loc()
97 } else {
98 run_func_statements
102 .get(run_func_statements.len().saturating_sub(2))
103 .unwrap()
104 .loc()
105 }
106 }
107 _ => final_statement.loc(),
108 };
109
110 if let Some(yul_return) = last_yul_return
112 && yul_return.end() < source_loc.start()
113 {
114 source_loc = yul_return;
115 }
116
117 let final_pc = {
120 let offset = source_loc.start() as u32;
121 let length = (source_loc.end() - source_loc.start()) as u32;
122 contract
123 .get_source_map_deployed()
124 .unwrap()
125 .unwrap()
126 .into_iter()
127 .zip(InstructionIter::new(&deployed_bytecode))
128 .filter(|(s, _)| s.offset() == offset && s.length() == length)
129 .map(|(_, i)| i.pc)
130 .max()
131 .unwrap_or_default()
132 };
133
134 let mut runner = self.prepare_runner(final_pc).await?;
136
137 runner.run(bytecode.into_owned())
139 } else {
140 Ok((Address::ZERO, ChiselResult::default()))
142 }
143 } else {
144 eyre::bail!("Failed to find REPL contract!")
145 }
146 }
147
148 pub async fn inspect(&self, input: &str) -> Result<(bool, Option<String>)> {
160 let line = format!("bytes memory inspectoor = abi.encode({input});");
161 let mut source = match self.clone_with_new_line(line.clone()) {
162 Ok((source, _)) => source,
163 Err(err) => {
164 debug!(%err, "failed to build new source");
165 return Ok((true, None));
166 }
167 };
168
169 let mut source_without_inspector = self.clone();
170
171 let (mut res, err) = match source.execute().await {
174 Ok((_, res)) => (res, None),
175 Err(err) => {
176 debug!(?err, %input, "execution failed");
177 match source_without_inspector.execute().await {
178 Ok((_, res)) => (res, Some(err)),
179 Err(_) => {
180 if self.config.foundry_config.verbosity >= 3 {
181 sh_err!("Could not inspect: {err}")?;
182 }
183 return Ok((true, None));
184 }
185 }
186 }
187 };
188
189 if let Some(err) = err {
191 let generated_output = source_without_inspector
192 .generated_output
193 .as_ref()
194 .ok_or_else(|| eyre::eyre!("Could not find generated output!"))?;
195
196 let intermediate_contract = generated_output
197 .intermediate
198 .intermediate_contracts
199 .get("REPL")
200 .ok_or_else(|| eyre::eyre!("Could not find intermediate contract!"))?;
201
202 if let Some(event_definition) = intermediate_contract.event_definitions.get(input) {
203 let formatted = format_event_definition(event_definition)?;
204 return Ok((false, Some(formatted)));
205 }
206
207 if self.config.foundry_config.verbosity >= 3 {
209 sh_err!("Failed eval: {err}")?;
210 }
211
212 debug!(%err, %input, "failed abi encode input");
213 return Ok((false, None));
214 }
215
216 let Some((stack, memory, _)) = &res.state else {
217 if let Ok(decoder) = ChiselDispatcher::decode_traces(&source.config, &mut res).await {
219 ChiselDispatcher::show_traces(&decoder, &mut res).await?;
220 }
221 let decoded_logs = decode_console_logs(&res.logs);
222 if !decoded_logs.is_empty() {
223 sh_println!("{}", "Logs:".green())?;
224 for log in decoded_logs {
225 sh_println!(" {log}")?;
226 }
227 }
228
229 return Err(eyre::eyre!("Failed to inspect expression"));
230 };
231
232 let generated_output = source
233 .generated_output
234 .as_ref()
235 .ok_or_else(|| eyre::eyre!("Could not find generated output!"))?;
236
237 let contract_expr = generated_output
240 .intermediate
241 .repl_contract_expressions
242 .get(input)
243 .or_else(|| source.infer_inner_expr_type());
244
245 let function_call_return_type =
248 Type::get_function_return_type(contract_expr, &generated_output.intermediate);
249
250 let (contract_expr, ty) = if let Some(function_call_return_type) = function_call_return_type
251 {
252 (function_call_return_type.0, function_call_return_type.1)
253 } else {
254 match contract_expr.and_then(|e| {
255 Type::ethabi(e, Some(&generated_output.intermediate)).map(|ty| (e, ty))
256 }) {
257 Some(res) => res,
258 None => return Ok((true, None)),
260 }
261 };
262
263 let mut offset = stack.last().unwrap().to::<usize>();
266 let mem_offset = &memory[offset..offset + 32];
267 let len = U256::try_from_be_slice(mem_offset).unwrap().to::<usize>();
268 offset += 32;
269 let data = &memory[offset..offset + len];
270 let token =
272 DynSolType::abi_decode(&ty, data).wrap_err("Could not decode inspected values")?;
273 Ok((should_continue(contract_expr), Some(format_token(token))))
274 }
275
276 fn infer_inner_expr_type(&self) -> Option<&pt::Expression> {
287 let out = self.generated_output.as_ref()?;
288 let run = out.intermediate.run_func_body().ok()?.last();
289 match run {
290 Some(pt::Statement::VariableDefinition(
291 _,
292 _,
293 Some(pt::Expression::FunctionCall(_, _, args)),
294 )) => {
295 Some(args.first().unwrap())
299 }
300 _ => None,
301 }
302 }
303
304 async fn prepare_runner(&mut self, final_pc: usize) -> Result<ChiselRunner> {
314 let env =
315 self.config.evm_opts.evm_env().await.expect("Could not instantiate fork environment");
316 let strategy = utils::get_executor_strategy(&self.config.foundry_config);
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(
324 fork,
325 strategy.runner.new_backend_strategy(strategy.context.as_ref()),
326 )?;
327 self.config.backend = Some(backend.clone());
328 backend
329 }
330 };
331
332 let executor = ExecutorBuilder::new()
334 .inspectors(|stack| {
335 stack.chisel_state(final_pc).trace_mode(TraceMode::Call).cheatcodes(
336 CheatsConfig::new(
337 strategy.runner.new_cheatcodes_strategy(strategy.context.as_ref()),
338 &self.config.foundry_config,
339 self.config.evm_opts.clone(),
340 None,
341 None,
342 )
343 .into(),
344 )
345 })
346 .gas_limit(self.config.evm_opts.gas_limit())
347 .spec_id(self.config.foundry_config.evm_spec_id())
348 .legacy_assertions(self.config.foundry_config.legacy_assertions)
349 .build(env, backend, strategy);
350
351 Ok(ChiselRunner::new(executor, U256::MAX, Address::ZERO, self.config.calldata.clone()))
354 }
355}
356
357fn format_token(token: DynSolValue) -> String {
360 match token {
361 DynSolValue::Address(a) => {
362 format!("Type: {}\n└ Data: {}", "address".red(), a.cyan())
363 }
364 DynSolValue::FixedBytes(b, byte_len) => {
365 format!(
366 "Type: {}\n└ Data: {}",
367 format!("bytes{byte_len}").red(),
368 hex::encode_prefixed(b).cyan()
369 )
370 }
371 DynSolValue::Int(i, bit_len) => {
372 format!(
373 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
374 format!("int{bit_len}").red(),
375 format!(
376 "0x{}",
377 format!("{i:x}")
378 .char_indices()
379 .skip(64 - bit_len / 4)
380 .take(bit_len / 4)
381 .map(|(_, c)| c)
382 .collect::<String>()
383 )
384 .cyan(),
385 hex::encode_prefixed(B256::from(i)).cyan(),
386 i.cyan()
387 )
388 }
389 DynSolValue::Uint(i, bit_len) => {
390 format!(
391 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
392 format!("uint{bit_len}").red(),
393 format!(
394 "0x{}",
395 format!("{i:x}")
396 .char_indices()
397 .skip(64 - bit_len / 4)
398 .take(bit_len / 4)
399 .map(|(_, c)| c)
400 .collect::<String>()
401 )
402 .cyan(),
403 hex::encode_prefixed(B256::from(i)).cyan(),
404 i.cyan()
405 )
406 }
407 DynSolValue::Bool(b) => {
408 format!("Type: {}\n└ Value: {}", "bool".red(), b.cyan())
409 }
410 DynSolValue::String(_) | DynSolValue::Bytes(_) => {
411 let hex = hex::encode(token.abi_encode());
412 let s = token.as_str();
413 format!(
414 "Type: {}\n{}├ Hex (Memory):\n├─ Length ({}): {}\n├─ Contents ({}): {}\n├ Hex (Tuple Encoded):\n├─ Pointer ({}): {}\n├─ Length ({}): {}\n└─ Contents ({}): {}",
415 if s.is_some() { "string" } else { "dynamic bytes" }.red(),
416 if let Some(s) = s {
417 format!("├ UTF-8: {}\n", s.cyan())
418 } else {
419 String::default()
420 },
421 "[0x00:0x20]".yellow(),
422 format!("0x{}", &hex[64..128]).cyan(),
423 "[0x20:..]".yellow(),
424 format!("0x{}", &hex[128..]).cyan(),
425 "[0x00:0x20]".yellow(),
426 format!("0x{}", &hex[..64]).cyan(),
427 "[0x20:0x40]".yellow(),
428 format!("0x{}", &hex[64..128]).cyan(),
429 "[0x40:..]".yellow(),
430 format!("0x{}", &hex[128..]).cyan(),
431 )
432 }
433 DynSolValue::FixedArray(tokens) | DynSolValue::Array(tokens) => {
434 let mut out = format!(
435 "{}({}) = {}",
436 "array".red(),
437 format!("{}", tokens.len()).yellow(),
438 '['.red()
439 );
440 for token in tokens {
441 out.push_str("\n ├ ");
442 out.push_str(&format_token(token).replace('\n', "\n "));
443 out.push('\n');
444 }
445 out.push_str(&']'.red().to_string());
446 out
447 }
448 DynSolValue::Tuple(tokens) => {
449 let displayed_types = tokens
450 .iter()
451 .map(|t| t.sol_type_name().unwrap_or_default())
452 .collect::<Vec<_>>()
453 .join(", ");
454 let mut out =
455 format!("{}({}) = {}", "tuple".red(), displayed_types.yellow(), '('.red());
456 for token in tokens {
457 out.push_str("\n ├ ");
458 out.push_str(&format_token(token).replace('\n', "\n "));
459 out.push('\n');
460 }
461 out.push_str(&')'.red().to_string());
462 out
463 }
464 _ => {
465 unimplemented!()
466 }
467 }
468}
469
470fn format_event_definition(event_definition: &pt::EventDefinition) -> Result<String> {
482 let event_name = event_definition.name.as_ref().expect("Event has a name").to_string();
483 let inputs = event_definition
484 .fields
485 .iter()
486 .map(|param| {
487 let name = param
488 .name
489 .as_ref()
490 .map(ToString::to_string)
491 .unwrap_or_else(|| "<anonymous>".to_string());
492 let kind = Type::from_expression(¶m.ty)
493 .and_then(Type::into_builtin)
494 .ok_or_else(|| eyre::eyre!("Invalid type in event {event_name}"))?;
495 Ok(EventParam {
496 name,
497 ty: kind.to_string(),
498 components: vec![],
499 indexed: param.indexed,
500 internal_type: None,
501 })
502 })
503 .collect::<Result<Vec<_>>>()?;
504 let event =
505 alloy_json_abi::Event { name: event_name, inputs, anonymous: event_definition.anonymous };
506
507 Ok(format!(
508 "Type: {}\n├ Name: {}\n├ Signature: {:?}\n└ Selector: {:?}",
509 "event".red(),
510 SolidityHelper::new().highlight(&format!(
511 "{}({})",
512 &event.name,
513 &event
514 .inputs
515 .iter()
516 .map(|param| format!(
517 "{}{}{}",
518 param.ty,
519 if param.indexed { " indexed" } else { "" },
520 if param.name.is_empty() {
521 String::default()
522 } else {
523 format!(" {}", ¶m.name)
524 },
525 ))
526 .collect::<Vec<_>>()
527 .join(", ")
528 )),
529 event.signature().cyan(),
530 event.selector().cyan(),
531 ))
532}
533
534#[derive(Clone, Debug, PartialEq)]
540enum Type {
541 Builtin(DynSolType),
543
544 Array(Box<Self>),
546
547 FixedArray(Box<Self>, usize),
549
550 ArrayIndex(Box<Self>, Option<usize>),
552
553 Tuple(Vec<Option<Self>>),
555
556 Function(Box<Self>, Vec<Option<Self>>, Vec<Option<Self>>),
558
559 Access(Box<Self>, String),
561
562 Custom(Vec<String>),
564}
565
566impl Type {
567 fn from_expression(expr: &pt::Expression) -> Option<Self> {
577 match expr {
578 pt::Expression::Type(_, ty) => Self::from_type(ty),
579
580 pt::Expression::Variable(ident) => Some(Self::Custom(vec![ident.name.clone()])),
581
582 pt::Expression::ArraySubscript(_, expr, num) => {
584 Self::from_expression(expr).and_then(|ty| {
587 let boxed = Box::new(ty);
588 let num = num.as_deref().and_then(parse_number_literal).and_then(|n| {
589 if n > USIZE_MAX_AS_U256 {
591 None
592 } else {
593 Some(n.to::<usize>())
594 }
595 });
596 match expr.as_ref() {
597 pt::Expression::Type(_, _) => {
599 if let Some(num) = num {
600 Some(Self::FixedArray(boxed, num))
601 } else {
602 Some(Self::Array(boxed))
603 }
604 }
605 pt::Expression::Variable(_) => {
607 Some(Self::ArrayIndex(boxed, num))
608 }
609 _ => None
610 }
611 })
612 }
613 pt::Expression::ArrayLiteral(_, values) => {
614 values.first().and_then(Self::from_expression).map(|ty| {
615 Self::FixedArray(Box::new(ty), values.len())
616 })
617 }
618
619 pt::Expression::List(_, params) => Some(Self::Tuple(map_parameters(params))),
621
622 pt::Expression::MemberAccess(_, lhs, rhs) => {
624 Self::from_expression(lhs).map(|lhs| {
625 Self::Access(Box::new(lhs), rhs.name.clone())
626 })
627 }
628
629 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),
653
654 pt::Expression::ConditionalOperator(_, _, if_true, if_false) => {
656 Self::from_expression(if_true).or_else(|| Self::from_expression(if_false))
657 }
658
659 pt::Expression::AddressLiteral(_, _) => Some(Self::Builtin(DynSolType::Address)),
661 pt::Expression::HexNumberLiteral(_, s, _) => {
662 match s.parse::<Address>() {
663 Ok(addr) => {
664 if *s == addr.to_checksum(None) {
665 Some(Self::Builtin(DynSolType::Address))
666 } else {
667 Some(Self::Builtin(DynSolType::Uint(256)))
668 }
669 },
670 _ => {
671 Some(Self::Builtin(DynSolType::Uint(256)))
672 }
673 }
674 }
675
676 pt::Expression::Negate(_, inner) => Self::from_expression(inner).map(Self::invert_int),
679
680 pt::Expression::Add(_, lhs, rhs) |
684 pt::Expression::Subtract(_, lhs, rhs) |
685 pt::Expression::Multiply(_, lhs, rhs) |
686 pt::Expression::Divide(_, lhs, rhs) => {
687 match (Self::ethabi(lhs, None), Self::ethabi(rhs, None)) {
688 (Some(DynSolType::Int(_)), Some(DynSolType::Int(_))) |
689 (Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) |
690 (Some(DynSolType::Uint(_)), Some(DynSolType::Int(_))) => {
691 Some(Self::Builtin(DynSolType::Int(256)))
692 }
693 _ => {
694 Some(Self::Builtin(DynSolType::Uint(256)))
695 }
696 }
697 }
698
699 pt::Expression::Modulo(_, _, _) |
701 pt::Expression::Power(_, _, _) |
702 pt::Expression::BitwiseOr(_, _, _) |
703 pt::Expression::BitwiseAnd(_, _, _) |
704 pt::Expression::BitwiseXor(_, _, _) |
705 pt::Expression::ShiftRight(_, _, _) |
706 pt::Expression::ShiftLeft(_, _, _) |
707 pt::Expression::NumberLiteral(_, _, _, _) => Some(Self::Builtin(DynSolType::Uint(256))),
708
709 pt::Expression::RationalNumberLiteral(_, _, _, _, _) => {
711 Some(Self::Builtin(DynSolType::Uint(256)))
712 }
713
714 pt::Expression::BoolLiteral(_, _) |
716 pt::Expression::And(_, _, _) |
717 pt::Expression::Or(_, _, _) |
718 pt::Expression::Equal(_, _, _) |
719 pt::Expression::NotEqual(_, _, _) |
720 pt::Expression::Less(_, _, _) |
721 pt::Expression::LessEqual(_, _, _) |
722 pt::Expression::More(_, _, _) |
723 pt::Expression::MoreEqual(_, _, _) |
724 pt::Expression::Not(_, _) => Some(Self::Builtin(DynSolType::Bool)),
725
726 pt::Expression::StringLiteral(_) => Some(Self::Builtin(DynSolType::String)),
728
729 pt::Expression::HexLiteral(_) => Some(Self::Builtin(DynSolType::Bytes)),
731
732 pt::Expression::FunctionCall(_, name, args) => {
734 Self::from_expression(name).map(|name| {
735 let args = args.iter().map(Self::from_expression).collect();
736 Self::Function(Box::new(name), args, vec![])
737 })
738 }
739 pt::Expression::NamedFunctionCall(_, name, args) => {
740 Self::from_expression(name).map(|name| {
741 let args = args.iter().map(|arg| Self::from_expression(&arg.expr)).collect();
742 Self::Function(Box::new(name), args, vec![])
743 })
744 }
745
746 pt::Expression::Delete(_, _) | pt::Expression::FunctionCallBlock(_, _, _) => None,
748 }
749 }
750
751 fn from_type(ty: &pt::Type) -> Option<Self> {
761 let ty = match ty {
762 pt::Type::Address | pt::Type::AddressPayable | pt::Type::Payable => {
763 Self::Builtin(DynSolType::Address)
764 }
765 pt::Type::Bool => Self::Builtin(DynSolType::Bool),
766 pt::Type::String => Self::Builtin(DynSolType::String),
767 pt::Type::Int(size) => Self::Builtin(DynSolType::Int(*size as usize)),
768 pt::Type::Uint(size) => Self::Builtin(DynSolType::Uint(*size as usize)),
769 pt::Type::Bytes(size) => Self::Builtin(DynSolType::FixedBytes(*size as usize)),
770 pt::Type::DynamicBytes => Self::Builtin(DynSolType::Bytes),
771 pt::Type::Mapping { value, .. } => Self::from_expression(value)?,
772 pt::Type::Function { params, returns, .. } => {
773 let params = map_parameters(params);
774 let returns = returns
775 .as_ref()
776 .map(|(returns, _)| map_parameters(returns))
777 .unwrap_or_default();
778 Self::Function(
779 Box::new(Self::Custom(vec!["__fn_type__".to_string()])),
780 params,
781 returns,
782 )
783 }
784 pt::Type::Rational => return None,
786 };
787 Some(ty)
788 }
789
790 fn map_special(self) -> Self {
794 if !matches!(self, Self::Function(_, _, _) | Self::Access(_, _) | Self::Custom(_)) {
795 return self;
796 }
797
798 let mut types = Vec::with_capacity(5);
799 let mut args = None;
800 self.recurse(&mut types, &mut args);
801
802 let len = types.len();
803 if len == 0 {
804 return self;
805 }
806
807 #[expect(clippy::single_match)]
809 match &self {
810 Self::Access(inner, access) => {
811 if let Some(ty) = inner.as_ref().clone().try_as_ethabi(None) {
812 let ty = Self::Builtin(ty);
814 match access.as_str() {
815 "length" if ty.is_dynamic() || ty.is_array() || ty.is_fixed_bytes() => {
816 return Self::Builtin(DynSolType::Uint(256));
817 }
818 "pop" if ty.is_dynamic_array() => return ty,
819 _ => {}
820 }
821 }
822 }
823 _ => {}
824 }
825
826 let this = {
827 let name = types.last().unwrap().as_str();
828 match len {
829 0 => unreachable!(),
830 1 => match name {
831 "gasleft" | "addmod" | "mulmod" => Some(DynSolType::Uint(256)),
832 "keccak256" | "sha256" | "blockhash" => Some(DynSolType::FixedBytes(32)),
833 "ripemd160" => Some(DynSolType::FixedBytes(20)),
834 "ecrecover" => Some(DynSolType::Address),
835 _ => None,
836 },
837 2 => {
838 let access = types.first().unwrap().as_str();
839 match name {
840 "block" => match access {
841 "coinbase" => Some(DynSolType::Address),
842 "timestamp" | "difficulty" | "prevrandao" | "number" | "gaslimit"
843 | "chainid" | "basefee" | "blobbasefee" => Some(DynSolType::Uint(256)),
844 _ => None,
845 },
846 "msg" => match access {
847 "sender" => Some(DynSolType::Address),
848 "gas" => Some(DynSolType::Uint(256)),
849 "value" => Some(DynSolType::Uint(256)),
850 "data" => Some(DynSolType::Bytes),
851 "sig" => Some(DynSolType::FixedBytes(4)),
852 _ => None,
853 },
854 "tx" => match access {
855 "origin" => Some(DynSolType::Address),
856 "gasprice" => Some(DynSolType::Uint(256)),
857 _ => None,
858 },
859 "abi" => match access {
860 "decode" => {
861 let mut args = args.unwrap();
865 let last = args.pop().unwrap();
866 match last {
867 Some(ty) => {
868 return match ty {
869 Self::Tuple(_) => ty,
870 ty => Self::Tuple(vec![Some(ty)]),
871 };
872 }
873 None => None,
874 }
875 }
876 s if s.starts_with("encode") => Some(DynSolType::Bytes),
877 _ => None,
878 },
879 "address" => match access {
880 "balance" => Some(DynSolType::Uint(256)),
881 "code" => Some(DynSolType::Bytes),
882 "codehash" => Some(DynSolType::FixedBytes(32)),
883 "send" => Some(DynSolType::Bool),
884 _ => None,
885 },
886 "type" => match access {
887 "name" => Some(DynSolType::String),
888 "creationCode" | "runtimeCode" => Some(DynSolType::Bytes),
889 "interfaceId" => Some(DynSolType::FixedBytes(4)),
890 "min" | "max" => Some(
891 (|| args?.pop()??.into_builtin())()
893 .unwrap_or(DynSolType::Uint(256)),
894 ),
895 _ => None,
896 },
897 "string" => match access {
898 "concat" => Some(DynSolType::String),
899 _ => None,
900 },
901 "bytes" => match access {
902 "concat" => Some(DynSolType::Bytes),
903 _ => None,
904 },
905 _ => None,
906 }
907 }
908 _ => None,
909 }
910 };
911
912 this.map(Self::Builtin).unwrap_or_else(|| match types.last().unwrap().as_str() {
913 "this" | "super" => Self::Custom(types),
914 _ => match self {
915 Self::Custom(_) | Self::Access(_, _) => Self::Custom(types),
916 Self::Function(_, _, _) => self,
917 _ => unreachable!(),
918 },
919 })
920 }
921
922 fn recurse(&self, types: &mut Vec<String>, args: &mut Option<Vec<Option<Self>>>) {
925 match self {
926 Self::Builtin(ty) => types.push(ty.to_string()),
927 Self::Custom(tys) => types.extend(tys.clone()),
928 Self::Access(expr, name) => {
929 types.push(name.clone());
930 expr.recurse(types, args);
931 }
932 Self::Function(fn_name, fn_args, _fn_ret) => {
933 if args.is_none() && !fn_args.is_empty() {
934 *args = Some(fn_args.clone());
935 }
936 fn_name.recurse(types, args);
937 }
938 _ => {}
939 }
940 }
941
942 fn infer_custom_type(
956 intermediate: &IntermediateOutput,
957 custom_type: &mut Vec<String>,
958 contract_name: Option<String>,
959 ) -> Result<Option<DynSolType>> {
960 if let Some("this") | Some("super") = custom_type.last().map(String::as_str) {
961 custom_type.pop();
962 }
963 if custom_type.is_empty() {
964 return Ok(None);
965 }
966
967 if let Some(contract_name) = contract_name {
970 let intermediate_contract = intermediate
971 .intermediate_contracts
972 .get(&contract_name)
973 .ok_or_else(|| eyre::eyre!("Could not find intermediate contract!"))?;
974
975 let cur_type = custom_type.last().unwrap();
976 if let Some(func) = intermediate_contract.function_definitions.get(cur_type) {
977 if let res @ Some(_) = func_members(func, custom_type) {
979 return Ok(res);
980 }
981
982 if func.returns.is_empty() {
985 eyre::bail!(
986 "This call expression does not return any values to inspect. Insert as statement."
987 )
988 }
989
990 let (_, param) = func.returns.first().unwrap();
992 let return_ty = ¶m.as_ref().unwrap().ty;
994
995 if let pt::Expression::Variable(ident) = return_ty {
999 custom_type.push(ident.name.clone());
1000 return Self::infer_custom_type(intermediate, custom_type, Some(contract_name));
1001 }
1002
1003 if let Some(pt::FunctionAttribute::Mutability(_mut)) = func
1007 .attributes
1008 .iter()
1009 .find(|attr| matches!(attr, pt::FunctionAttribute::Mutability(_)))
1010 {
1011 if let pt::Mutability::Payable(_) = _mut {
1012 eyre::bail!("This function mutates state. Insert as a statement.")
1013 }
1014 } else {
1015 eyre::bail!("This function mutates state. Insert as a statement.")
1016 }
1017
1018 Ok(Self::ethabi(return_ty, Some(intermediate)))
1019 } else if let Some(var) = intermediate_contract.variable_definitions.get(cur_type) {
1020 Self::infer_var_expr(&var.ty, Some(intermediate), custom_type)
1021 } else if let Some(strukt) = intermediate_contract.struct_definitions.get(cur_type) {
1022 let inner_types = strukt
1023 .fields
1024 .iter()
1025 .map(|var| {
1026 Self::ethabi(&var.ty, Some(intermediate))
1027 .ok_or_else(|| eyre::eyre!("Struct `{cur_type}` has invalid fields"))
1028 })
1029 .collect::<Result<Vec<_>>>()?;
1030 Ok(Some(DynSolType::Tuple(inner_types)))
1031 } else {
1032 eyre::bail!(
1033 "Could not find any definition in contract \"{contract_name}\" for type: {custom_type:?}"
1034 )
1035 }
1036 } else {
1037 if let Ok(res) = Self::infer_custom_type(intermediate, custom_type, Some("REPL".into()))
1040 {
1041 return Ok(res);
1042 }
1043
1044 let name = custom_type.last().unwrap();
1047 let contract = intermediate.intermediate_contracts.get(name);
1048 if contract.is_some() {
1049 let contract_name = custom_type.pop();
1050 return Self::infer_custom_type(intermediate, custom_type, contract_name);
1051 }
1052
1053 let name = custom_type.last().unwrap();
1055 if let Some(expr) = intermediate.repl_contract_expressions.get(name) {
1056 return Self::infer_var_expr(expr, Some(intermediate), custom_type);
1057 }
1058
1059 Ok(None)
1062 }
1063 }
1064
1065 fn infer_var_expr(
1067 expr: &pt::Expression,
1068 intermediate: Option<&IntermediateOutput>,
1069 custom_type: &mut Vec<String>,
1070 ) -> Result<Option<DynSolType>> {
1071 let res = match &expr {
1073 pt::Expression::Variable(ident) => {
1075 let name = &ident.name;
1076
1077 if let Some(intermediate) = intermediate {
1078 if let Some(expr) = intermediate.repl_contract_expressions.get(name) {
1080 Self::infer_var_expr(expr, Some(intermediate), custom_type)
1081 } else if intermediate.intermediate_contracts.contains_key(name) {
1082 if custom_type.len() > 1 {
1083 custom_type.pop();
1085 Self::infer_custom_type(intermediate, custom_type, Some(name.clone()))
1086 } else {
1087 Ok(Some(DynSolType::Address))
1089 }
1090 } else {
1091 Err(eyre::eyre!("Could not infer variable type"))
1092 }
1093 } else {
1094 Ok(None)
1095 }
1096 }
1097 ty => Ok(Self::ethabi(ty, intermediate)),
1098 };
1099 match res {
1102 Ok(Some(ty)) => {
1103 let box_ty = Box::new(Self::Builtin(ty.clone()));
1104 let access = Self::Access(box_ty, custom_type.drain(..).next().unwrap_or_default());
1105 if let Some(mapped) = access.map_special().try_as_ethabi(intermediate) {
1106 Ok(Some(mapped))
1107 } else {
1108 Ok(Some(ty))
1109 }
1110 }
1111 res => res,
1112 }
1113 }
1114
1115 fn try_as_ethabi(self, intermediate: Option<&IntermediateOutput>) -> Option<DynSolType> {
1123 match self {
1124 Self::Builtin(ty) => Some(ty),
1125 Self::Tuple(types) => Some(DynSolType::Tuple(types_to_parameters(types, intermediate))),
1126 Self::Array(inner) => match *inner {
1127 ty @ Self::Custom(_) => ty.try_as_ethabi(intermediate),
1128 _ => inner
1129 .try_as_ethabi(intermediate)
1130 .map(|inner| DynSolType::Array(Box::new(inner))),
1131 },
1132 Self::FixedArray(inner, size) => match *inner {
1133 ty @ Self::Custom(_) => ty.try_as_ethabi(intermediate),
1134 _ => inner
1135 .try_as_ethabi(intermediate)
1136 .map(|inner| DynSolType::FixedArray(Box::new(inner), size)),
1137 },
1138 ty @ Self::ArrayIndex(_, _) => ty.into_array_index(intermediate),
1139 Self::Function(ty, _, _) => ty.try_as_ethabi(intermediate),
1140 Self::Access(_, _) => None,
1142 Self::Custom(mut types) => {
1143 intermediate.and_then(|intermediate| {
1145 Self::infer_custom_type(intermediate, &mut types, None).ok().flatten()
1146 })
1147 }
1148 }
1149 }
1150
1151 fn ethabi(
1153 expr: &pt::Expression,
1154 intermediate: Option<&IntermediateOutput>,
1155 ) -> Option<DynSolType> {
1156 Self::from_expression(expr)
1157 .map(Self::map_special)
1158 .and_then(|ty| ty.try_as_ethabi(intermediate))
1159 }
1160
1161 fn get_function_return_type<'a>(
1163 contract_expr: Option<&'a pt::Expression>,
1164 intermediate: &IntermediateOutput,
1165 ) -> Option<(&'a pt::Expression, DynSolType)> {
1166 let function_call = match contract_expr? {
1167 pt::Expression::FunctionCall(_, function_call, _) => function_call,
1168 _ => return None,
1169 };
1170 let (contract_name, function_name) = match function_call.as_ref() {
1171 pt::Expression::MemberAccess(_, contract_name, function_name) => {
1172 (contract_name, function_name)
1173 }
1174 _ => return None,
1175 };
1176 let contract_name = match contract_name.as_ref() {
1177 pt::Expression::Variable(contract_name) => contract_name.to_owned(),
1178 _ => return None,
1179 };
1180
1181 let pt::Expression::Variable(contract_name) =
1182 intermediate.repl_contract_expressions.get(&contract_name.name)?
1183 else {
1184 return None;
1185 };
1186
1187 let contract = intermediate
1188 .intermediate_contracts
1189 .get(&contract_name.name)?
1190 .function_definitions
1191 .get(&function_name.name)?;
1192 let return_parameter = contract.as_ref().returns.first()?.to_owned().1?;
1193 Self::ethabi(&return_parameter.ty, Some(intermediate)).map(|p| (contract_expr.unwrap(), p))
1194 }
1195
1196 fn invert_int(self) -> Self {
1198 match self {
1199 Self::Builtin(DynSolType::Uint(n)) => Self::Builtin(DynSolType::Int(n)),
1200 Self::Builtin(DynSolType::Int(n)) => Self::Builtin(DynSolType::Uint(n)),
1201 x => x,
1202 }
1203 }
1204
1205 #[inline]
1207 fn into_builtin(self) -> Option<DynSolType> {
1208 match self {
1209 Self::Builtin(ty) => Some(ty),
1210 _ => None,
1211 }
1212 }
1213
1214 fn into_array_index(self, intermediate: Option<&IntermediateOutput>) -> Option<DynSolType> {
1216 match self {
1217 Self::Array(inner) | Self::FixedArray(inner, _) | Self::ArrayIndex(inner, _) => {
1218 match inner.try_as_ethabi(intermediate) {
1219 Some(DynSolType::Array(inner)) | Some(DynSolType::FixedArray(inner, _)) => {
1220 Some(*inner)
1221 }
1222 Some(DynSolType::Bytes)
1223 | Some(DynSolType::String)
1224 | Some(DynSolType::FixedBytes(_)) => Some(DynSolType::FixedBytes(1)),
1225 ty => ty,
1226 }
1227 }
1228 _ => None,
1229 }
1230 }
1231
1232 #[inline]
1234 fn is_dynamic(&self) -> bool {
1235 match self {
1236 Self::Builtin(DynSolType::Bytes | DynSolType::String | DynSolType::Array(_)) => true,
1239 Self::Array(_) => true,
1240 _ => false,
1241 }
1242 }
1243
1244 #[inline]
1246 fn is_array(&self) -> bool {
1247 matches!(
1248 self,
1249 Self::Array(_)
1250 | Self::FixedArray(_, _)
1251 | Self::Builtin(DynSolType::Array(_))
1252 | Self::Builtin(DynSolType::FixedArray(_, _))
1253 )
1254 }
1255
1256 #[inline]
1258 fn is_dynamic_array(&self) -> bool {
1259 matches!(self, Self::Array(_) | Self::Builtin(DynSolType::Array(_)))
1260 }
1261
1262 fn is_fixed_bytes(&self) -> bool {
1263 matches!(self, Self::Builtin(DynSolType::FixedBytes(_)))
1264 }
1265}
1266
1267#[inline]
1271fn func_members(func: &pt::FunctionDefinition, custom_type: &[String]) -> Option<DynSolType> {
1272 if !matches!(func.ty, pt::FunctionTy::Function) {
1273 return None;
1274 }
1275
1276 let vis = func.attributes.iter().find_map(|attr| match attr {
1277 pt::FunctionAttribute::Visibility(vis) => Some(vis),
1278 _ => None,
1279 });
1280 match vis {
1281 Some(pt::Visibility::External(_)) | Some(pt::Visibility::Public(_)) => {
1282 match custom_type.first().unwrap().as_str() {
1283 "address" => Some(DynSolType::Address),
1284 "selector" => Some(DynSolType::FixedBytes(4)),
1285 _ => None,
1286 }
1287 }
1288 _ => None,
1289 }
1290}
1291
1292#[inline]
1294fn should_continue(expr: &pt::Expression) -> bool {
1295 match expr {
1296 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(_, _, _) => {
1313 true
1314 }
1315
1316 pt::Expression::FunctionCall(_, lhs, _) => {
1318 match lhs.as_ref() {
1319 pt::Expression::MemberAccess(_, _inner, access) => access.name == "pop",
1320 _ => false
1321 }
1322 }
1323
1324 _ => false
1325 }
1326}
1327
1328fn map_parameters(params: &[(pt::Loc, Option<pt::Parameter>)]) -> Vec<Option<Type>> {
1329 params
1330 .iter()
1331 .map(|(_, param)| param.as_ref().and_then(|param| Type::from_expression(¶m.ty)))
1332 .collect()
1333}
1334
1335fn types_to_parameters(
1336 types: Vec<Option<Type>>,
1337 intermediate: Option<&IntermediateOutput>,
1338) -> Vec<DynSolType> {
1339 types.into_iter().filter_map(|ty| ty.and_then(|ty| ty.try_as_ethabi(intermediate))).collect()
1340}
1341
1342fn parse_number_literal(expr: &pt::Expression) -> Option<U256> {
1343 match expr {
1344 pt::Expression::NumberLiteral(_, num, exp, unit) => {
1345 let num = U256::from_str(num).unwrap_or(U256::ZERO);
1346 let exp = exp.parse().unwrap_or(0u32);
1347 if exp > 77 {
1348 None
1349 } else {
1350 let exp = U256::from(10usize.pow(exp));
1351 let unit_mul = unit_multiplier(unit).ok()?;
1352 Some(num * exp * unit_mul)
1353 }
1354 }
1355 pt::Expression::HexNumberLiteral(_, num, unit) => {
1356 let unit_mul = unit_multiplier(unit).ok()?;
1357 num.parse::<U256>().map(|num| num * unit_mul).ok()
1358 }
1359 pt::Expression::RationalNumberLiteral(..) => None,
1361 _ => None,
1362 }
1363}
1364
1365#[inline]
1366fn unit_multiplier(unit: &Option<pt::Identifier>) -> Result<U256> {
1367 if let Some(unit) = unit {
1368 let mul = match unit.name.as_str() {
1369 "seconds" => 1,
1370 "minutes" => 60,
1371 "hours" => 60 * 60,
1372 "days" => 60 * 60 * 24,
1373 "weeks" => 60 * 60 * 24 * 7,
1374 "wei" => 1,
1375 "gwei" => 10_usize.pow(9),
1376 "ether" => 10_usize.pow(18),
1377 other => eyre::bail!("unknown unit: {other}"),
1378 };
1379 Ok(U256::from(mul))
1380 } else {
1381 Ok(U256::from(1))
1382 }
1383}
1384
1385#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1386struct Instruction {
1387 pub pc: usize,
1388 pub opcode: u8,
1389 pub data: [u8; 32],
1390 pub data_len: u8,
1391}
1392
1393struct InstructionIter<'a> {
1394 bytes: &'a [u8],
1395 offset: usize,
1396}
1397
1398impl<'a> InstructionIter<'a> {
1399 pub fn new(bytes: &'a [u8]) -> Self {
1400 Self { bytes, offset: 0 }
1401 }
1402}
1403
1404impl Iterator for InstructionIter<'_> {
1405 type Item = Instruction;
1406 fn next(&mut self) -> Option<Self::Item> {
1407 let pc = self.offset;
1408 self.offset += 1;
1409 let opcode = *self.bytes.get(pc)?;
1410 let (data, data_len) = if matches!(opcode, 0x60..=0x7F) {
1411 let mut data = [0; 32];
1412 let data_len = (opcode - 0x60 + 1) as usize;
1413 data[..data_len].copy_from_slice(&self.bytes[self.offset..self.offset + data_len]);
1414 self.offset += data_len;
1415 (data, data_len as u8)
1416 } else {
1417 ([0; 32], 0)
1418 };
1419 Some(Instruction { pc, opcode, data, data_len })
1420 }
1421}
1422
1423#[cfg(test)]
1424mod tests {
1425 use super::*;
1426 use foundry_compilers::{error::SolcError, solc::Solc};
1427 use semver::Version;
1428 use std::sync::Mutex;
1429
1430 #[test]
1431 fn test_const() {
1432 assert_eq!(USIZE_MAX_AS_U256.to::<u64>(), usize::MAX as u64);
1433 assert_eq!(USIZE_MAX_AS_U256.to::<u64>(), usize::MAX as u64);
1434 }
1435
1436 #[test]
1437 fn test_expressions() {
1438 static EXPRESSIONS: &[(&str, DynSolType)] = {
1439 use DynSolType::*;
1440 &[
1441 ("1 seconds", Uint(256)),
1444 ("1 minutes", Uint(256)),
1445 ("1 hours", Uint(256)),
1446 ("1 days", Uint(256)),
1447 ("1 weeks", Uint(256)),
1448 ("1 wei", Uint(256)),
1449 ("1 gwei", Uint(256)),
1450 ("1 ether", Uint(256)),
1451 ("-1 seconds", Int(256)),
1453 ("-1 minutes", Int(256)),
1454 ("-1 hours", Int(256)),
1455 ("-1 days", Int(256)),
1456 ("-1 weeks", Int(256)),
1457 ("-1 wei", Int(256)),
1458 ("-1 gwei", Int(256)),
1459 ("-1 ether", Int(256)),
1460 ("true ? 1 : 0", Uint(256)),
1462 ("true ? -1 : 0", Int(256)),
1463 ("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 ("1 ** 1", Uint(256)),
1474 ("1 | 1", Uint(256)),
1475 ("1 & 1", Uint(256)),
1476 ("1 ^ 1", Uint(256)),
1477 ("1 >> 1", Uint(256)),
1478 ("1 << 1", Uint(256)),
1479 ("int(1) + 1", Int(256)),
1481 ("int(1) - 1", Int(256)),
1482 ("int(1) * 1", Int(256)),
1483 ("int(1) / 1", Int(256)),
1484 ("1 + int(1)", Int(256)),
1485 ("1 - int(1)", Int(256)),
1486 ("1 * int(1)", Int(256)),
1487 ("1 / int(1)", Int(256)),
1488 ("uint256 a; a--", Uint(256)),
1492 ("uint256 a; --a", Uint(256)),
1493 ("uint256 a; a++", Uint(256)),
1494 ("uint256 a; ++a", 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 ("uint256 a; a %= 1", Uint(256)),
1501 ("uint256 a; a &= 1", Uint(256)),
1502 ("uint256 a; a |= 1", Uint(256)),
1503 ("uint256 a; a ^= 1", Uint(256)),
1504 ("uint256 a; a <<= 1", Uint(256)),
1505 ("uint256 a; a >>= 1", Uint(256)),
1506 ("true && true", Bool),
1510 ("true || true", Bool),
1511 ("true == true", Bool),
1512 ("true != true", Bool),
1513 ("true < true", Bool),
1514 ("true <= true", Bool),
1515 ("true > true", Bool),
1516 ("true >= true", Bool),
1517 ("!true", Bool),
1518 ]
1520 };
1521
1522 let source = &mut source();
1523
1524 let array_expressions: &[(&str, DynSolType)] = &[
1525 ("[1, 2, 3]", fixed_array(DynSolType::Uint(256), 3)),
1526 ("[uint8(1), 2, 3]", fixed_array(DynSolType::Uint(8), 3)),
1527 ("[int8(1), 2, 3]", fixed_array(DynSolType::Int(8), 3)),
1528 ("new uint256[](3)", array(DynSolType::Uint(256))),
1529 ("uint256[] memory a = new uint256[](3);\na[0]", DynSolType::Uint(256)),
1530 ("uint256[] memory a = new uint256[](3);\na[0:3]", array(DynSolType::Uint(256))),
1531 ];
1532 generic_type_test(source, array_expressions);
1533 generic_type_test(source, EXPRESSIONS);
1534 }
1535
1536 #[test]
1537 fn test_types() {
1538 static TYPES: &[(&str, DynSolType)] = {
1539 use DynSolType::*;
1540 &[
1541 ("bool", Bool),
1543 ("true", Bool),
1544 ("false", Bool),
1545 ("uint", Uint(256)),
1549 ("uint(1)", Uint(256)),
1550 ("1", Uint(256)),
1551 ("0x01", Uint(256)),
1552 ("int", Int(256)),
1553 ("int(1)", Int(256)),
1554 ("int(-1)", Int(256)),
1555 ("-1", Int(256)),
1556 ("-0x01", Int(256)),
1557 ("address", Address),
1561 ("address(0)", Address),
1562 ("0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990", Address),
1563 ("payable(0)", Address),
1564 ("payable(address(0))", Address),
1565 ("string", String),
1569 ("string(\"hello world\")", String),
1570 ("\"hello world\"", String),
1571 ("unicode\"hello world 😀\"", String),
1572 ("bytes", Bytes),
1576 ("bytes(\"hello world\")", Bytes),
1577 ("bytes(unicode\"hello world 😀\")", Bytes),
1578 ("hex\"68656c6c6f20776f726c64\"", Bytes),
1579 ]
1581 };
1582
1583 let mut types: Vec<(String, DynSolType)> = Vec::with_capacity(96 + 32 + 100);
1584 for (n, b) in (8..=256).step_by(8).zip(1..=32) {
1585 types.push((format!("uint{n}(0)"), DynSolType::Uint(n)));
1586 types.push((format!("int{n}(0)"), DynSolType::Int(n)));
1587 types.push((format!("bytes{b}(0x00)"), DynSolType::FixedBytes(b)));
1588 }
1589
1590 for n in 0..=32 {
1591 types.push((
1592 format!("uint256[{n}]"),
1593 DynSolType::FixedArray(Box::new(DynSolType::Uint(256)), n),
1594 ));
1595 }
1596
1597 generic_type_test(&mut source(), TYPES);
1598 generic_type_test(&mut source(), &types);
1599 }
1600
1601 #[test]
1602 fn test_global_vars() {
1603 init_tracing();
1604
1605 let global_variables = {
1607 use DynSolType::*;
1608 &[
1609 ("abi.decode(bytes, (uint8[13]))", Tuple(vec![FixedArray(Box::new(Uint(8)), 13)])),
1611 ("abi.decode(bytes, (address, bytes))", Tuple(vec![Address, Bytes])),
1612 ("abi.decode(bytes, (uint112, uint48))", Tuple(vec![Uint(112), Uint(48)])),
1613 ("abi.encode(_, _)", Bytes),
1614 ("abi.encodePacked(_, _)", Bytes),
1615 ("abi.encodeWithSelector(bytes4, _, _)", Bytes),
1616 ("abi.encodeCall(function(), (_, _))", Bytes),
1617 ("abi.encodeWithSignature(string, _, _)", Bytes),
1618 ("bytes.concat()", Bytes),
1622 ("bytes.concat(_)", Bytes),
1623 ("bytes.concat(_, _)", Bytes),
1624 ("string.concat()", String),
1625 ("string.concat(_)", String),
1626 ("string.concat(_, _)", String),
1627 ("block.basefee", Uint(256)),
1631 ("block.chainid", Uint(256)),
1632 ("block.coinbase", Address),
1633 ("block.difficulty", Uint(256)),
1634 ("block.gaslimit", Uint(256)),
1635 ("block.number", Uint(256)),
1636 ("block.timestamp", Uint(256)),
1637 ("gasleft()", Uint(256)),
1641 ("msg.data", Bytes),
1642 ("msg.sender", Address),
1643 ("msg.sig", FixedBytes(4)),
1644 ("msg.value", Uint(256)),
1645 ("tx.gasprice", Uint(256)),
1646 ("tx.origin", Address),
1647 ("blockhash(uint)", FixedBytes(32)),
1658 ("keccak256(bytes)", FixedBytes(32)),
1659 ("sha256(bytes)", FixedBytes(32)),
1660 ("ripemd160(bytes)", FixedBytes(20)),
1661 ("ecrecover(bytes32, uint8, bytes32, bytes32)", Address),
1662 ("addmod(uint, uint, uint)", Uint(256)),
1663 ("mulmod(uint, uint, uint)", Uint(256)),
1664 ("address(_)", Address),
1668 ("address(this)", Address),
1669 ("address.balance", Uint(256)),
1672 ("address.code", Bytes),
1673 ("address.codehash", FixedBytes(32)),
1674 ("address.send(uint256)", Bool),
1675 ("type(C).name", String),
1680 ("type(C).creationCode", Bytes),
1681 ("type(C).runtimeCode", Bytes),
1682 ("type(I).interfaceId", FixedBytes(4)),
1683 ("type(uint256).min", Uint(256)),
1684 ("type(int128).min", Int(128)),
1685 ("type(int256).min", Int(256)),
1686 ("type(uint256).max", Uint(256)),
1687 ("type(int128).max", Int(128)),
1688 ("type(int256).max", Int(256)),
1689 ("type(Enum1).min", Uint(256)),
1690 ("type(Enum1).max", Uint(256)),
1691 ("this.run.address", Address),
1693 ("this.run.selector", FixedBytes(4)),
1694 ]
1695 };
1696
1697 generic_type_test(&mut source(), global_variables);
1698 }
1699
1700 #[track_caller]
1701 fn source() -> SessionSource {
1702 static PRE_INSTALL_SOLC_LOCK: Mutex<bool> = Mutex::new(false);
1704
1705 let version = "0.8.20";
1708 for _ in 0..3 {
1709 let mut is_preinstalled = PRE_INSTALL_SOLC_LOCK.lock().unwrap();
1710 if !*is_preinstalled {
1711 let solc = Solc::find_or_install(&version.parse().unwrap())
1712 .map(|solc| (solc.version.clone(), solc));
1713 match solc {
1714 Ok((v, solc)) => {
1715 let _ = sh_println!("found installed Solc v{v} @ {}", solc.solc.display());
1717 break;
1718 }
1719 Err(e) => {
1720 let _ = sh_err!("error while trying to re-install Solc v{version}: {e}");
1722 let solc = Solc::blocking_install(&version.parse().unwrap());
1723 if solc.map_err(SolcError::from).is_ok() {
1724 *is_preinstalled = true;
1725 break;
1726 }
1727 }
1728 }
1729 }
1730 }
1731
1732 let solc = Solc::find_or_install(&Version::new(0, 8, 19)).expect("could not install solc");
1733 SessionSource::new(solc, Default::default())
1734 }
1735
1736 fn array(ty: DynSolType) -> DynSolType {
1737 DynSolType::Array(Box::new(ty))
1738 }
1739
1740 fn fixed_array(ty: DynSolType, len: usize) -> DynSolType {
1741 DynSolType::FixedArray(Box::new(ty), len)
1742 }
1743
1744 fn parse(s: &mut SessionSource, input: &str, clear: bool) -> IntermediateOutput {
1745 if clear {
1746 s.drain_run();
1747 s.drain_top_level_code();
1748 s.drain_global_code();
1749 }
1750
1751 *s = s.clone_with_new_line("enum Enum1 { A }".into()).unwrap().0;
1752
1753 let input = format!("{};", input.trim_end().trim_end_matches(';'));
1754 let (mut _s, _) = s.clone_with_new_line(input).unwrap();
1755 *s = _s.clone();
1756 let s = &mut _s;
1757
1758 if let Err(e) = s.parse() {
1759 for err in e {
1760 let _ = sh_eprintln!("{}:{}: {}", err.loc.start(), err.loc.end(), err.message);
1761 }
1762 let source = s.to_repl_source();
1763 panic!("could not parse input:\n{source}")
1764 }
1765 s.generate_intermediate_output().expect("could not generate intermediate output")
1766 }
1767
1768 fn expr(stmts: &[pt::Statement]) -> pt::Expression {
1769 match stmts.last().expect("no statements") {
1770 pt::Statement::Expression(_, e) => e.clone(),
1771 s => panic!("Not an expression: {s:?}"),
1772 }
1773 }
1774
1775 fn get_type(
1776 s: &mut SessionSource,
1777 input: &str,
1778 clear: bool,
1779 ) -> (Option<Type>, IntermediateOutput) {
1780 let intermediate = parse(s, input, clear);
1781 let run_func_body = intermediate.run_func_body().expect("no run func body");
1782 let expr = expr(run_func_body);
1783 (Type::from_expression(&expr).map(Type::map_special), intermediate)
1784 }
1785
1786 fn get_type_ethabi(s: &mut SessionSource, input: &str, clear: bool) -> Option<DynSolType> {
1787 let (ty, intermediate) = get_type(s, input, clear);
1788 ty.and_then(|ty| ty.try_as_ethabi(Some(&intermediate)))
1789 }
1790
1791 fn generic_type_test<'a, T, I>(s: &mut SessionSource, input: I)
1792 where
1793 T: AsRef<str> + std::fmt::Display + 'a,
1794 I: IntoIterator<Item = &'a (T, DynSolType)> + 'a,
1795 {
1796 for (input, expected) in input.into_iter() {
1797 let input = input.as_ref();
1798 let ty = get_type_ethabi(s, input, true);
1799 assert_eq!(ty.as_ref(), Some(expected), "\n{input}");
1800 }
1801 }
1802
1803 fn init_tracing() {
1804 let _ = tracing_subscriber::FmtSubscriber::builder()
1805 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
1806 .try_init();
1807 }
1808}