1use std::{collections::HashSet, sync::Arc};
50
51use alloy_dyn_abi::DynSolValue;
52use alloy_primitives::U256;
53use edb_common::types::{parse_callable_abi_entries, CallableAbiEntry, TraceEntry};
54use eyre::{bail, eyre, Result};
55use revm::{database::CacheDB, Database, DatabaseCommit, DatabaseRef};
56use tracing::debug;
57
58use super::*;
59use crate::{EngineContext, Snapshot, SnapshotDetail};
60
61static EDB_EVAL_PLACEHOLDER_MAGIC: &str = "edb_eval_placeholder";
62
63fn from_abi_info(entry: &CallableAbiEntry) -> Option<DynSolValue> {
64 if entry.is_function() {
65 return None; }
67 let magic = DynSolValue::String(EDB_EVAL_PLACEHOLDER_MAGIC.to_string());
68 let serial_abi = DynSolValue::String(serde_json::to_string(entry).ok()?);
69
70 Some(DynSolValue::Tuple(vec![magic, serial_abi]))
71}
72
73fn into_abi_info(value: &DynSolValue) -> Option<CallableAbiEntry> {
74 if let DynSolValue::Tuple(elements) = value {
75 if elements.len() == 2 {
76 if let DynSolValue::String(magic) = &elements[0] {
77 if magic == EDB_EVAL_PLACEHOLDER_MAGIC {
78 if let DynSolValue::String(serial_abi) = &elements[1] {
79 return serde_json::from_str(serial_abi).ok();
80 }
81 }
82 }
83 }
84 }
85 None
86}
87
88pub struct EdbHandler<DB>
90where
91 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
92 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
93 <DB as Database>::Error: Clone + Send + Sync,
94{
95 context: Arc<EngineContext<DB>>,
96}
97
98impl<DB> EdbHandler<DB>
99where
100 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
101 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
102 <DB as Database>::Error: Clone + Send + Sync,
103{
104 pub fn new(context: Arc<EngineContext<DB>>) -> Self {
112 Self { context }
113 }
114
115 pub fn create_handlers(context: Arc<EngineContext<DB>>) -> EvaluatorHandlers {
117 let handler = Arc::new(Self::new(context));
118
119 EvaluatorHandlers::new()
120 .with_variable_handler(Box::new(EdbVariableHandler(handler.clone())))
121 .with_mapping_array_handler(Box::new(EdbMappingArrayHandler(handler.clone())))
122 .with_function_call_handler(Box::new(EdbFunctionCallHandler(handler.clone())))
123 .with_member_access_handler(Box::new(EdbMemberAccessHandler(handler.clone())))
124 .with_msg_handler(Box::new(EdbMsgHandler(handler.clone())))
125 .with_tx_handler(Box::new(EdbTxHandler(handler.clone())))
126 .with_block_handler(Box::new(EdbBlockHandler(handler.clone())))
127 .with_validation_handler(Box::new(EdbValidationHandler(handler)))
128 }
129}
130
131#[derive(Clone)]
138pub struct EdbVariableHandler<DB>(Arc<EdbHandler<DB>>)
139where
140 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
141 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
142 <DB as Database>::Error: Clone + Send + Sync;
143
144#[derive(Clone)]
149pub struct EdbMappingArrayHandler<DB>(Arc<EdbHandler<DB>>)
150where
151 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
152 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
153 <DB as Database>::Error: Clone + Send + Sync;
154
155#[derive(Clone)]
163pub struct EdbFunctionCallHandler<DB>(Arc<EdbHandler<DB>>)
164where
165 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
166 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
167 <DB as Database>::Error: Clone + Send + Sync;
168
169#[derive(Clone)]
174pub struct EdbMemberAccessHandler<DB>(Arc<EdbHandler<DB>>)
175where
176 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
177 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
178 <DB as Database>::Error: Clone + Send + Sync;
179
180#[derive(Clone)]
185pub struct EdbMsgHandler<DB>(Arc<EdbHandler<DB>>)
186where
187 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
188 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
189 <DB as Database>::Error: Clone + Send + Sync;
190
191#[derive(Clone)]
196pub struct EdbTxHandler<DB>(Arc<EdbHandler<DB>>)
197where
198 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
199 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
200 <DB as Database>::Error: Clone + Send + Sync;
201
202#[derive(Clone)]
207pub struct EdbBlockHandler<DB>(Arc<EdbHandler<DB>>)
208where
209 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
210 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
211 <DB as Database>::Error: Clone + Send + Sync;
212
213#[allow(dead_code)]
218#[derive(Clone)]
219pub struct EdbValidationHandler<DB>(Arc<EdbHandler<DB>>)
220where
221 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
222 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
223 <DB as Database>::Error: Clone + Send + Sync;
224
225impl<DB> VariableHandler for EdbVariableHandler<DB>
227where
228 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
229 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
230 <DB as Database>::Error: Clone + Send + Sync,
231{
232 fn get_variable_value(&self, name: &str, snapshot_id: usize) -> Result<DynSolValue> {
233 let snapshot = &self
234 .0
235 .context
236 .snapshots
237 .get(snapshot_id)
238 .ok_or_else(|| {
239 eyre::eyre!(
240 "Snapshot ID {} not found in EdbHandler::get_variable_value",
241 snapshot_id
242 )
243 })?
244 .1;
245
246 if name == "this" {
247 return Ok(DynSolValue::Address(snapshot.target_address()));
248 }
249
250 let SnapshotDetail::Hook(detail) = &snapshot.detail() else {
251 bail!("Cannot get variable value from opcode snapshot (except this)");
252 };
253
254 if let Some(Some(value)) =
256 detail.locals.get(name).or_else(|| detail.state_variables.get(name))
257 {
258 return Ok((**value).clone().into());
259 }
260
261 let bytecode_address = snapshot.bytecode_address();
263 let Some(contract) = self
264 .0
265 .context
266 .recompiled_artifacts
267 .get(&bytecode_address)
268 .and_then(|art| art.contract())
269 else {
270 bail!("No contract found for bytecode address {:?}", bytecode_address);
271 };
272
273 for entry in parse_callable_abi_entries(contract) {
274 if entry.name == name && entry.is_state_variable() {
275 if let Some(value) = from_abi_info(&entry) {
276 return Ok(value);
277 }
278 }
279 }
280
281 bail!("No value found for name='{}', snapshot_id={}", name, snapshot_id)
282 }
283}
284
285impl<DB> MappingArrayHandler for EdbMappingArrayHandler<DB>
286where
287 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
288 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
289 <DB as Database>::Error: Clone + Send + Sync,
290{
291 fn get_mapping_or_array_value(
292 &self,
293 root: DynSolValue,
294 indices: Vec<DynSolValue>,
295 snapshot_id: usize,
296 ) -> Result<DynSolValue> {
297 if let Some(abi_info) = into_abi_info(&root) {
298 let (_, info) = self.0.context.snapshots.get(snapshot_id).ok_or_else(|| {
299 eyre::eyre!(
300 "Snapshot ID {} not found in EdbHandler::get_mapping_or_array_value",
301 snapshot_id
302 )
303 })?;
304 let to = info.target_address();
305
306 self.0.context.call_in_derived_evm(snapshot_id, to, &abi_info.abi, &indices, None)
307 } else {
308 if indices.is_empty() {
310 return Ok(root);
311 }
312
313 let (first_index, remaining_indices) = indices.split_first().unwrap();
314
315 let next_value = match &root {
316 DynSolValue::Tuple(elements) => {
317 match first_index {
319 DynSolValue::Uint(idx, _) => {
320 let idx = idx.to::<usize>();
321 if idx >= elements.len() {
322 bail!(
323 "Index {} out of bounds for tuple with {} elements",
324 idx,
325 elements.len()
326 );
327 }
328 elements[idx].clone()
329 }
330 _ => bail!(
331 "Invalid index type for tuple access: expected uint, got {:?}",
332 first_index
333 ),
334 }
335 }
336 DynSolValue::Array(elements) => {
337 match first_index {
339 DynSolValue::Uint(idx, _) => {
340 let idx = idx.to::<usize>();
341 if idx >= elements.len() {
342 bail!(
343 "Index {} out of bounds for array with {} elements",
344 idx,
345 elements.len()
346 );
347 }
348 elements[idx].clone()
349 }
350 _ => bail!(
351 "Invalid index type for array access: expected uint, got {:?}",
352 first_index
353 ),
354 }
355 }
356 DynSolValue::FixedArray(elements) => {
357 match first_index {
359 DynSolValue::Uint(idx, _) => {
360 let idx = idx.to::<usize>();
361 if idx >= elements.len() {
362 bail!(
363 "Index {} out of bounds for fixed array with {} elements",
364 idx,
365 elements.len()
366 );
367 }
368 elements[idx].clone()
369 }
370 _ => bail!(
371 "Invalid index type for fixed array access: expected uint, got {:?}",
372 first_index
373 ),
374 }
375 }
376 _ => {
377 bail!(
378 "Cannot index into value of type {:?} with index {:?}",
379 root,
380 first_index
381 );
382 }
383 };
384
385 self.get_mapping_or_array_value(next_value, remaining_indices.to_vec(), snapshot_id)
387 }
388 }
389}
390
391fn edb_keccak256(bytes: DynSolValue) -> Result<DynSolValue> {
392 match bytes {
393 DynSolValue::Bytes(b) => {
394 let hash = alloy_primitives::keccak256(&b);
395 Ok(DynSolValue::Bytes(hash.to_vec()))
396 }
397 DynSolValue::FixedBytes(b, n_bytes) => {
398 if n_bytes > 32 {
400 bail!("FixedBytes length {} exceeds maximum of 32", n_bytes);
401 } else {
402 let mut v = vec![0u8; n_bytes];
403 v.copy_from_slice(&b[..n_bytes]);
404 let hash = alloy_primitives::keccak256(&v);
405 Ok(DynSolValue::Bytes(hash.to_vec()))
406 }
407 }
408 _ => {
409 bail!("Invalid argument to edb_keccak256: expected bytes, got {:?}", bytes);
410 }
411 }
412}
413
414fn edb_sload<DB>(
415 snapshot: &Snapshot<DB>,
416 address: &DynSolValue,
417 slot: &DynSolValue,
418) -> Result<DynSolValue>
419where
420 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
421 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
422 <DB as Database>::Error: Clone + Send + Sync,
423{
424 match (address, slot) {
425 (DynSolValue::Address(address), DynSolValue::Uint(slot, ..)) => {
426 let db = snapshot.db();
427 let cached_storage = db
428 .cache
429 .accounts
430 .get(address)
431 .map(|acc| &acc.storage)
432 .ok_or(eyre!("Account {:?} not found in edb_sload", address))?;
433 let value = cached_storage.get(slot).cloned().unwrap_or_default();
434
435 Ok(DynSolValue::Uint(value, 256))
436 }
437 _ => {
438 bail!(
439 "Invalid arguments to edb_sload: expected (address, u256), got ({:?}, {:?})",
440 address,
441 slot
442 );
443 }
444 }
445}
446
447fn edb_stack<DB>(snapshot: &Snapshot<DB>, index: &DynSolValue) -> Result<DynSolValue>
448where
449 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
450 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
451 <DB as Database>::Error: Clone + Send + Sync,
452{
453 let SnapshotDetail::Opcode(detail) = snapshot.detail() else {
454 bail!("edb_stack can only be called from an opcode snapshot");
455 };
456
457 match index {
458 DynSolValue::Uint(idx, ..) => {
459 let idx = idx.to::<usize>();
460 let stack = &detail.stack;
461 if idx >= stack.len() {
462 bail!("Index {} out of bounds for stack with {} elements", idx, stack.len());
463 }
464 Ok(stack[stack.len() - 1 - idx].into())
465 }
466 _ => {
467 bail!("Invalid argument to edb_stack: expected u256, got {:?}", index);
468 }
469 }
470}
471
472fn edb_tsload<DB>(
473 snapshot: &Snapshot<DB>,
474 address: &DynSolValue,
475 slot: &DynSolValue,
476) -> Result<DynSolValue>
477where
478 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
479 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
480 <DB as Database>::Error: Clone + Send + Sync,
481{
482 let SnapshotDetail::Opcode(detail) = snapshot.detail() else {
483 bail!("edb_tsload can only be called from an opcode snapshot");
484 };
485
486 match (address, slot) {
487 (DynSolValue::Address(addr), DynSolValue::Uint(idx, ..)) => {
488 let value = detail.transient_storage.get(&(*addr, *idx)).cloned().unwrap_or_default();
489 Ok(DynSolValue::Uint(value, 256))
490 }
491 _ => {
492 bail!(
493 "Invalid arguments to edb_tsload: expected (address, u256), got ({:?}, {:?})",
494 address,
495 slot
496 );
497 }
498 }
499}
500
501fn edb_memory<DB>(
502 snapshot: &Snapshot<DB>,
503 offset: &DynSolValue,
504 size: &DynSolValue,
505) -> Result<DynSolValue>
506where
507 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
508 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
509 <DB as Database>::Error: Clone + Send + Sync,
510{
511 let SnapshotDetail::Opcode(detail) = snapshot.detail() else {
512 bail!("edb_memory can only be called from an opcode snapshot");
513 };
514
515 match (offset, size) {
516 (DynSolValue::Uint(off, ..), DynSolValue::Uint(sz, ..)) => {
517 let off = off.to::<usize>();
518 let sz = sz.to::<usize>();
519 let memory = &detail.memory;
520 if off + sz > memory.len() {
521 bail!(
522 "edb_memory out of bounds: offset {} + size {} > memory length {}",
523 off,
524 sz,
525 memory.len()
526 );
527 }
528 let slice = &memory[off..off + sz];
529 Ok(DynSolValue::Bytes(slice.to_vec()))
530 }
531 _ => {
532 bail!(
533 "Invalid arguments to edb_memory: expected (u256, u256), got ({:?}, {:?})",
534 offset,
535 size
536 );
537 }
538 }
539}
540
541fn edb_calldata(
542 entry: &TraceEntry,
543 offset: &DynSolValue,
544 size: &DynSolValue,
545) -> Result<DynSolValue> {
546 match (offset, size) {
547 (DynSolValue::Uint(off, ..), DynSolValue::Uint(sz, ..)) => {
548 let off = off.to::<usize>();
549 let sz = sz.to::<usize>();
550 let calldata = &entry.input;
551 if off + sz > calldata.len() {
552 bail!(
553 "edb_calldata out of bounds: offset {} + size {} > calldata length {}",
554 off,
555 sz,
556 calldata.len()
557 );
558 }
559 let slice = &calldata[off..off + sz];
560 Ok(DynSolValue::Bytes(slice.to_vec()))
561 }
562 _ => {
563 bail!(
564 "Invalid arguments to edb_calldata: expected (u256, u256), got ({:?}, {:?})",
565 offset,
566 size
567 );
568 }
569 }
570}
571
572fn edb_help() -> Result<DynSolValue> {
573 let help_text = r#"EDB Expression Evaluator Help
574
575OVERVIEW:
576Real-time expression evaluation over debug modes. Evaluate Solidity-like expressions
577against contract state, variables, and blockchain context at any execution point.
578
579VARIABLES:
580• this - Current contract address
581• Local variables - Access by name (e.g., balance, owner)
582• State variables - Access by name (e.g., totalSupply, users)
583• Mapping/array variables - Access with bracket notation (e.g., balances[addr])
584
585BLOCKCHAIN CONTEXT:
586• msg.sender - Transaction sender address
587• msg.value - Transaction value in wei
588• tx.origin - Original transaction sender
589• block.number - Current block number
590• block.timestamp - Current block timestamp
591
592PRE-COMPILED FUNCTIONS (EDB-VERSION):
593• edb_sload(address, slot) - Read storage slot from address
594• edb_tsload(address, slot) - Read transient storage (opcode mode only)
595• edb_stack(index) - Read EVM stack value (opcode mode only)
596• edb_memory(offset, size) - Read EVM memory (opcode mode only)
597• edb_calldata(offset, size) - Read call data slice
598• keccak256(bytes) - Compute keccak256 hash
599• edb_help() - Show this help
600
601CONTRACT FUNCTIONS:
602• Call any contract function by name with arguments
603• Access state variables and view functions
604• Member access on addresses (e.g., addr.balanceOf(user))
605• Cross-contract calls (e.g., token.transfer(to, amount))
606• State variable access on different addresses (e.g., addr.owner)
607
608OPERATORS:
609• Arithmetic: +, -, *, /, %, **
610• Comparison: ==, !=, <, <=, >, >=
611• Logical: &&, ||, !
612• Bitwise: &, |, ^, ~, <<, >>
613• Ternary: condition ? true_value : false_value
614
615EXAMPLES:
616• balances[msg.sender]
617• totalSupply() > 1000000
618• block.timestamp - lastUpdate > 3600
619• edb_sload(this, 0x123...)
620• owner == msg.sender && msg.value > 0
621• token.balanceOf(user) * price / 1e18
622• addr.owner == this
623• contractAddr.getUserBalance(msg.sender)
624
625Note: Use 'this' to reference the current contract address in expressions."#;
626
627 Ok(DynSolValue::String(help_text.to_string()))
628}
629
630impl<DB> FunctionCallHandler for EdbFunctionCallHandler<DB>
631where
632 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
633 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
634 <DB as Database>::Error: Clone + Send + Sync,
635{
636 fn call_function(
637 &self,
638 name: &str,
639 args: &[DynSolValue],
640 callee: Option<&DynSolValue>,
641 snapshot_id: usize,
642 ) -> Result<DynSolValue> {
643 debug!(
644 "EdbHandler::call_function name='{}', args={:?}, callee={:?}, snapshot_id={}",
645 name, args, callee, snapshot_id
646 );
647
648 let (frame_id, snapshot) = self.0.context.snapshots.get(snapshot_id).ok_or_else(|| {
649 eyre::eyre!("Snapshot ID {} not found in EdbHandler::call_function", snapshot_id)
650 })?;
651
652 let entry = self.0.context.trace.get(frame_id.trace_entry_id()).ok_or_else(|| {
653 eyre::eyre!("Frame ID {} not found in EdbHandler::call_function", frame_id)
654 })?;
655
656 if name == "edb_sload" && args.len() == 2 {
658 return edb_sload(snapshot, &args[0], &args[1]);
659 } else if name == "edb_tsload" && args.len() == 2 {
660 return edb_tsload(snapshot, &args[0], &args[1]);
661 } else if name == "edb_stack" && args.len() == 1 {
662 return edb_stack(snapshot, &args[0]);
663 } else if name == "edb_calldata" && args.len() == 2 {
664 return edb_calldata(entry, &args[0], &args[1]);
665 } else if name == "edb_memory" && args.len() == 2 {
666 return edb_memory(snapshot, &args[0], &args[1]);
667 } else if name == "keccak256" && args.len() == 1 {
668 return edb_keccak256(args[0].clone());
669 } else if name == "edb_help" && args.is_empty() {
670 return edb_help();
671 }
672
673 let to = if let Some(v) = callee {
675 match v {
676 DynSolValue::Address(addr) => *addr,
677 _ => {
678 bail!("Callee must be an address, got {:?}", callee);
679 }
680 }
681 } else {
682 snapshot.target_address()
683 };
684
685 let mut address_candidates = self
686 .0
687 .context
688 .address_code_address_map()
689 .get(&to)
690 .cloned()
691 .unwrap_or_default()
692 .into_iter()
693 .collect::<Vec<_>>();
694 address_candidates.insert(0, to); let mut errors = Vec::new();
697 for address_candidate in address_candidates {
698 if let Some(contract) = self
699 .0
700 .context
701 .recompiled_artifacts
702 .get(&address_candidate)
703 .and_then(|art| art.contract())
704 {
705 for entry in parse_callable_abi_entries(contract) {
706 if entry.name == name && entry.inputs.len() == args.len() {
707 match self.0.context.call_in_derived_evm(
708 snapshot_id,
709 to,
710 &entry.abi,
711 args,
712 None,
713 ) {
714 Ok(result) => return Ok(result),
715 Err(e) => {
716 errors.push(e);
717 debug!(
718 "Function '{}' found in contract at address {:?}, but call failed",
719 name, address_candidate
720 );
721 }
722 }
723 }
724 }
725 }
726 }
727
728 if errors.is_empty() {
729 bail!(
730 "No function found for name='{}', args={:?}, callee={:?}, snapshot_id={}",
731 name,
732 args,
733 callee,
734 snapshot_id
735 )
736 } else {
737 let combined_error = errors
738 .into_iter()
739 .map(|e| format!("{e}"))
740 .collect::<HashSet<_>>()
741 .into_iter()
742 .collect::<Vec<_>>()
743 .join(";\n");
744 bail!(
745 "Function call failed for name='{}', args={:?}, callee={:?}, snapshot_id={}:\n{}",
746 name,
747 args,
748 callee,
749 snapshot_id,
750 combined_error
751 )
752 }
753 }
754}
755
756impl<DB> MemberAccessHandler for EdbMemberAccessHandler<DB>
757where
758 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
759 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
760 <DB as Database>::Error: Clone + Send + Sync,
761{
762 fn access_member(
763 &self,
764 value: DynSolValue,
765 member: &str,
766 snapshot_id: usize,
767 ) -> Result<DynSolValue> {
768 if let DynSolValue::Address(addr) = value {
769 let mut address_candidates = self
770 .0
771 .context
772 .address_code_address_map()
773 .get(&addr)
774 .cloned()
775 .unwrap_or_default()
776 .into_iter()
777 .collect::<Vec<_>>();
778 address_candidates.insert(0, addr); for address_candidate in address_candidates {
781 if let Some(contract) = self
782 .0
783 .context
784 .recompiled_artifacts
785 .get(&address_candidate)
786 .and_then(|art| art.contract())
787 {
788 for entry in parse_callable_abi_entries(contract) {
789 if entry.name == member
790 && entry.is_state_variable()
791 && entry.inputs.is_empty()
792 {
793 match self.0.context.call_in_derived_evm(
794 snapshot_id,
795 addr,
796 &entry.abi,
797 &[],
798 None,
799 ) {
800 Ok(result) => return Ok(result),
801 Err(_e) => {
802 debug!(
803 "Function '{}' found in contract at address {:?}, but call failed",
804 member, address_candidate
805 );
806 }
807 }
808 }
809 }
810 }
811 }
812 }
813
814 bail!(
815 "Invalid member access for value={:?}, member='{}', snapshot_id={}",
816 value,
817 member,
818 snapshot_id
819 )
820 }
821}
822
823impl<DB> MsgHandler for EdbMsgHandler<DB>
824where
825 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
826 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
827 <DB as Database>::Error: Clone + Send + Sync,
828{
829 fn get_msg_sender(&self, snapshot_id: usize) -> Result<DynSolValue> {
830 let (frame_id, _) = self.0.context.snapshots.get(snapshot_id).ok_or_else(|| {
831 eyre::eyre!("Snapshot ID {} not found in EdbHandler::get_msg_sender", snapshot_id)
832 })?;
833
834 let entry = self.0.context.trace.get(frame_id.trace_entry_id()).ok_or_else(|| {
835 eyre::eyre!("Frame ID {} not found in EdbHandler::get_msg_sender", frame_id)
836 })?;
837
838 Ok(DynSolValue::Address(entry.caller))
839 }
840
841 fn get_msg_value(&self, snapshot_id: usize) -> Result<DynSolValue> {
842 let (frame_id, _) = self.0.context.snapshots.get(snapshot_id).ok_or_else(|| {
843 eyre::eyre!("Snapshot ID {} not found in EdbHandler::get_msg_value", snapshot_id)
844 })?;
845
846 let entry = self.0.context.trace.get(frame_id.trace_entry_id()).ok_or_else(|| {
847 eyre::eyre!("Frame ID {} not found in EdbHandler::get_msg_value", frame_id)
848 })?;
849
850 Ok(DynSolValue::Uint(entry.value, 256))
851 }
852}
853
854impl<DB> TxHandler for EdbTxHandler<DB>
855where
856 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
857 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
858 <DB as Database>::Error: Clone + Send + Sync,
859{
860 fn get_tx_origin(&self, _snapshot_id: usize) -> Result<DynSolValue> {
861 let entry = self
862 .0
863 .context
864 .trace
865 .first()
866 .ok_or_else(|| eyre::eyre!("No trace entry found in EdbHandler::get_tx_origin"))?;
867
868 Ok(DynSolValue::Address(entry.caller))
869 }
870}
871
872impl<DB> BlockHandler for EdbBlockHandler<DB>
873where
874 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
875 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
876 <DB as Database>::Error: Clone + Send + Sync,
877{
878 fn get_block_number(&self, _snapshot_id: usize) -> Result<DynSolValue> {
879 Ok(DynSolValue::Uint(U256::from(self.0.context.fork_info.block_number), 256))
880 }
881
882 fn get_block_timestamp(&self, _snapshot_id: usize) -> Result<DynSolValue> {
883 Ok(DynSolValue::Uint(self.0.context.block.timestamp, 256))
884 }
885}
886
887impl<DB> ValidationHandler for EdbValidationHandler<DB>
888where
889 DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
890 <CacheDB<DB> as Database>::Error: Clone + Send + Sync,
891 <DB as Database>::Error: Clone + Send + Sync,
892{
893 fn validate_value(&self, value: DynSolValue) -> Result<DynSolValue> {
894 if into_abi_info(&value).is_some() {
895 bail!("Mapping or array value cannot be directly returned; please access a member or call a function to get a concrete value");
896 } else {
897 Ok(value)
898 }
899 }
900}