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_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 && let Some(statement) = block.statements.last()
58 && let pt::YulStatement::FunctionCall(yul_call) = statement
59 && yul_call.id.name == "return"
60 {
61 return Some(statement.loc());
62 }
63 None
64 });
65
66 if let Some(final_statement) = run_func_statements.last() {
69 let mut source_loc = match final_statement {
76 pt::Statement::Assembly { loc: _, dialect: _, flags: _, block } => {
77 let last_statement = block.statements.iter().rev().find(|statement| {
79 !matches!(statement, pt::YulStatement::VariableDeclaration(_, _, _))
80 });
81 if let Some(statement) = last_statement {
82 statement.loc()
83 } else {
84 run_func_statements
88 .get(run_func_statements.len().saturating_sub(2))
89 .unwrap()
90 .loc()
91 }
92 }
93 pt::Statement::Block { loc: _, unchecked: _, statements } => {
94 if let Some(statement) = statements.last() {
95 statement.loc()
96 } else {
97 run_func_statements
101 .get(run_func_statements.len().saturating_sub(2))
102 .unwrap()
103 .loc()
104 }
105 }
106 _ => final_statement.loc(),
107 };
108
109 if let Some(yul_return) = last_yul_return
111 && yul_return.end() < source_loc.start()
112 {
113 source_loc = yul_return;
114 }
115
116 let final_pc = {
119 let offset = source_loc.start() as u32;
120 let length = (source_loc.end() - source_loc.start()) as u32;
121 contract
122 .get_source_map_deployed()
123 .unwrap()
124 .unwrap()
125 .into_iter()
126 .zip(InstructionIter::new(&deployed_bytecode))
127 .filter(|(s, _)| s.offset() == offset && s.length() == length)
128 .map(|(_, i)| i.pc)
129 .max()
130 .unwrap_or_default()
131 };
132
133 let mut runner = self.prepare_runner(final_pc).await?;
135
136 runner.run(bytecode.into_owned())
138 } else {
139 Ok((Address::ZERO, ChiselResult::default()))
141 }
142 } else {
143 eyre::bail!("Failed to find REPL contract!")
144 }
145 }
146
147 pub async fn inspect(&self, input: &str) -> Result<(bool, Option<String>)> {
159 let line = format!("bytes memory inspectoor = abi.encode({input});");
160 let mut source = match self.clone_with_new_line(line.clone()) {
161 Ok((source, _)) => source,
162 Err(err) => {
163 debug!(%err, "failed to build new source");
164 return Ok((true, None));
165 }
166 };
167
168 let mut source_without_inspector = self.clone();
169
170 let (mut res, err) = match source.execute().await {
173 Ok((_, res)) => (res, None),
174 Err(err) => {
175 debug!(?err, %input, "execution failed");
176 match source_without_inspector.execute().await {
177 Ok((_, res)) => (res, Some(err)),
178 Err(_) => {
179 if self.config.foundry_config.verbosity >= 3 {
180 sh_err!("Could not inspect: {err}")?;
181 }
182 return Ok((true, None));
183 }
184 }
185 }
186 };
187
188 if let Some(err) = err {
190 let generated_output = source_without_inspector
191 .generated_output
192 .as_ref()
193 .ok_or_else(|| eyre::eyre!("Could not find generated output!"))?;
194
195 let intermediate_contract = generated_output
196 .intermediate
197 .intermediate_contracts
198 .get("REPL")
199 .ok_or_else(|| eyre::eyre!("Could not find intermediate contract!"))?;
200
201 if let Some(event_definition) = intermediate_contract.event_definitions.get(input) {
202 let formatted = format_event_definition(event_definition)?;
203 return Ok((false, Some(formatted)));
204 }
205
206 if self.config.foundry_config.verbosity >= 3 {
208 sh_err!("Failed eval: {err}")?;
209 }
210
211 debug!(%err, %input, "failed abi encode input");
212 return Ok((false, None));
213 }
214
215 let Some((stack, memory, _)) = &res.state else {
216 if let Ok(decoder) = ChiselDispatcher::decode_traces(&source.config, &mut res).await {
218 ChiselDispatcher::show_traces(&decoder, &mut res).await?;
219 }
220 let decoded_logs = decode_console_logs(&res.logs);
221 if !decoded_logs.is_empty() {
222 sh_println!("{}", "Logs:".green())?;
223 for log in decoded_logs {
224 sh_println!(" {log}")?;
225 }
226 }
227
228 return Err(eyre::eyre!("Failed to inspect expression"));
229 };
230
231 let generated_output = source
232 .generated_output
233 .as_ref()
234 .ok_or_else(|| eyre::eyre!("Could not find generated output!"))?;
235
236 let contract_expr = generated_output
239 .intermediate
240 .repl_contract_expressions
241 .get(input)
242 .or_else(|| source.infer_inner_expr_type());
243
244 let function_call_return_type =
247 Type::get_function_return_type(contract_expr, &generated_output.intermediate);
248
249 let (contract_expr, ty) = if let Some(function_call_return_type) = function_call_return_type
250 {
251 (function_call_return_type.0, function_call_return_type.1)
252 } else {
253 match contract_expr.and_then(|e| {
254 Type::ethabi(e, Some(&generated_output.intermediate)).map(|ty| (e, ty))
255 }) {
256 Some(res) => res,
257 None => return Ok((true, None)),
259 }
260 };
261
262 let mut offset = stack.last().unwrap().to::<usize>();
265 let mem_offset = &memory[offset..offset + 32];
266 let len = U256::try_from_be_slice(mem_offset).unwrap().to::<usize>();
267 offset += 32;
268 let data = &memory[offset..offset + len];
269 let token =
271 DynSolType::abi_decode(&ty, data).wrap_err("Could not decode inspected values")?;
272 Ok((should_continue(contract_expr), Some(format_token(token))))
273 }
274
275 fn infer_inner_expr_type(&self) -> Option<&pt::Expression> {
286 let out = self.generated_output.as_ref()?;
287 let run = out.intermediate.run_func_body().ok()?.last();
288 match run {
289 Some(pt::Statement::VariableDefinition(
290 _,
291 _,
292 Some(pt::Expression::FunctionCall(_, _, args)),
293 )) => {
294 Some(args.first().unwrap())
298 }
299 _ => None,
300 }
301 }
302
303 async fn prepare_runner(&mut self, final_pc: usize) -> Result<ChiselRunner> {
313 let env =
314 self.config.evm_opts.evm_env().await.expect("Could not instantiate fork environment");
315
316 let backend = match self.config.backend.take() {
318 Some(backend) => backend,
319 None => {
320 let fork = self.config.evm_opts.get_fork(&self.config.foundry_config, env.clone());
321 let backend = Backend::spawn(fork)?;
322 self.config.backend = Some(backend.clone());
323 backend
324 }
325 };
326
327 let executor = ExecutorBuilder::new()
329 .inspectors(|stack| {
330 stack.chisel_state(final_pc).trace_mode(TraceMode::Call).cheatcodes(
331 CheatsConfig::new(
332 &self.config.foundry_config,
333 self.config.evm_opts.clone(),
334 None,
335 None,
336 )
337 .into(),
338 )
339 })
340 .gas_limit(self.config.evm_opts.gas_limit())
341 .spec_id(self.config.foundry_config.evm_spec_id())
342 .legacy_assertions(self.config.foundry_config.legacy_assertions)
343 .build(env, backend);
344
345 Ok(ChiselRunner::new(executor, U256::MAX, Address::ZERO, self.config.calldata.clone()))
348 }
349}
350
351fn format_token(token: DynSolValue) -> String {
354 match token {
355 DynSolValue::Address(a) => {
356 format!("Type: {}\n└ Data: {}", "address".red(), a.cyan())
357 }
358 DynSolValue::FixedBytes(b, byte_len) => {
359 format!(
360 "Type: {}\n└ Data: {}",
361 format!("bytes{byte_len}").red(),
362 hex::encode_prefixed(b).cyan()
363 )
364 }
365 DynSolValue::Int(i, bit_len) => {
366 format!(
367 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
368 format!("int{bit_len}").red(),
369 format!(
370 "0x{}",
371 format!("{i:x}")
372 .char_indices()
373 .skip(64 - bit_len / 4)
374 .take(bit_len / 4)
375 .map(|(_, c)| c)
376 .collect::<String>()
377 )
378 .cyan(),
379 hex::encode_prefixed(B256::from(i)).cyan(),
380 i.cyan()
381 )
382 }
383 DynSolValue::Uint(i, bit_len) => {
384 format!(
385 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
386 format!("uint{bit_len}").red(),
387 format!(
388 "0x{}",
389 format!("{i:x}")
390 .char_indices()
391 .skip(64 - bit_len / 4)
392 .take(bit_len / 4)
393 .map(|(_, c)| c)
394 .collect::<String>()
395 )
396 .cyan(),
397 hex::encode_prefixed(B256::from(i)).cyan(),
398 i.cyan()
399 )
400 }
401 DynSolValue::Bool(b) => {
402 format!("Type: {}\n└ Value: {}", "bool".red(), b.cyan())
403 }
404 DynSolValue::String(_) | DynSolValue::Bytes(_) => {
405 let hex = hex::encode(token.abi_encode());
406 let s = token.as_str();
407 format!(
408 "Type: {}\n{}├ Hex (Memory):\n├─ Length ({}): {}\n├─ Contents ({}): {}\n├ Hex (Tuple Encoded):\n├─ Pointer ({}): {}\n├─ Length ({}): {}\n└─ Contents ({}): {}",
409 if s.is_some() { "string" } else { "dynamic bytes" }.red(),
410 if let Some(s) = s {
411 format!("├ UTF-8: {}\n", s.cyan())
412 } else {
413 String::default()
414 },
415 "[0x00:0x20]".yellow(),
416 format!("0x{}", &hex[64..128]).cyan(),
417 "[0x20:..]".yellow(),
418 format!("0x{}", &hex[128..]).cyan(),
419 "[0x00:0x20]".yellow(),
420 format!("0x{}", &hex[..64]).cyan(),
421 "[0x20:0x40]".yellow(),
422 format!("0x{}", &hex[64..128]).cyan(),
423 "[0x40:..]".yellow(),
424 format!("0x{}", &hex[128..]).cyan(),
425 )
426 }
427 DynSolValue::FixedArray(tokens) | DynSolValue::Array(tokens) => {
428 let mut out = format!(
429 "{}({}) = {}",
430 "array".red(),
431 format!("{}", tokens.len()).yellow(),
432 '['.red()
433 );
434 for token in tokens {
435 out.push_str("\n ├ ");
436 out.push_str(&format_token(token).replace('\n', "\n "));
437 out.push('\n');
438 }
439 out.push_str(&']'.red().to_string());
440 out
441 }
442 DynSolValue::Tuple(tokens) => {
443 let displayed_types = tokens
444 .iter()
445 .map(|t| t.sol_type_name().unwrap_or_default())
446 .collect::<Vec<_>>()
447 .join(", ");
448 let mut out =
449 format!("{}({}) = {}", "tuple".red(), displayed_types.yellow(), '('.red());
450 for token in tokens {
451 out.push_str("\n ├ ");
452 out.push_str(&format_token(token).replace('\n', "\n "));
453 out.push('\n');
454 }
455 out.push_str(&')'.red().to_string());
456 out
457 }
458 _ => {
459 unimplemented!()
460 }
461 }
462}
463
464fn format_event_definition(event_definition: &pt::EventDefinition) -> Result<String> {
476 let event_name = event_definition.name.as_ref().expect("Event has a name").to_string();
477 let inputs = event_definition
478 .fields
479 .iter()
480 .map(|param| {
481 let name = param
482 .name
483 .as_ref()
484 .map(ToString::to_string)
485 .unwrap_or_else(|| "<anonymous>".to_string());
486 let kind = Type::from_expression(¶m.ty)
487 .and_then(Type::into_builtin)
488 .ok_or_else(|| eyre::eyre!("Invalid type in event {event_name}"))?;
489 Ok(EventParam {
490 name,
491 ty: kind.to_string(),
492 components: vec![],
493 indexed: param.indexed,
494 internal_type: None,
495 })
496 })
497 .collect::<Result<Vec<_>>>()?;
498 let event =
499 alloy_json_abi::Event { name: event_name, inputs, anonymous: event_definition.anonymous };
500
501 Ok(format!(
502 "Type: {}\n├ Name: {}\n├ Signature: {:?}\n└ Selector: {:?}",
503 "event".red(),
504 SolidityHelper::new().highlight(&format!(
505 "{}({})",
506 &event.name,
507 &event
508 .inputs
509 .iter()
510 .map(|param| format!(
511 "{}{}{}",
512 param.ty,
513 if param.indexed { " indexed" } else { "" },
514 if param.name.is_empty() {
515 String::default()
516 } else {
517 format!(" {}", ¶m.name)
518 },
519 ))
520 .collect::<Vec<_>>()
521 .join(", ")
522 )),
523 event.signature().cyan(),
524 event.selector().cyan(),
525 ))
526}
527
528#[derive(Clone, Debug, PartialEq)]
534enum Type {
535 Builtin(DynSolType),
537
538 Array(Box<Type>),
540
541 FixedArray(Box<Type>, usize),
543
544 ArrayIndex(Box<Type>, Option<usize>),
546
547 Tuple(Vec<Option<Type>>),
549
550 Function(Box<Type>, Vec<Option<Type>>, Vec<Option<Type>>),
552
553 Access(Box<Type>, String),
555
556 Custom(Vec<String>),
558}
559
560impl Type {
561 fn from_expression(expr: &pt::Expression) -> Option<Self> {
571 match expr {
572 pt::Expression::Type(_, ty) => Self::from_type(ty),
573
574 pt::Expression::Variable(ident) => Some(Self::Custom(vec![ident.name.clone()])),
575
576 pt::Expression::ArraySubscript(_, expr, num) => {
578 Self::from_expression(expr).and_then(|ty| {
581 let boxed = Box::new(ty);
582 let num = num.as_deref().and_then(parse_number_literal).and_then(|n| {
583 if n > USIZE_MAX_AS_U256 {
585 None
586 } else {
587 Some(n.to::<usize>())
588 }
589 });
590 match expr.as_ref() {
591 pt::Expression::Type(_, _) => {
593 if let Some(num) = num {
594 Some(Self::FixedArray(boxed, num))
595 } else {
596 Some(Self::Array(boxed))
597 }
598 }
599 pt::Expression::Variable(_) => {
601 Some(Self::ArrayIndex(boxed, num))
602 }
603 _ => None
604 }
605 })
606 }
607 pt::Expression::ArrayLiteral(_, values) => {
608 values.first().and_then(Self::from_expression).map(|ty| {
609 Self::FixedArray(Box::new(ty), values.len())
610 })
611 }
612
613 pt::Expression::List(_, params) => Some(Self::Tuple(map_parameters(params))),
615
616 pt::Expression::MemberAccess(_, lhs, rhs) => {
618 Self::from_expression(lhs).map(|lhs| {
619 Self::Access(Box::new(lhs), rhs.name.clone())
620 })
621 }
622
623 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),
647
648 pt::Expression::ConditionalOperator(_, _, if_true, if_false) => {
650 Self::from_expression(if_true).or_else(|| Self::from_expression(if_false))
651 }
652
653 pt::Expression::AddressLiteral(_, _) => Some(Self::Builtin(DynSolType::Address)),
655 pt::Expression::HexNumberLiteral(_, s, _) => {
656 match s.parse::<Address>() {
657 Ok(addr) => {
658 if *s == addr.to_checksum(None) {
659 Some(Self::Builtin(DynSolType::Address))
660 } else {
661 Some(Self::Builtin(DynSolType::Uint(256)))
662 }
663 },
664 _ => {
665 Some(Self::Builtin(DynSolType::Uint(256)))
666 }
667 }
668 }
669
670 pt::Expression::Negate(_, inner) => Self::from_expression(inner).map(Self::invert_int),
673
674 pt::Expression::Add(_, lhs, rhs) |
678 pt::Expression::Subtract(_, lhs, rhs) |
679 pt::Expression::Multiply(_, lhs, rhs) |
680 pt::Expression::Divide(_, lhs, rhs) => {
681 match (Self::ethabi(lhs, None), Self::ethabi(rhs, None)) {
682 (Some(DynSolType::Int(_)), Some(DynSolType::Int(_))) |
683 (Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) |
684 (Some(DynSolType::Uint(_)), Some(DynSolType::Int(_))) => {
685 Some(Self::Builtin(DynSolType::Int(256)))
686 }
687 _ => {
688 Some(Self::Builtin(DynSolType::Uint(256)))
689 }
690 }
691 }
692
693 pt::Expression::Modulo(_, _, _) |
695 pt::Expression::Power(_, _, _) |
696 pt::Expression::BitwiseOr(_, _, _) |
697 pt::Expression::BitwiseAnd(_, _, _) |
698 pt::Expression::BitwiseXor(_, _, _) |
699 pt::Expression::ShiftRight(_, _, _) |
700 pt::Expression::ShiftLeft(_, _, _) |
701 pt::Expression::NumberLiteral(_, _, _, _) => Some(Self::Builtin(DynSolType::Uint(256))),
702
703 pt::Expression::RationalNumberLiteral(_, _, _, _, _) => {
705 Some(Self::Builtin(DynSolType::Uint(256)))
706 }
707
708 pt::Expression::BoolLiteral(_, _) |
710 pt::Expression::And(_, _, _) |
711 pt::Expression::Or(_, _, _) |
712 pt::Expression::Equal(_, _, _) |
713 pt::Expression::NotEqual(_, _, _) |
714 pt::Expression::Less(_, _, _) |
715 pt::Expression::LessEqual(_, _, _) |
716 pt::Expression::More(_, _, _) |
717 pt::Expression::MoreEqual(_, _, _) |
718 pt::Expression::Not(_, _) => Some(Self::Builtin(DynSolType::Bool)),
719
720 pt::Expression::StringLiteral(_) => Some(Self::Builtin(DynSolType::String)),
722
723 pt::Expression::HexLiteral(_) => Some(Self::Builtin(DynSolType::Bytes)),
725
726 pt::Expression::FunctionCall(_, name, args) => {
728 Self::from_expression(name).map(|name| {
729 let args = args.iter().map(Self::from_expression).collect();
730 Self::Function(Box::new(name), args, vec![])
731 })
732 }
733 pt::Expression::NamedFunctionCall(_, name, args) => {
734 Self::from_expression(name).map(|name| {
735 let args = args.iter().map(|arg| Self::from_expression(&arg.expr)).collect();
736 Self::Function(Box::new(name), args, vec![])
737 })
738 }
739
740 pt::Expression::Delete(_, _) | pt::Expression::FunctionCallBlock(_, _, _) => None,
742 }
743 }
744
745 fn from_type(ty: &pt::Type) -> Option<Self> {
755 let ty = match ty {
756 pt::Type::Address | pt::Type::AddressPayable | pt::Type::Payable => {
757 Self::Builtin(DynSolType::Address)
758 }
759 pt::Type::Bool => Self::Builtin(DynSolType::Bool),
760 pt::Type::String => Self::Builtin(DynSolType::String),
761 pt::Type::Int(size) => Self::Builtin(DynSolType::Int(*size as usize)),
762 pt::Type::Uint(size) => Self::Builtin(DynSolType::Uint(*size as usize)),
763 pt::Type::Bytes(size) => Self::Builtin(DynSolType::FixedBytes(*size as usize)),
764 pt::Type::DynamicBytes => Self::Builtin(DynSolType::Bytes),
765 pt::Type::Mapping { value, .. } => Self::from_expression(value)?,
766 pt::Type::Function { params, returns, .. } => {
767 let params = map_parameters(params);
768 let returns = returns
769 .as_ref()
770 .map(|(returns, _)| map_parameters(returns))
771 .unwrap_or_default();
772 Self::Function(
773 Box::new(Self::Custom(vec!["__fn_type__".to_string()])),
774 params,
775 returns,
776 )
777 }
778 pt::Type::Rational => return None,
780 };
781 Some(ty)
782 }
783
784 fn map_special(self) -> Self {
788 if !matches!(self, Self::Function(_, _, _) | Self::Access(_, _) | Self::Custom(_)) {
789 return self;
790 }
791
792 let mut types = Vec::with_capacity(5);
793 let mut args = None;
794 self.recurse(&mut types, &mut args);
795
796 let len = types.len();
797 if len == 0 {
798 return self;
799 }
800
801 #[expect(clippy::single_match)]
803 match &self {
804 Self::Access(inner, access) => {
805 if let Some(ty) = inner.as_ref().clone().try_as_ethabi(None) {
806 let ty = Self::Builtin(ty);
808 match access.as_str() {
809 "length" if ty.is_dynamic() || ty.is_array() || ty.is_fixed_bytes() => {
810 return Self::Builtin(DynSolType::Uint(256));
811 }
812 "pop" if ty.is_dynamic_array() => return ty,
813 _ => {}
814 }
815 }
816 }
817 _ => {}
818 }
819
820 let this = {
821 let name = types.last().unwrap().as_str();
822 match len {
823 0 => unreachable!(),
824 1 => match name {
825 "gasleft" | "addmod" | "mulmod" => Some(DynSolType::Uint(256)),
826 "keccak256" | "sha256" | "blockhash" => Some(DynSolType::FixedBytes(32)),
827 "ripemd160" => Some(DynSolType::FixedBytes(20)),
828 "ecrecover" => Some(DynSolType::Address),
829 _ => None,
830 },
831 2 => {
832 let access = types.first().unwrap().as_str();
833 match name {
834 "block" => match access {
835 "coinbase" => Some(DynSolType::Address),
836 "timestamp" | "difficulty" | "prevrandao" | "number" | "gaslimit"
837 | "chainid" | "basefee" | "blobbasefee" => Some(DynSolType::Uint(256)),
838 _ => None,
839 },
840 "msg" => match access {
841 "sender" => Some(DynSolType::Address),
842 "gas" => Some(DynSolType::Uint(256)),
843 "value" => Some(DynSolType::Uint(256)),
844 "data" => Some(DynSolType::Bytes),
845 "sig" => Some(DynSolType::FixedBytes(4)),
846 _ => None,
847 },
848 "tx" => match access {
849 "origin" => Some(DynSolType::Address),
850 "gasprice" => Some(DynSolType::Uint(256)),
851 _ => None,
852 },
853 "abi" => match access {
854 "decode" => {
855 let mut args = args.unwrap();
859 let last = args.pop().unwrap();
860 match last {
861 Some(ty) => {
862 return match ty {
863 Self::Tuple(_) => ty,
864 ty => Self::Tuple(vec![Some(ty)]),
865 };
866 }
867 None => None,
868 }
869 }
870 s if s.starts_with("encode") => Some(DynSolType::Bytes),
871 _ => None,
872 },
873 "address" => match access {
874 "balance" => Some(DynSolType::Uint(256)),
875 "code" => Some(DynSolType::Bytes),
876 "codehash" => Some(DynSolType::FixedBytes(32)),
877 "send" => Some(DynSolType::Bool),
878 _ => None,
879 },
880 "type" => match access {
881 "name" => Some(DynSolType::String),
882 "creationCode" | "runtimeCode" => Some(DynSolType::Bytes),
883 "interfaceId" => Some(DynSolType::FixedBytes(4)),
884 "min" | "max" => Some(
885 (|| args?.pop()??.into_builtin())()
887 .unwrap_or(DynSolType::Uint(256)),
888 ),
889 _ => None,
890 },
891 "string" => match access {
892 "concat" => Some(DynSolType::String),
893 _ => None,
894 },
895 "bytes" => match access {
896 "concat" => Some(DynSolType::Bytes),
897 _ => None,
898 },
899 _ => None,
900 }
901 }
902 _ => None,
903 }
904 };
905
906 this.map(Self::Builtin).unwrap_or_else(|| match types.last().unwrap().as_str() {
907 "this" | "super" => Self::Custom(types),
908 _ => match self {
909 Self::Custom(_) | Self::Access(_, _) => Self::Custom(types),
910 Self::Function(_, _, _) => self,
911 _ => unreachable!(),
912 },
913 })
914 }
915
916 fn recurse(&self, types: &mut Vec<String>, args: &mut Option<Vec<Option<Self>>>) {
919 match self {
920 Self::Builtin(ty) => types.push(ty.to_string()),
921 Self::Custom(tys) => types.extend(tys.clone()),
922 Self::Access(expr, name) => {
923 types.push(name.clone());
924 expr.recurse(types, args);
925 }
926 Self::Function(fn_name, fn_args, _fn_ret) => {
927 if args.is_none() && !fn_args.is_empty() {
928 *args = Some(fn_args.clone());
929 }
930 fn_name.recurse(types, args);
931 }
932 _ => {}
933 }
934 }
935
936 fn infer_custom_type(
950 intermediate: &IntermediateOutput,
951 custom_type: &mut Vec<String>,
952 contract_name: Option<String>,
953 ) -> Result<Option<DynSolType>> {
954 if let Some("this") | Some("super") = custom_type.last().map(String::as_str) {
955 custom_type.pop();
956 }
957 if custom_type.is_empty() {
958 return Ok(None);
959 }
960
961 if let Some(contract_name) = contract_name {
964 let intermediate_contract = intermediate
965 .intermediate_contracts
966 .get(&contract_name)
967 .ok_or_else(|| eyre::eyre!("Could not find intermediate contract!"))?;
968
969 let cur_type = custom_type.last().unwrap();
970 if let Some(func) = intermediate_contract.function_definitions.get(cur_type) {
971 if let res @ Some(_) = func_members(func, custom_type) {
973 return Ok(res);
974 }
975
976 if func.returns.is_empty() {
979 eyre::bail!(
980 "This call expression does not return any values to inspect. Insert as statement."
981 )
982 }
983
984 let (_, param) = func.returns.first().unwrap();
986 let return_ty = ¶m.as_ref().unwrap().ty;
988
989 if let pt::Expression::Variable(ident) = return_ty {
993 custom_type.push(ident.name.clone());
994 return Self::infer_custom_type(intermediate, custom_type, Some(contract_name));
995 }
996
997 if let Some(pt::FunctionAttribute::Mutability(_mut)) = func
1001 .attributes
1002 .iter()
1003 .find(|attr| matches!(attr, pt::FunctionAttribute::Mutability(_)))
1004 {
1005 if let pt::Mutability::Payable(_) = _mut {
1006 eyre::bail!("This function mutates state. Insert as a statement.")
1007 }
1008 } else {
1009 eyre::bail!("This function mutates state. Insert as a statement.")
1010 }
1011
1012 Ok(Self::ethabi(return_ty, Some(intermediate)))
1013 } else if let Some(var) = intermediate_contract.variable_definitions.get(cur_type) {
1014 Self::infer_var_expr(&var.ty, Some(intermediate), custom_type)
1015 } else if let Some(strukt) = intermediate_contract.struct_definitions.get(cur_type) {
1016 let inner_types = strukt
1017 .fields
1018 .iter()
1019 .map(|var| {
1020 Self::ethabi(&var.ty, Some(intermediate))
1021 .ok_or_else(|| eyre::eyre!("Struct `{cur_type}` has invalid fields"))
1022 })
1023 .collect::<Result<Vec<_>>>()?;
1024 Ok(Some(DynSolType::Tuple(inner_types)))
1025 } else {
1026 eyre::bail!(
1027 "Could not find any definition in contract \"{contract_name}\" for type: {custom_type:?}"
1028 )
1029 }
1030 } else {
1031 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}