2
2
use crate :: { term, TestFunctionExt } ;
3
3
use comfy_table:: { modifiers:: UTF8_ROUND_CORNERS , presets:: UTF8_FULL , * } ;
4
4
use ethers_solc:: {
5
- artifacts:: { ContractBytecodeSome , Source , Sources } ,
5
+ artifacts:: { BytecodeObject , Contract , ContractBytecodeSome , Source , Sources } ,
6
6
report:: NoReporter ,
7
7
Artifact , ArtifactId , FileFilter , Graph , Project , ProjectCompileOutput , Solc ,
8
8
} ;
@@ -67,8 +67,6 @@ impl ProjectCompiler {
67
67
where
68
68
F : FnOnce ( & Project ) -> eyre:: Result < ProjectCompileOutput > ,
69
69
{
70
- let ProjectCompiler { print_sizes, print_names } = self ;
71
-
72
70
if !project. paths . has_input_files ( ) {
73
71
println ! ( "Nothing to compile" ) ;
74
72
// nothing to do here
@@ -88,60 +86,62 @@ impl ProjectCompiler {
88
86
eyre:: bail!( output. to_string( ) )
89
87
} else if output. is_unchanged ( ) {
90
88
println ! ( "No files changed, compilation skipped" ) ;
89
+ self . handle_output ( & output) ;
91
90
} else {
92
91
// print the compiler output / warnings
93
92
println ! ( "{output}" ) ;
94
93
95
- // print any sizes or names
96
- if print_names {
97
- let compiled_contracts = output. compiled_contracts_by_compiler_version ( ) ;
98
- for ( version, contracts) in compiled_contracts. into_iter ( ) {
99
- println ! (
100
- " compiler version: {}.{}.{}" ,
101
- version. major, version. minor, version. patch
102
- ) ;
103
- for ( name, _) in contracts {
104
- println ! ( " - {name}" ) ;
105
- }
94
+ self . handle_output ( & output) ;
95
+ }
96
+
97
+ Ok ( output)
98
+ }
99
+
100
+ /// If configured, this will print sizes or names
101
+ fn handle_output ( & self , output : & ProjectCompileOutput ) {
102
+ // print any sizes or names
103
+ if self . print_names {
104
+ let compiled_contracts = output. compiled_contracts_by_compiler_version ( ) ;
105
+ for ( version, contracts) in compiled_contracts. into_iter ( ) {
106
+ println ! (
107
+ " compiler version: {}.{}.{}" ,
108
+ version. major, version. minor, version. patch
109
+ ) ;
110
+ for ( name, _) in contracts {
111
+ println ! ( " - {name}" ) ;
106
112
}
107
113
}
108
- if print_sizes {
109
- // add extra newline if names were already printed
110
- if print_names {
111
- println ! ( ) ;
112
- }
113
- let compiled_contracts = output. compiled_contracts_by_compiler_version ( ) ;
114
- let mut size_report = SizeReport { contracts : BTreeMap :: new ( ) } ;
115
- for ( _, contracts) in compiled_contracts. into_iter ( ) {
116
- for ( name, contract) in contracts {
117
- let size = contract
118
- . get_deployed_bytecode_bytes ( )
119
- . map ( |bytes| bytes. 0 . len ( ) )
120
- . unwrap_or_default ( ) ;
121
-
122
- let dev_functions =
123
- contract. abi . as_ref ( ) . unwrap ( ) . abi . functions ( ) . into_iter ( ) . filter (
124
- |func| {
125
- func. name . is_test ( ) ||
126
- func. name . eq ( "IS_TEST" ) ||
127
- func. name . eq ( "IS_SCRIPT" )
128
- } ,
129
- ) ;
130
-
131
- let is_dev_contract = dev_functions. into_iter ( ) . count ( ) > 0 ;
132
- size_report. contracts . insert ( name, ContractInfo { size, is_dev_contract } ) ;
133
- }
114
+ }
115
+ if self . print_sizes {
116
+ // add extra newline if names were already printed
117
+ if self . print_names {
118
+ println ! ( ) ;
119
+ }
120
+ let compiled_contracts = output. compiled_contracts_by_compiler_version ( ) ;
121
+ let mut size_report = SizeReport { contracts : BTreeMap :: new ( ) } ;
122
+ for ( _, contracts) in compiled_contracts. into_iter ( ) {
123
+ for ( name, contract) in contracts {
124
+ let size = deployed_contract_size ( & contract) . unwrap_or_default ( ) ;
125
+
126
+ let dev_functions =
127
+ contract. abi . as_ref ( ) . unwrap ( ) . abi . functions ( ) . into_iter ( ) . filter ( |func| {
128
+ func. name . is_test ( ) ||
129
+ func. name . eq ( "IS_TEST" ) ||
130
+ func. name . eq ( "IS_SCRIPT" )
131
+ } ) ;
132
+
133
+ let is_dev_contract = dev_functions. into_iter ( ) . count ( ) > 0 ;
134
+ size_report. contracts . insert ( name, ContractInfo { size, is_dev_contract } ) ;
134
135
}
136
+ }
135
137
136
- println ! ( "{size_report}" ) ;
138
+ println ! ( "{size_report}" ) ;
137
139
138
- // exit with error if any contract exceeds the size limit, excluding test contracts.
139
- let exit_status = size_report. exceeds_size_limit ( ) . into ( ) ;
140
- std:: process:: exit ( exit_status ) ;
140
+ // exit with error if any contract exceeds the size limit, excluding test contracts.
141
+ if size_report. exceeds_size_limit ( ) {
142
+ std:: process:: exit ( 1 ) ;
141
143
}
142
144
}
143
-
144
- Ok ( output)
145
145
}
146
146
}
147
147
@@ -203,7 +203,27 @@ impl Display for SizeReport {
203
203
}
204
204
}
205
205
206
+ /// Returns the size of the deployed contract
207
+ pub fn deployed_contract_size ( contract : & Contract ) -> Option < usize > {
208
+ let bytecode = contract. get_deployed_bytecode_object ( ) ?;
209
+ let size = match bytecode. as_ref ( ) {
210
+ BytecodeObject :: Bytecode ( bytes) => bytes. len ( ) ,
211
+ BytecodeObject :: Unlinked ( unlinked) => {
212
+ // we don't need to account for placeholders here, because library placeholders take up
213
+ // 40 characters: `__$<library hash>$__` which is the same as a 20byte address in hex.
214
+ let mut size = unlinked. as_bytes ( ) . len ( ) ;
215
+ if unlinked. starts_with ( "0x" ) {
216
+ size -= 2 ;
217
+ }
218
+ // hex -> bytes
219
+ size / 2
220
+ }
221
+ } ;
222
+ Some ( size)
223
+ }
224
+
206
225
/// How big the contract is and whether it is a dev contract where size limits can be neglected
226
+ #[ derive( Debug , Clone , Copy ) ]
207
227
pub struct ContractInfo {
208
228
/// size of the contract in bytes
209
229
pub size : usize ,
0 commit comments