1use crate::prelude::{ChiselDispatcher, ChiselResult, ChiselRunner, SessionSource, SolidityHelper};
6use alloy_dyn_abi::{DynSolType, DynSolValue};
7use alloy_json_abi::EventParam;
8use alloy_primitives::{Address, B256, U256, hex};
9use eyre::{Result, WrapErr};
10use foundry_compilers::Artifact;
11use foundry_evm::{
12 backend::Backend,
13 core::evm::{BlockEnvFor, FoundryEvmNetwork, SpecFor, TxEnvFor},
14 decode::decode_console_logs,
15 executors::ExecutorBuilder,
16 inspectors::CheatsConfig,
17 traces::TraceMode,
18};
19use solar::{
20 ast::{ElementaryType, LitKind, StrKind, UnOpKind},
21 sema::{
22 hir::{Event, Expr, ExprKind, StmtKind},
23 ty::{Gcx, Ty, TyKind},
24 },
25};
26use std::ops::ControlFlow;
27use yansi::Paint;
28
29impl<FEN: FoundryEvmNetwork> SessionSource<FEN> {
31 pub async fn execute(&mut self) -> Result<ChiselResult> {
33 let output = self.build()?;
35
36 let (bytecode, final_pc) = output.enter(|output| -> Result<_> {
37 let contract = output
38 .repl_contract()
39 .ok_or_else(|| eyre::eyre!("failed to find REPL contract"))?;
40 trace!(?contract, "REPL contract");
41 let bytecode = contract
42 .get_bytecode_bytes()
43 .ok_or_else(|| eyre::eyre!("No bytecode found for `REPL` contract"))?;
44 Ok((bytecode.into_owned(), output.final_pc(contract)?))
45 })?;
46 let final_pc = final_pc.unwrap_or_default();
47 let mut runner = self.build_runner(final_pc).await?;
48 runner.run(bytecode)
49 }
50
51 pub async fn inspect(&self, input: &str) -> Result<(ControlFlow<()>, Option<String>)> {
63 let line = format!("bytes memory inspectoor = abi.encode({input});");
64 let mut source = match self.clone_with_new_line(line) {
65 Ok((source, _)) => source,
66 Err(err) => {
67 debug!(%err, "failed to build new source for inspection");
68 return Ok((ControlFlow::Continue(()), None));
69 }
70 };
71
72 let mut source_without_inspector = self.clone();
73
74 let (mut res, err) = match source.execute().await {
77 Ok(res) => (res, None),
78 Err(err) => {
79 debug!(?err, %input, "execution failed");
80 match source_without_inspector.execute().await {
81 Ok(res) => (res, Some(err)),
82 Err(_) => {
83 if self.config.foundry_config.verbosity >= 3 {
84 sh_err!("Could not inspect: {err}")?;
85 }
86 return Ok((ControlFlow::Continue(()), None));
87 }
88 }
89 }
90 };
91
92 if let Some(err) = err {
94 let output = source_without_inspector.build()?;
95
96 let formatted_event = output.enter(|output| {
97 let gcx = output.gcx();
98 output.get_event(input).map(|eid| format_event_definition(gcx, gcx.hir.event(eid)))
99 });
100 if let Some(formatted_event) = formatted_event {
101 return Ok((ControlFlow::Break(()), Some(formatted_event?)));
102 }
103
104 if self.config.foundry_config.verbosity >= 3 {
106 sh_err!("Failed eval: {err}")?;
107 }
108
109 debug!(%err, %input, "failed abi encode input");
110 return Ok((ControlFlow::Break(()), None));
111 }
112 drop(source_without_inspector);
113
114 let Some((stack, memory)) = &res.state else {
115 if let Ok(decoder) = ChiselDispatcher::decode_traces(&source.config, &mut res).await {
117 ChiselDispatcher::<FEN>::show_traces(&decoder, &mut res).await?;
118 }
119 let decoded_logs = decode_console_logs(&res.logs);
120 if !decoded_logs.is_empty() {
121 sh_println!("{}", "Logs:".green())?;
122 for log in decoded_logs {
123 sh_println!(" {log}")?;
124 }
125 }
126
127 return Err(eyre::eyre!("Failed to inspect expression"));
128 };
129
130 let generated_output = source.build()?;
133
134 let res_ty = generated_output.enter(|out| -> Option<(bool, DynSolType)> {
137 let gcx = out.gcx();
138
139 let block = out.run_func_body();
142 let last = block.last()?;
143 let StmtKind::DeclSingle(vid) = last.kind else { return None };
144 let var = gcx.hir.variable(vid);
145 let init = var.initializer?;
146 let ExprKind::Call(_callee, args, _) = &init.kind else { return None };
147 let inner_expr = args.exprs().next()?;
148
149 let ty = expr_to_dyn(gcx, inner_expr)?;
150 Some((should_continue(inner_expr), ty))
151 });
152
153 let Some((cont, ty)) = res_ty else {
154 return Ok((ControlFlow::Continue(()), None));
155 };
156
157 let data = (|| -> Option<_> {
160 let mut offset: usize = stack.last()?.try_into().ok()?;
161 debug!("inspect memory @ {offset}: {}", hex::encode(memory));
162 let mem_offset = memory.get(offset..offset + 32)?;
163 let len: usize = U256::try_from_be_slice(mem_offset)?.try_into().ok()?;
164 offset += 32;
165 memory.get(offset..offset + len)
166 })();
167 let Some(data) = data else {
168 eyre::bail!("Failed to inspect last expression: could not retrieve data from memory")
169 };
170 let token = ty.abi_decode(data).wrap_err("Could not decode inspected values")?;
171 let c = if cont { ControlFlow::Continue(()) } else { ControlFlow::Break(()) };
172 Ok((c, Some(format_token(token))))
173 }
174
175 async fn build_runner(&mut self, final_pc: usize) -> Result<ChiselRunner<FEN>> {
176 let (evm_env, tx_env, fork_block) =
177 self.config.evm_opts.env::<SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>().await?;
178
179 let backend = match self.config.backend.clone() {
180 Some(backend) => backend,
181 None => {
182 let fork = self.config.evm_opts.get_fork(
183 &self.config.foundry_config,
184 evm_env.cfg_env.chain_id,
185 fork_block,
186 );
187 let backend = Backend::spawn(fork)?;
188 self.config.backend = Some(backend.clone());
189 backend
190 }
191 };
192
193 let executor = ExecutorBuilder::default()
194 .inspectors(|stack| {
195 stack
196 .logs(self.config.foundry_config.live_logs)
197 .chisel_state(final_pc)
198 .trace_mode(TraceMode::Call)
199 .cheatcodes(
200 CheatsConfig::new(
201 &self.config.foundry_config,
202 self.config.evm_opts.clone(),
203 None,
204 None,
205 None,
206 false,
207 )
208 .into(),
209 )
210 })
211 .gas_limit(self.config.evm_opts.gas_limit())
212 .spec_id(self.config.foundry_config.evm_spec_id::<SpecFor<FEN>>())
213 .legacy_assertions(self.config.foundry_config.legacy_assertions)
214 .build(evm_env, tx_env, backend);
215
216 Ok(ChiselRunner::new(executor, U256::MAX, Address::ZERO, self.config.calldata.clone()))
217 }
218}
219
220fn format_token(token: DynSolValue) -> String {
223 match token {
224 DynSolValue::Address(a) => {
225 format!("Type: {}\n└ Data: {}", "address".red(), a.cyan())
226 }
227 DynSolValue::FixedBytes(b, byte_len) => {
228 format!(
229 "Type: {}\n└ Data: {}",
230 format!("bytes{byte_len}").red(),
231 hex::encode_prefixed(b).cyan()
232 )
233 }
234 DynSolValue::Int(i, bit_len) => {
235 format!(
236 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
237 format!("int{bit_len}").red(),
238 format!(
239 "0x{}",
240 format!("{i:x}")
241 .chars()
242 .skip(if i.is_negative() { 64 - bit_len / 4 } else { 0 })
243 .collect::<String>()
244 )
245 .cyan(),
246 hex::encode_prefixed(B256::from(i)).cyan(),
247 i.cyan()
248 )
249 }
250 DynSolValue::Uint(i, bit_len) => {
251 format!(
252 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
253 format!("uint{bit_len}").red(),
254 format!("0x{i:x}").cyan(),
255 hex::encode_prefixed(B256::from(i)).cyan(),
256 i.cyan()
257 )
258 }
259 DynSolValue::Bool(b) => {
260 format!("Type: {}\n└ Value: {}", "bool".red(), b.cyan())
261 }
262 DynSolValue::String(_) | DynSolValue::Bytes(_) => {
263 let hex = hex::encode(token.abi_encode());
264 let s = token.as_str();
265 format!(
266 "Type: {}\n{}├ Hex (Memory):\n├─ Length ({}): {}\n├─ Contents ({}): {}\n├ Hex (Tuple Encoded):\n├─ Pointer ({}): {}\n├─ Length ({}): {}\n└─ Contents ({}): {}",
267 if s.is_some() { "string" } else { "dynamic bytes" }.red(),
268 if let Some(s) = s {
269 format!("├ UTF-8: {}\n", s.cyan())
270 } else {
271 String::default()
272 },
273 "[0x00:0x20]".yellow(),
274 format!("0x{}", &hex[64..128]).cyan(),
275 "[0x20:..]".yellow(),
276 format!("0x{}", &hex[128..]).cyan(),
277 "[0x00:0x20]".yellow(),
278 format!("0x{}", &hex[..64]).cyan(),
279 "[0x20:0x40]".yellow(),
280 format!("0x{}", &hex[64..128]).cyan(),
281 "[0x40:..]".yellow(),
282 format!("0x{}", &hex[128..]).cyan(),
283 )
284 }
285 DynSolValue::FixedArray(tokens) | DynSolValue::Array(tokens) => {
286 let mut out = format!(
287 "{}({}) = {}",
288 "array".red(),
289 format!("{}", tokens.len()).yellow(),
290 '['.red()
291 );
292 for token in tokens {
293 out.push_str("\n ├ ");
294 out.push_str(&format_token(token).replace('\n', "\n "));
295 out.push('\n');
296 }
297 out.push_str(&']'.red().to_string());
298 out
299 }
300 DynSolValue::Tuple(tokens) => {
301 let displayed_types = tokens
302 .iter()
303 .map(|t| t.sol_type_name().unwrap_or_default())
304 .collect::<Vec<_>>()
305 .join(", ");
306 let mut out =
307 format!("{}({}) = {}", "tuple".red(), displayed_types.yellow(), '('.red());
308 for token in tokens {
309 out.push_str("\n ├ ");
310 out.push_str(&format_token(token).replace('\n', "\n "));
311 out.push('\n');
312 }
313 out.push_str(&')'.red().to_string());
314 out
315 }
316 _ => {
317 unimplemented!()
318 }
319 }
320}
321
322fn format_event_definition(gcx: Gcx<'_>, event: &Event<'_>) -> Result<String> {
325 let event_name = event.name.as_str().to_string();
326 let inputs = event
327 .parameters
328 .iter()
329 .map(|&pid| {
330 let var = gcx.hir.variable(pid);
331 let name =
332 var.name.map(|n| n.as_str().to_string()).unwrap_or_else(|| "<anonymous>".into());
333 let kind = solar_ty_to_dyn(gcx, gcx.type_of_item(pid.into()))
334 .ok_or_else(|| eyre::eyre!("Invalid type in event {event_name}"))?;
335 Ok(EventParam {
336 name,
337 ty: kind.to_string(),
338 components: vec![],
339 indexed: var.indexed,
340 internal_type: None,
341 })
342 })
343 .collect::<Result<Vec<_>>>()?;
344 let event = alloy_json_abi::Event { name: event_name, inputs, anonymous: event.anonymous };
345
346 Ok(format!(
347 "Type: {}\n├ Name: {}\n├ Signature: {:?}\n└ Selector: {:?}",
348 "event".red(),
349 SolidityHelper::new().highlight(&format!(
350 "{}({})",
351 event.name,
352 event
353 .inputs
354 .iter()
355 .map(|param| format!(
356 "{}{}{}",
357 param.ty,
358 if param.indexed { " indexed" } else { "" },
359 if param.name.is_empty() {
360 String::default()
361 } else {
362 format!(" {}", param.name)
363 },
364 ))
365 .collect::<Vec<_>>()
366 .join(", ")
367 )),
368 event.signature().cyan(),
369 event.selector().cyan(),
370 ))
371}
372
373fn expr_to_dyn(gcx: Gcx<'_>, expr: &Expr<'_>) -> Option<DynSolType> {
375 gcx.type_of_expr(expr.id).and_then(|ty| solar_expr_ty_to_dyn(gcx, ty, expr))
376}
377
378#[inline]
380fn should_continue(expr: &Expr<'_>) -> bool {
381 match &expr.kind {
382 ExprKind::Assign(_, _, _) => true,
384 ExprKind::Unary(op, _) => matches!(
386 op.kind,
387 UnOpKind::PreInc | UnOpKind::PreDec | UnOpKind::PostInc | UnOpKind::PostDec
388 ),
389 ExprKind::Call(callee, _, _) => match &callee.kind {
391 ExprKind::Member(_, ident) => ident.as_str() == "pop",
392 _ => false,
393 },
394 _ => false,
395 }
396}
397
398const fn elementary_to_dyn(et: ElementaryType) -> Option<DynSolType> {
400 Some(match et {
401 ElementaryType::Address(_) => DynSolType::Address,
402 ElementaryType::Bool => DynSolType::Bool,
403 ElementaryType::String => DynSolType::String,
404 ElementaryType::Bytes => DynSolType::Bytes,
405 ElementaryType::Int(size) => DynSolType::Int(size.bits() as usize),
406 ElementaryType::UInt(size) => DynSolType::Uint(size.bits() as usize),
407 ElementaryType::FixedBytes(size) => DynSolType::FixedBytes(size.bytes() as usize),
408 ElementaryType::Fixed(_, _) | ElementaryType::UFixed(_, _) => return None,
410 })
411}
412
413fn solar_expr_ty_to_dyn<'gcx>(gcx: Gcx<'gcx>, ty: Ty<'gcx>, expr: &Expr<'_>) -> Option<DynSolType> {
415 let expr = expr.peel_parens();
419 if matches!(expr.kind, ExprKind::Lit(lit) if matches!(lit.kind, LitKind::Str(StrKind::Hex, ..)))
420 {
421 return Some(DynSolType::Bytes);
422 }
423
424 solar_ty_to_dyn(gcx, ty)
425}
426
427fn solar_ty_to_dyn<'gcx>(gcx: Gcx<'gcx>, ty: Ty<'gcx>) -> Option<DynSolType> {
428 match ty.kind {
429 TyKind::Elementary(et) => elementary_to_dyn(et),
430 TyKind::Ref(inner, _) => solar_ty_to_dyn(gcx, inner),
431 TyKind::Array(elem, n) => {
432 let inner = solar_ty_to_dyn(gcx, elem)?;
433 let size: usize = n.try_into().ok()?;
434 Some(DynSolType::FixedArray(Box::new(inner), size))
435 }
436 TyKind::DynArray(elem) => {
437 let inner = solar_ty_to_dyn(gcx, elem)?;
438 Some(DynSolType::Array(Box::new(inner)))
439 }
440 TyKind::Slice(array) => solar_ty_to_dyn(gcx, array),
441 TyKind::Tuple(tys) => {
442 Some(DynSolType::Tuple(tys.iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect()))
443 }
444 TyKind::Mapping(_, _) => None,
445 TyKind::Struct(sid) => Some(DynSolType::Tuple(
446 gcx.struct_field_types(sid).iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect(),
447 )),
448 TyKind::Enum(_) => Some(DynSolType::Uint(8)),
449 TyKind::Udvt(inner, _) => solar_ty_to_dyn(gcx, inner),
450 TyKind::Contract(_) => Some(DynSolType::Address),
451 TyKind::Fn(f) => match f.returns.len() {
456 0 => None,
457 1 => solar_ty_to_dyn(gcx, f.returns[0]),
458 _ => Some(DynSolType::Tuple(
459 f.returns.iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect(),
460 )),
461 },
462 TyKind::Type(inner) => solar_ty_to_dyn(gcx, inner),
463 TyKind::Meta(inner) => solar_ty_to_dyn(gcx, inner),
464 TyKind::IntLiteral(neg, size, _) => {
465 let bits = (size.bits() as usize).max(8);
466 let bits = bits.div_ceil(8) * 8;
468 let bits = bits.min(256);
469 if neg {
470 Some(DynSolType::Int(bits.max(8)))
471 } else {
472 Some(DynSolType::Uint(bits.max(8)))
473 }
474 }
475 TyKind::StringLiteral(valid_utf8, _) => {
476 if valid_utf8 {
477 Some(DynSolType::String)
478 } else {
479 Some(DynSolType::Bytes)
480 }
481 }
482 TyKind::Module(_)
483 | TyKind::BuiltinModule(_)
484 | TyKind::Error(_, _)
485 | TyKind::Event(_, _)
486 | TyKind::Err(_) => None,
487 _ => None,
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494 use foundry_compilers::{error::SolcError, solc::Solc};
495 use foundry_evm::core::evm::EthEvmNetwork;
496 use solar::sema::Compiler;
497 use std::sync::Mutex;
498
499 type TestSessionSource = SessionSource<EthEvmNetwork>;
500
501 #[test]
502 fn test_expressions() {
503 static EXPRESSIONS: &[(&str, DynSolType)] = {
504 use DynSolType::*;
505 &[
506 ("1 seconds", Uint(8)),
509 ("1 minutes", Uint(8)),
510 ("1 hours", Uint(16)),
511 ("1 days", Uint(24)),
512 ("1 weeks", Uint(24)),
513 ("1 wei", Uint(8)),
514 ("1 gwei", Uint(32)),
515 ("1 ether", Uint(64)),
516 ("-1 seconds", Int(8)),
518 ("-1 minutes", Int(8)),
519 ("-1 hours", Int(16)),
520 ("-1 days", Int(24)),
521 ("-1 weeks", Int(24)),
522 ("-1 wei", Int(8)),
523 ("-1 gwei", Int(32)),
524 ("-1 ether", Int(64)),
525 ("true ? 1 : 0", Uint(8)),
527 ("1 + 1", Uint(8)),
533 ("1 - 1", Uint(8)),
534 ("1 * 1", Uint(8)),
535 ("1 / 1", Uint(8)),
536 ("1 % 1", Uint(8)),
537 ("1 ** 1", Uint(8)),
538 ("1 | 1", Uint(8)),
539 ("1 & 1", Uint(8)),
540 ("1 ^ 1", Uint(8)),
541 ("1 >> 1", Uint(8)),
542 ("1 << 1", Uint(8)),
543 ("int(1) + 1", Int(256)),
545 ("int(1) - 1", Int(256)),
546 ("int(1) * 1", Int(256)),
547 ("int(1) / 1", Int(256)),
548 ("1 + int(1)", Int(256)),
549 ("1 - int(1)", Int(256)),
550 ("1 * int(1)", Int(256)),
551 ("1 / int(1)", Int(256)),
552 ("uint256 a; a--", Uint(256)),
556 ("uint256 a; --a", Uint(256)),
557 ("uint256 a; a++", Uint(256)),
558 ("uint256 a; ++a", Uint(256)),
559 ("uint256 a; a = 1", Uint(256)),
560 ("uint256 a; a += 1", Uint(256)),
561 ("uint256 a; a -= 1", Uint(256)),
562 ("uint256 a; a *= 1", Uint(256)),
563 ("uint256 a; a /= 1", Uint(256)),
564 ("uint256 a; a %= 1", Uint(256)),
565 ("uint256 a; a &= 1", Uint(256)),
566 ("uint256 a; a |= 1", Uint(256)),
567 ("uint256 a; a ^= 1", Uint(256)),
568 ("uint256 a; a <<= 1", Uint(256)),
569 ("uint256 a; a >>= 1", Uint(256)),
570 ("true && true", Bool),
574 ("true || true", Bool),
575 ("true == true", Bool),
576 ("true != true", Bool),
577 ("!true", Bool),
578 ]
580 };
581
582 let source = &mut source();
583
584 let array_expressions: &[(&str, DynSolType)] = &[
585 ("[1, 2, 3]", fixed_array(DynSolType::Uint(8), 3)),
586 ("[uint8(1), 2, 3]", fixed_array(DynSolType::Uint(8), 3)),
587 ("[int8(1), 2, 3]", fixed_array(DynSolType::Int(8), 3)),
588 ("new uint256[](3)", array(DynSolType::Uint(256))),
589 ("uint256[] memory a = new uint256[](3);\na[0]", DynSolType::Uint(256)),
590 ];
591 generic_type_test(source, array_expressions);
592 generic_type_test(source, EXPRESSIONS);
593 }
594
595 #[test]
596 fn test_types() {
597 static TYPES: &[(&str, DynSolType)] = {
598 use DynSolType::*;
599 &[
600 ("bool", Bool),
602 ("true", Bool),
603 ("false", Bool),
604 ("uint", Uint(256)),
608 ("uint(1)", Uint(256)),
609 ("1", Uint(8)),
610 ("0x01", Uint(8)),
611 ("int", Int(256)),
612 ("int(1)", Int(256)),
613 ("int(-1)", Int(256)),
614 ("-1", Int(8)),
615 ("-0x01", Int(8)),
616 ("address", Address),
620 ("address(0)", Address),
621 ("0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990", Address),
622 ("payable(0)", Address),
623 ("payable(address(0))", Address),
624 ("string", String),
628 ("string(\"hello world\")", String),
629 ("\"hello world\"", String),
630 ("unicode\"hello world 😀\"", String),
631 ("bytes", Bytes),
635 ("bytes(\"hello world\")", Bytes),
636 ("bytes(unicode\"hello world 😀\")", Bytes),
637 ("hex\"68656c6c6f20776f726c64\"", Bytes),
638 ]
640 };
641
642 let mut types: Vec<(String, DynSolType)> = Vec::with_capacity(96 + 32 + 100);
643 for (n, b) in (8..=256).step_by(8).zip(1..=32) {
644 types.push((format!("uint{n}(0)"), DynSolType::Uint(n)));
645 types.push((format!("int{n}(0)"), DynSolType::Int(n)));
646 types.push((format!("bytes{b}(0x00)"), DynSolType::FixedBytes(b)));
647 }
648
649 for n in 1..=32 {
650 types.push((
651 format!("uint256[{n}]"),
652 DynSolType::FixedArray(Box::new(DynSolType::Uint(256)), n),
653 ));
654 }
655
656 generic_type_test(&mut source(), TYPES);
657 generic_type_test(&mut source(), &types);
658 }
659
660 #[test]
661 fn test_global_vars() {
662 init_tracing();
663
664 let global_variables = {
666 use DynSolType::*;
667 &[
668 ("abi.decode(bytes(\"\"), (uint8[13]))", FixedArray(Box::new(Uint(8)), 13)),
670 ("abi.decode(bytes(\"\"), (address, bytes))", Tuple(vec![Address, Bytes])),
671 ("abi.decode(bytes(\"\"), (uint112, uint48))", Tuple(vec![Uint(112), Uint(48)])),
672 ("abi.encode(1, 2)", Bytes),
673 ("abi.encodePacked(uint256(1), uint256(2))", Bytes),
674 ("abi.encodeWithSelector(bytes4(0), 1, 2)", Bytes),
675 ("abi.encodeWithSignature(\"f(uint256)\", 1)", Bytes),
676 ("bytes.concat()", Bytes),
680 ("bytes.concat(bytes(\"\"))", Bytes),
681 ("bytes.concat(bytes(\"\"), bytes(\"\"))", Bytes),
682 ("string.concat()", String),
683 ("string.concat(\"\")", String),
684 ("string.concat(\"\", \"\")", String),
685 ("block.basefee", Uint(256)),
689 ("block.chainid", Uint(256)),
690 ("block.coinbase", Address),
691 ("block.difficulty", Uint(256)),
692 ("block.gaslimit", Uint(256)),
693 ("block.number", Uint(256)),
694 ("block.timestamp", Uint(256)),
695 ("gasleft()", Uint(256)),
699 ("msg.data", Bytes),
700 ("msg.sender", Address),
701 ("msg.sig", FixedBytes(4)),
702 ("msg.value", Uint(256)),
703 ("tx.gasprice", Uint(256)),
704 ("tx.origin", Address),
705 ("blockhash(0)", FixedBytes(32)),
716 ("keccak256(bytes(\"\"))", FixedBytes(32)),
717 ("sha256(bytes(\"\"))", FixedBytes(32)),
718 ("ripemd160(bytes(\"\"))", FixedBytes(20)),
719 ("ecrecover(bytes32(0), 0, bytes32(0), bytes32(0))", Address),
720 ("addmod(1, 2, 3)", Uint(256)),
721 ("mulmod(1, 2, 3)", Uint(256)),
722 ("address(0)", Address),
726 ("address(this)", Address),
727 ("address(0).balance", Uint(256)),
730 ("address(0).code", Bytes),
731 ("address(0).codehash", FixedBytes(32)),
732 ("payable(address(0)).send(1)", Bool),
733 ("type(C).name", String),
738 ("type(C).creationCode", Bytes),
739 ("type(C).runtimeCode", Bytes),
740 ("type(I).interfaceId", FixedBytes(4)),
741 ("type(uint256).min", Uint(256)),
742 ("type(int128).min", Int(128)),
743 ("type(int256).min", Int(256)),
744 ("type(uint256).max", Uint(256)),
745 ("type(int128).max", Int(128)),
746 ("type(int256).max", Int(256)),
747 ("type(Enum1).min", Uint(8)),
748 ("type(Enum1).max", Uint(8)),
749 ("this.run.address", Address),
751 ("this.run.selector", FixedBytes(4)),
752 ]
753 };
754
755 generic_type_test(&mut source(), global_variables);
756 }
757
758 #[track_caller]
759 fn source() -> TestSessionSource {
760 static PRE_INSTALL_SOLC_LOCK: Mutex<bool> = Mutex::new(false);
762
763 let version = "0.8.20";
766 for _ in 0..3 {
767 let mut is_preinstalled = PRE_INSTALL_SOLC_LOCK.lock().unwrap();
768 if !*is_preinstalled {
769 let solc = Solc::find_or_install(&version.parse().unwrap())
770 .map(|solc| (solc.version.clone(), solc));
771 match solc {
772 Ok((v, solc)) => {
773 let _ = sh_println!("found installed Solc v{v} @ {}", solc.solc.display());
775 break;
776 }
777 Err(e) => {
778 let _ = sh_err!("error while trying to re-install Solc v{version}: {e}");
780 let solc = Solc::blocking_install(&version.parse().unwrap());
781 if solc.map_err(SolcError::from).is_ok() {
782 *is_preinstalled = true;
783 break;
784 }
785 }
786 }
787 }
788 }
789
790 SessionSource::new(Default::default()).unwrap()
791 }
792
793 fn array(ty: DynSolType) -> DynSolType {
794 DynSolType::Array(Box::new(ty))
795 }
796
797 fn fixed_array(ty: DynSolType, len: usize) -> DynSolType {
798 DynSolType::FixedArray(Box::new(ty), len)
799 }
800
801 fn get_type_ethabi(s: &mut TestSessionSource, input: &str, clear: bool) -> Option<DynSolType> {
808 if clear {
809 s.clear();
810 }
811
812 *s = s.clone_with_new_line("enum Enum1 { A }".into()).unwrap().0;
814 *s = s.clone_with_new_line("contract C {}".into()).unwrap().0;
815 *s = s.clone_with_new_line("interface I {}".into()).unwrap().0;
816
817 let input = format!("{};", input.trim_end().trim_end_matches(';'));
818 let (new_source, _) = s.clone_with_new_line(input).unwrap();
819 *s = new_source.clone();
820
821 let src = new_source.to_repl_source();
822 let mut opts = solar::interface::config::Opts::default();
823 opts.unstable.typeck = true;
824 let sess = solar::interface::Session::builder()
825 .opts(opts)
826 .with_buffer_emitter(Default::default())
827 .build();
828 let mut compiler = Compiler::new(sess);
829
830 compiler.enter_mut(|c| -> Option<DynSolType> {
831 let analyzed = {
833 let mut pcx = c.parse();
834 let file = c
835 .sess()
836 .source_map()
837 .new_source_file(
838 std::path::PathBuf::from(new_source.file_name.clone()),
839 src.clone(),
840 )
841 .ok()?;
842 pcx.add_file(file);
843 pcx.parse();
844 matches!(c.lower_asts(), Ok(ControlFlow::Continue(())))
845 && matches!(c.analysis(), Ok(ControlFlow::Continue(())))
846 };
847 if !analyzed {
848 return None;
849 }
850
851 let gcx = c.gcx();
853 let hir = &gcx.hir;
854 let repl = hir.contracts().find(|c| c.name.as_str() == "REPL")?;
855 let run_fid = repl
856 .functions()
857 .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str()) == Some("run"))?;
858 let body = hir.function(run_fid).body?;
859 let last = body.last()?;
860 let expr = match last.kind {
861 StmtKind::Expr(e) => e,
862 _ => return None,
863 };
864 expr_to_dyn(gcx, expr)
865 })
866 }
867
868 fn generic_type_test<'a, T, I>(s: &mut TestSessionSource, input: I)
869 where
870 T: AsRef<str> + std::fmt::Display + 'a,
871 I: IntoIterator<Item = &'a (T, DynSolType)> + 'a,
872 {
873 let mut failures = Vec::new();
874 for (input, expected) in input {
875 let input = input.as_ref();
876 let ty = get_type_ethabi(s, input, true);
877 if ty.as_ref() != Some(expected) {
878 failures.push(format!("{input}: got {ty:?}, expected {expected:?}"));
879 }
880 }
881 assert!(failures.is_empty(), "\n{}", failures.join("\n"));
882 }
883
884 fn init_tracing() {
885 let _ = tracing_subscriber::FmtSubscriber::builder()
886 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
887 .try_init();
888 }
889}