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