Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(forge): add forge sig-collision cmd #4973

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cli/src/cmd/forge/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub mod inspect;
pub mod install;
pub mod remappings;
pub mod remove;
pub mod sig_collision;
pub mod script;
pub mod snapshot;
pub mod test;
Expand Down
100 changes: 100 additions & 0 deletions cli/src/cmd/forge/sig_collision.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use crate::{
cmd::{forge::build::CoreBuildArgs, Cmd},
};
use clap::Parser;
use ethers::{
prelude::{
info::ContractInfo
},
solc::{
utils::canonicalize
}
};
use foundry_common::compile;
use tracing::trace;

#[derive(Debug, Clone, Parser)]
pub struct SigCollisionArgs {
#[clap(
help = "The first of the two contracts for which to look method signature collisions in the form `(<path>:)?<contractname>`.",
value_name = "FIRST_CONTRACT"
)]
pub first_contract: ContractInfo,

#[clap(
help = "The second of the two contracts for which to look method signature collisions in the form `(<path>:)?<contractname>`.",
value_name = "SECOND_CONTRACT"
)]
pub second_contract: ContractInfo,
Comment on lines +18 to +28
Copy link
Member

@DaniPopes DaniPopes May 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please adhere to the style of the other Clap Parser structs? This would mean the help and about are turned into doc comments, and value_name is removed as it's redundant.


/// Support build arguments
#[clap(flatten)]
build: CoreBuildArgs,
}

impl Cmd for SigCollisionArgs {
type Output = ();

fn run(self) -> eyre::Result<Self::Output> {
let SigCollisionArgs {mut first_contract, mut second_contract, build} = self;

trace!(target: "forge", ?first_contract, ?second_contract, "running forge sig-collision");

println!("{} {}", first_contract.path.as_ref().unwrap(), second_contract.path.as_ref().unwrap());

// Build first project
let first_project = build.project()?;
let first_outcome = if let Some(ref mut contract_path) = first_contract.path {
let target_path = canonicalize(&*contract_path)?;
*contract_path = target_path.to_string_lossy().to_string();
compile::compile_files(&first_project, vec![target_path], true)
} else {
compile::suppress_compile(&first_project)
}?;

// Build second project
let second_project = build.project()?;
let second_outcome = if let Some(ref mut contract_path) = second_contract.path {
let target_path = canonicalize(&*contract_path)?;
*contract_path = target_path.to_string_lossy().to_string();
compile::compile_files(&second_project, vec![target_path], true)
} else {
compile::suppress_compile(&second_project)
}?;

// Find the artifacts
let first_found_artifact = first_outcome.find_contract(&first_contract);
let second_found_artifact = second_outcome.find_contract(&second_contract);

trace!(target: "forge", artifact=?first_found_artifact, input=?first_contract, "Found artifact");
trace!(target: "forge", artifact=?second_found_artifact, input=?second_contract, "Found artifact");

// Unwrapping inner artifacts
let first_artifact = first_found_artifact.ok_or_else( || {
eyre::eyre!("Failed to extract first artifact bytecode as a string")
})?;
let second_artifact = second_found_artifact.ok_or_else( || {
eyre::eyre!("Failed to extract second artifact bytecode as a string")
})?;

let first_method_map = first_artifact.method_identifiers.as_ref().unwrap();
let second_method_map = second_artifact.method_identifiers.as_ref().unwrap();

let mut clashing_methods = Vec::new();
for (k1, v1) in first_method_map {
if let Some(k2) = second_method_map.iter().find_map(| (k2, v2) | if v1 == v2 {Some(k2)} else {None} ) {
clashing_methods.push((k1.clone(), k2.clone()))
};
}

if clashing_methods.is_empty() {
println!("No clashing methods between the two contracts.");
} else {
println!("The two contracts have the following methods whose signatures clash: {:#?}",
clashing_methods
);
}

Ok(())
}
}
1 change: 1 addition & 0 deletions cli/src/forge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,6 @@ fn main() -> eyre::Result<()> {
Ok(())
}
Subcommands::Doc(cmd) => cmd.run(),
Subcommands::SigCollision(cmd) => cmd.run(),
}
}
7 changes: 7 additions & 0 deletions cli/src/opts/forge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::cmd::forge::{
install::InstallArgs,
remappings::RemappingArgs,
remove::RemoveArgs,
sig_collision::SigCollisionArgs,
script::ScriptArgs,
snapshot, test, tree, update,
verify::{VerifyArgs, VerifyCheckArgs},
Expand Down Expand Up @@ -155,6 +156,12 @@ pub enum Subcommands {

/// Generate documentation for the project.
Doc(DocArgs),

/// Detects methods from 2 contracts whose selectors clash
#[clap(
about = "Detect method signature collision between two contracts."
)]
SigCollision(SigCollisionArgs),
}

// A set of solc compiler settings that can be set via command line arguments, which are intended
Expand Down