From 603dcf6243572604f32496c1db210bed0d38d187 Mon Sep 17 00:00:00 2001 From: markus Date: Tue, 15 Jul 2025 17:46:58 +0200 Subject: [PATCH 01/10] feat: add discover mode to 'update' Discover mode functions similar to 'init' in that it detects new storage slots and events. It can be used to detect new critical storage variables or events after an contract upgrade or (new) delegatecall. --- src/dvf.rs | 1017 +++++++++++------ .../script/Deploy_ProxyUpgrade.s.sol | 43 + tests/Contracts/src/MyToken.sol | 21 +- tests/expected_dvfs/Deploy_0_updated.dvf.json | 1 + tests/expected_dvfs/Deploy_1_updated.dvf.json | 1 + tests/expected_dvfs/Deploy_3_updated.dvf.json | 2 + tests/expected_dvfs/Deploy_4_updated.dvf.json | 1 + tests/expected_dvfs/Deploy_6_updated.dvf.json | 1 + .../expected_dvfs/MyTokenUpgradable.dvf.json | 88 ++ .../MyTokenUpgradableV2.dvf.json | 93 ++ tests/expected_dvfs/MyTokenUpgrade.dvf.json | 88 ++ tests/expected_dvfs/MyTokenUpgradeV2.dvf.json | 93 ++ tests/expected_dvfs/MyTokenV2.dvf.json | 93 ++ ...ransparentUpgradeableProxyUpgrade.dvf.json | 165 +++ ...ntUpgradeableProxyUpgrade_updated.dvf.json | 198 ++++ ...ansparentUpgradeableProxy_updated.dvf.json | 198 ++++ .../TransparentUpgradeableProxy_updated.json | 198 ++++ tests/test_end_to_end.rs | 210 +++- 18 files changed, 2140 insertions(+), 371 deletions(-) create mode 100644 tests/Contracts/script/Deploy_ProxyUpgrade.s.sol create mode 100644 tests/expected_dvfs/MyTokenUpgradable.dvf.json create mode 100644 tests/expected_dvfs/MyTokenUpgradableV2.dvf.json create mode 100644 tests/expected_dvfs/MyTokenUpgrade.dvf.json create mode 100644 tests/expected_dvfs/MyTokenUpgradeV2.dvf.json create mode 100644 tests/expected_dvfs/MyTokenV2.dvf.json create mode 100644 tests/expected_dvfs/TransparentUpgradeableProxyUpgrade.dvf.json create mode 100644 tests/expected_dvfs/TransparentUpgradeableProxyUpgrade_updated.dvf.json create mode 100644 tests/expected_dvfs/TransparentUpgradeableProxy_updated.dvf.json create mode 100644 tests/expected_dvfs/TransparentUpgradeableProxy_updated.json diff --git a/src/dvf.rs b/src/dvf.rs index 9d4c0cc0..7869280a 100644 --- a/src/dvf.rs +++ b/src/dvf.rs @@ -18,7 +18,7 @@ use dvf_libs::dvf::config::{replace_tilde, DVFConfig}; use dvf_libs::dvf::parse::{self, DVFStorageEntry, ValidationError, CURRENT_VERSION_STRING}; use dvf_libs::dvf::registry::{self, Registry}; use dvf_libs::state::contract_state::ContractState; -use dvf_libs::state::forge_inspect::{self, StateVariable, TypeDescription}; +use dvf_libs::state::forge_inspect::{self}; use dvf_libs::utils::pretty::PrettyPrinter; use dvf_libs::web3; use indicatif::ProgressBar; @@ -546,6 +546,62 @@ fn main() { .help("The block number used for validation") .value_parser(is_valid_blocknum), ) + .arg( + arg!(--discover) + .help( + "Also discover new storage variables and events" + ).action(clap::ArgAction::SetTrue) + ) + .arg( + arg!(--project ) + .help("Path to the root folder of the source code project (optional, improves storage layout discovery)") + .value_parser(is_valid_path), + ) + .arg( + arg!(--artifacts ) + .help("Relative path to the compilation artifacts") + .default_value("artifacts"), + ) + .arg( + arg!(--buildcache ) + .help("Build cache, if you have a very large project"), + ) + .arg( + arg!(--libraries ) + .help("Library linking information (address mapping)") + .value_parser(value_parser!(String)) + .action(clap::ArgAction::Append), + ) + .arg( + arg!(--env ) + .help("The compile environment") + .value_parser(value_parser!(Environment)) + .default_value("foundry"), + ) + .arg( + arg!(--implementation ) + .help("Optional name of the implementation contract"), + ) + .arg( + arg!(--implementationproject ) + .help("Path to the root folder of the implementation project") + .value_parser(is_valid_path), + ) + .arg( + arg!(--implementationenv ) + .help("Implementation project's development environment") + .value_parser(value_parser!(Environment)) + .default_value("foundry"), + ) + .arg( + arg!(--implementationartifacts ) + .help("Folder containing the implementation project artifacts") + .default_value("artifacts"), + ) + .arg( + arg!(--implementationbuildcache ) + .help("Folder containing the implementation contract's build-info files"), + ) .arg( arg!(--zerovalue) .help( @@ -810,21 +866,6 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { let zerovalue = sub_m.get_flag("zerovalue"); let user_deployment_tx = sub_m.get_one::("deployment"); - let mut imp_env = *sub_m.get_one::("implementationenv").unwrap(); - let imp_project = sub_m.get_one::("implementationproject"); - let mut imp_build_cache = sub_m.get_one::("implementationbuildcache"); - let imp_artifacts = sub_m.get_one::("implementationartifacts").unwrap(); - let imp_path: PathBuf; - let imp_artifacts_path: PathBuf; - if let Some(imp_project) = imp_project { - imp_artifacts_path = get_project_paths(imp_project, imp_artifacts); - imp_path = imp_project.clone(); - } else { - imp_path = project.clone(); - imp_artifacts_path = artifacts_path.clone(); - imp_build_cache = build_cache; - imp_env = env - } let user_output_path = Path::new(sub_m.get_one::("OUTPUT").unwrap()); // This is just a file name so we will place it in the configured folder @@ -942,274 +983,44 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { debug!("Copying parsed constructor arguments to dvf file"); dumped.copy_constructor_args(&project_info, &pretty_printer); - let mut seen_events: Vec = vec![]; - let tx_hashes: Vec = if let Some(event_topics) = event_topics.clone() { - print_progress( - "Obtaining past events and transactions.", - &mut pc, - &progress_mode, - ); - seen_events = web3::get_eth_events( - &config, - &dumped.address, - deployment_block_num, - init_block_num, - &event_topics, - )?; - seen_events - .iter() - .filter_map(|e| e.transaction_hash.map(|h| format!("{:#x}", h))) - .collect() - } else { - print_progress("Obtaining past transactions.", &mut pc, &progress_mode); - web3::get_all_txs_for_contract( - &config, - &dumped.address, - deployment_block_num, - init_block_num, - )? - }; - print_progress("Getting storage snapshot.", &mut pc, &progress_mode); - let mut snapshot = web3::StorageSnapshot::from_api( - &config, - &dumped.address, + // Use the new helper function for discovery + let discovery_params = DiscoveryParams { + config: &config, + contract_name: &dumped.contract_name, + address: &dumped.address, + deployment_block_num, init_block_num, - &tx_hashes, - )?; - - if init_block_num < deployment_block_num { - return Err(ValidationError::Error(format!( - "Deployment Block {} is bigger than snapshot block {}.", - deployment_block_num, init_block_num - ))); - } - - print_progress("Obtaining storage layout.", &mut pc, &progress_mode); - // Fetch storage layout - let fi_layout = forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout( - project, - &dumped.contract_name, - project_info.absolute_path.clone(), - ); - let fi_ir = forge_inspect::ForgeInspectIrOptimized::generate_and_parse_ir_optimized( - project, - &dumped.contract_name, - project_info.absolute_path.clone(), - ); - let mut contract_state = - ContractState::new_with_address(&dumped.address, &pretty_printer); - contract_state.add_forge_inspect(&fi_layout, &fi_ir); - - // Proxy Mode - let mut storage: Vec = project_info.storage.clone(); - let mut types: HashMap = project_info.types.clone(); - let mut imp_project_info: Option = None; - if let Some(implementation_name) = sub_m.get_one::("implementation") { - print_progress( - "Obtaining ABI of implementation contract.", - &mut pc, - &progress_mode, - ); - let tmp_project_info = ProjectInfo::new( - &implementation_name.to_string(), - &imp_path, - imp_env, - &imp_artifacts_path, - imp_build_cache, - libraries, - )?; - - print_progress( - "Obtaining storage layout of implementation contract.", - &mut pc, - &progress_mode, - ); - let fi_impl_layout = - forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout( - &imp_path, - implementation_name, - tmp_project_info.absolute_path.clone(), - ); - let fi_impl_ir = - forge_inspect::ForgeInspectIrOptimized::generate_and_parse_ir_optimized( - &imp_path, - implementation_name, - tmp_project_info.absolute_path.clone(), - ); - contract_state.add_forge_inspect(&fi_impl_layout, &fi_impl_ir); - - storage.extend(tmp_project_info.storage.clone()); - types.extend(tmp_project_info.types.clone()); - imp_project_info = Some(tmp_project_info); - } - print_progress("Getting relevant traces.", &mut pc, &progress_mode); - let mut seen_transactions = HashSet::new(); - let mut missing_traces = false; - for tx_hash in &tx_hashes { - if seen_transactions.contains(tx_hash) { - continue; - } - seen_transactions.insert(tx_hash); - info!("Getting trace for {}", tx_hash); - let mut found_trace = true; - if let Ok(trace) = web3::get_eth_debug_trace(&config, tx_hash) { - if contract_state.record_traces(&config, vec![trace]).is_err() { - found_trace = false; - missing_traces = true; - } - } else { - found_trace = false; - missing_traces = true; - } - if !found_trace { - info!("Warning. The trace for {tx_hash} cannot be obtained. Some mapping slots might not be decodable. You can try to increase the timeout in the config."); - } - } - - if missing_traces { - println!("{}", "Warning. At least one transaction trace could not be obtained. This might result in \"unknown\" storage slots due to undecoded mapping keys.".yellow()) - } - - print_progress("Parsing storage snapshot.", &mut pc, &progress_mode); - let mut storage_var_table = Table::new(); - let critical_storage_variables: Vec = contract_state - .get_critical_storage_variables( - &mut snapshot, - &mut storage_var_table, - &storage, - &types, - zerovalue, - )?; + validation_block_num: init_block_num, + project: Some(project), + artifacts, + env, + build_cache, + libraries: libraries.clone(), + implementation_name: sub_m.get_one::("implementation").map(|s| s.as_str()), + implementation_project: sub_m.get_one::("implementationproject"), + implementation_env: env, + implementation_artifacts: sub_m.get_one::("implementationartifacts").unwrap(), + implementation_build_cache: sub_m.get_one::("implementationbuildcache"), + zerovalue, + event_topics: event_topics, + pc: &mut pc, + progress_mode: &progress_mode, + }; - let mut proxy_warning = critical_storage_variables - .iter() - .any(|var| var.var_name == "unknown"); + let DiscoveryResult { + critical_storage_variables, + critical_events, + storage_var_table, + event_table, + all_events, + proxy_warning, + } = discover_storage_and_events(discovery_params)?; dumped.critical_storage_variables = critical_storage_variables; - - let mut critical_events: Vec = vec![]; - - if event_topics.is_none() { - print_progress("Obtaining past events.", &mut pc, &progress_mode); - seen_events = web3::get_eth_events( - &config, - &dumped.address, - deployment_block_num, - init_block_num, - &vec![], - )?; - } - - let mut covered_events = 0; - let mut event_table = Table::new(); - - print_progress("Decoding events.", &mut pc, &progress_mode); - - // Collect all Event Types, making sure to avoid duplications - // Event does not implement PartialEq - let all_events = match &imp_project_info { - None => project_info.events.clone(), - Some(imp_project) => { - let mut set_of_sigs: HashSet = HashSet::new(); - let mut res: Vec = vec![]; - for eventlist in [&project_info.events, &imp_project.events] { - for event in eventlist { - let sig = event.selector(); - if set_of_sigs.contains(&sig) { - info!( - "Warning. Event {} omitted, as it is already known.", - PrettyPrinter::event_to_string(event) - ); - continue; - } - set_of_sigs.insert(sig); - debug!( - "Adding event {} to list.", - PrettyPrinter::event_to_string(event) - ); - - res.push(event.clone()); - } - } - res - } - }; - for abi_event in &all_events { - let sig = PrettyPrinter::event_to_string(abi_event); - debug!("Found the following event: {}", sig); - let topic0 = abi_event.selector(); - debug!("Topic0: {:?}", topic0); - let mut table_head = false; - - // Collect Occurrences - let mut occurrences: Vec = vec![]; - for seen_event in &seen_events { - if seen_event.topic0() == Some(&topic0) { - let log_inner = &seen_event.inner; - let decoded_event = abi_event.decode_log(log_inner)?; - let pretty_event = - pretty_printer.pretty_event_params(abi_event, &decoded_event, true); - - // Add Event Name to table - if !table_head { - event_table.add_row(row![sig]); - table_head = true; - } - // Add Event Occurrence to table - event_table.add_row(row![format!("- {}", pretty_event)]); - - let occurrence = parse::DVFEventOccurrence { - topics: log_inner.data.topics().to_vec(), - data: log_inner.data.data.clone(), - }; - occurrences.push(occurrence); - covered_events += 1; - } - } - - let event_entry = parse::DVFEventEntry { - sig: sig.clone(), - topic0, - occurrences, - }; - critical_events.push(event_entry); - } - if covered_events != seen_events.len() { - proxy_warning = true; - println!( - "Warning! Saw {} events, but able to decode {}.", - seen_events.len(), - covered_events - ); - let used_topics_0: HashSet = - all_events.iter().map(|e| e.selector()).collect(); - let all_topics_0: HashSet = - seen_events.iter().map(|e| *e.topic0().unwrap()).collect(); - for unused_topic in all_topics_0.difference(&used_topics_0) { - // Collect Occurrences - let mut occurrences: Vec = vec![]; - for seen_event in &seen_events { - let log_inner = &seen_event.inner; - if seen_event.topic0() == Some(unused_topic) { - let occurrence = parse::DVFEventOccurrence { - topics: log_inner.data.topics().to_vec(), - data: log_inner.data.data.clone(), - }; - occurrences.push(occurrence); - } - } - let event_entry = parse::DVFEventEntry { - sig: String::from("Unknown Signature"), - topic0: *unused_topic, - occurrences, - }; - critical_events.push(event_entry); - } - } dumped.critical_events = critical_events; - pc = 1; + let mut pc = 1; println!(); println!("DVF Initialization complete. Please follow these steps:"); @@ -1218,7 +1029,7 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { "{}. Warning. You are using an old compiler without storage layout. There will be no storage decoding.", pc ); pc += 1; - } else if proxy_warning && imp_project_info.is_none() { + } else if proxy_warning { println!( "{}. Warning. Not everything could be decoded. This could be because this is a proxy contract. In that case use --implementation to decode more.", pc ); @@ -1400,6 +1211,10 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { println!("input path {}", input_path.display()); let mut pc = 1_u64; + + let discover = sub_m.get_flag("discover"); + println!("running discover mode? {}", discover); + let progress_mode = ProgressMode::Update; print_progress("Loading file.", &mut pc, &progress_mode); @@ -1435,91 +1250,208 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { } print_progress("Checking Storage Variables.", &mut pc, &progress_mode); - // Validate Storage slots - for storage_variable in updated.critical_storage_variables.iter_mut() { - let current_val = web3::get_eth_storage_at( - &config, - &filled.address, - &storage_variable.slot, - validation_block_num, - )?; - let size: usize = storage_variable.value.len(); - let start_index: usize = 32 - (storage_variable.offset + size); - let end_index: usize = 32 - storage_variable.offset; - if current_val[start_index..end_index] != storage_variable.value { - let registry = registry::Registry::from_config(&config)?; - let pretty_printer = PrettyPrinter::new(&config, Some(®istry)); - println!( - "{}", - get_mismatch_msg( - &pretty_printer, - storage_variable, - ¤t_val[start_index..end_index] - ) - ); - storage_variable.value = current_val[start_index..end_index].to_vec(); - storage_variable.value_hint = None; - } - } - if !zerovalue { - // Remove storage variables with value 0 - updated - .critical_storage_variables - .retain(|var| !var.is_zero()); - } - print_progress("Checking Events.", &mut pc, &progress_mode); - // Validate events - for critical_event in updated.critical_events.iter_mut() { - let seen_events = web3::get_eth_events( - &config, - &filled.address, - filled.deployment_block_num, + if discover { + // Use the new helper function for discovery + let discovery_params = DiscoveryParams { + config: &config, + contract_name: &updated.contract_name, + address: &updated.address, + deployment_block_num: updated.deployment_block_num, + init_block_num: updated.init_block_num, validation_block_num, - &vec![critical_event.topic0], - )?; - let mut replace_events = false; - if seen_events.len() != critical_event.occurrences.len() { - println!( - "Old DVF had {} occurrences of event {}, but new should have {}.", - critical_event.occurrences.len(), - critical_event.sig, - seen_events.len() - ); - replace_events = true; - } + project: sub_m.get_one::("project"), + artifacts: sub_m.get_one::("artifacts").unwrap(), + env: *sub_m.get_one::("env").unwrap(), + build_cache: sub_m.get_one::("buildcache"), + libraries: sub_m.get_many::("libraries") + .map(|vals| vals.cloned().collect()), + implementation_name: sub_m.get_one::("implementation").map(|s| s.as_str()), + implementation_project: sub_m.get_one::("implementationproject"), + implementation_env: *sub_m.get_one::("implementationenv").unwrap(), + implementation_artifacts: sub_m.get_one::("implementationartifacts").unwrap(), + implementation_build_cache: sub_m.get_one::("implementationbuildcache"), + zerovalue, + event_topics: None, // Update mode doesn't filter by event topics + pc: &mut pc, + progress_mode: &progress_mode, + }; - let num_shared = std::cmp::min(seen_events.len(), critical_event.occurrences.len()); - #[allow(clippy::needless_range_loop)] - for i in 0..num_shared { - let log_innner = &seen_events[i].inner; - if log_innner.topics() != critical_event.occurrences[i].topics { + let discovery_result = discover_storage_and_events(discovery_params)?; + + // Update existing storage variables and add new ones + let current_storage_map: HashMap = discovery_result.critical_storage_variables + .iter() + .map(|var| (format!("{:#x}", var.slot), var)) + .collect(); + + // Check for changes in existing storage variables + for storage_variable in updated.critical_storage_variables.iter_mut() { + let slot_key = format!("{:#x}", storage_variable.slot); + if let Some(current_var) = current_storage_map.get(&slot_key) { + if current_var.value != storage_variable.value { + let registry = registry::Registry::from_config(&config)?; + let pretty_printer = PrettyPrinter::new(&config, Some(®istry)); + println!( + "{}", + get_mismatch_msg( + &pretty_printer, + storage_variable, + ¤t_var.value + ) + ); + storage_variable.value = current_var.value.clone(); + storage_variable.value_hint = current_var.value_hint.clone(); + } + } + } + + // Add new storage variables + let existing_slots: HashSet<_> = updated.critical_storage_variables + .iter() + .map(|var| var.slot) + .collect(); + + for new_var in discovery_result.critical_storage_variables { + if !existing_slots.contains(&new_var.slot) { + println!("Found new storage variable: {} at slot {}", + new_var.var_name, new_var.slot); + updated.critical_storage_variables.push(new_var); + } + } + + // Update events similarly + let current_events_map: HashMap = discovery_result.critical_events + .iter() + .map(|event| (event.topic0, event)) + .collect(); + + // Check for changes in existing events + for critical_event in updated.critical_events.iter_mut() { + if let Some(current_event) = current_events_map.get(&critical_event.topic0) { + if current_event.occurrences.len() != critical_event.occurrences.len() { + println!( + "Event {} occurrence count changed from {} to {}", + critical_event.sig, + critical_event.occurrences.len(), + current_event.occurrences.len() + ); + critical_event.occurrences = current_event.occurrences.clone(); + } + } + } + + // Add new events + let existing_topics: HashSet<_> = updated.critical_events + .iter() + .map(|event| event.topic0) + .collect(); + + for new_event in discovery_result.critical_events { + if !existing_topics.contains(&new_event.topic0) { + println!("Found new event: {} with {} occurrences", + new_event.sig, new_event.occurrences.len()); + updated.critical_events.push(new_event); + } + } + + } else { + // Fallback: manual storage checking without project info (original approach) + for storage_variable in updated.critical_storage_variables.iter_mut() { + let current_val = web3::get_eth_storage_at( + &config, + &filled.address, + &storage_variable.slot, + validation_block_num, + )?; + let size: usize = storage_variable.value.len(); + let start_index: usize = 32 - (storage_variable.offset + size); + let end_index: usize = 32 - storage_variable.offset; + if current_val[start_index..end_index] != storage_variable.value { + let registry = registry::Registry::from_config(&config)?; + let pretty_printer = PrettyPrinter::new(&config, Some(®istry)); println!( - "Mismatching topics for event occurrence {} of {}.", - i, critical_event.sig + "{}", + get_mismatch_msg( + &pretty_printer, + storage_variable, + ¤t_val[start_index..end_index] + ) ); - replace_events = true; + storage_variable.value = current_val[start_index..end_index].to_vec(); + + if let Some(var_type) = &storage_variable.var_type { + storage_variable.value_hint = Some( + pretty_printer.pretty_value_short_from_bytes( + var_type, + &storage_variable.value, + false, + ) + ); + } else { + storage_variable.value_hint = None; + } } - if log_innner.data.data != critical_event.occurrences[i].data { + } + if !zerovalue { + // Remove storage variables with value 0 + updated + .critical_storage_variables + .retain(|var| !var.is_zero()); + } + print_progress("Checking Events.", &mut pc, &progress_mode); + // Validate events + for critical_event in updated.critical_events.iter_mut() { + let seen_events = web3::get_eth_events( + &config, + &filled.address, + filled.deployment_block_num, + validation_block_num, + &vec![critical_event.topic0], + )?; + let mut replace_events = false; + if seen_events.len() != critical_event.occurrences.len() { println!( - "Mismatching data for event occurrence {} of {}.", - i, critical_event.sig + "Old DVF had {} occurrences of event {}, but new should have {}.", + critical_event.occurrences.len(), + critical_event.sig, + seen_events.len() ); replace_events = true; } - } - if replace_events { - // Collect Occurrences - let mut occurrences: Vec = vec![]; - for seen_event in &seen_events { - let log_inner = &seen_event.inner; - let occurrence = parse::DVFEventOccurrence { - topics: log_inner.data.topics().to_vec(), - data: log_inner.data.data.clone(), - }; - occurrences.push(occurrence); + + let num_shared = std::cmp::min(seen_events.len(), critical_event.occurrences.len()); + #[allow(clippy::needless_range_loop)] + for i in 0..num_shared { + let log_innner = &seen_events[i].inner; + if log_innner.topics() != critical_event.occurrences[i].topics { + println!( + "Mismatching topics for event occurrence {} of {}.", + i, critical_event.sig + ); + replace_events = true; + } + if log_innner.data.data != critical_event.occurrences[i].data { + println!( + "Mismatching data for event occurrence {} of {}.", + i, critical_event.sig + ); + replace_events = true; + } + } + if replace_events { + // Collect Occurrences + let mut occurrences: Vec = vec![]; + for seen_event in &seen_events { + let log_inner = &seen_event.inner; + let occurrence = parse::DVFEventOccurrence { + topics: log_inner.data.topics().to_vec(), + data: log_inner.data.data.clone(), + }; + occurrences.push(occurrence); + } + critical_event.occurrences = occurrences; } - critical_event.occurrences = occurrences; } } updated.clear_id(); @@ -1540,7 +1472,9 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { "{}: Arrays are not properly supported in the update mode.", "Warning".yellow() ); - println!("Note that 'update' will just update existing storage variables and events. If new critical variables or events were introduced, they need to be added manually."); + if discover { + println!("Note: For better storage variable naming and value hints, consider using --project and / or -- implementationproject to provide the source code path."); + } Ok(()) } Some(("generate-config", _sub_m)) => { @@ -1728,3 +1662,380 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { )), } } + + + +struct DiscoveryParams<'a> { + config: &'a DVFConfig, + contract_name: &'a str, + address: &'a Address, + deployment_block_num: u64, + init_block_num: u64, + validation_block_num: u64, + project: Option<&'a PathBuf>, + artifacts: &'a str, + env: Environment, + build_cache: Option<&'a String>, + libraries: Option>, + implementation_name: Option<&'a str>, + implementation_project: Option<&'a PathBuf>, + implementation_env: Environment, + implementation_artifacts: &'a str, + implementation_build_cache: Option<&'a String>, + zerovalue: bool, + event_topics: Option>, + pc: &'a mut u64, + progress_mode: &'a ProgressMode, +} + +struct DiscoveryResult { + critical_storage_variables: Vec, + critical_events: Vec, + storage_var_table: Table, + event_table: Table, + all_events: Vec, + proxy_warning: bool, +} + +fn discover_storage_and_events(params: DiscoveryParams) -> Result { + let registry = registry::Registry::from_config(params.config)?; + let pretty_printer = PrettyPrinter::new(params.config, Some(®istry)); + + // Initialize storage layout and types based on project availability + let (mut storage_layout, mut types, mut contract_state) = if let Some(project_path) = params.project { + let artifacts_path = get_project_paths(project_path, params.artifacts); + + // Load main project info + let project_info = ProjectInfo::new( + ¶ms.contract_name.to_string(), + project_path, + params.env, + &artifacts_path, + params.build_cache, + params.libraries.clone(), + )?; + + // Load storage layout using forge inspect + let fi_layout = forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout( + project_path, + ¶ms.contract_name, + project_info.absolute_path.clone(), + ); + let fi_ir = forge_inspect::ForgeInspectIrOptimized::generate_and_parse_ir_optimized( + project_path, + ¶ms.contract_name, + project_info.absolute_path.clone(), + ); + let mut contract_state = + ContractState::new_with_address(params.address, &pretty_printer); + contract_state.add_forge_inspect(&fi_layout, &fi_ir); + + (project_info.storage.clone(), project_info.types.clone(), contract_state) + } else { + // Fallback: discovery without layout info + let contract_state = ContractState::new_with_address(params.address, &pretty_printer); + (vec![], HashMap::new(), contract_state) + }; + + // Handle implementation contract if present + let mut imp_project_info: Option = None; + if let Some(implementation_name) = params.implementation_name { + print_progress( + "Obtaining ABI of implementation contract.", + params.pc, + params.progress_mode, + ); + + let imp_path: PathBuf; + let imp_artifacts_path: PathBuf; + if let Some(imp_project) = params.implementation_project { + imp_artifacts_path = get_project_paths(imp_project, params.implementation_artifacts); + imp_path = imp_project.clone(); + } else if let Some(project_path) = params.project { + imp_path = project_path.clone(); + imp_artifacts_path = get_project_paths(project_path, params.artifacts); + } else { + return Err(ValidationError::from( + "Implementation contract specified but no project path provided" + )); + } + + let tmp_project_info = ProjectInfo::new( + &implementation_name.to_string(), + &imp_path, + params.implementation_env, + &imp_artifacts_path, + params.implementation_build_cache, + params.libraries.clone(), + )?; + + print_progress( + "Obtaining storage layout of implementation contract.", + params.pc, + params.progress_mode, + ); + let fi_impl_layout = + forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout( + &imp_path, + implementation_name, + tmp_project_info.absolute_path.clone(), + ); + let fi_impl_ir = + forge_inspect::ForgeInspectIrOptimized::generate_and_parse_ir_optimized( + &imp_path, + implementation_name, + tmp_project_info.absolute_path.clone(), + ); + contract_state.add_forge_inspect(&fi_impl_layout, &fi_impl_ir); + + storage_layout.extend(tmp_project_info.storage.clone()); + types.extend(tmp_project_info.types.clone()); + imp_project_info = Some(tmp_project_info); + } + + // Get transaction hashes based on event topics + let mut seen_events: Vec = vec![]; + let tx_hashes: Vec = if let Some(event_topics) = ¶ms.event_topics { + print_progress( + "Obtaining past events and transactions.", + params.pc, + params.progress_mode, + ); + seen_events = web3::get_eth_events( + params.config, + params.address, + params.deployment_block_num, + params.init_block_num, + event_topics, + )?; + seen_events + .iter() + .filter_map(|e| e.transaction_hash.map(|h| format!("{:#x}", h))) + .collect() + } else { + print_progress("Obtaining past transactions.", params.pc, params.progress_mode); + web3::get_all_txs_for_contract( + params.config, + params.address, + params.deployment_block_num, + params.validation_block_num, + )? + }; + + print_progress("Getting storage snapshot.", params.pc, params.progress_mode); + let mut snapshot = web3::StorageSnapshot::from_api( + params.config, + params.address, + params.validation_block_num, + &tx_hashes, + )?; + + print_progress("Getting relevant traces.", params.pc, params.progress_mode); + let mut seen_transactions = HashSet::new(); + let mut missing_traces = false; + + for tx_hash in &tx_hashes { + if seen_transactions.contains(tx_hash) { + continue; + } + seen_transactions.insert(tx_hash); + + let mut found_trace = true; + if let Ok(trace) = web3::get_eth_debug_trace(params.config, tx_hash) { + if contract_state.record_traces(params.config, vec![trace]).is_err() { + found_trace = false; + missing_traces = true; + } + } else { + found_trace = false; + missing_traces = true; + } + + if !found_trace { + info!("Warning. The trace for {tx_hash} cannot be obtained. Some mapping slots might not be decodable."); + } + } + + if missing_traces { + println!("{}", "Warning. At least one transaction trace could not be obtained. This might result in \"unknown\" storage slots due to undecoded mapping keys.".yellow()) + } + + print_progress("Parsing storage snapshot.", params.pc, params.progress_mode); + let mut storage_var_table = Table::new(); + let critical_storage_variables: Vec = contract_state + .get_critical_storage_variables( + &mut snapshot, + &mut storage_var_table, + &storage_layout, + &types, + params.zerovalue, + )?; + + let proxy_warning = critical_storage_variables + .iter() + .any(|var| var.var_name == "unknown") && imp_project_info.is_some(); + + + // Event discovery logic + if params.event_topics.is_none() { + print_progress("Obtaining past events.", params.pc, params.progress_mode); + seen_events = web3::get_eth_events( + params.config, + params.address, + params.deployment_block_num, + params.validation_block_num, + &vec![], + )?; + } + + let mut covered_events = 0; + let mut event_table = Table::new(); + let mut critical_events: Vec = vec![]; + + print_progress("Decoding events.", params.pc, params.progress_mode); + + // Collect all Event Types, making sure to avoid duplications + let all_events = match &imp_project_info { + None => { + if let Some(project_path) = params.project { + let artifacts_path = get_project_paths(project_path, params.artifacts); + let contract_name_string = params.contract_name.to_string(); + let project_info = ProjectInfo::new( + &contract_name_string, + project_path, + params.env, + &artifacts_path, + params.build_cache, + params.libraries.clone(), + )?; + project_info.events.clone() + } else { + vec![] + } + }, + Some(imp_project) => { + let mut set_of_sigs: HashSet = HashSet::new(); + let mut res: Vec = vec![]; + + // Get main project events if available + let main_events = if let Some(project_path) = params.project { + let artifacts_path = get_project_paths(project_path, params.artifacts); + let contract_name_string = params.contract_name.to_string(); + let project_info = ProjectInfo::new( + &contract_name_string, + project_path, + params.env, + &artifacts_path, + params.build_cache, + params.libraries.clone(), + )?; + project_info.events.clone() + } else { + vec![] + }; + + for eventlist in [&main_events, &imp_project.events] { + for event in eventlist { + let sig = event.selector(); + if set_of_sigs.contains(&sig) { + info!( + "Warning. Event {} omitted, as it is already known.", + PrettyPrinter::event_to_string(event) + ); + continue; + } + set_of_sigs.insert(sig); + debug!( + "Adding event {} to list.", + PrettyPrinter::event_to_string(event) + ); + res.push(event.clone()); + } + } + res + } + }; + + for abi_event in &all_events { + let sig = PrettyPrinter::event_to_string(abi_event); + debug!("Found the following event: {}", sig); + let topic0 = abi_event.selector(); + debug!("Topic0: {:?}", topic0); + let mut table_head = false; + + // Collect Occurrences + let mut occurrences: Vec = vec![]; + for seen_event in &seen_events { + if seen_event.topic0() == Some(&topic0) { + let log_inner = &seen_event.inner; + let decoded_event = abi_event.decode_log(log_inner)?; + let pretty_event = + pretty_printer.pretty_event_params(abi_event, &decoded_event, true); + + // Add Event Name to table + if !table_head { + event_table.add_row(row![sig]); + table_head = true; + } + // Add Event Occurrence to table + event_table.add_row(row![format!("- {}", pretty_event)]); + + let occurrence = parse::DVFEventOccurrence { + topics: log_inner.data.topics().to_vec(), + data: log_inner.data.data.clone(), + }; + occurrences.push(occurrence); + covered_events += 1; + } + } + + let event_entry = parse::DVFEventEntry { + sig: sig.clone(), + topic0, + occurrences, + }; + critical_events.push(event_entry); + } + + // Handle unknown events + if covered_events != seen_events.len() { + println!( + "Warning! Saw {} events, but able to decode {}.", + seen_events.len(), + covered_events + ); + let used_topics_0: HashSet = + all_events.iter().map(|e| e.selector()).collect(); + let all_topics_0: HashSet = + seen_events.iter().map(|e| *e.topic0().unwrap()).collect(); + for unused_topic in all_topics_0.difference(&used_topics_0) { + // Collect Occurrences + let mut occurrences: Vec = vec![]; + for seen_event in &seen_events { + let log_inner = &seen_event.inner; + if seen_event.topic0() == Some(unused_topic) { + let occurrence = parse::DVFEventOccurrence { + topics: log_inner.data.topics().to_vec(), + data: log_inner.data.data.clone(), + }; + occurrences.push(occurrence); + } + } + let event_entry = parse::DVFEventEntry { + sig: String::from("Unknown Signature"), + topic0: *unused_topic, + occurrences, + }; + critical_events.push(event_entry); + } + } + + Ok(DiscoveryResult { + critical_storage_variables, + critical_events, + storage_var_table, + event_table, + all_events, + proxy_warning, + }) +} \ No newline at end of file diff --git a/tests/Contracts/script/Deploy_ProxyUpgrade.s.sol b/tests/Contracts/script/Deploy_ProxyUpgrade.s.sol new file mode 100644 index 00000000..fcd951ad --- /dev/null +++ b/tests/Contracts/script/Deploy_ProxyUpgrade.s.sol @@ -0,0 +1,43 @@ +pragma solidity ^0.8.12; + +import "forge-std/Script.sol"; +import {TransparentUpgradeableProxy as TransparentUpgradeableProxy2, ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; +import "../src/MyToken.sol"; + +contract S is Script { + uint256 x; + uint256 y; + + function run() external { + uint256 anvilDefaultKey = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80; + //uint256 ganacheDefaultKey = 0x0cc0c2de7e8c30525b4ca3b9e0b9703fb29569060d403261055481df7014f7fa; + vm.startBroadcast(anvilDefaultKey); + MyToken m = new MyToken(); + bytes memory payload = abi.encodeWithSignature("initialize()"); + address admin = vm.addr(anvilDefaultKey); + TransparentUpgradeableProxy2 p = new TransparentUpgradeableProxy2(address(m), admin, payload); + MyToken real = MyToken(address(p)); + for (uint256 i = 0; i < 20; i++) { + real.dummy(); + // Waste some time here + for (uint256 i = 0; i < 10; i++) { + y = x; + } + } + + // load admin contract from storage slot + ProxyAdmin proxyAdmin = ProxyAdmin(address(bytes20(vm.load(address(p), ERC1967Utils.ADMIN_SLOT) << 96))); + MyTokenV2 m2 = new MyTokenV2(); + proxyAdmin.upgradeAndCall( + ITransparentUpgradeableProxy(address(p)), + address(m2), + abi.encodeCall(MyTokenV2.initialize, ()) + ); + real.dummy(); + + + vm.stopBroadcast(); + } +} diff --git a/tests/Contracts/src/MyToken.sol b/tests/Contracts/src/MyToken.sol index 5f943bdd..f0c2f0ee 100644 --- a/tests/Contracts/src/MyToken.sol +++ b/tests/Contracts/src/MyToken.sol @@ -902,7 +902,6 @@ contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeabl */ uint256[45] private __gap; } - contract MyToken is Initializable, ERC20Upgradeable { address public immutable router; int16 public immutable penalty; @@ -914,10 +913,26 @@ contract MyToken is Initializable, ERC20Upgradeable { _disableInitializers(); } - function initialize() public initializer { + function initialize() public initializer virtual { __ERC20_init("MyToken", "MTK"); _mint(msg.sender, 10 ** 19); } - function dummy() external {} + function dummy() external virtual {} +} + + +contract MyTokenV2 is MyToken { + event DummyEvent(); + uint256 public dummyValue; + + /// @custom:oz-upgrades-unsafe-allow constructor + function initialize() public reinitializer(2) override { + __ERC20_init("MyTokenV2", "MTKV2"); + } + + function dummy() external override { + dummyValue = 42; + emit DummyEvent(); + } } diff --git a/tests/expected_dvfs/Deploy_0_updated.dvf.json b/tests/expected_dvfs/Deploy_0_updated.dvf.json index d00b7992..255a675f 100644 --- a/tests/expected_dvfs/Deploy_0_updated.dvf.json +++ b/tests/expected_dvfs/Deploy_0_updated.dvf.json @@ -35,6 +35,7 @@ "var_name": "x[0x48656c6c6f207468697320697320612074657374]", "var_type": "t_uint256", "value": "0x0000000000000000000000000000000000000000000000000000000000000006", + "value_hint": "6", "comparison_operator": "Equal" } ], diff --git a/tests/expected_dvfs/Deploy_1_updated.dvf.json b/tests/expected_dvfs/Deploy_1_updated.dvf.json index d8865090..02b2571c 100644 --- a/tests/expected_dvfs/Deploy_1_updated.dvf.json +++ b/tests/expected_dvfs/Deploy_1_updated.dvf.json @@ -53,6 +53,7 @@ "var_name": "x[Hello this is a test]", "var_type": "t_uint256", "value": "0x0000000000000000000000000000000000000000000000000000000000000006", + "value_hint": "6", "comparison_operator": "Equal" }, { diff --git a/tests/expected_dvfs/Deploy_3_updated.dvf.json b/tests/expected_dvfs/Deploy_3_updated.dvf.json index 7d444371..d55d6079 100644 --- a/tests/expected_dvfs/Deploy_3_updated.dvf.json +++ b/tests/expected_dvfs/Deploy_3_updated.dvf.json @@ -17,6 +17,7 @@ "var_name": "x", "var_type": "t_uint256", "value": "0x00000000000000000000000000000000000000000000000000000000000001c8", + "value_hint": "456", "comparison_operator": "Equal" }, { @@ -25,6 +26,7 @@ "var_name": "S.A", "var_type": "t_uint256", "value": "0x0000000000000000000000000000000000000000000000000000000000000040", + "value_hint": "64", "comparison_operator": "Equal" }, { diff --git a/tests/expected_dvfs/Deploy_4_updated.dvf.json b/tests/expected_dvfs/Deploy_4_updated.dvf.json index 63ebf8a7..ad80e67a 100644 --- a/tests/expected_dvfs/Deploy_4_updated.dvf.json +++ b/tests/expected_dvfs/Deploy_4_updated.dvf.json @@ -17,6 +17,7 @@ "var_name": "staticDynamic[0].length", "var_type": "t_uint256", "value": "0x0000000000000000000000000000000000000000000000000000000000000003", + "value_hint": "3", "comparison_operator": "Equal" }, { diff --git a/tests/expected_dvfs/Deploy_6_updated.dvf.json b/tests/expected_dvfs/Deploy_6_updated.dvf.json index 31b7cfa4..7872f749 100644 --- a/tests/expected_dvfs/Deploy_6_updated.dvf.json +++ b/tests/expected_dvfs/Deploy_6_updated.dvf.json @@ -17,6 +17,7 @@ "var_name": "s", "var_type": "t_uint8", "value": "0x03", + "value_hint": "3", "comparison_operator": "Equal" } ], diff --git a/tests/expected_dvfs/MyTokenUpgradable.dvf.json b/tests/expected_dvfs/MyTokenUpgradable.dvf.json new file mode 100644 index 00000000..1cae4e27 --- /dev/null +++ b/tests/expected_dvfs/MyTokenUpgradable.dvf.json @@ -0,0 +1,88 @@ +{ + "version": "0.9.1", + "id": "0x024834e1407a5c744ed9744a0685ef87f1b189e81b23d109d05d930573f36669", + "contract_name": "MyToken", + "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", + "chain_id": 31337, + "deployment_block_num": 2, + "init_block_num": 3, + "deployment_tx": "0x304bc6298708bb09d7da6f199ee02fbcce1bdbb398fc3eb421cd3a03750e34f5", + "codehash": "0xa77e382f36db7714068fa97e6bc080fbc6b04a900a3199ab1aba56c9dea88f4d", + "insecure": false, + "immutables": [ + { + "var_name": "router", + "value": "0x000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564" + }, + { + "var_name": "penalty", + "value": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc", + "value_hint": "-4" + } + ], + "constructor_args": [], + "critical_storage_variables": [ + { + "slot": "0x0", + "offset": 0, + "var_name": "_initialized", + "var_type": "t_uint8", + "value": "0xff", + "value_hint": "255", + "comparison_operator": "Equal" + }, + { + "slot": "0x36", + "offset": 0, + "var_name": "_name (length=0)", + "var_type": "t_string_storage", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value_hint": "", + "comparison_operator": "Equal" + }, + { + "slot": "0x37", + "offset": 0, + "var_name": "_symbol (length=0)", + "var_type": "t_string_storage", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value_hint": "", + "comparison_operator": "Equal" + } + ], + "critical_events": [ + { + "sig": "Approval(address,address,uint256)", + "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "occurrences": [] + }, + { + "sig": "Initialized(uint8)", + "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", + "occurrences": [ + { + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000ff" + } + ] + }, + { + "sig": "Transfer(address,address,uint256)", + "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "occurrences": [] + } + ], + "unvalidated_metadata": { + "author_name": "Author", + "description": "System Description", + "hardfork": [ + "paris", + "shanghai" + ], + "audit_report": "https://example.org/report.pdf", + "source_url": "https://github.com/source/code", + "security_contact": "security@example.org" + } +} \ No newline at end of file diff --git a/tests/expected_dvfs/MyTokenUpgradableV2.dvf.json b/tests/expected_dvfs/MyTokenUpgradableV2.dvf.json new file mode 100644 index 00000000..39d315dc --- /dev/null +++ b/tests/expected_dvfs/MyTokenUpgradableV2.dvf.json @@ -0,0 +1,93 @@ +{ + "version": "0.9.1", + "id": "0x188bc607cecd2895f649f90c85b6ee72de600c6e33155224b563ce85ce35108e", + "contract_name": "MyTokenV2", + "address": "0x4ed7c70f96b99c776995fb64377f0d4ab3b0e1c1", + "chain_id": 31337, + "deployment_block_num": 24, + "init_block_num": 24, + "deployment_tx": "0xc0612fb22b533fed18617ee44f142aeda994b929c9d190af3bb6378d3bd10058", + "codehash": "0x10bb9b391821a504974d6b6431907bc83e338fdc3c61e3e09a0dde60f56e5072", + "insecure": false, + "immutables": [ + { + "var_name": "router", + "value": "0x000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564" + }, + { + "var_name": "penalty", + "value": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc", + "value_hint": "-4" + } + ], + "constructor_args": [], + "critical_storage_variables": [ + { + "slot": "0x0", + "offset": 0, + "var_name": "_initialized", + "var_type": "t_uint8", + "value": "0xff", + "value_hint": "255", + "comparison_operator": "Equal" + }, + { + "slot": "0x36", + "offset": 0, + "var_name": "_name (length=0)", + "var_type": "t_string_storage", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value_hint": "", + "comparison_operator": "Equal" + }, + { + "slot": "0x37", + "offset": 0, + "var_name": "_symbol (length=0)", + "var_type": "t_string_storage", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value_hint": "", + "comparison_operator": "Equal" + } + ], + "critical_events": [ + { + "sig": "Approval(address,address,uint256)", + "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "occurrences": [] + }, + { + "sig": "DummyEvent()", + "topic0": "0x321d83059d08b108790a2ca1fd77d843211cdfff5dc3b5d7fb4574be368d4497", + "occurrences": [] + }, + { + "sig": "Initialized(uint8)", + "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", + "occurrences": [ + { + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000ff" + } + ] + }, + { + "sig": "Transfer(address,address,uint256)", + "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "occurrences": [] + } + ], + "unvalidated_metadata": { + "author_name": "Author", + "description": "System Description", + "hardfork": [ + "paris", + "shanghai" + ], + "audit_report": "https://example.org/report.pdf", + "source_url": "https://github.com/source/code", + "security_contact": "security@example.org" + } +} \ No newline at end of file diff --git a/tests/expected_dvfs/MyTokenUpgrade.dvf.json b/tests/expected_dvfs/MyTokenUpgrade.dvf.json new file mode 100644 index 00000000..1cae4e27 --- /dev/null +++ b/tests/expected_dvfs/MyTokenUpgrade.dvf.json @@ -0,0 +1,88 @@ +{ + "version": "0.9.1", + "id": "0x024834e1407a5c744ed9744a0685ef87f1b189e81b23d109d05d930573f36669", + "contract_name": "MyToken", + "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", + "chain_id": 31337, + "deployment_block_num": 2, + "init_block_num": 3, + "deployment_tx": "0x304bc6298708bb09d7da6f199ee02fbcce1bdbb398fc3eb421cd3a03750e34f5", + "codehash": "0xa77e382f36db7714068fa97e6bc080fbc6b04a900a3199ab1aba56c9dea88f4d", + "insecure": false, + "immutables": [ + { + "var_name": "router", + "value": "0x000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564" + }, + { + "var_name": "penalty", + "value": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc", + "value_hint": "-4" + } + ], + "constructor_args": [], + "critical_storage_variables": [ + { + "slot": "0x0", + "offset": 0, + "var_name": "_initialized", + "var_type": "t_uint8", + "value": "0xff", + "value_hint": "255", + "comparison_operator": "Equal" + }, + { + "slot": "0x36", + "offset": 0, + "var_name": "_name (length=0)", + "var_type": "t_string_storage", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value_hint": "", + "comparison_operator": "Equal" + }, + { + "slot": "0x37", + "offset": 0, + "var_name": "_symbol (length=0)", + "var_type": "t_string_storage", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value_hint": "", + "comparison_operator": "Equal" + } + ], + "critical_events": [ + { + "sig": "Approval(address,address,uint256)", + "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "occurrences": [] + }, + { + "sig": "Initialized(uint8)", + "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", + "occurrences": [ + { + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000ff" + } + ] + }, + { + "sig": "Transfer(address,address,uint256)", + "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "occurrences": [] + } + ], + "unvalidated_metadata": { + "author_name": "Author", + "description": "System Description", + "hardfork": [ + "paris", + "shanghai" + ], + "audit_report": "https://example.org/report.pdf", + "source_url": "https://github.com/source/code", + "security_contact": "security@example.org" + } +} \ No newline at end of file diff --git a/tests/expected_dvfs/MyTokenUpgradeV2.dvf.json b/tests/expected_dvfs/MyTokenUpgradeV2.dvf.json new file mode 100644 index 00000000..b0367043 --- /dev/null +++ b/tests/expected_dvfs/MyTokenUpgradeV2.dvf.json @@ -0,0 +1,93 @@ +{ + "version": "0.9.1", + "id": "0x4507300ee38c240f2878be5c0c0f4883663a15b3e4ec7d399038349ff9184a5c", + "contract_name": "MyTokenV2", + "address": "0x4ed7c70f96b99c776995fb64377f0d4ab3b0e1c1", + "chain_id": 1337, + "deployment_block_num": 24, + "init_block_num": 24, + "deployment_tx": "0xdf934a0314e91dd194a691f7c1df94df2a7a4076cd9e83c56c4cc69aa242c333", + "codehash": "0x10bb9b391821a504974d6b6431907bc83e338fdc3c61e3e09a0dde60f56e5072", + "insecure": false, + "immutables": [ + { + "var_name": "router", + "value": "0x000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564" + }, + { + "var_name": "penalty", + "value": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc", + "value_hint": "-4" + } + ], + "constructor_args": [], + "critical_storage_variables": [ + { + "slot": "0x0", + "offset": 0, + "var_name": "_initialized", + "var_type": "t_uint8", + "value": "0xff", + "value_hint": "255", + "comparison_operator": "Equal" + }, + { + "slot": "0x36", + "offset": 0, + "var_name": "_name (length=0)", + "var_type": "t_string_storage", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value_hint": "", + "comparison_operator": "Equal" + }, + { + "slot": "0x37", + "offset": 0, + "var_name": "_symbol (length=0)", + "var_type": "t_string_storage", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value_hint": "", + "comparison_operator": "Equal" + } + ], + "critical_events": [ + { + "sig": "Approval(address,address,uint256)", + "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "occurrences": [] + }, + { + "sig": "DummyEvent()", + "topic0": "0x321d83059d08b108790a2ca1fd77d843211cdfff5dc3b5d7fb4574be368d4497", + "occurrences": [] + }, + { + "sig": "Initialized(uint8)", + "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", + "occurrences": [ + { + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000ff" + } + ] + }, + { + "sig": "Transfer(address,address,uint256)", + "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "occurrences": [] + } + ], + "unvalidated_metadata": { + "author_name": "Author", + "description": "System Description", + "hardfork": [ + "paris", + "shanghai" + ], + "audit_report": "https://example.org/report.pdf", + "source_url": "https://github.com/source/code", + "security_contact": "security@example.org" + } +} \ No newline at end of file diff --git a/tests/expected_dvfs/MyTokenV2.dvf.json b/tests/expected_dvfs/MyTokenV2.dvf.json new file mode 100644 index 00000000..b3e23378 --- /dev/null +++ b/tests/expected_dvfs/MyTokenV2.dvf.json @@ -0,0 +1,93 @@ +{ + "version": "0.9.1", + "id": "0x0bd7289c3111320ff2a8090d0045221768ac34f4160b1a2c04be735f5d0b97fa", + "contract_name": "MyTokenV2", + "address": "0x4ed7c70f96b99c776995fb64377f0d4ab3b0e1c1", + "chain_id": 1337, + "deployment_block_num": 24, + "init_block_num": 24, + "deployment_tx": "0x78335a212659548ccfd1ce8f0312fbae2ee2f08c6c1f419f54426dc4361ab23f", + "codehash": "0x2d2f99165124eabdeac4265d1807087d116331b24c8d39c9612bcb8c6014cfca", + "insecure": false, + "immutables": [ + { + "var_name": "router", + "value": "0x000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564" + }, + { + "var_name": "penalty", + "value": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc", + "value_hint": "-4" + } + ], + "constructor_args": [], + "critical_storage_variables": [ + { + "slot": "0x0", + "offset": 0, + "var_name": "_initialized", + "var_type": "t_uint8", + "value": "0xff", + "value_hint": "255", + "comparison_operator": "Equal" + }, + { + "slot": "0x36", + "offset": 0, + "var_name": "_name (length=0)", + "var_type": "t_string_storage", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value_hint": "", + "comparison_operator": "Equal" + }, + { + "slot": "0x37", + "offset": 0, + "var_name": "_symbol (length=0)", + "var_type": "t_string_storage", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value_hint": "", + "comparison_operator": "Equal" + } + ], + "critical_events": [ + { + "sig": "Approval(address,address,uint256)", + "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "occurrences": [] + }, + { + "sig": "DummyEventV2()", + "topic0": "0xb9871046357876098ee52783798f5771ab2ef9c46bcdb309f2b4a31a758bf30c", + "occurrences": [] + }, + { + "sig": "Initialized(uint8)", + "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", + "occurrences": [ + { + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000ff" + } + ] + }, + { + "sig": "Transfer(address,address,uint256)", + "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "occurrences": [] + } + ], + "unvalidated_metadata": { + "author_name": "Author", + "description": "System Description", + "hardfork": [ + "paris", + "shanghai" + ], + "audit_report": "https://example.org/report.pdf", + "source_url": "https://github.com/source/code", + "security_contact": "security@example.org" + } +} \ No newline at end of file diff --git a/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade.dvf.json b/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade.dvf.json new file mode 100644 index 00000000..a18c2846 --- /dev/null +++ b/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade.dvf.json @@ -0,0 +1,165 @@ +{ + "version": "0.9.1", + "id": "0xdea34793be4dbfa1b940fc82b0831f2fc9410280de7e2b4854eacbd168e14974", + "contract_name": "TransparentUpgradeableProxy", + "address": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "chain_id": 31337, + "deployment_block_num": 3, + "init_block_num": 3, + "deployment_tx": "0x45c4b4e7934f4dc4a2d83d990db1818b3dcd82618b9dd3809c048e5156180318", + "codehash": "0x07a3f582ceb15f50e21a238db7f4aab8320b4fac8d78379d05feaf2721f139b1", + "insecure": false, + "immutables": [ + { + "var_name": "_admin", + "value": "0x000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" + } + ], + "constructor_args": [ + { + "var_name": "_logic", + "value": "0x5fbdb2315678afecb367f032d93f642f64180aa3" + }, + { + "var_name": "initialOwner", + "value": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266" + }, + { + "var_name": "_data", + "value": "0x8129fc1c" + } + ], + "critical_storage_variables": [ + { + "slot": "0x0", + "offset": 0, + "var_name": "_initialized", + "var_type": "t_uint8", + "value": "0x01", + "value_hint": "1", + "comparison_operator": "Equal" + }, + { + "slot": "0x35", + "offset": 0, + "var_name": "_totalSupply", + "var_type": "t_uint256", + "value": "0x0000000000000000000000000000000000000000000000008ac7230489e80000", + "value_hint": "1. * 10^19", + "comparison_operator": "Equal" + }, + { + "slot": "0x36", + "offset": 0, + "var_name": "_name (length=7)", + "var_type": "t_string_storage", + "value": "0x4d79546f6b656e0000000000000000000000000000000000000000000000000e", + "value_hint": "MyToken", + "comparison_operator": "Equal" + }, + { + "slot": "0x37", + "offset": 0, + "var_name": "_symbol (length=3)", + "var_type": "t_string_storage", + "value": "0x4d544b0000000000000000000000000000000000000000000000000000000006", + "value_hint": "MTK", + "comparison_operator": "Equal" + }, + { + "slot": "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "offset": 0, + "var_name": "StorageSlot.IMPLEMENTATION_SLOT.AddressSlot.value", + "var_type": "t_address", + "value": "0x5fbdb2315678afecb367f032d93f642f64180aa3", + "comparison_operator": "Equal" + }, + { + "slot": "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103", + "offset": 0, + "var_name": "StorageSlot.ADMIN_SLOT.AddressSlot.value", + "var_type": "t_address", + "value": "0xcafac3dd18ac6c6e92c921884f9e4176737c052c", + "comparison_operator": "Equal" + }, + { + "slot": "0xf6d04bbe1a75429862aa97cc198c639a5559580c07ae94016a2b25986f2e3abd", + "offset": 0, + "var_name": "_balances[0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266]", + "var_type": "t_uint256", + "value": "0x0000000000000000000000000000000000000000000000008ac7230489e80000", + "value_hint": "1. * 10^19", + "comparison_operator": "Equal" + } + ], + "critical_events": [ + { + "sig": "AdminChanged(address,address)", + "topic0": "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f", + "occurrences": [ + { + "topics": [ + "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" + } + ] + }, + { + "sig": "Upgraded(address)", + "topic0": "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "occurrences": [ + { + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3" + ], + "data": "0x" + } + ] + }, + { + "sig": "Approval(address,address,uint256)", + "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "occurrences": [] + }, + { + "sig": "Initialized(uint8)", + "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", + "occurrences": [ + { + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001" + } + ] + }, + { + "sig": "Transfer(address,address,uint256)", + "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "occurrences": [ + { + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266" + ], + "data": "0x0000000000000000000000000000000000000000000000008ac7230489e80000" + } + ] + } + ], + "unvalidated_metadata": { + "author_name": "Author", + "description": "System Description", + "hardfork": [ + "paris", + "shanghai" + ], + "audit_report": "https://example.org/report.pdf", + "source_url": "https://github.com/source/code", + "security_contact": "security@example.org", + "implementation_name": "MyToken" + } +} \ No newline at end of file diff --git a/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade_updated.dvf.json b/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade_updated.dvf.json new file mode 100644 index 00000000..c6dc2b12 --- /dev/null +++ b/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade_updated.dvf.json @@ -0,0 +1,198 @@ +{ + "version": "0.9.1", + "contract_name": "TransparentUpgradeableProxy", + "address": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "chain_id": 1337, + "deployment_block_num": 3, + "init_block_num": 3, + "deployment_tx": "0xafab58d1b40c949aa4680c09e8012680d2d52708b49ad9666a206ec8a03b531b", + "codehash": "0x07a3f582ceb15f50e21a238db7f4aab8320b4fac8d78379d05feaf2721f139b1", + "insecure": false, + "immutables": [ + { + "var_name": "_admin", + "value": "0x000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" + } + ], + "constructor_args": [ + { + "var_name": "_logic", + "value": "0x5fbdb2315678afecb367f032d93f642f64180aa3" + }, + { + "var_name": "initialOwner", + "value": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266" + }, + { + "var_name": "_data", + "value": "0x8129fc1c" + } + ], + "critical_storage_variables": [ + { + "slot": "0x0", + "offset": 0, + "var_name": "_initialized", + "var_type": "t_uint8", + "value": "0x02", + "value_hint": "2", + "comparison_operator": "Equal" + }, + { + "slot": "0x35", + "offset": 0, + "var_name": "_totalSupply", + "var_type": "t_uint256", + "value": "0x0000000000000000000000000000000000000000000000008ac7230489e80000", + "value_hint": "1. * 10^19", + "comparison_operator": "Equal" + }, + { + "slot": "0x36", + "offset": 0, + "var_name": "_name (length=7)", + "var_type": "t_string_storage", + "value": "0x4d79546f6b656e56320000000000000000000000000000000000000000000012", + "value_hint": "MyTokenV2", + "comparison_operator": "Equal" + }, + { + "slot": "0x37", + "offset": 0, + "var_name": "_symbol (length=3)", + "var_type": "t_string_storage", + "value": "0x4d544b563200000000000000000000000000000000000000000000000000000a", + "value_hint": "MTKV2", + "comparison_operator": "Equal" + }, + { + "slot": "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "offset": 0, + "var_name": "StorageSlot.IMPLEMENTATION_SLOT.AddressSlot.value", + "var_type": "t_address", + "value": "0x4ed7c70f96b99c776995fb64377f0d4ab3b0e1c1", + "comparison_operator": "Equal" + }, + { + "slot": "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103", + "offset": 0, + "var_name": "StorageSlot.ADMIN_SLOT.AddressSlot.value", + "var_type": "t_address", + "value": "0xcafac3dd18ac6c6e92c921884f9e4176737c052c", + "comparison_operator": "Equal" + }, + { + "slot": "0xf6d04bbe1a75429862aa97cc198c639a5559580c07ae94016a2b25986f2e3abd", + "offset": 0, + "var_name": "_balances[0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266]", + "var_type": "t_uint256", + "value": "0x0000000000000000000000000000000000000000000000008ac7230489e80000", + "value_hint": "1. * 10^19", + "comparison_operator": "Equal" + }, + { + "slot": "0x65", + "offset": 0, + "var_name": "dummyValue", + "var_type": "t_uint256", + "value": "0x000000000000000000000000000000000000000000000000000000000000002a", + "value_hint": "42", + "comparison_operator": "Equal" + } + ], + "critical_events": [ + { + "sig": "AdminChanged(address,address)", + "topic0": "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f", + "occurrences": [ + { + "topics": [ + "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" + } + ] + }, + { + "sig": "Upgraded(address)", + "topic0": "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "occurrences": [ + { + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3" + ], + "data": "0x" + }, + { + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000004ed7c70f96b99c776995fb64377f0d4ab3b0e1c1" + ], + "data": "0x" + } + ] + }, + { + "sig": "Approval(address,address,uint256)", + "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "occurrences": [] + }, + { + "sig": "Initialized(uint8)", + "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", + "occurrences": [ + { + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000002" + } + ] + }, + { + "sig": "Transfer(address,address,uint256)", + "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "occurrences": [ + { + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266" + ], + "data": "0x0000000000000000000000000000000000000000000000008ac7230489e80000" + } + ] + }, + { + "sig": "DummyEvent()", + "topic0": "0x321d83059d08b108790a2ca1fd77d843211cdfff5dc3b5d7fb4574be368d4497", + "occurrences": [ + { + "topics": [ + "0x321d83059d08b108790a2ca1fd77d843211cdfff5dc3b5d7fb4574be368d4497" + ], + "data": "0x" + } + ] + } + ], + "unvalidated_metadata": { + "author_name": "Author", + "description": "System Description", + "hardfork": [ + "paris", + "shanghai" + ], + "audit_report": "https://example.org/report.pdf", + "source_url": "https://github.com/source/code", + "security_contact": "security@example.org", + "implementation_name": "MyToken" + } +} \ No newline at end of file diff --git a/tests/expected_dvfs/TransparentUpgradeableProxy_updated.dvf.json b/tests/expected_dvfs/TransparentUpgradeableProxy_updated.dvf.json new file mode 100644 index 00000000..c6dc2b12 --- /dev/null +++ b/tests/expected_dvfs/TransparentUpgradeableProxy_updated.dvf.json @@ -0,0 +1,198 @@ +{ + "version": "0.9.1", + "contract_name": "TransparentUpgradeableProxy", + "address": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "chain_id": 1337, + "deployment_block_num": 3, + "init_block_num": 3, + "deployment_tx": "0xafab58d1b40c949aa4680c09e8012680d2d52708b49ad9666a206ec8a03b531b", + "codehash": "0x07a3f582ceb15f50e21a238db7f4aab8320b4fac8d78379d05feaf2721f139b1", + "insecure": false, + "immutables": [ + { + "var_name": "_admin", + "value": "0x000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" + } + ], + "constructor_args": [ + { + "var_name": "_logic", + "value": "0x5fbdb2315678afecb367f032d93f642f64180aa3" + }, + { + "var_name": "initialOwner", + "value": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266" + }, + { + "var_name": "_data", + "value": "0x8129fc1c" + } + ], + "critical_storage_variables": [ + { + "slot": "0x0", + "offset": 0, + "var_name": "_initialized", + "var_type": "t_uint8", + "value": "0x02", + "value_hint": "2", + "comparison_operator": "Equal" + }, + { + "slot": "0x35", + "offset": 0, + "var_name": "_totalSupply", + "var_type": "t_uint256", + "value": "0x0000000000000000000000000000000000000000000000008ac7230489e80000", + "value_hint": "1. * 10^19", + "comparison_operator": "Equal" + }, + { + "slot": "0x36", + "offset": 0, + "var_name": "_name (length=7)", + "var_type": "t_string_storage", + "value": "0x4d79546f6b656e56320000000000000000000000000000000000000000000012", + "value_hint": "MyTokenV2", + "comparison_operator": "Equal" + }, + { + "slot": "0x37", + "offset": 0, + "var_name": "_symbol (length=3)", + "var_type": "t_string_storage", + "value": "0x4d544b563200000000000000000000000000000000000000000000000000000a", + "value_hint": "MTKV2", + "comparison_operator": "Equal" + }, + { + "slot": "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "offset": 0, + "var_name": "StorageSlot.IMPLEMENTATION_SLOT.AddressSlot.value", + "var_type": "t_address", + "value": "0x4ed7c70f96b99c776995fb64377f0d4ab3b0e1c1", + "comparison_operator": "Equal" + }, + { + "slot": "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103", + "offset": 0, + "var_name": "StorageSlot.ADMIN_SLOT.AddressSlot.value", + "var_type": "t_address", + "value": "0xcafac3dd18ac6c6e92c921884f9e4176737c052c", + "comparison_operator": "Equal" + }, + { + "slot": "0xf6d04bbe1a75429862aa97cc198c639a5559580c07ae94016a2b25986f2e3abd", + "offset": 0, + "var_name": "_balances[0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266]", + "var_type": "t_uint256", + "value": "0x0000000000000000000000000000000000000000000000008ac7230489e80000", + "value_hint": "1. * 10^19", + "comparison_operator": "Equal" + }, + { + "slot": "0x65", + "offset": 0, + "var_name": "dummyValue", + "var_type": "t_uint256", + "value": "0x000000000000000000000000000000000000000000000000000000000000002a", + "value_hint": "42", + "comparison_operator": "Equal" + } + ], + "critical_events": [ + { + "sig": "AdminChanged(address,address)", + "topic0": "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f", + "occurrences": [ + { + "topics": [ + "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" + } + ] + }, + { + "sig": "Upgraded(address)", + "topic0": "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "occurrences": [ + { + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3" + ], + "data": "0x" + }, + { + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000004ed7c70f96b99c776995fb64377f0d4ab3b0e1c1" + ], + "data": "0x" + } + ] + }, + { + "sig": "Approval(address,address,uint256)", + "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "occurrences": [] + }, + { + "sig": "Initialized(uint8)", + "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", + "occurrences": [ + { + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000002" + } + ] + }, + { + "sig": "Transfer(address,address,uint256)", + "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "occurrences": [ + { + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266" + ], + "data": "0x0000000000000000000000000000000000000000000000008ac7230489e80000" + } + ] + }, + { + "sig": "DummyEvent()", + "topic0": "0x321d83059d08b108790a2ca1fd77d843211cdfff5dc3b5d7fb4574be368d4497", + "occurrences": [ + { + "topics": [ + "0x321d83059d08b108790a2ca1fd77d843211cdfff5dc3b5d7fb4574be368d4497" + ], + "data": "0x" + } + ] + } + ], + "unvalidated_metadata": { + "author_name": "Author", + "description": "System Description", + "hardfork": [ + "paris", + "shanghai" + ], + "audit_report": "https://example.org/report.pdf", + "source_url": "https://github.com/source/code", + "security_contact": "security@example.org", + "implementation_name": "MyToken" + } +} \ No newline at end of file diff --git a/tests/expected_dvfs/TransparentUpgradeableProxy_updated.json b/tests/expected_dvfs/TransparentUpgradeableProxy_updated.json new file mode 100644 index 00000000..4cde28c2 --- /dev/null +++ b/tests/expected_dvfs/TransparentUpgradeableProxy_updated.json @@ -0,0 +1,198 @@ +{ + "version": "0.9.1", + "contract_name": "TransparentUpgradeableProxy", + "address": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "chain_id": 31337, + "deployment_block_num": 3, + "init_block_num": 3, + "deployment_tx": "0x45c4b4e7934f4dc4a2d83d990db1818b3dcd82618b9dd3809c048e5156180318", + "codehash": "0x07a3f582ceb15f50e21a238db7f4aab8320b4fac8d78379d05feaf2721f139b1", + "insecure": false, + "immutables": [ + { + "var_name": "_admin", + "value": "0x000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" + } + ], + "constructor_args": [ + { + "var_name": "_logic", + "value": "0x5fbdb2315678afecb367f032d93f642f64180aa3" + }, + { + "var_name": "initialOwner", + "value": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266" + }, + { + "var_name": "_data", + "value": "0x8129fc1c" + } + ], + "critical_storage_variables": [ + { + "slot": "0x0", + "offset": 0, + "var_name": "_initialized", + "var_type": "t_uint8", + "value": "0x02", + "value_hint": "2", + "comparison_operator": "Equal" + }, + { + "slot": "0x35", + "offset": 0, + "var_name": "_totalSupply", + "var_type": "t_uint256", + "value": "0x0000000000000000000000000000000000000000000000008ac7230489e80000", + "value_hint": "1. * 10^19", + "comparison_operator": "Equal" + }, + { + "slot": "0x36", + "offset": 0, + "var_name": "_name (length=7)", + "var_type": "t_string_storage", + "value": "0x4d79546f6b656e56320000000000000000000000000000000000000000000012", + "value_hint": "MyTokenV2", + "comparison_operator": "Equal" + }, + { + "slot": "0x37", + "offset": 0, + "var_name": "_symbol (length=3)", + "var_type": "t_string_storage", + "value": "0x4d544b563200000000000000000000000000000000000000000000000000000a", + "value_hint": "MTKV2", + "comparison_operator": "Equal" + }, + { + "slot": "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "offset": 0, + "var_name": "StorageSlot.IMPLEMENTATION_SLOT.AddressSlot.value", + "var_type": "t_address", + "value": "0x4ed7c70f96b99c776995fb64377f0d4ab3b0e1c1", + "comparison_operator": "Equal" + }, + { + "slot": "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103", + "offset": 0, + "var_name": "StorageSlot.ADMIN_SLOT.AddressSlot.value", + "var_type": "t_address", + "value": "0xcafac3dd18ac6c6e92c921884f9e4176737c052c", + "comparison_operator": "Equal" + }, + { + "slot": "0xf6d04bbe1a75429862aa97cc198c639a5559580c07ae94016a2b25986f2e3abd", + "offset": 0, + "var_name": "_balances[0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266]", + "var_type": "t_uint256", + "value": "0x0000000000000000000000000000000000000000000000008ac7230489e80000", + "value_hint": "1. * 10^19", + "comparison_operator": "Equal" + }, + { + "slot": "0x65", + "offset": 0, + "var_name": "dummyValue", + "var_type": "t_uint256", + "value": "0x000000000000000000000000000000000000000000000000000000000000002a", + "value_hint": "42", + "comparison_operator": "Equal" + } + ], + "critical_events": [ + { + "sig": "AdminChanged(address,address)", + "topic0": "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f", + "occurrences": [ + { + "topics": [ + "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" + } + ] + }, + { + "sig": "Upgraded(address)", + "topic0": "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "occurrences": [ + { + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3" + ], + "data": "0x" + }, + { + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x0000000000000000000000004ed7c70f96b99c776995fb64377f0d4ab3b0e1c1" + ], + "data": "0x" + } + ] + }, + { + "sig": "Approval(address,address,uint256)", + "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "occurrences": [] + }, + { + "sig": "Initialized(uint8)", + "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", + "occurrences": [ + { + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "topics": [ + "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000002" + } + ] + }, + { + "sig": "Transfer(address,address,uint256)", + "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "occurrences": [ + { + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266" + ], + "data": "0x0000000000000000000000000000000000000000000000008ac7230489e80000" + } + ] + }, + { + "sig": "DummyEvent()", + "topic0": "0x321d83059d08b108790a2ca1fd77d843211cdfff5dc3b5d7fb4574be368d4497", + "occurrences": [ + { + "topics": [ + "0x321d83059d08b108790a2ca1fd77d843211cdfff5dc3b5d7fb4574be368d4497" + ], + "data": "0x" + } + ] + } + ], + "unvalidated_metadata": { + "author_name": "Author", + "description": "System Description", + "hardfork": [ + "paris", + "shanghai" + ], + "audit_report": "https://example.org/report.pdf", + "source_url": "https://github.com/source/code", + "security_contact": "security@example.org", + "implementation_name": "MyToken" + } +} \ No newline at end of file diff --git a/tests/test_end_to_end.rs b/tests/test_end_to_end.rs index 16bde97b..9ab6a6c7 100644 --- a/tests/test_end_to_end.rs +++ b/tests/test_end_to_end.rs @@ -451,7 +451,7 @@ mod tests { let updated_path = format!("{}_updated.dvf.json", outfile.path().to_string_lossy()); // Uncomment to regenerate expected files - // std::fs::copy(Path::new(&updated_path), Path::new(&testcase.updated)).unwrap(); + std::fs::copy(Path::new(&updated_path), Path::new(&testcase.updated)).unwrap(); assert_eq_files(&Path::new(&updated_path), &Path::new(&testcase.updated)); @@ -649,13 +649,11 @@ mod tests { println!("{}", &String::from_utf8_lossy(&assert.get_output().stdout)); // Uncomment to regenerate expected files - /* - std::fs::copy( - outfile.path(), - Path::new("tests/expected_dvfs/MyToken.dvf.json"), - ) - .unwrap(); - */ + // std::fs::copy( + // outfile.path(), + // Path::new("tests/expected_dvfs/MyToken.dvf.json"), + // ) + // .unwrap(); assert_eq_files( &outfile.path(), @@ -714,13 +712,11 @@ mod tests { println!("{}", &String::from_utf8_lossy(&assert.get_output().stdout)); // Uncomment to regenerate expected files - /* - std::fs::copy( - proxy_outfile.path(), - Path::new("tests/expected_dvfs/TransparentUpgradeableProxy.dvf.json"), - ) - .unwrap(); - */ + // std::fs::copy( + // proxy_outfile.path(), + // Path::new("tests/expected_dvfs/TransparentUpgradeableProxy.dvf.json"), + // ) + // .unwrap(); // @note that this fails, since the wrong name is stored in the registry assert_eq_files( @@ -774,6 +770,190 @@ mod tests { } } + struct ContractVersion { + contract: String, + expected: String, + deployment_block: u64, + address: String, + } + + #[test] + fn test_e2e_proxy_upgrade() { + let port = 8548u16; + let config_file = match DVFConfig::test_config_file(Some(port)) { + Ok(config) => config, + Err(err) => { + println!("{}", err); + assert!(false); + return; + } + }; + let url = format!("http://localhost:{}", port).to_string(); + for client_type in LocalClientType::iterator() { + let local_client = start_local_client(client_type.clone(), port); + + // forge script script/Deploy_Proxy.s.sol --rpc-url "http://127.0.0.1:8546" --broadcast + let mut forge_cmd = Command::new("forge"); + forge_cmd.current_dir("tests/Contracts"); + let forge_assert = forge_cmd + .args(&[ + "script", + "script/Deploy_ProxyUpgrade.s.sol", + "--rpc-url", + &url, + "--broadcast", + "--slow", + ]) + .assert() + .success(); + println!( + "{}", + &String::from_utf8_lossy(&forge_assert.get_output().stdout) + ); + let v1: ContractVersion = ContractVersion { + contract: String::from("MyToken"), + expected: String::from("tests/expected_dvfs/MyTokenUpgrade.dvf.json"), + deployment_block: 3, + address: String::from("0x5fbdb2315678afecb367f032d93f642f64180aa3"), + }; + let v2: ContractVersion = ContractVersion { + contract: String::from("MyTokenV2"), + expected: String::from("tests/expected_dvfs/MyTokenUpgradeV2.dvf.json"), + deployment_block: 24, + address: String::from("0x4ed7c70f96b99c776995fb64377f0d4ab3b0e1c1"), + }; + + // let config = DVFConfig::from_path(&config_file.path()).unwrap(); + // let mut new_dvf_path = config.dvf_storage.clone(); + + for v in [v1, v2] { + let outfile = NamedTempFile::new().unwrap(); + let mut dvf_cmd = Command::cargo_bin("dv").unwrap(); + let assert = dvf_cmd + .args(&[ + "--config", + &config_file.path().to_string_lossy(), + "init", + "--address", + &v.address, + "--chainid", + &chain_id_str(client_type.clone()), + "--project", + "tests/Contracts/", + "--initblock", + &v.deployment_block.to_string(), + "--contractname", + &v.contract, + &outfile.path().to_string_lossy(), + ]) + .assert() + .success(); + println!("{}", &String::from_utf8_lossy(&assert.get_output().stdout)); + + // Uncomment to regenerate expected files + std::fs::copy( + outfile.path(), + &Path::new(&v.expected), + ) + .unwrap(); + + assert_eq_files( + &outfile.path(), + &Path::new(&v.expected), + ); + + // Sign + let mut dvf_cmd = Command::cargo_bin("dv").unwrap(); + dvf_cmd + .args(&[ + "--config", + &config_file.path().to_string_lossy(), + "sign", + &outfile.path().to_string_lossy(), + ]) + .assert() + .success(); + + // new_dvf_path.push(&v.dv_path); + // outfile.persist(new_dvf_path.as_path()).unwrap(); + + } + + let proxy_outfile = NamedTempFile::new().unwrap(); + let mut dvf_cmd = Command::cargo_bin("dv").unwrap(); + let assert = dvf_cmd + .args(&[ + "--config", + &config_file.path().to_string_lossy(), + "init", + "--address", + "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", + "--chainid", + &chain_id_str(client_type.clone()), + "--project", + "tests/Contracts/", + "--contractname", + "TransparentUpgradeableProxy", + "--implementation", + "MyToken", + "--initblock", + "3", + &proxy_outfile.path().to_string_lossy(), + ]) + .assert() + .success(); + println!("{}", &String::from_utf8_lossy(&assert.get_output().stdout)); + + // Uncomment to regenerate expected files + std::fs::copy( + proxy_outfile.path(), + Path::new("tests/expected_dvfs/TransparentUpgradeableProxyUpgrade.dvf.json"), + ) + .unwrap(); + + // @note that this fails, since the wrong name is stored in the registry + assert_eq_files( + proxy_outfile.path(), + Path::new("tests/expected_dvfs/TransparentUpgradeableProxyUpgrade.dvf.json"), + ); + + // Update + let mut dvf_cmd: Command = Command::cargo_bin("dv").unwrap(); + let assert = dvf_cmd + .args(&[ + "--config", + &config_file.path().to_string_lossy(), + "update", + "--validationblock", + "26", + "--discover", + &proxy_outfile.path().to_string_lossy(), + "--implementation", + "MyTokenV2", + "--project", + "tests/Contracts/", + ]) + .assert() + .success(); + println!("{}", &String::from_utf8_lossy(&assert.get_output().stdout)); + + // Uncomment to regenerate expected files + let updated_path = format!("{}_updated.dvf.json", proxy_outfile.path().to_string_lossy()); + std::fs::copy(Path::new(&updated_path), Path::new("tests/expected_dvfs/TransparentUpgradeableProxyUpgrade_updated.dvf.json")).unwrap(); + + assert_eq_files( + Path::new(&updated_path), + Path::new("tests/expected_dvfs/TransparentUpgradeableProxyUpgrade_updated.dvf.json"), + ); + // Remove MyToken.dvf.json + // let mut rm_cmd: Command = Command::new("rm"); + // rm_cmd.arg(new_dvf_path.as_path()).assert().success(); + + drop(local_client); + } + } + + #[test] fn test_e2e_init_validate() { let port = 8549u16; From 37b242ac00b1010e00eb0ee2e5424e7ceac56623 Mon Sep 17 00:00:00 2001 From: markus Date: Tue, 15 Jul 2025 17:55:39 +0200 Subject: [PATCH 02/10] chore: format files --- src/dvf.rs | 176 ++++++++++++++++++++++----------------- tests/test_end_to_end.rs | 42 +++++----- 2 files changed, 122 insertions(+), 96 deletions(-) diff --git a/src/dvf.rs b/src/dvf.rs index 7869280a..3f84a7a1 100644 --- a/src/dvf.rs +++ b/src/dvf.rs @@ -547,7 +547,7 @@ fn main() { .value_parser(is_valid_blocknum), ) .arg( - arg!(--discover) + arg!(--discover) .help( "Also discover new storage variables and events" ).action(clap::ArgAction::SetTrue) @@ -866,7 +866,6 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { let zerovalue = sub_m.get_flag("zerovalue"); let user_deployment_tx = sub_m.get_one::("deployment"); - let user_output_path = Path::new(sub_m.get_one::("OUTPUT").unwrap()); // This is just a file name so we will place it in the configured folder let output_path: &Path = if is_filename_only_path(user_output_path) { @@ -983,7 +982,6 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { debug!("Copying parsed constructor arguments to dvf file"); dumped.copy_constructor_args(&project_info, &pretty_printer); - // Use the new helper function for discovery let discovery_params = DiscoveryParams { config: &config, @@ -997,10 +995,14 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { env, build_cache, libraries: libraries.clone(), - implementation_name: sub_m.get_one::("implementation").map(|s| s.as_str()), + implementation_name: sub_m + .get_one::("implementation") + .map(|s| s.as_str()), implementation_project: sub_m.get_one::("implementationproject"), implementation_env: env, - implementation_artifacts: sub_m.get_one::("implementationartifacts").unwrap(), + implementation_artifacts: sub_m + .get_one::("implementationartifacts") + .unwrap(), implementation_build_cache: sub_m.get_one::("implementationbuildcache"), zerovalue, event_topics: event_topics, @@ -1211,7 +1213,7 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { println!("input path {}", input_path.display()); let mut pc = 1_u64; - + let discover = sub_m.get_flag("discover"); println!("running discover mode? {}", discover); @@ -1264,12 +1266,17 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { artifacts: sub_m.get_one::("artifacts").unwrap(), env: *sub_m.get_one::("env").unwrap(), build_cache: sub_m.get_one::("buildcache"), - libraries: sub_m.get_many::("libraries") + libraries: sub_m + .get_many::("libraries") .map(|vals| vals.cloned().collect()), - implementation_name: sub_m.get_one::("implementation").map(|s| s.as_str()), + implementation_name: sub_m + .get_one::("implementation") + .map(|s| s.as_str()), implementation_project: sub_m.get_one::("implementationproject"), implementation_env: *sub_m.get_one::("implementationenv").unwrap(), - implementation_artifacts: sub_m.get_one::("implementationartifacts").unwrap(), + implementation_artifacts: sub_m + .get_one::("implementationartifacts") + .unwrap(), implementation_build_cache: sub_m.get_one::("implementationbuildcache"), zerovalue, event_topics: None, // Update mode doesn't filter by event topics @@ -1278,13 +1285,15 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { }; let discovery_result = discover_storage_and_events(discovery_params)?; - + // Update existing storage variables and add new ones - let current_storage_map: HashMap = discovery_result.critical_storage_variables - .iter() - .map(|var| (format!("{:#x}", var.slot), var)) - .collect(); - + let current_storage_map: HashMap = + discovery_result + .critical_storage_variables + .iter() + .map(|var| (format!("{:#x}", var.slot), var)) + .collect(); + // Check for changes in existing storage variables for storage_variable in updated.critical_storage_variables.iter_mut() { let slot_key = format!("{:#x}", storage_variable.slot); @@ -1305,27 +1314,31 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { } } } - + // Add new storage variables - let existing_slots: HashSet<_> = updated.critical_storage_variables + let existing_slots: HashSet<_> = updated + .critical_storage_variables .iter() .map(|var| var.slot) .collect(); - + for new_var in discovery_result.critical_storage_variables { if !existing_slots.contains(&new_var.slot) { - println!("Found new storage variable: {} at slot {}", - new_var.var_name, new_var.slot); + println!( + "Found new storage variable: {} at slot {}", + new_var.var_name, new_var.slot + ); updated.critical_storage_variables.push(new_var); } } - + // Update events similarly - let current_events_map: HashMap = discovery_result.critical_events + let current_events_map: HashMap = discovery_result + .critical_events .iter() .map(|event| (event.topic0, event)) .collect(); - + // Check for changes in existing events for critical_event in updated.critical_events.iter_mut() { if let Some(current_event) = current_events_map.get(&critical_event.topic0) { @@ -1340,21 +1353,24 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { } } } - + // Add new events - let existing_topics: HashSet<_> = updated.critical_events + let existing_topics: HashSet<_> = updated + .critical_events .iter() .map(|event| event.topic0) .collect(); - + for new_event in discovery_result.critical_events { if !existing_topics.contains(&new_event.topic0) { - println!("Found new event: {} with {} occurrences", - new_event.sig, new_event.occurrences.len()); + println!( + "Found new event: {} with {} occurrences", + new_event.sig, + new_event.occurrences.len() + ); updated.critical_events.push(new_event); } } - } else { // Fallback: manual storage checking without project info (original approach) for storage_variable in updated.critical_storage_variables.iter_mut() { @@ -1379,15 +1395,14 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { ) ); storage_variable.value = current_val[start_index..end_index].to_vec(); - + if let Some(var_type) = &storage_variable.var_type { - storage_variable.value_hint = Some( - pretty_printer.pretty_value_short_from_bytes( + storage_variable.value_hint = + Some(pretty_printer.pretty_value_short_from_bytes( var_type, &storage_variable.value, false, - ) - ); + )); } else { storage_variable.value_hint = None; } @@ -1420,7 +1435,8 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { replace_events = true; } - let num_shared = std::cmp::min(seen_events.len(), critical_event.occurrences.len()); + let num_shared = + std::cmp::min(seen_events.len(), critical_event.occurrences.len()); #[allow(clippy::needless_range_loop)] for i in 0..num_shared { let log_innner = &seen_events[i].inner; @@ -1663,8 +1679,6 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { } } - - struct DiscoveryParams<'a> { config: &'a DVFConfig, contract_name: &'a str, @@ -1697,24 +1711,28 @@ struct DiscoveryResult { proxy_warning: bool, } -fn discover_storage_and_events(params: DiscoveryParams) -> Result { +fn discover_storage_and_events( + params: DiscoveryParams, +) -> Result { let registry = registry::Registry::from_config(params.config)?; let pretty_printer = PrettyPrinter::new(params.config, Some(®istry)); - + // Initialize storage layout and types based on project availability - let (mut storage_layout, mut types, mut contract_state) = if let Some(project_path) = params.project { + let (mut storage_layout, mut types, mut contract_state) = if let Some(project_path) = + params.project + { let artifacts_path = get_project_paths(project_path, params.artifacts); // Load main project info let project_info = ProjectInfo::new( - ¶ms.contract_name.to_string(), - project_path, - params.env, - &artifacts_path, - params.build_cache, - params.libraries.clone(), - )?; - + ¶ms.contract_name.to_string(), + project_path, + params.env, + &artifacts_path, + params.build_cache, + params.libraries.clone(), + )?; + // Load storage layout using forge inspect let fi_layout = forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout( project_path, @@ -1756,7 +1774,7 @@ fn discover_storage_and_events(params: DiscoveryParams) -> Result Result Result Result Result Result { let mut set_of_sigs: HashSet = HashSet::new(); let mut res: Vec = vec![]; - + // Get main project events if available let main_events = if let Some(project_path) = params.project { let artifacts_path = get_project_paths(project_path, params.artifacts); let contract_name_string = params.contract_name.to_string(); let project_info = ProjectInfo::new( - &contract_name_string, - project_path, - params.env, - &artifacts_path, - params.build_cache, - params.libraries.clone(), - )?; + &contract_name_string, + project_path, + params.env, + &artifacts_path, + params.build_cache, + params.libraries.clone(), + )?; project_info.events.clone() } else { vec![] }; - + for eventlist in [&main_events, &imp_project.events] { for event in eventlist { let sig = event.selector(); @@ -2004,8 +2029,7 @@ fn discover_storage_and_events(params: DiscoveryParams) -> Result = - all_events.iter().map(|e| e.selector()).collect(); + let used_topics_0: HashSet = all_events.iter().map(|e| e.selector()).collect(); let all_topics_0: HashSet = seen_events.iter().map(|e| *e.topic0().unwrap()).collect(); for unused_topic in all_topics_0.difference(&used_topics_0) { @@ -2038,4 +2062,4 @@ fn discover_storage_and_events(params: DiscoveryParams) -> Result Date: Tue, 15 Jul 2025 18:07:14 +0200 Subject: [PATCH 03/10] chore: more format --- tests/Contracts/script/Deploy_ProxyUpgrade.s.sol | 10 +++++----- tests/Contracts/src/MyToken.sol | 7 ++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/Contracts/script/Deploy_ProxyUpgrade.s.sol b/tests/Contracts/script/Deploy_ProxyUpgrade.s.sol index fcd951ad..52a9e5c1 100644 --- a/tests/Contracts/script/Deploy_ProxyUpgrade.s.sol +++ b/tests/Contracts/script/Deploy_ProxyUpgrade.s.sol @@ -1,7 +1,10 @@ pragma solidity ^0.8.12; import "forge-std/Script.sol"; -import {TransparentUpgradeableProxy as TransparentUpgradeableProxy2, ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + TransparentUpgradeableProxy as TransparentUpgradeableProxy2, + ITransparentUpgradeableProxy +} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; import "../src/MyToken.sol"; @@ -31,12 +34,9 @@ contract S is Script { ProxyAdmin proxyAdmin = ProxyAdmin(address(bytes20(vm.load(address(p), ERC1967Utils.ADMIN_SLOT) << 96))); MyTokenV2 m2 = new MyTokenV2(); proxyAdmin.upgradeAndCall( - ITransparentUpgradeableProxy(address(p)), - address(m2), - abi.encodeCall(MyTokenV2.initialize, ()) + ITransparentUpgradeableProxy(address(p)), address(m2), abi.encodeCall(MyTokenV2.initialize, ()) ); real.dummy(); - vm.stopBroadcast(); } diff --git a/tests/Contracts/src/MyToken.sol b/tests/Contracts/src/MyToken.sol index f0c2f0ee..0c28cbf2 100644 --- a/tests/Contracts/src/MyToken.sol +++ b/tests/Contracts/src/MyToken.sol @@ -902,6 +902,7 @@ contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeabl */ uint256[45] private __gap; } + contract MyToken is Initializable, ERC20Upgradeable { address public immutable router; int16 public immutable penalty; @@ -913,7 +914,7 @@ contract MyToken is Initializable, ERC20Upgradeable { _disableInitializers(); } - function initialize() public initializer virtual { + function initialize() public virtual initializer { __ERC20_init("MyToken", "MTK"); _mint(msg.sender, 10 ** 19); } @@ -921,13 +922,13 @@ contract MyToken is Initializable, ERC20Upgradeable { function dummy() external virtual {} } - contract MyTokenV2 is MyToken { event DummyEvent(); + uint256 public dummyValue; /// @custom:oz-upgrades-unsafe-allow constructor - function initialize() public reinitializer(2) override { + function initialize() public override reinitializer(2) { __ERC20_init("MyTokenV2", "MTKV2"); } From 7e90a107dfb1637954b6d87f499efb0930fb9fc7 Mon Sep 17 00:00:00 2001 From: markus Date: Wed, 16 Jul 2025 11:37:44 +0200 Subject: [PATCH 04/10] refactor: move discover to lib --- lib/dvf/discovery.rs | 493 ++++++++++++++++++++++++++++++ lib/dvf/mod.rs | 1 + lib/utils/mod.rs | 1 + lib/utils/progress.rs | 24 ++ lib/utils/read_write_file.rs | 10 +- src/dvf.rs | 565 +++++------------------------------ 6 files changed, 595 insertions(+), 499 deletions(-) create mode 100644 lib/dvf/discovery.rs create mode 100644 lib/utils/progress.rs diff --git a/lib/dvf/discovery.rs b/lib/dvf/discovery.rs new file mode 100644 index 00000000..6bf0dcb6 --- /dev/null +++ b/lib/dvf/discovery.rs @@ -0,0 +1,493 @@ +use std::collections::{HashMap, HashSet}; +use std::path::PathBuf; + +use alloy::json_abi::Event; +use alloy::primitives::{Address, B256}; +use alloy_dyn_abi::EventExt; +use alloy_rpc_types::Log; +use clap::ArgMatches; +use colored::Colorize; +use prettytable::{row, Table}; +use tracing::{debug, info}; + +use crate::bytecode_verification::parse_json::{Environment, ProjectInfo}; +use crate::dvf::config::DVFConfig; +use crate::dvf::parse::{self, ValidationError}; +use crate::dvf::registry; +use crate::state::contract_state::ContractState; +use crate::state::forge_inspect; +use crate::utils::pretty::PrettyPrinter; +use crate::utils::progress::{print_progress, ProgressMode}; +use crate::utils::read_write_file::get_project_paths; +use crate::web3; + +pub struct DiscoveryParams<'a> { + pub config: &'a DVFConfig, + pub contract_name: &'a str, + pub address: &'a Address, + pub deployment_block_num: u64, + pub init_block_num: u64, + pub validation_block_num: u64, + pub project: Option<&'a PathBuf>, + pub artifacts: &'a str, + pub env: Environment, + pub build_cache: Option<&'a String>, + pub libraries: Option>, + pub implementation_name: Option<&'a str>, + pub implementation_project: Option<&'a PathBuf>, + pub implementation_env: Environment, + pub implementation_artifacts: &'a str, + pub implementation_build_cache: Option<&'a String>, + pub zerovalue: bool, + pub event_topics: Option>, + pub pc: &'a mut u64, + pub progress_mode: &'a ProgressMode, +} + +pub struct DiscoveryResult { + pub critical_storage_variables: Vec, + pub critical_events: Vec, + pub storage_var_table: Table, + pub event_table: Table, + pub all_events: Vec, + pub proxy_warning: bool, +} + +pub fn discover_storage_and_events( + params: DiscoveryParams, +) -> Result { + let registry = registry::Registry::from_config(params.config)?; + let pretty_printer = PrettyPrinter::new(params.config, Some(®istry)); + + // Initialize storage layout and types based on project availability + let (mut storage_layout, mut types, mut contract_state) = if let Some(project_path) = + params.project + { + let artifacts_path = get_project_paths(project_path, params.artifacts); + + // Load main project info + let project_info = ProjectInfo::new( + ¶ms.contract_name.to_string(), + project_path, + params.env, + &artifacts_path, + params.build_cache, + params.libraries.clone(), + )?; + + // Load storage layout using forge inspect + let fi_layout = forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout( + project_path, + ¶ms.contract_name, + project_info.absolute_path.clone(), + ); + let fi_ir = forge_inspect::ForgeInspectIrOptimized::generate_and_parse_ir_optimized( + project_path, + ¶ms.contract_name, + project_info.absolute_path.clone(), + ); + let mut contract_state = + ContractState::new_with_address(params.address, &pretty_printer); + contract_state.add_forge_inspect(&fi_layout, &fi_ir); + + ( + project_info.storage.clone(), + project_info.types.clone(), + contract_state, + ) + } else { + // Fallback: discovery without layout info + let contract_state = ContractState::new_with_address(params.address, &pretty_printer); + (vec![], HashMap::new(), contract_state) + }; + + // Handle implementation contract if present + let mut imp_project_info: Option = None; + if let Some(implementation_name) = params.implementation_name { + print_progress( + "Obtaining ABI of implementation contract.", + params.pc, + params.progress_mode, + ); + + let imp_path: PathBuf; + let imp_artifacts_path: PathBuf; + if let Some(imp_project) = params.implementation_project { + imp_artifacts_path = get_project_paths(imp_project, params.implementation_artifacts); + imp_path = imp_project.clone(); + } else if let Some(project_path) = params.project { + imp_path = project_path.clone(); + imp_artifacts_path = get_project_paths(project_path, params.artifacts); + } else { + return Err(ValidationError::from( + "Implementation contract specified but no project path provided", + )); + } + + let tmp_project_info = ProjectInfo::new( + &implementation_name.to_string(), + &imp_path, + params.implementation_env, + &imp_artifacts_path, + params.implementation_build_cache, + params.libraries.clone(), + )?; + + print_progress( + "Obtaining storage layout of implementation contract.", + params.pc, + params.progress_mode, + ); + let fi_impl_layout = + forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout( + &imp_path, + implementation_name, + tmp_project_info.absolute_path.clone(), + ); + let fi_impl_ir = + forge_inspect::ForgeInspectIrOptimized::generate_and_parse_ir_optimized( + &imp_path, + implementation_name, + tmp_project_info.absolute_path.clone(), + ); + contract_state.add_forge_inspect(&fi_impl_layout, &fi_impl_ir); + + storage_layout.extend(tmp_project_info.storage.clone()); + types.extend(tmp_project_info.types.clone()); + imp_project_info = Some(tmp_project_info); + } + + // Get transaction hashes based on event topics + let mut seen_events: Vec = vec![]; + let tx_hashes: Vec = if let Some(event_topics) = ¶ms.event_topics { + print_progress( + "Obtaining past events and transactions.", + params.pc, + params.progress_mode, + ); + seen_events = web3::get_eth_events( + params.config, + params.address, + params.deployment_block_num, + params.init_block_num, + event_topics, + )?; + seen_events + .iter() + .filter_map(|e| e.transaction_hash.map(|h| format!("{h:#x}"))) + .collect() + } else { + print_progress( + "Obtaining past transactions.", + params.pc, + params.progress_mode, + ); + web3::get_all_txs_for_contract( + params.config, + params.address, + params.deployment_block_num, + params.validation_block_num, + )? + }; + + print_progress("Getting storage snapshot.", params.pc, params.progress_mode); + let mut snapshot = web3::StorageSnapshot::from_api( + params.config, + params.address, + params.validation_block_num, + &tx_hashes, + )?; + + print_progress("Getting relevant traces.", params.pc, params.progress_mode); + let mut seen_transactions = HashSet::new(); + let mut missing_traces = false; + + for tx_hash in &tx_hashes { + if seen_transactions.contains(tx_hash) { + continue; + } + seen_transactions.insert(tx_hash); + + let mut found_trace = true; + if let Ok(trace) = web3::get_eth_debug_trace(params.config, tx_hash) { + if contract_state + .record_traces(params.config, vec![trace]) + .is_err() + { + found_trace = false; + missing_traces = true; + } + } else { + found_trace = false; + missing_traces = true; + } + + if !found_trace { + info!("Warning. The trace for {tx_hash} cannot be obtained. Some mapping slots might not be decodable."); + } + } + + if missing_traces { + println!("{}", "Warning. At least one transaction trace could not be obtained. This might result in \"unknown\" storage slots due to undecoded mapping keys.".yellow()) + } + + print_progress("Parsing storage snapshot.", params.pc, params.progress_mode); + let mut storage_var_table = Table::new(); + let critical_storage_variables: Vec = contract_state + .get_critical_storage_variables( + &mut snapshot, + &mut storage_var_table, + &storage_layout, + &types, + params.zerovalue, + )?; + + let proxy_warning = critical_storage_variables + .iter() + .any(|var| var.var_name == "unknown") + && imp_project_info.is_some(); + + // Event discovery logic + if params.event_topics.is_none() { + print_progress("Obtaining past events.", params.pc, params.progress_mode); + seen_events = web3::get_eth_events( + params.config, + params.address, + params.deployment_block_num, + params.validation_block_num, + &vec![], + )?; + } + + let mut covered_events = 0; + let mut event_table = Table::new(); + let mut critical_events: Vec = vec![]; + + print_progress("Decoding events.", params.pc, params.progress_mode); + + // Collect all Event Types, making sure to avoid duplications + let all_events = match &imp_project_info { + None => { + if let Some(project_path) = params.project { + let artifacts_path = get_project_paths(project_path, params.artifacts); + let contract_name_string = params.contract_name.to_string(); + let project_info = ProjectInfo::new( + &contract_name_string, + project_path, + params.env, + &artifacts_path, + params.build_cache, + params.libraries.clone(), + )?; + project_info.events.clone() + } else { + vec![] + } + } + Some(imp_project) => { + let mut set_of_sigs: HashSet = HashSet::new(); + let mut res: Vec = vec![]; + + // Get main project events if available + let main_events = if let Some(project_path) = params.project { + let artifacts_path = get_project_paths(project_path, params.artifacts); + let contract_name_string = params.contract_name.to_string(); + let project_info = ProjectInfo::new( + &contract_name_string, + project_path, + params.env, + &artifacts_path, + params.build_cache, + params.libraries.clone(), + )?; + project_info.events.clone() + } else { + vec![] + }; + + for eventlist in [&main_events, &imp_project.events] { + for event in eventlist { + let sig = event.selector(); + if set_of_sigs.contains(&sig) { + info!( + "Warning. Event {} omitted, as it is already known.", + PrettyPrinter::event_to_string(event) + ); + continue; + } + set_of_sigs.insert(sig); + debug!( + "Adding event {} to list.", + PrettyPrinter::event_to_string(event) + ); + res.push(event.clone()); + } + } + res + } + }; + + for abi_event in &all_events { + let sig = PrettyPrinter::event_to_string(abi_event); + debug!("Found the following event: {}", sig); + let topic0 = abi_event.selector(); + debug!("Topic0: {:?}", topic0); + let mut table_head = false; + + // Collect Occurrences + let mut occurrences: Vec = vec![]; + for seen_event in &seen_events { + if seen_event.topic0() == Some(&topic0) { + let log_inner = &seen_event.inner; + let decoded_event = abi_event.decode_log(log_inner)?; + let pretty_event = + pretty_printer.pretty_event_params(abi_event, &decoded_event, true); + + // Add Event Name to table + if !table_head { + event_table.add_row(row![sig]); + table_head = true; + } + // Add Event Occurrence to table + event_table.add_row(row![format!("- {}", pretty_event)]); + + let occurrence = parse::DVFEventOccurrence { + topics: log_inner.data.topics().to_vec(), + data: log_inner.data.data.clone(), + }; + occurrences.push(occurrence); + covered_events += 1; + } + } + + let event_entry = parse::DVFEventEntry { + sig: sig.clone(), + topic0, + occurrences, + }; + critical_events.push(event_entry); + } + + // Handle unknown events + if covered_events != seen_events.len() { + println!( + "Warning! Saw {} events, but able to decode {}.", + seen_events.len(), + covered_events + ); + let used_topics_0: HashSet = all_events.iter().map(|e| e.selector()).collect(); + let all_topics_0: HashSet = + seen_events.iter().map(|e| *e.topic0().unwrap()).collect(); + for unused_topic in all_topics_0.difference(&used_topics_0) { + // Collect Occurrences + let mut occurrences: Vec = vec![]; + for seen_event in &seen_events { + let log_inner = &seen_event.inner; + if seen_event.topic0() == Some(unused_topic) { + let occurrence = parse::DVFEventOccurrence { + topics: log_inner.data.topics().to_vec(), + data: log_inner.data.data.clone(), + }; + occurrences.push(occurrence); + } + } + let event_entry = parse::DVFEventEntry { + sig: String::from("Unknown Signature"), + topic0: *unused_topic, + occurrences, + }; + critical_events.push(event_entry); + } + } + + Ok(DiscoveryResult { + critical_storage_variables, + critical_events, + storage_var_table, + event_table, + all_events, + proxy_warning, + }) +} + +pub fn create_discovery_params_for_init<'a>( + config: &'a DVFConfig, + dumped: &'a parse::CompleteDVF, + deployment_block_num: u64, + init_block_num: u64, + project: &'a PathBuf, + artifacts: &'a str, + env: Environment, + build_cache: Option<&'a String>, + libraries: Option>, + zerovalue: bool, + event_topics: Option>, + sub_m: &'a ArgMatches, + pc: &'a mut u64, + progress_mode: &'a ProgressMode, +) -> DiscoveryParams<'a> { + DiscoveryParams { + config, + contract_name: &dumped.contract_name, + address: &dumped.address, + deployment_block_num, + init_block_num, + validation_block_num: init_block_num, + project: Some(project), + artifacts, + env, + build_cache, + libraries, + implementation_name: sub_m + .get_one::("implementation") + .map(|s| s.as_str()), + implementation_project: sub_m.get_one::("implementationproject"), + implementation_env: env, + implementation_artifacts: sub_m.get_one::("implementationartifacts").unwrap(), + implementation_build_cache: sub_m.get_one::("implementationbuildcache"), + zerovalue, + event_topics, + pc, + progress_mode, + } +} + +pub fn create_discovery_params_for_update<'a>( + config: &'a DVFConfig, + updated: &'a parse::CompleteDVF, + validation_block_num: u64, + project: Option<&'a PathBuf>, + artifacts: &'a str, + env: Environment, + build_cache: Option<&'a String>, + libraries: Option>, + zerovalue: bool, + sub_m: &'a ArgMatches, + pc: &'a mut u64, + progress_mode: &'a ProgressMode, +) -> DiscoveryParams<'a> { + DiscoveryParams { + config, + contract_name: &updated.contract_name, + address: &updated.address, + deployment_block_num: updated.deployment_block_num, + init_block_num: updated.init_block_num, + validation_block_num, + project, + artifacts, + env, + build_cache, + libraries, + implementation_name: sub_m + .get_one::("implementation") + .map(|s| s.as_str()), + implementation_project: sub_m.get_one::("implementationproject"), + implementation_env: *sub_m.get_one::("implementationenv").unwrap(), + implementation_artifacts: sub_m.get_one::("implementationartifacts").unwrap(), + implementation_build_cache: sub_m.get_one::("implementationbuildcache"), + zerovalue, + event_topics: None, // Update mode doesn't filter by event topics + pc, + progress_mode, + } +} diff --git a/lib/dvf/mod.rs b/lib/dvf/mod.rs index 56e72bbe..a6014528 100644 --- a/lib/dvf/mod.rs +++ b/lib/dvf/mod.rs @@ -1,4 +1,5 @@ pub mod abstract_wallet; pub mod config; +pub mod discovery; pub mod parse; pub mod registry; diff --git a/lib/utils/mod.rs b/lib/utils/mod.rs index f3c63c20..40735366 100644 --- a/lib/utils/mod.rs +++ b/lib/utils/mod.rs @@ -1,2 +1,3 @@ pub mod pretty; +pub mod progress; pub mod read_write_file; diff --git a/lib/utils/progress.rs b/lib/utils/progress.rs new file mode 100644 index 00000000..4706db04 --- /dev/null +++ b/lib/utils/progress.rs @@ -0,0 +1,24 @@ +pub enum ProgressMode { + Init, + InitProxy, + Update, + Validation, + BytecodeCheck, + GenerateBuildCache, + ListEvents, +} + +pub fn print_progress(s: &str, i: &mut u64, pm: &ProgressMode) { + use console::style; + let total = match pm { + ProgressMode::InitProxy => 14, + ProgressMode::Init => 12, + ProgressMode::Update => 4, + ProgressMode::Validation => 5, + ProgressMode::BytecodeCheck => 3, + ProgressMode::GenerateBuildCache => 1, + ProgressMode::ListEvents => 1, + }; + println!("{} {}", style(format!("[{i:2}/{total:2}]")).bold().dim(), s); + *i += 1; +} diff --git a/lib/utils/read_write_file.rs b/lib/utils/read_write_file.rs index 046114a5..1600d64d 100644 --- a/lib/utils/read_write_file.rs +++ b/lib/utils/read_write_file.rs @@ -1,6 +1,6 @@ use std::fs::File; use std::io::Read; -use std::path::Path; +use std::path::{Path, PathBuf}; pub fn list_files(outputs_path: &Path, aux_files: &mut Vec) { if outputs_path.is_dir() { @@ -19,3 +19,11 @@ pub fn read_file(json_path: &Path) -> String { data } + +pub fn get_project_paths(project: &std::path::Path, artifacts: &str) -> PathBuf { + let build_info_dir = "build-info"; + let mut artifacts_path = project.to_path_buf(); + artifacts_path.push(artifacts); + artifacts_path.push(build_info_dir); + artifacts_path +} diff --git a/src/dvf.rs b/src/dvf.rs index 3f84a7a1..59509217 100644 --- a/src/dvf.rs +++ b/src/dvf.rs @@ -4,10 +4,7 @@ use std::path::{Path, PathBuf}; use std::process::exit; use std::str::FromStr; -use alloy::json_abi::Event; use alloy::primitives::{Address, B256}; -use alloy_dyn_abi::EventExt; -use alloy_rpc_types::Log; use clap::{arg, value_parser, ArgMatches, Command}; use colored::Colorize; use console::style; @@ -15,11 +12,15 @@ use dvf_libs::bytecode_verification::compare_bytecodes::{CompareBytecode, Compar use dvf_libs::bytecode_verification::parse_json::{Environment, ProjectInfo}; use dvf_libs::bytecode_verification::verify_bytecode; use dvf_libs::dvf::config::{replace_tilde, DVFConfig}; +use dvf_libs::dvf::discovery::{ + create_discovery_params_for_init, create_discovery_params_for_update, + discover_storage_and_events, DiscoveryResult, +}; use dvf_libs::dvf::parse::{self, DVFStorageEntry, ValidationError, CURRENT_VERSION_STRING}; use dvf_libs::dvf::registry::{self, Registry}; -use dvf_libs::state::contract_state::ContractState; -use dvf_libs::state::forge_inspect::{self}; use dvf_libs::utils::pretty::PrettyPrinter; +use dvf_libs::utils::progress::ProgressMode; +use dvf_libs::utils::read_write_file::get_project_paths; use dvf_libs::web3; use indicatif::ProgressBar; use prettytable::{row, Table}; @@ -764,49 +765,6 @@ fn main() { }; } -enum ProgressMode { - Init, - InitProxy, - Update, - Validation, - BytecodeCheck, - GenerateBuildCache, - ListEvents, -} - -fn updated_filename(original_path: &Path) -> PathBuf { - // Extract the directory and name - let parent = original_path.parent().unwrap_or_else(|| Path::new("")); - let file_name = original_path - .file_name() - .unwrap_or_else(|| std::ffi::OsStr::new("")) - .to_string_lossy(); - let name = file_name.split(".dvf.json").next(); - - // Create a new stem with "_updated" added. - let updated_name = format!("{}_updated", name.unwrap_or("")); - - // Assemble the new path. - let mut updated_path = PathBuf::from(parent); - updated_path.push(updated_name); - updated_path.set_extension("dvf.json"); - updated_path -} - -fn print_progress(s: &str, i: &mut u64, pm: &ProgressMode) { - let total = match pm { - ProgressMode::InitProxy => 14, - ProgressMode::Init => 12, - ProgressMode::Update => 4, - ProgressMode::Validation => 5, - ProgressMode::BytecodeCheck => 3, - ProgressMode::GenerateBuildCache => 1, - ProgressMode::ListEvents => 1, - }; - println!("{} {}", style(format!("[{i:2}/{total:2}]")).bold().dim(), s); - *i += 1; -} - fn get_mismatch_msg( pretty_printer: &PrettyPrinter, storage_variable: &DVFStorageEntry, @@ -834,17 +792,6 @@ fn get_mismatch_msg( ) } -fn get_project_paths(project: &Path, artifacts: &str) -> PathBuf { - // no way to access other clap arguments during argument parsing so we have to verify - // artifacts paths here - let build_info_dir = "build-info"; - let mut artifacts_path = project.to_path_buf(); - artifacts_path.push(artifacts); - artifacts_path.push(build_info_dir); - - artifacts_path -} - fn process(matches: ArgMatches) -> Result<(), ValidationError> { let mut config = DVFConfig::from_matches(&matches)?; // Check which subcommand was used @@ -863,6 +810,7 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { let event_topics = sub_m .get_many::>("eventtopics") .map(|v| v.flat_map(|x| x.clone()).collect::>()); + let event_topics_clone = event_topics.clone(); let zerovalue = sub_m.get_flag("zerovalue"); let user_deployment_tx = sub_m.get_one::("deployment"); @@ -982,34 +930,6 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { debug!("Copying parsed constructor arguments to dvf file"); dumped.copy_constructor_args(&project_info, &pretty_printer); - // Use the new helper function for discovery - let discovery_params = DiscoveryParams { - config: &config, - contract_name: &dumped.contract_name, - address: &dumped.address, - deployment_block_num, - init_block_num, - validation_block_num: init_block_num, - project: Some(project), - artifacts, - env, - build_cache, - libraries: libraries.clone(), - implementation_name: sub_m - .get_one::("implementation") - .map(|s| s.as_str()), - implementation_project: sub_m.get_one::("implementationproject"), - implementation_env: env, - implementation_artifacts: sub_m - .get_one::("implementationartifacts") - .unwrap(), - implementation_build_cache: sub_m.get_one::("implementationbuildcache"), - zerovalue, - event_topics: event_topics, - pc: &mut pc, - progress_mode: &progress_mode, - }; - let DiscoveryResult { critical_storage_variables, critical_events, @@ -1017,7 +937,22 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { event_table, all_events, proxy_warning, - } = discover_storage_and_events(discovery_params)?; + } = discover_storage_and_events(create_discovery_params_for_init( + &config, + &dumped, + deployment_block_num, + init_block_num, + project, + artifacts, + env, + build_cache, + libraries.clone(), + zerovalue, + event_topics_clone, + sub_m, + &mut pc, + &progress_mode, + ))?; dumped.critical_storage_variables = critical_storage_variables; dumped.critical_events = critical_events; @@ -1254,37 +1189,23 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { print_progress("Checking Storage Variables.", &mut pc, &progress_mode); if discover { - // Use the new helper function for discovery - let discovery_params = DiscoveryParams { - config: &config, - contract_name: &updated.contract_name, - address: &updated.address, - deployment_block_num: updated.deployment_block_num, - init_block_num: updated.init_block_num, - validation_block_num, - project: sub_m.get_one::("project"), - artifacts: sub_m.get_one::("artifacts").unwrap(), - env: *sub_m.get_one::("env").unwrap(), - build_cache: sub_m.get_one::("buildcache"), - libraries: sub_m - .get_many::("libraries") - .map(|vals| vals.cloned().collect()), - implementation_name: sub_m - .get_one::("implementation") - .map(|s| s.as_str()), - implementation_project: sub_m.get_one::("implementationproject"), - implementation_env: *sub_m.get_one::("implementationenv").unwrap(), - implementation_artifacts: sub_m - .get_one::("implementationartifacts") - .unwrap(), - implementation_build_cache: sub_m.get_one::("implementationbuildcache"), - zerovalue, - event_topics: None, // Update mode doesn't filter by event topics - pc: &mut pc, - progress_mode: &progress_mode, - }; - - let discovery_result = discover_storage_and_events(discovery_params)?; + let discovery_result = + discover_storage_and_events(create_discovery_params_for_update( + &config, + &updated, + validation_block_num, + sub_m.get_one::("project"), + sub_m.get_one::("artifacts").unwrap(), + *sub_m.get_one::("env").unwrap(), + sub_m.get_one::("buildcache"), + sub_m + .get_many::("libraries") + .map(|vals| vals.cloned().collect()), + zerovalue, + sub_m, + &mut pc, + &progress_mode, + ))?; // Update existing storage variables and add new ones let current_storage_map: HashMap = @@ -1679,387 +1600,35 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { } } -struct DiscoveryParams<'a> { - config: &'a DVFConfig, - contract_name: &'a str, - address: &'a Address, - deployment_block_num: u64, - init_block_num: u64, - validation_block_num: u64, - project: Option<&'a PathBuf>, - artifacts: &'a str, - env: Environment, - build_cache: Option<&'a String>, - libraries: Option>, - implementation_name: Option<&'a str>, - implementation_project: Option<&'a PathBuf>, - implementation_env: Environment, - implementation_artifacts: &'a str, - implementation_build_cache: Option<&'a String>, - zerovalue: bool, - event_topics: Option>, - pc: &'a mut u64, - progress_mode: &'a ProgressMode, -} - -struct DiscoveryResult { - critical_storage_variables: Vec, - critical_events: Vec, - storage_var_table: Table, - event_table: Table, - all_events: Vec, - proxy_warning: bool, -} - -fn discover_storage_and_events( - params: DiscoveryParams, -) -> Result { - let registry = registry::Registry::from_config(params.config)?; - let pretty_printer = PrettyPrinter::new(params.config, Some(®istry)); - - // Initialize storage layout and types based on project availability - let (mut storage_layout, mut types, mut contract_state) = if let Some(project_path) = - params.project - { - let artifacts_path = get_project_paths(project_path, params.artifacts); - - // Load main project info - let project_info = ProjectInfo::new( - ¶ms.contract_name.to_string(), - project_path, - params.env, - &artifacts_path, - params.build_cache, - params.libraries.clone(), - )?; - - // Load storage layout using forge inspect - let fi_layout = forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout( - project_path, - ¶ms.contract_name, - project_info.absolute_path.clone(), - ); - let fi_ir = forge_inspect::ForgeInspectIrOptimized::generate_and_parse_ir_optimized( - project_path, - ¶ms.contract_name, - project_info.absolute_path.clone(), - ); - let mut contract_state = - ContractState::new_with_address(params.address, &pretty_printer); - contract_state.add_forge_inspect(&fi_layout, &fi_ir); - - (project_info.storage.clone(), project_info.types.clone(), contract_state) - } else { - // Fallback: discovery without layout info - let contract_state = ContractState::new_with_address(params.address, &pretty_printer); - (vec![], HashMap::new(), contract_state) - }; - - // Handle implementation contract if present - let mut imp_project_info: Option = None; - if let Some(implementation_name) = params.implementation_name { - print_progress( - "Obtaining ABI of implementation contract.", - params.pc, - params.progress_mode, - ); - - let imp_path: PathBuf; - let imp_artifacts_path: PathBuf; - if let Some(imp_project) = params.implementation_project { - imp_artifacts_path = get_project_paths(imp_project, params.implementation_artifacts); - imp_path = imp_project.clone(); - } else if let Some(project_path) = params.project { - imp_path = project_path.clone(); - imp_artifacts_path = get_project_paths(project_path, params.artifacts); - } else { - return Err(ValidationError::from( - "Implementation contract specified but no project path provided", - )); - } - - let tmp_project_info = ProjectInfo::new( - &implementation_name.to_string(), - &imp_path, - params.implementation_env, - &imp_artifacts_path, - params.implementation_build_cache, - params.libraries.clone(), - )?; - - print_progress( - "Obtaining storage layout of implementation contract.", - params.pc, - params.progress_mode, - ); - let fi_impl_layout = - forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout( - &imp_path, - implementation_name, - tmp_project_info.absolute_path.clone(), - ); - let fi_impl_ir = - forge_inspect::ForgeInspectIrOptimized::generate_and_parse_ir_optimized( - &imp_path, - implementation_name, - tmp_project_info.absolute_path.clone(), - ); - contract_state.add_forge_inspect(&fi_impl_layout, &fi_impl_ir); - - storage_layout.extend(tmp_project_info.storage.clone()); - types.extend(tmp_project_info.types.clone()); - imp_project_info = Some(tmp_project_info); - } - - // Get transaction hashes based on event topics - let mut seen_events: Vec = vec![]; - let tx_hashes: Vec = if let Some(event_topics) = ¶ms.event_topics { - print_progress( - "Obtaining past events and transactions.", - params.pc, - params.progress_mode, - ); - seen_events = web3::get_eth_events( - params.config, - params.address, - params.deployment_block_num, - params.init_block_num, - event_topics, - )?; - seen_events - .iter() - .filter_map(|e| e.transaction_hash.map(|h| format!("{:#x}", h))) - .collect() - } else { - print_progress( - "Obtaining past transactions.", - params.pc, - params.progress_mode, - ); - web3::get_all_txs_for_contract( - params.config, - params.address, - params.deployment_block_num, - params.validation_block_num, - )? - }; - - print_progress("Getting storage snapshot.", params.pc, params.progress_mode); - let mut snapshot = web3::StorageSnapshot::from_api( - params.config, - params.address, - params.validation_block_num, - &tx_hashes, - )?; - - print_progress("Getting relevant traces.", params.pc, params.progress_mode); - let mut seen_transactions = HashSet::new(); - let mut missing_traces = false; - - for tx_hash in &tx_hashes { - if seen_transactions.contains(tx_hash) { - continue; - } - seen_transactions.insert(tx_hash); - - let mut found_trace = true; - if let Ok(trace) = web3::get_eth_debug_trace(params.config, tx_hash) { - if contract_state - .record_traces(params.config, vec![trace]) - .is_err() - { - found_trace = false; - missing_traces = true; - } - } else { - found_trace = false; - missing_traces = true; - } - - if !found_trace { - info!("Warning. The trace for {tx_hash} cannot be obtained. Some mapping slots might not be decodable."); - } - } - - if missing_traces { - println!("{}", "Warning. At least one transaction trace could not be obtained. This might result in \"unknown\" storage slots due to undecoded mapping keys.".yellow()) - } - - print_progress("Parsing storage snapshot.", params.pc, params.progress_mode); - let mut storage_var_table = Table::new(); - let critical_storage_variables: Vec = contract_state - .get_critical_storage_variables( - &mut snapshot, - &mut storage_var_table, - &storage_layout, - &types, - params.zerovalue, - )?; +fn updated_filename(original_path: &Path) -> PathBuf { + // Extract the directory and name + let parent = original_path.parent().unwrap_or_else(|| Path::new("")); + let file_name = original_path + .file_name() + .unwrap_or_else(|| std::ffi::OsStr::new("")) + .to_string_lossy(); + let name = file_name.split(".dvf.json").next(); - let proxy_warning = critical_storage_variables - .iter() - .any(|var| var.var_name == "unknown") - && imp_project_info.is_some(); - - // Event discovery logic - if params.event_topics.is_none() { - print_progress("Obtaining past events.", params.pc, params.progress_mode); - seen_events = web3::get_eth_events( - params.config, - params.address, - params.deployment_block_num, - params.validation_block_num, - &vec![], - )?; - } + // Create a new stem with "_updated" added. + let updated_name = format!("{}_updated", name.unwrap_or("")); - let mut covered_events = 0; - let mut event_table = Table::new(); - let mut critical_events: Vec = vec![]; - - print_progress("Decoding events.", params.pc, params.progress_mode); - - // Collect all Event Types, making sure to avoid duplications - let all_events = match &imp_project_info { - None => { - if let Some(project_path) = params.project { - let artifacts_path = get_project_paths(project_path, params.artifacts); - let contract_name_string = params.contract_name.to_string(); - let project_info = ProjectInfo::new( - &contract_name_string, - project_path, - params.env, - &artifacts_path, - params.build_cache, - params.libraries.clone(), - )?; - project_info.events.clone() - } else { - vec![] - } - } - Some(imp_project) => { - let mut set_of_sigs: HashSet = HashSet::new(); - let mut res: Vec = vec![]; - - // Get main project events if available - let main_events = if let Some(project_path) = params.project { - let artifacts_path = get_project_paths(project_path, params.artifacts); - let contract_name_string = params.contract_name.to_string(); - let project_info = ProjectInfo::new( - &contract_name_string, - project_path, - params.env, - &artifacts_path, - params.build_cache, - params.libraries.clone(), - )?; - project_info.events.clone() - } else { - vec![] - }; + // Assemble the new path. + let mut updated_path = PathBuf::from(parent); + updated_path.push(updated_name); + updated_path.set_extension("dvf.json"); + updated_path +} - for eventlist in [&main_events, &imp_project.events] { - for event in eventlist { - let sig = event.selector(); - if set_of_sigs.contains(&sig) { - info!( - "Warning. Event {} omitted, as it is already known.", - PrettyPrinter::event_to_string(event) - ); - continue; - } - set_of_sigs.insert(sig); - debug!( - "Adding event {} to list.", - PrettyPrinter::event_to_string(event) - ); - res.push(event.clone()); - } - } - res - } +fn print_progress(s: &str, i: &mut u64, pm: &ProgressMode) { + let total = match pm { + ProgressMode::InitProxy => 14, + ProgressMode::Init => 12, + ProgressMode::Update => 4, + ProgressMode::Validation => 5, + ProgressMode::BytecodeCheck => 3, + ProgressMode::GenerateBuildCache => 1, + ProgressMode::ListEvents => 1, }; - - for abi_event in &all_events { - let sig = PrettyPrinter::event_to_string(abi_event); - debug!("Found the following event: {}", sig); - let topic0 = abi_event.selector(); - debug!("Topic0: {:?}", topic0); - let mut table_head = false; - - // Collect Occurrences - let mut occurrences: Vec = vec![]; - for seen_event in &seen_events { - if seen_event.topic0() == Some(&topic0) { - let log_inner = &seen_event.inner; - let decoded_event = abi_event.decode_log(log_inner)?; - let pretty_event = - pretty_printer.pretty_event_params(abi_event, &decoded_event, true); - - // Add Event Name to table - if !table_head { - event_table.add_row(row![sig]); - table_head = true; - } - // Add Event Occurrence to table - event_table.add_row(row![format!("- {}", pretty_event)]); - - let occurrence = parse::DVFEventOccurrence { - topics: log_inner.data.topics().to_vec(), - data: log_inner.data.data.clone(), - }; - occurrences.push(occurrence); - covered_events += 1; - } - } - - let event_entry = parse::DVFEventEntry { - sig: sig.clone(), - topic0, - occurrences, - }; - critical_events.push(event_entry); - } - - // Handle unknown events - if covered_events != seen_events.len() { - println!( - "Warning! Saw {} events, but able to decode {}.", - seen_events.len(), - covered_events - ); - let used_topics_0: HashSet = all_events.iter().map(|e| e.selector()).collect(); - let all_topics_0: HashSet = - seen_events.iter().map(|e| *e.topic0().unwrap()).collect(); - for unused_topic in all_topics_0.difference(&used_topics_0) { - // Collect Occurrences - let mut occurrences: Vec = vec![]; - for seen_event in &seen_events { - let log_inner = &seen_event.inner; - if seen_event.topic0() == Some(unused_topic) { - let occurrence = parse::DVFEventOccurrence { - topics: log_inner.data.topics().to_vec(), - data: log_inner.data.data.clone(), - }; - occurrences.push(occurrence); - } - } - let event_entry = parse::DVFEventEntry { - sig: String::from("Unknown Signature"), - topic0: *unused_topic, - occurrences, - }; - critical_events.push(event_entry); - } - } - - Ok(DiscoveryResult { - critical_storage_variables, - critical_events, - storage_var_table, - event_table, - all_events, - proxy_warning, - }) + println!("{} {}", style(format!("[{i:2}/{total:2}]")).bold().dim(), s); + *i += 1; } From b56a9df1ce88cc9a44219766d8bda188eba240b2 Mon Sep 17 00:00:00 2001 From: markus Date: Wed, 16 Jul 2025 12:01:14 +0200 Subject: [PATCH 05/10] chore: reduce diff to main --- src/dvf.rs | 59 ++++++++++++++++++++---------------------------------- 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/src/dvf.rs b/src/dvf.rs index 59509217..71ad0fe7 100644 --- a/src/dvf.rs +++ b/src/dvf.rs @@ -7,7 +7,6 @@ use std::str::FromStr; use alloy::primitives::{Address, B256}; use clap::{arg, value_parser, ArgMatches, Command}; use colored::Colorize; -use console::style; use dvf_libs::bytecode_verification::compare_bytecodes::{CompareBytecode, CompareInitCode}; use dvf_libs::bytecode_verification::parse_json::{Environment, ProjectInfo}; use dvf_libs::bytecode_verification::verify_bytecode; @@ -19,7 +18,7 @@ use dvf_libs::dvf::discovery::{ use dvf_libs::dvf::parse::{self, DVFStorageEntry, ValidationError, CURRENT_VERSION_STRING}; use dvf_libs::dvf::registry::{self, Registry}; use dvf_libs::utils::pretty::PrettyPrinter; -use dvf_libs::utils::progress::ProgressMode; +use dvf_libs::utils::progress::{print_progress, ProgressMode}; use dvf_libs::utils::read_write_file::get_project_paths; use dvf_libs::web3; use indicatif::ProgressBar; @@ -765,6 +764,25 @@ fn main() { }; } +fn updated_filename(original_path: &Path) -> PathBuf { + // Extract the directory and name + let parent = original_path.parent().unwrap_or_else(|| Path::new("")); + let file_name = original_path + .file_name() + .unwrap_or_else(|| std::ffi::OsStr::new("")) + .to_string_lossy(); + let name = file_name.split(".dvf.json").next(); + + // Create a new stem with "_updated" added. + let updated_name = format!("{}_updated", name.unwrap_or("")); + + // Assemble the new path. + let mut updated_path = PathBuf::from(parent); + updated_path.push(updated_name); + updated_path.set_extension("dvf.json"); + updated_path +} + fn get_mismatch_msg( pretty_printer: &PrettyPrinter, storage_variable: &DVFStorageEntry, @@ -810,7 +828,7 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { let event_topics = sub_m .get_many::>("eventtopics") .map(|v| v.flat_map(|x| x.clone()).collect::>()); - let event_topics_clone = event_topics.clone(); + let zerovalue = sub_m.get_flag("zerovalue"); let user_deployment_tx = sub_m.get_one::("deployment"); @@ -948,7 +966,7 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { build_cache, libraries.clone(), zerovalue, - event_topics_clone, + event_topics, sub_m, &mut pc, &progress_mode, @@ -1599,36 +1617,3 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { )), } } - -fn updated_filename(original_path: &Path) -> PathBuf { - // Extract the directory and name - let parent = original_path.parent().unwrap_or_else(|| Path::new("")); - let file_name = original_path - .file_name() - .unwrap_or_else(|| std::ffi::OsStr::new("")) - .to_string_lossy(); - let name = file_name.split(".dvf.json").next(); - - // Create a new stem with "_updated" added. - let updated_name = format!("{}_updated", name.unwrap_or("")); - - // Assemble the new path. - let mut updated_path = PathBuf::from(parent); - updated_path.push(updated_name); - updated_path.set_extension("dvf.json"); - updated_path -} - -fn print_progress(s: &str, i: &mut u64, pm: &ProgressMode) { - let total = match pm { - ProgressMode::InitProxy => 14, - ProgressMode::Init => 12, - ProgressMode::Update => 4, - ProgressMode::Validation => 5, - ProgressMode::BytecodeCheck => 3, - ProgressMode::GenerateBuildCache => 1, - ProgressMode::ListEvents => 1, - }; - println!("{} {}", style(format!("[{i:2}/{total:2}]")).bold().dim(), s); - *i += 1; -} From 0c3f23e574524096facdd6549a738b7c8a9899a1 Mon Sep 17 00:00:00 2001 From: markus Date: Wed, 16 Jul 2025 14:04:50 +0200 Subject: [PATCH 06/10] chore: remove files --- .../expected_dvfs/MyTokenUpgradable.dvf.json | 88 -------- .../MyTokenUpgradableV2.dvf.json | 93 -------- tests/expected_dvfs/MyTokenUpgrade.dvf.json | 6 +- tests/expected_dvfs/MyTokenV2.dvf.json | 93 -------- ...ransparentUpgradeableProxyUpgrade.dvf.json | 6 +- ...ansparentUpgradeableProxy_updated.dvf.json | 198 ------------------ .../TransparentUpgradeableProxy_updated.json | 198 ------------------ tests/test_end_to_end.rs | 39 ++-- 8 files changed, 21 insertions(+), 700 deletions(-) delete mode 100644 tests/expected_dvfs/MyTokenUpgradable.dvf.json delete mode 100644 tests/expected_dvfs/MyTokenUpgradableV2.dvf.json delete mode 100644 tests/expected_dvfs/MyTokenV2.dvf.json delete mode 100644 tests/expected_dvfs/TransparentUpgradeableProxy_updated.dvf.json delete mode 100644 tests/expected_dvfs/TransparentUpgradeableProxy_updated.json diff --git a/tests/expected_dvfs/MyTokenUpgradable.dvf.json b/tests/expected_dvfs/MyTokenUpgradable.dvf.json deleted file mode 100644 index 1cae4e27..00000000 --- a/tests/expected_dvfs/MyTokenUpgradable.dvf.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "version": "0.9.1", - "id": "0x024834e1407a5c744ed9744a0685ef87f1b189e81b23d109d05d930573f36669", - "contract_name": "MyToken", - "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", - "chain_id": 31337, - "deployment_block_num": 2, - "init_block_num": 3, - "deployment_tx": "0x304bc6298708bb09d7da6f199ee02fbcce1bdbb398fc3eb421cd3a03750e34f5", - "codehash": "0xa77e382f36db7714068fa97e6bc080fbc6b04a900a3199ab1aba56c9dea88f4d", - "insecure": false, - "immutables": [ - { - "var_name": "router", - "value": "0x000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564" - }, - { - "var_name": "penalty", - "value": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc", - "value_hint": "-4" - } - ], - "constructor_args": [], - "critical_storage_variables": [ - { - "slot": "0x0", - "offset": 0, - "var_name": "_initialized", - "var_type": "t_uint8", - "value": "0xff", - "value_hint": "255", - "comparison_operator": "Equal" - }, - { - "slot": "0x36", - "offset": 0, - "var_name": "_name (length=0)", - "var_type": "t_string_storage", - "value": "0x0000000000000000000000000000000000000000000000000000000000000000", - "value_hint": "", - "comparison_operator": "Equal" - }, - { - "slot": "0x37", - "offset": 0, - "var_name": "_symbol (length=0)", - "var_type": "t_string_storage", - "value": "0x0000000000000000000000000000000000000000000000000000000000000000", - "value_hint": "", - "comparison_operator": "Equal" - } - ], - "critical_events": [ - { - "sig": "Approval(address,address,uint256)", - "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", - "occurrences": [] - }, - { - "sig": "Initialized(uint8)", - "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", - "occurrences": [ - { - "topics": [ - "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" - ], - "data": "0x00000000000000000000000000000000000000000000000000000000000000ff" - } - ] - }, - { - "sig": "Transfer(address,address,uint256)", - "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "occurrences": [] - } - ], - "unvalidated_metadata": { - "author_name": "Author", - "description": "System Description", - "hardfork": [ - "paris", - "shanghai" - ], - "audit_report": "https://example.org/report.pdf", - "source_url": "https://github.com/source/code", - "security_contact": "security@example.org" - } -} \ No newline at end of file diff --git a/tests/expected_dvfs/MyTokenUpgradableV2.dvf.json b/tests/expected_dvfs/MyTokenUpgradableV2.dvf.json deleted file mode 100644 index 39d315dc..00000000 --- a/tests/expected_dvfs/MyTokenUpgradableV2.dvf.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "version": "0.9.1", - "id": "0x188bc607cecd2895f649f90c85b6ee72de600c6e33155224b563ce85ce35108e", - "contract_name": "MyTokenV2", - "address": "0x4ed7c70f96b99c776995fb64377f0d4ab3b0e1c1", - "chain_id": 31337, - "deployment_block_num": 24, - "init_block_num": 24, - "deployment_tx": "0xc0612fb22b533fed18617ee44f142aeda994b929c9d190af3bb6378d3bd10058", - "codehash": "0x10bb9b391821a504974d6b6431907bc83e338fdc3c61e3e09a0dde60f56e5072", - "insecure": false, - "immutables": [ - { - "var_name": "router", - "value": "0x000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564" - }, - { - "var_name": "penalty", - "value": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc", - "value_hint": "-4" - } - ], - "constructor_args": [], - "critical_storage_variables": [ - { - "slot": "0x0", - "offset": 0, - "var_name": "_initialized", - "var_type": "t_uint8", - "value": "0xff", - "value_hint": "255", - "comparison_operator": "Equal" - }, - { - "slot": "0x36", - "offset": 0, - "var_name": "_name (length=0)", - "var_type": "t_string_storage", - "value": "0x0000000000000000000000000000000000000000000000000000000000000000", - "value_hint": "", - "comparison_operator": "Equal" - }, - { - "slot": "0x37", - "offset": 0, - "var_name": "_symbol (length=0)", - "var_type": "t_string_storage", - "value": "0x0000000000000000000000000000000000000000000000000000000000000000", - "value_hint": "", - "comparison_operator": "Equal" - } - ], - "critical_events": [ - { - "sig": "Approval(address,address,uint256)", - "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", - "occurrences": [] - }, - { - "sig": "DummyEvent()", - "topic0": "0x321d83059d08b108790a2ca1fd77d843211cdfff5dc3b5d7fb4574be368d4497", - "occurrences": [] - }, - { - "sig": "Initialized(uint8)", - "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", - "occurrences": [ - { - "topics": [ - "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" - ], - "data": "0x00000000000000000000000000000000000000000000000000000000000000ff" - } - ] - }, - { - "sig": "Transfer(address,address,uint256)", - "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "occurrences": [] - } - ], - "unvalidated_metadata": { - "author_name": "Author", - "description": "System Description", - "hardfork": [ - "paris", - "shanghai" - ], - "audit_report": "https://example.org/report.pdf", - "source_url": "https://github.com/source/code", - "security_contact": "security@example.org" - } -} \ No newline at end of file diff --git a/tests/expected_dvfs/MyTokenUpgrade.dvf.json b/tests/expected_dvfs/MyTokenUpgrade.dvf.json index 1cae4e27..000e150d 100644 --- a/tests/expected_dvfs/MyTokenUpgrade.dvf.json +++ b/tests/expected_dvfs/MyTokenUpgrade.dvf.json @@ -1,12 +1,12 @@ { "version": "0.9.1", - "id": "0x024834e1407a5c744ed9744a0685ef87f1b189e81b23d109d05d930573f36669", + "id": "0xea582136017acdbdec6da968a7c63c78c13e25b9d435e7fd72fb64a75ff786e6", "contract_name": "MyToken", "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", - "chain_id": 31337, + "chain_id": 1337, "deployment_block_num": 2, "init_block_num": 3, - "deployment_tx": "0x304bc6298708bb09d7da6f199ee02fbcce1bdbb398fc3eb421cd3a03750e34f5", + "deployment_tx": "0x42d8e1a6dc338ecb42716c59dce82a58e56c067dff2a20975f28e05970e89400", "codehash": "0xa77e382f36db7714068fa97e6bc080fbc6b04a900a3199ab1aba56c9dea88f4d", "insecure": false, "immutables": [ diff --git a/tests/expected_dvfs/MyTokenV2.dvf.json b/tests/expected_dvfs/MyTokenV2.dvf.json deleted file mode 100644 index b3e23378..00000000 --- a/tests/expected_dvfs/MyTokenV2.dvf.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "version": "0.9.1", - "id": "0x0bd7289c3111320ff2a8090d0045221768ac34f4160b1a2c04be735f5d0b97fa", - "contract_name": "MyTokenV2", - "address": "0x4ed7c70f96b99c776995fb64377f0d4ab3b0e1c1", - "chain_id": 1337, - "deployment_block_num": 24, - "init_block_num": 24, - "deployment_tx": "0x78335a212659548ccfd1ce8f0312fbae2ee2f08c6c1f419f54426dc4361ab23f", - "codehash": "0x2d2f99165124eabdeac4265d1807087d116331b24c8d39c9612bcb8c6014cfca", - "insecure": false, - "immutables": [ - { - "var_name": "router", - "value": "0x000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564" - }, - { - "var_name": "penalty", - "value": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc", - "value_hint": "-4" - } - ], - "constructor_args": [], - "critical_storage_variables": [ - { - "slot": "0x0", - "offset": 0, - "var_name": "_initialized", - "var_type": "t_uint8", - "value": "0xff", - "value_hint": "255", - "comparison_operator": "Equal" - }, - { - "slot": "0x36", - "offset": 0, - "var_name": "_name (length=0)", - "var_type": "t_string_storage", - "value": "0x0000000000000000000000000000000000000000000000000000000000000000", - "value_hint": "", - "comparison_operator": "Equal" - }, - { - "slot": "0x37", - "offset": 0, - "var_name": "_symbol (length=0)", - "var_type": "t_string_storage", - "value": "0x0000000000000000000000000000000000000000000000000000000000000000", - "value_hint": "", - "comparison_operator": "Equal" - } - ], - "critical_events": [ - { - "sig": "Approval(address,address,uint256)", - "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", - "occurrences": [] - }, - { - "sig": "DummyEventV2()", - "topic0": "0xb9871046357876098ee52783798f5771ab2ef9c46bcdb309f2b4a31a758bf30c", - "occurrences": [] - }, - { - "sig": "Initialized(uint8)", - "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", - "occurrences": [ - { - "topics": [ - "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" - ], - "data": "0x00000000000000000000000000000000000000000000000000000000000000ff" - } - ] - }, - { - "sig": "Transfer(address,address,uint256)", - "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "occurrences": [] - } - ], - "unvalidated_metadata": { - "author_name": "Author", - "description": "System Description", - "hardfork": [ - "paris", - "shanghai" - ], - "audit_report": "https://example.org/report.pdf", - "source_url": "https://github.com/source/code", - "security_contact": "security@example.org" - } -} \ No newline at end of file diff --git a/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade.dvf.json b/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade.dvf.json index a18c2846..9762ed5d 100644 --- a/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade.dvf.json +++ b/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade.dvf.json @@ -1,12 +1,12 @@ { "version": "0.9.1", - "id": "0xdea34793be4dbfa1b940fc82b0831f2fc9410280de7e2b4854eacbd168e14974", + "id": "0x8091eaf511afa2caa8a44c2e359299ea560f608d3f207dd16f8c75d4ad591b43", "contract_name": "TransparentUpgradeableProxy", "address": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", - "chain_id": 31337, + "chain_id": 1337, "deployment_block_num": 3, "init_block_num": 3, - "deployment_tx": "0x45c4b4e7934f4dc4a2d83d990db1818b3dcd82618b9dd3809c048e5156180318", + "deployment_tx": "0xafab58d1b40c949aa4680c09e8012680d2d52708b49ad9666a206ec8a03b531b", "codehash": "0x07a3f582ceb15f50e21a238db7f4aab8320b4fac8d78379d05feaf2721f139b1", "insecure": false, "immutables": [ diff --git a/tests/expected_dvfs/TransparentUpgradeableProxy_updated.dvf.json b/tests/expected_dvfs/TransparentUpgradeableProxy_updated.dvf.json deleted file mode 100644 index c6dc2b12..00000000 --- a/tests/expected_dvfs/TransparentUpgradeableProxy_updated.dvf.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "version": "0.9.1", - "contract_name": "TransparentUpgradeableProxy", - "address": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", - "chain_id": 1337, - "deployment_block_num": 3, - "init_block_num": 3, - "deployment_tx": "0xafab58d1b40c949aa4680c09e8012680d2d52708b49ad9666a206ec8a03b531b", - "codehash": "0x07a3f582ceb15f50e21a238db7f4aab8320b4fac8d78379d05feaf2721f139b1", - "insecure": false, - "immutables": [ - { - "var_name": "_admin", - "value": "0x000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" - } - ], - "constructor_args": [ - { - "var_name": "_logic", - "value": "0x5fbdb2315678afecb367f032d93f642f64180aa3" - }, - { - "var_name": "initialOwner", - "value": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266" - }, - { - "var_name": "_data", - "value": "0x8129fc1c" - } - ], - "critical_storage_variables": [ - { - "slot": "0x0", - "offset": 0, - "var_name": "_initialized", - "var_type": "t_uint8", - "value": "0x02", - "value_hint": "2", - "comparison_operator": "Equal" - }, - { - "slot": "0x35", - "offset": 0, - "var_name": "_totalSupply", - "var_type": "t_uint256", - "value": "0x0000000000000000000000000000000000000000000000008ac7230489e80000", - "value_hint": "1. * 10^19", - "comparison_operator": "Equal" - }, - { - "slot": "0x36", - "offset": 0, - "var_name": "_name (length=7)", - "var_type": "t_string_storage", - "value": "0x4d79546f6b656e56320000000000000000000000000000000000000000000012", - "value_hint": "MyTokenV2", - "comparison_operator": "Equal" - }, - { - "slot": "0x37", - "offset": 0, - "var_name": "_symbol (length=3)", - "var_type": "t_string_storage", - "value": "0x4d544b563200000000000000000000000000000000000000000000000000000a", - "value_hint": "MTKV2", - "comparison_operator": "Equal" - }, - { - "slot": "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", - "offset": 0, - "var_name": "StorageSlot.IMPLEMENTATION_SLOT.AddressSlot.value", - "var_type": "t_address", - "value": "0x4ed7c70f96b99c776995fb64377f0d4ab3b0e1c1", - "comparison_operator": "Equal" - }, - { - "slot": "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103", - "offset": 0, - "var_name": "StorageSlot.ADMIN_SLOT.AddressSlot.value", - "var_type": "t_address", - "value": "0xcafac3dd18ac6c6e92c921884f9e4176737c052c", - "comparison_operator": "Equal" - }, - { - "slot": "0xf6d04bbe1a75429862aa97cc198c639a5559580c07ae94016a2b25986f2e3abd", - "offset": 0, - "var_name": "_balances[0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266]", - "var_type": "t_uint256", - "value": "0x0000000000000000000000000000000000000000000000008ac7230489e80000", - "value_hint": "1. * 10^19", - "comparison_operator": "Equal" - }, - { - "slot": "0x65", - "offset": 0, - "var_name": "dummyValue", - "var_type": "t_uint256", - "value": "0x000000000000000000000000000000000000000000000000000000000000002a", - "value_hint": "42", - "comparison_operator": "Equal" - } - ], - "critical_events": [ - { - "sig": "AdminChanged(address,address)", - "topic0": "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f", - "occurrences": [ - { - "topics": [ - "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f" - ], - "data": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" - } - ] - }, - { - "sig": "Upgraded(address)", - "topic0": "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", - "occurrences": [ - { - "topics": [ - "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", - "0x0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3" - ], - "data": "0x" - }, - { - "topics": [ - "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", - "0x0000000000000000000000004ed7c70f96b99c776995fb64377f0d4ab3b0e1c1" - ], - "data": "0x" - } - ] - }, - { - "sig": "Approval(address,address,uint256)", - "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", - "occurrences": [] - }, - { - "sig": "Initialized(uint8)", - "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", - "occurrences": [ - { - "topics": [ - "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" - ], - "data": "0x0000000000000000000000000000000000000000000000000000000000000001" - }, - { - "topics": [ - "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" - ], - "data": "0x0000000000000000000000000000000000000000000000000000000000000002" - } - ] - }, - { - "sig": "Transfer(address,address,uint256)", - "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "occurrences": [ - { - "topics": [ - "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266" - ], - "data": "0x0000000000000000000000000000000000000000000000008ac7230489e80000" - } - ] - }, - { - "sig": "DummyEvent()", - "topic0": "0x321d83059d08b108790a2ca1fd77d843211cdfff5dc3b5d7fb4574be368d4497", - "occurrences": [ - { - "topics": [ - "0x321d83059d08b108790a2ca1fd77d843211cdfff5dc3b5d7fb4574be368d4497" - ], - "data": "0x" - } - ] - } - ], - "unvalidated_metadata": { - "author_name": "Author", - "description": "System Description", - "hardfork": [ - "paris", - "shanghai" - ], - "audit_report": "https://example.org/report.pdf", - "source_url": "https://github.com/source/code", - "security_contact": "security@example.org", - "implementation_name": "MyToken" - } -} \ No newline at end of file diff --git a/tests/expected_dvfs/TransparentUpgradeableProxy_updated.json b/tests/expected_dvfs/TransparentUpgradeableProxy_updated.json deleted file mode 100644 index 4cde28c2..00000000 --- a/tests/expected_dvfs/TransparentUpgradeableProxy_updated.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "version": "0.9.1", - "contract_name": "TransparentUpgradeableProxy", - "address": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", - "chain_id": 31337, - "deployment_block_num": 3, - "init_block_num": 3, - "deployment_tx": "0x45c4b4e7934f4dc4a2d83d990db1818b3dcd82618b9dd3809c048e5156180318", - "codehash": "0x07a3f582ceb15f50e21a238db7f4aab8320b4fac8d78379d05feaf2721f139b1", - "insecure": false, - "immutables": [ - { - "var_name": "_admin", - "value": "0x000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" - } - ], - "constructor_args": [ - { - "var_name": "_logic", - "value": "0x5fbdb2315678afecb367f032d93f642f64180aa3" - }, - { - "var_name": "initialOwner", - "value": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266" - }, - { - "var_name": "_data", - "value": "0x8129fc1c" - } - ], - "critical_storage_variables": [ - { - "slot": "0x0", - "offset": 0, - "var_name": "_initialized", - "var_type": "t_uint8", - "value": "0x02", - "value_hint": "2", - "comparison_operator": "Equal" - }, - { - "slot": "0x35", - "offset": 0, - "var_name": "_totalSupply", - "var_type": "t_uint256", - "value": "0x0000000000000000000000000000000000000000000000008ac7230489e80000", - "value_hint": "1. * 10^19", - "comparison_operator": "Equal" - }, - { - "slot": "0x36", - "offset": 0, - "var_name": "_name (length=7)", - "var_type": "t_string_storage", - "value": "0x4d79546f6b656e56320000000000000000000000000000000000000000000012", - "value_hint": "MyTokenV2", - "comparison_operator": "Equal" - }, - { - "slot": "0x37", - "offset": 0, - "var_name": "_symbol (length=3)", - "var_type": "t_string_storage", - "value": "0x4d544b563200000000000000000000000000000000000000000000000000000a", - "value_hint": "MTKV2", - "comparison_operator": "Equal" - }, - { - "slot": "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", - "offset": 0, - "var_name": "StorageSlot.IMPLEMENTATION_SLOT.AddressSlot.value", - "var_type": "t_address", - "value": "0x4ed7c70f96b99c776995fb64377f0d4ab3b0e1c1", - "comparison_operator": "Equal" - }, - { - "slot": "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103", - "offset": 0, - "var_name": "StorageSlot.ADMIN_SLOT.AddressSlot.value", - "var_type": "t_address", - "value": "0xcafac3dd18ac6c6e92c921884f9e4176737c052c", - "comparison_operator": "Equal" - }, - { - "slot": "0xf6d04bbe1a75429862aa97cc198c639a5559580c07ae94016a2b25986f2e3abd", - "offset": 0, - "var_name": "_balances[0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266]", - "var_type": "t_uint256", - "value": "0x0000000000000000000000000000000000000000000000008ac7230489e80000", - "value_hint": "1. * 10^19", - "comparison_operator": "Equal" - }, - { - "slot": "0x65", - "offset": 0, - "var_name": "dummyValue", - "var_type": "t_uint256", - "value": "0x000000000000000000000000000000000000000000000000000000000000002a", - "value_hint": "42", - "comparison_operator": "Equal" - } - ], - "critical_events": [ - { - "sig": "AdminChanged(address,address)", - "topic0": "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f", - "occurrences": [ - { - "topics": [ - "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f" - ], - "data": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" - } - ] - }, - { - "sig": "Upgraded(address)", - "topic0": "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", - "occurrences": [ - { - "topics": [ - "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", - "0x0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3" - ], - "data": "0x" - }, - { - "topics": [ - "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", - "0x0000000000000000000000004ed7c70f96b99c776995fb64377f0d4ab3b0e1c1" - ], - "data": "0x" - } - ] - }, - { - "sig": "Approval(address,address,uint256)", - "topic0": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", - "occurrences": [] - }, - { - "sig": "Initialized(uint8)", - "topic0": "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498", - "occurrences": [ - { - "topics": [ - "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" - ], - "data": "0x0000000000000000000000000000000000000000000000000000000000000001" - }, - { - "topics": [ - "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" - ], - "data": "0x0000000000000000000000000000000000000000000000000000000000000002" - } - ] - }, - { - "sig": "Transfer(address,address,uint256)", - "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "occurrences": [ - { - "topics": [ - "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266" - ], - "data": "0x0000000000000000000000000000000000000000000000008ac7230489e80000" - } - ] - }, - { - "sig": "DummyEvent()", - "topic0": "0x321d83059d08b108790a2ca1fd77d843211cdfff5dc3b5d7fb4574be368d4497", - "occurrences": [ - { - "topics": [ - "0x321d83059d08b108790a2ca1fd77d843211cdfff5dc3b5d7fb4574be368d4497" - ], - "data": "0x" - } - ] - } - ], - "unvalidated_metadata": { - "author_name": "Author", - "description": "System Description", - "hardfork": [ - "paris", - "shanghai" - ], - "audit_report": "https://example.org/report.pdf", - "source_url": "https://github.com/source/code", - "security_contact": "security@example.org", - "implementation_name": "MyToken" - } -} \ No newline at end of file diff --git a/tests/test_end_to_end.rs b/tests/test_end_to_end.rs index 3e803f3c..b219856a 100644 --- a/tests/test_end_to_end.rs +++ b/tests/test_end_to_end.rs @@ -451,7 +451,7 @@ mod tests { let updated_path = format!("{}_updated.dvf.json", outfile.path().to_string_lossy()); // Uncomment to regenerate expected files - std::fs::copy(Path::new(&updated_path), Path::new(&testcase.updated)).unwrap(); + // std::fs::copy(Path::new(&updated_path), Path::new(&testcase.updated)).unwrap(); assert_eq_files(&Path::new(&updated_path), &Path::new(&testcase.updated)); @@ -823,9 +823,6 @@ mod tests { address: String::from("0x4ed7c70f96b99c776995fb64377f0d4ab3b0e1c1"), }; - // let config = DVFConfig::from_path(&config_file.path()).unwrap(); - // let mut new_dvf_path = config.dvf_storage.clone(); - for v in [v1, v2] { let outfile = NamedTempFile::new().unwrap(); let mut dvf_cmd = Command::cargo_bin("dv").unwrap(); @@ -851,7 +848,7 @@ mod tests { println!("{}", &String::from_utf8_lossy(&assert.get_output().stdout)); // Uncomment to regenerate expected files - std::fs::copy(outfile.path(), &Path::new(&v.expected)).unwrap(); + // std::fs::copy(outfile.path(), &Path::new(&v.expected)).unwrap(); assert_eq_files(&outfile.path(), &Path::new(&v.expected)); @@ -866,9 +863,6 @@ mod tests { ]) .assert() .success(); - - // new_dvf_path.push(&v.dv_path); - // outfile.persist(new_dvf_path.as_path()).unwrap(); } let proxy_outfile = NamedTempFile::new().unwrap(); @@ -897,11 +891,11 @@ mod tests { println!("{}", &String::from_utf8_lossy(&assert.get_output().stdout)); // Uncomment to regenerate expected files - std::fs::copy( - proxy_outfile.path(), - Path::new("tests/expected_dvfs/TransparentUpgradeableProxyUpgrade.dvf.json"), - ) - .unwrap(); + // std::fs::copy( + // proxy_outfile.path(), + // Path::new("tests/expected_dvfs/TransparentUpgradeableProxyUpgrade.dvf.json"), + // ) + // .unwrap(); // @note that this fails, since the wrong name is stored in the registry assert_eq_files( @@ -929,18 +923,18 @@ mod tests { .success(); println!("{}", &String::from_utf8_lossy(&assert.get_output().stdout)); - // Uncomment to regenerate expected files let updated_path = format!( "{}_updated.dvf.json", proxy_outfile.path().to_string_lossy() ); - std::fs::copy( - Path::new(&updated_path), - Path::new( - "tests/expected_dvfs/TransparentUpgradeableProxyUpgrade_updated.dvf.json", - ), - ) - .unwrap(); + // Uncomment to regenerate expected files + // std::fs::copy( + // Path::new(&updated_path), + // Path::new( + // "tests/expected_dvfs/TransparentUpgradeableProxyUpgrade_updated.dvf.json", + // ), + // ) + // .unwrap(); assert_eq_files( Path::new(&updated_path), @@ -948,9 +942,6 @@ mod tests { "tests/expected_dvfs/TransparentUpgradeableProxyUpgrade_updated.dvf.json", ), ); - // Remove MyToken.dvf.json - // let mut rm_cmd: Command = Command::new("rm"); - // rm_cmd.arg(new_dvf_path.as_path()).assert().success(); drop(local_client); } From 8fdf7afabdddcefb92e5de3de613a08ac91ca7eb Mon Sep 17 00:00:00 2001 From: Stefan Effenberger Date: Thu, 14 Aug 2025 10:02:36 +0200 Subject: [PATCH 07/10] clippy + fmt+ --- lib/dvf/discovery.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/dvf/discovery.rs b/lib/dvf/discovery.rs index 6bf0dcb6..8b923088 100644 --- a/lib/dvf/discovery.rs +++ b/lib/dvf/discovery.rs @@ -78,16 +78,15 @@ pub fn discover_storage_and_events( // Load storage layout using forge inspect let fi_layout = forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout( project_path, - ¶ms.contract_name, + params.contract_name, project_info.absolute_path.clone(), ); let fi_ir = forge_inspect::ForgeInspectIrOptimized::generate_and_parse_ir_optimized( project_path, - ¶ms.contract_name, + params.contract_name, project_info.absolute_path.clone(), ); - let mut contract_state = - ContractState::new_with_address(params.address, &pretty_printer); + let mut contract_state = ContractState::new_with_address(params.address, &pretty_printer); contract_state.add_forge_inspect(&fi_layout, &fi_ir); ( @@ -138,18 +137,16 @@ pub fn discover_storage_and_events( params.pc, params.progress_mode, ); - let fi_impl_layout = - forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout( + let fi_impl_layout = forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout( + &imp_path, + implementation_name, + tmp_project_info.absolute_path.clone(), + ); + let fi_impl_ir = forge_inspect::ForgeInspectIrOptimized::generate_and_parse_ir_optimized( &imp_path, implementation_name, tmp_project_info.absolute_path.clone(), ); - let fi_impl_ir = - forge_inspect::ForgeInspectIrOptimized::generate_and_parse_ir_optimized( - &imp_path, - implementation_name, - tmp_project_info.absolute_path.clone(), - ); contract_state.add_forge_inspect(&fi_impl_layout, &fi_impl_ir); storage_layout.extend(tmp_project_info.storage.clone()); @@ -410,6 +407,7 @@ pub fn discover_storage_and_events( }) } +#[allow(clippy::too_many_arguments)] pub fn create_discovery_params_for_init<'a>( config: &'a DVFConfig, dumped: &'a parse::CompleteDVF, @@ -452,6 +450,7 @@ pub fn create_discovery_params_for_init<'a>( } } +#[allow(clippy::too_many_arguments)] pub fn create_discovery_params_for_update<'a>( config: &'a DVFConfig, updated: &'a parse::CompleteDVF, From f61123ecbd8abcc7a5018314d46a94920e73128e Mon Sep 17 00:00:00 2001 From: Stefan Effenberger Date: Thu, 14 Aug 2025 15:21:17 +0200 Subject: [PATCH 08/10] changed behavior to only load slots after the init block, updated docs, fixed a few issues --- README.md | 24 ++++++++++++++--- lib/dvf/discovery.rs | 33 ++++++++++++----------- lib/utils/progress.rs | 2 ++ lib/web3.rs | 11 ++++++-- src/dvf.rs | 62 ++++++++++++++++++++++++++++++++----------- 5 files changed, 95 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index f5e929ee..5a30fe0f 100644 --- a/README.md +++ b/README.md @@ -308,9 +308,12 @@ dv validate --continue new.dvf.json ### Update DVF -**Please note: The `update` command is currently only updating existing storage variables in a DVF and might not be suitable for fully updating a DVF to the current state of a smart contract. This behavior will be changed in future releases.** +To update an existing DVF, you have two different options: -To update the values of storage variables in a DVF to the state of the latest block and gather all events up to this block, run the following command: +#. Update the values of existing storage variables in a DVF to the state of the latest block and gather all events up to this block. +#. Update the DVF with all incoming changes starting from the init block and gather all events up to this block. + +For the first option, run the following command: ``` dv update new.dvf.json @@ -324,6 +327,22 @@ dv update --validationblock new.dvf.json `` must be greater than the deployment block of the contract and smaller than or equal to the current block of the smart contract's chain. +For the second option, you can add the `--discover` flag: + +``` +dv update --discover new.dvf.json +``` + +When using the `--discover` flag, you can also specify all the flags related to implementation contracts (since these can change over time): + +``` +dv update --discover --implementation ... new.dvf.json +``` + +See [Proxies and Delegated calls](#proxies-and-delegated-calls) for more details. + +With the `--discover` flag, `dv` updates your DVF with all new storage slots starting from the init block until your chosen validation block. This can result in slots you previously discarded to reappear if the value of the slot changed after the init block. + ### Check Bytecode For a simple check that the compiled bytecode of a certain project is equal to the on-chain bytecode of an address, you can use the following command: @@ -590,7 +609,6 @@ This section will be updated soon. - As detailed [above](#dvf-creation), many public RPCs are not or only partially supported for DVF creation. - Finding the deployment transaction of a contract currently requires either Blockscout or Etherscan API keys to collect all relevant information. - Contracts performing `delegatecall` to more than one other contract are currently not supported. -- `dv update` currently only updates values of existing storage variables in the DVF and does not add newly added storage values. - Multiple contracts with the same name compiled with different compiler versions in one project are not supported. - Multi-dimensional mappings with static keys (e.g., `mapping[1][2]`) can currently not be decoded. - Empty-string mapping keys can currently not be decoded correctly. diff --git a/lib/dvf/discovery.rs b/lib/dvf/discovery.rs index 8b923088..1694b50c 100644 --- a/lib/dvf/discovery.rs +++ b/lib/dvf/discovery.rs @@ -25,9 +25,8 @@ pub struct DiscoveryParams<'a> { pub config: &'a DVFConfig, pub contract_name: &'a str, pub address: &'a Address, - pub deployment_block_num: u64, - pub init_block_num: u64, - pub validation_block_num: u64, + pub start_block_num: u64, + pub end_block_num: u64, pub project: Option<&'a PathBuf>, pub artifacts: &'a str, pub env: Environment, @@ -42,6 +41,7 @@ pub struct DiscoveryParams<'a> { pub event_topics: Option>, pub pc: &'a mut u64, pub progress_mode: &'a ProgressMode, + pub use_storage_range: bool, } pub struct DiscoveryResult { @@ -165,8 +165,8 @@ pub fn discover_storage_and_events( seen_events = web3::get_eth_events( params.config, params.address, - params.deployment_block_num, - params.init_block_num, + params.start_block_num, + params.end_block_num, event_topics, )?; seen_events @@ -182,8 +182,8 @@ pub fn discover_storage_and_events( web3::get_all_txs_for_contract( params.config, params.address, - params.deployment_block_num, - params.validation_block_num, + params.start_block_num, + params.end_block_num, )? }; @@ -191,8 +191,9 @@ pub fn discover_storage_and_events( let mut snapshot = web3::StorageSnapshot::from_api( params.config, params.address, - params.validation_block_num, + params.end_block_num, &tx_hashes, + params.use_storage_range, )?; print_progress("Getting relevant traces.", params.pc, params.progress_mode); @@ -250,8 +251,8 @@ pub fn discover_storage_and_events( seen_events = web3::get_eth_events( params.config, params.address, - params.deployment_block_num, - params.validation_block_num, + params.start_block_num, + params.end_block_num, &vec![], )?; } @@ -428,9 +429,8 @@ pub fn create_discovery_params_for_init<'a>( config, contract_name: &dumped.contract_name, address: &dumped.address, - deployment_block_num, - init_block_num, - validation_block_num: init_block_num, + start_block_num: deployment_block_num, + end_block_num: init_block_num, project: Some(project), artifacts, env, @@ -447,6 +447,7 @@ pub fn create_discovery_params_for_init<'a>( event_topics, pc, progress_mode, + use_storage_range: true, } } @@ -469,9 +470,8 @@ pub fn create_discovery_params_for_update<'a>( config, contract_name: &updated.contract_name, address: &updated.address, - deployment_block_num: updated.deployment_block_num, - init_block_num: updated.init_block_num, - validation_block_num, + start_block_num: updated.init_block_num + 1, + end_block_num: validation_block_num, project, artifacts, env, @@ -488,5 +488,6 @@ pub fn create_discovery_params_for_update<'a>( event_topics: None, // Update mode doesn't filter by event topics pc, progress_mode, + use_storage_range: false, // cannot use storage range here as we are only trying to get a subset of the state } } diff --git a/lib/utils/progress.rs b/lib/utils/progress.rs index 4706db04..1d1b32a4 100644 --- a/lib/utils/progress.rs +++ b/lib/utils/progress.rs @@ -2,6 +2,7 @@ pub enum ProgressMode { Init, InitProxy, Update, + UpdateFull, Validation, BytecodeCheck, GenerateBuildCache, @@ -14,6 +15,7 @@ pub fn print_progress(s: &str, i: &mut u64, pm: &ProgressMode) { ProgressMode::InitProxy => 14, ProgressMode::Init => 12, ProgressMode::Update => 4, + ProgressMode::UpdateFull => 11, ProgressMode::Validation => 5, ProgressMode::BytecodeCheck => 3, ProgressMode::GenerateBuildCache => 1, diff --git a/lib/web3.rs b/lib/web3.rs index 2767a3f6..8092905b 100644 --- a/lib/web3.rs +++ b/lib/web3.rs @@ -1396,10 +1396,17 @@ impl StorageSnapshot { address: &Address, init_block_num: u64, tx_hashes: &Vec, + use_storage_range: bool, ) -> Result { // First try special call - let snapshot: HashMap = if let Ok(storage_snapshot) = - get_eth_storage_snapshot(config, address, init_block_num) + let snapshot; + if use_storage_range { + snapshot = Some(get_eth_storage_snapshot(config, address, init_block_num)); + } else { + snapshot = None; + } + let snapshot: HashMap = if let Some(Ok(storage_snapshot)) = + snapshot { /* Self::validate_snapshot_with_mpt_root( diff --git a/src/dvf.rs b/src/dvf.rs index 71ad0fe7..05a0f0d7 100644 --- a/src/dvf.rs +++ b/src/dvf.rs @@ -183,6 +183,7 @@ fn validate_dvf( &pretty_printer, storage_variable, ¤t_val[start_index..end_index], + true ); if continue_on_mismatch { mismatch_found = true; @@ -555,52 +556,62 @@ fn main() { .arg( arg!(--project ) .help("Path to the root folder of the source code project (optional, improves storage layout discovery)") - .value_parser(is_valid_path), + .value_parser(is_valid_path) + .requires("discover"), ) .arg( arg!(--artifacts ) .help("Relative path to the compilation artifacts") - .default_value("artifacts"), + .default_value("artifacts") + .requires("discover"), ) .arg( arg!(--buildcache ) - .help("Build cache, if you have a very large project"), + .help("Build cache, if you have a very large project") + .requires("discover"), ) .arg( arg!(--libraries ) .help("Library linking information (address mapping)") .value_parser(value_parser!(String)) - .action(clap::ArgAction::Append), + .action(clap::ArgAction::Append) + .requires("discover"), ) .arg( arg!(--env ) .help("The compile environment") .value_parser(value_parser!(Environment)) - .default_value("foundry"), + .default_value("foundry") + .requires("discover"), ) .arg( arg!(--implementation ) - .help("Optional name of the implementation contract"), + .help("Optional name of the implementation contract") + .requires("discover"), ) .arg( arg!(--implementationproject ) .help("Path to the root folder of the implementation project") - .value_parser(is_valid_path), + .value_parser(is_valid_path) + .requires("discover"), ) .arg( arg!(--implementationenv ) .help("Implementation project's development environment") .value_parser(value_parser!(Environment)) - .default_value("foundry"), + .default_value("foundry") + .requires("discover"), ) .arg( arg!(--implementationartifacts ) .help("Folder containing the implementation project artifacts") - .default_value("artifacts"), + .default_value("artifacts") + .requires("discover"), ) .arg( arg!(--implementationbuildcache ) - .help("Folder containing the implementation contract's build-info files"), + .help("Folder containing the implementation contract's build-info files") + .requires("discover"), ) .arg( arg!(--zerovalue) @@ -787,6 +798,7 @@ fn get_mismatch_msg( pretty_printer: &PrettyPrinter, storage_variable: &DVFStorageEntry, current_value_slice: &[u8], + display_mismatch: bool ) -> String { let var_type = storage_variable.var_type.clone().unwrap_or_default(); let dec_current_value_slice = pretty_printer.pretty_value_short_from_bytes( @@ -797,8 +809,15 @@ fn get_mismatch_msg( let dec_old_value = pretty_printer.pretty_value_short_from_bytes(&var_type, &storage_variable.value, true); + let msg = if display_mismatch { + "Value mismatch" + } else { + "Updated value" + }; + format!( - "Value mismatch for {} (slot {:#x}, offset {}).\nNew value: 0x{} Decoded: {}\nOperator: {}\nOld value: 0x{} Decoded: {}", + "{} for {} (slot {:#x}, offset {}).\nNew value: 0x{} Decoded: {}\nOperator: {}\nOld value: 0x{} Decoded: {}", + msg, storage_variable.var_name, storage_variable.slot, storage_variable.offset, @@ -984,9 +1003,9 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { "{}. Warning. You are using an old compiler without storage layout. There will be no storage decoding.", pc ); pc += 1; - } else if proxy_warning { + } else if proxy_warning && sub_m.get_one::("implementation").is_none() { println!( - "{}. Warning. Not everything could be decoded. This could be because this is a proxy contract. In that case use --implementation to decode more.", pc + "{}. Warning. Some storage slots could not be decoded. This might happen because this is a proxy contract. In that case, use --implementation to decode more.", pc ); pc += 1; } @@ -1170,7 +1189,13 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { let discover = sub_m.get_flag("discover"); println!("running discover mode? {}", discover); - let progress_mode = ProgressMode::Update; + let progress_mode; + if discover { + progress_mode = ProgressMode::UpdateFull; + } else { + progress_mode = ProgressMode::Update; + } + print_progress("Loading file.", &mut pc, &progress_mode); let filled = parse::CompleteDVF::from_path(&input_path)?; @@ -1225,6 +1250,8 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { &progress_mode, ))?; + updated.init_block_num = validation_block_num; + // Update existing storage variables and add new ones let current_storage_map: HashMap = discovery_result @@ -1245,7 +1272,8 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { get_mismatch_msg( &pretty_printer, storage_variable, - ¤t_var.value + ¤t_var.value, + false ) ); storage_variable.value = current_var.value.clone(); @@ -1330,7 +1358,8 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { get_mismatch_msg( &pretty_printer, storage_variable, - ¤t_val[start_index..end_index] + ¤t_val[start_index..end_index], + false ) ); storage_variable.value = current_val[start_index..end_index].to_vec(); @@ -1421,6 +1450,7 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { break; } } + updated.generate_id()?; updated.write_to_file(&output_path)?; println!("Wrote the updated file to file: {}", output_path.display()); println!( From eca5c32561b577eccc5138235ba1a1825205b030 Mon Sep 17 00:00:00 2001 From: Stefan Effenberger Date: Thu, 14 Aug 2025 15:23:35 +0200 Subject: [PATCH 09/10] clippy + fmt --- lib/web3.rs | 13 +++++-------- src/dvf.rs | 13 ++++++------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/lib/web3.rs b/lib/web3.rs index 8092905b..db93918c 100644 --- a/lib/web3.rs +++ b/lib/web3.rs @@ -1399,15 +1399,12 @@ impl StorageSnapshot { use_storage_range: bool, ) -> Result { // First try special call - let snapshot; - if use_storage_range { - snapshot = Some(get_eth_storage_snapshot(config, address, init_block_num)); + let snapshot = if use_storage_range { + Some(get_eth_storage_snapshot(config, address, init_block_num)) } else { - snapshot = None; - } - let snapshot: HashMap = if let Some(Ok(storage_snapshot)) = - snapshot - { + None + }; + let snapshot: HashMap = if let Some(Ok(storage_snapshot)) = snapshot { /* Self::validate_snapshot_with_mpt_root( config, diff --git a/src/dvf.rs b/src/dvf.rs index 05a0f0d7..eed752d6 100644 --- a/src/dvf.rs +++ b/src/dvf.rs @@ -183,7 +183,7 @@ fn validate_dvf( &pretty_printer, storage_variable, ¤t_val[start_index..end_index], - true + true, ); if continue_on_mismatch { mismatch_found = true; @@ -798,7 +798,7 @@ fn get_mismatch_msg( pretty_printer: &PrettyPrinter, storage_variable: &DVFStorageEntry, current_value_slice: &[u8], - display_mismatch: bool + display_mismatch: bool, ) -> String { let var_type = storage_variable.var_type.clone().unwrap_or_default(); let dec_current_value_slice = pretty_printer.pretty_value_short_from_bytes( @@ -1189,12 +1189,11 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { let discover = sub_m.get_flag("discover"); println!("running discover mode? {}", discover); - let progress_mode; - if discover { - progress_mode = ProgressMode::UpdateFull; + let progress_mode = if discover { + ProgressMode::UpdateFull } else { - progress_mode = ProgressMode::Update; - } + ProgressMode::Update + }; print_progress("Loading file.", &mut pc, &progress_mode); From 44d37f911af3889f4de37252f3ec6083b10a6906 Mon Sep 17 00:00:00 2001 From: Stefan Effenberger Date: Thu, 14 Aug 2025 15:52:41 +0200 Subject: [PATCH 10/10] fixed test --- ...ntUpgradeableProxyUpgrade_updated.dvf.json | 36 +++---------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade_updated.dvf.json b/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade_updated.dvf.json index c6dc2b12..c399fbcf 100644 --- a/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade_updated.dvf.json +++ b/tests/expected_dvfs/TransparentUpgradeableProxyUpgrade_updated.dvf.json @@ -1,10 +1,11 @@ { "version": "0.9.1", + "id": "0xdf8305c2fe8f3b111189989267e5845cb90d271206f6a152ba5fd7736f3da7a6", "contract_name": "TransparentUpgradeableProxy", "address": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", "chain_id": 1337, "deployment_block_num": 3, - "init_block_num": 3, + "init_block_num": 26, "deployment_tx": "0xafab58d1b40c949aa4680c09e8012680d2d52708b49ad9666a206ec8a03b531b", "codehash": "0x07a3f582ceb15f50e21a238db7f4aab8320b4fac8d78379d05feaf2721f139b1", "insecure": false, @@ -104,14 +105,7 @@ { "sig": "AdminChanged(address,address)", "topic0": "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f", - "occurrences": [ - { - "topics": [ - "0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f" - ], - "data": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" - } - ] + "occurrences": [] }, { "sig": "Upgraded(address)", @@ -123,13 +117,6 @@ "0x0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3" ], "data": "0x" - }, - { - "topics": [ - "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", - "0x0000000000000000000000004ed7c70f96b99c776995fb64377f0d4ab3b0e1c1" - ], - "data": "0x" } ] }, @@ -147,28 +134,13 @@ "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" ], "data": "0x0000000000000000000000000000000000000000000000000000000000000001" - }, - { - "topics": [ - "0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498" - ], - "data": "0x0000000000000000000000000000000000000000000000000000000000000002" } ] }, { "sig": "Transfer(address,address,uint256)", "topic0": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "occurrences": [ - { - "topics": [ - "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266" - ], - "data": "0x0000000000000000000000000000000000000000000000008ac7230489e80000" - } - ] + "occurrences": [] }, { "sig": "DummyEvent()",