11//! Commonly used contract types and functions.
22
3+ use crate :: compile:: PathOrContractInfo ;
34use alloy_json_abi:: { Event , Function , JsonAbi } ;
45use alloy_primitives:: { hex, Address , Bytes , Selector , B256 } ;
5- use eyre:: Result ;
6+ use eyre:: { OptionExt , Result } ;
67use foundry_compilers:: {
78 artifacts:: {
89 BytecodeObject , CompactBytecode , CompactContractBytecode , CompactDeployedBytecode ,
9- ContractBytecodeSome , Offsets ,
10+ ConfigurableContractArtifact , ContractBytecodeSome , Offsets ,
1011 } ,
11- ArtifactId ,
12+ utils:: canonicalized,
13+ ArtifactId , Project , ProjectCompileOutput ,
14+ } ;
15+ use std:: {
16+ collections:: BTreeMap ,
17+ ops:: Deref ,
18+ path:: { Path , PathBuf } ,
19+ str:: FromStr ,
20+ sync:: Arc ,
1221} ;
13- use std:: { collections:: BTreeMap , ops:: Deref , str:: FromStr , sync:: Arc } ;
1422
1523/// Libraries' runtime code always starts with the following instruction:
1624/// `PUSH20 0x0000000000000000000000000000000000000000`
@@ -268,6 +276,17 @@ impl ContractsByArtifact {
268276 . map ( |( _, contract) | contract. abi . clone ( ) )
269277 }
270278
279+ /// Finds abi by name or source path
280+ ///
281+ /// Returns the abi and the contract name.
282+ pub fn find_abi_by_name_or_src_path ( & self , name_or_path : & str ) -> Option < ( JsonAbi , String ) > {
283+ self . iter ( )
284+ . find ( |( artifact, _) | {
285+ artifact. name == name_or_path || artifact. source == PathBuf :: from ( name_or_path)
286+ } )
287+ . map ( |( _, contract) | ( contract. abi . clone ( ) , contract. name . clone ( ) ) )
288+ }
289+
271290 /// Flattens the contracts into functions, events and errors.
272291 pub fn flatten ( & self ) -> ( BTreeMap < Selector , Function > , BTreeMap < B256 , Event > , JsonAbi ) {
273292 let mut funcs = BTreeMap :: new ( ) ;
@@ -288,6 +307,21 @@ impl ContractsByArtifact {
288307 }
289308}
290309
310+ impl From < ProjectCompileOutput > for ContractsByArtifact {
311+ fn from ( value : ProjectCompileOutput ) -> Self {
312+ Self :: new ( value. into_artifacts ( ) . map ( |( id, ar) | {
313+ (
314+ id,
315+ CompactContractBytecode {
316+ abi : ar. abi ,
317+ bytecode : ar. bytecode ,
318+ deployed_bytecode : ar. deployed_bytecode ,
319+ } ,
320+ )
321+ } ) )
322+ }
323+ }
324+
291325impl Deref for ContractsByArtifact {
292326 type Target = BTreeMap < ArtifactId , ContractData > ;
293327
@@ -399,6 +433,61 @@ pub fn compact_to_contract(contract: CompactContractBytecode) -> Result<Contract
399433 } )
400434}
401435
436+ /// Returns the canonicalized target path for the given identifier.
437+ pub fn find_target_path ( project : & Project , identifier : & PathOrContractInfo ) -> Result < PathBuf > {
438+ match identifier {
439+ PathOrContractInfo :: Path ( path) => Ok ( canonicalized ( project. root ( ) . join ( path) ) ) ,
440+ PathOrContractInfo :: ContractInfo ( info) => {
441+ let path = project. find_contract_path ( & info. name ) ?;
442+ Ok ( path)
443+ }
444+ }
445+ }
446+
447+ /// Returns the target artifact given the path and name.
448+ pub fn find_matching_contract_artifact (
449+ output : & mut ProjectCompileOutput ,
450+ target_path : & Path ,
451+ target_name : Option < & str > ,
452+ ) -> eyre:: Result < ConfigurableContractArtifact > {
453+ if let Some ( name) = target_name {
454+ output
455+ . remove ( target_path, name)
456+ . ok_or_eyre ( format ! ( "Could not find artifact `{name}` in the compiled artifacts" ) )
457+ } else {
458+ let possible_targets = output
459+ . artifact_ids ( )
460+ . filter ( |( id, _artifact) | id. source == target_path)
461+ . collect :: < Vec < _ > > ( ) ;
462+
463+ if possible_targets. is_empty ( ) {
464+ eyre:: bail!( "Could not find artifact linked to source `{target_path:?}` in the compiled artifacts" ) ;
465+ }
466+
467+ let ( target_id, target_artifact) = possible_targets[ 0 ] . clone ( ) ;
468+ if possible_targets. len ( ) == 1 {
469+ return Ok ( target_artifact. clone ( ) ) ;
470+ }
471+
472+ // If all artifact_ids in `possible_targets` have the same name (without ".", indicates
473+ // additional compiler profiles), it means that there are multiple contracts in the
474+ // same file.
475+ if !target_id. name . contains ( "." ) &&
476+ possible_targets. iter ( ) . any ( |( id, _) | id. name != target_id. name )
477+ {
478+ eyre:: bail!( "Multiple contracts found in the same file, please specify the target <path>:<contract> or <contract>" ) ;
479+ }
480+
481+ // Otherwise, we're dealing with additional compiler profiles wherein `id.source` is the
482+ // same but `id.path` is different.
483+ let artifact = possible_targets
484+ . iter ( )
485+ . find_map ( |( id, artifact) | if id. profile == "default" { Some ( * artifact) } else { None } )
486+ . unwrap_or ( target_artifact) ;
487+
488+ Ok ( artifact. clone ( ) )
489+ }
490+ }
402491#[ cfg( test) ]
403492mod tests {
404493 use super :: * ;
0 commit comments