1
1
//! Commonly used contract types and functions.
2
2
3
+ use crate :: compile:: PathOrContractInfo ;
3
4
use alloy_json_abi:: { Event , Function , JsonAbi } ;
4
5
use alloy_primitives:: { hex, Address , Bytes , Selector , B256 } ;
5
- use eyre:: Result ;
6
+ use eyre:: { OptionExt , Result } ;
6
7
use foundry_compilers:: {
7
8
artifacts:: {
8
9
BytecodeObject , CompactBytecode , CompactContractBytecode , CompactDeployedBytecode ,
9
- ContractBytecodeSome , Offsets ,
10
+ ConfigurableContractArtifact , ContractBytecodeSome , Offsets ,
10
11
} ,
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 ,
12
21
} ;
13
- use std:: { collections:: BTreeMap , ops:: Deref , str:: FromStr , sync:: Arc } ;
14
22
15
23
/// Libraries' runtime code always starts with the following instruction:
16
24
/// `PUSH20 0x0000000000000000000000000000000000000000`
@@ -268,6 +276,17 @@ impl ContractsByArtifact {
268
276
. map ( |( _, contract) | contract. abi . clone ( ) )
269
277
}
270
278
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
+
271
290
/// Flattens the contracts into functions, events and errors.
272
291
pub fn flatten ( & self ) -> ( BTreeMap < Selector , Function > , BTreeMap < B256 , Event > , JsonAbi ) {
273
292
let mut funcs = BTreeMap :: new ( ) ;
@@ -288,6 +307,21 @@ impl ContractsByArtifact {
288
307
}
289
308
}
290
309
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
+
291
325
impl Deref for ContractsByArtifact {
292
326
type Target = BTreeMap < ArtifactId , ContractData > ;
293
327
@@ -399,6 +433,61 @@ pub fn compact_to_contract(contract: CompactContractBytecode) -> Result<Contract
399
433
} )
400
434
}
401
435
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
+ }
402
491
#[ cfg( test) ]
403
492
mod tests {
404
493
use super :: * ;
0 commit comments