crytic_compile.source_unit

Module handling the source unit Each source unit represents one file so may be associated with One or more source units are associated with each compilation unit

  1"""
  2Module handling the source unit
  3Each source unit represents one file so may be associated with
  4One or more source units are associated with each compilation unit
  5"""
  6import re
  7from typing import Dict, List, Optional, Union, Tuple, TYPE_CHECKING
  8import cbor2
  9
 10from Crypto.Hash import keccak
 11
 12from crytic_compile.utils.naming import Filename
 13from crytic_compile.utils.natspec import Natspec
 14
 15if TYPE_CHECKING:
 16    from crytic_compile.compilation_unit import CompilationUnit
 17
 18
 19def get_library_candidate(filename: Filename, contract_name: str) -> List[str]:
 20    """
 21    Return candidate name for library linking. A candidate is a str that might be found in other bytecodes
 22
 23    Args:
 24        filename: filename of the contract
 25        contract_name: contract name
 26
 27    Returns:
 28        The list of candidates
 29    """
 30
 31    # Some platform use only the contract name
 32    # Some use fimename:contract_name
 33
 34    ret: List[str] = []
 35
 36    name_with_absolute_filename = filename.absolute + ":" + contract_name
 37    name_with_used_filename = filename.used + ":" + contract_name
 38
 39    # Only 36 char were used in the past
 40    # See https://docs.soliditylang.org/en/develop/using-the-compiler.html#library-linking
 41    names_candidates = [
 42        name_with_absolute_filename,
 43        name_with_absolute_filename[0:36],
 44        name_with_used_filename,
 45        name_with_used_filename[0:36],
 46    ]
 47
 48    # Solidity 0.4
 49    ret.append("__" + contract_name + "_" * (38 - len(contract_name)))
 50
 51    for name_candidate in names_candidates:
 52        # Solidity 0.4 with filename
 53        ret.append("__" + name_candidate + "_" * (38 - len(name_candidate)))
 54
 55        # Solidity 0.5
 56        sha3_result = keccak.new(digest_bits=256)
 57        sha3_result.update(name_candidate.encode("utf-8"))
 58        ret.append("__$" + sha3_result.hexdigest()[:34] + "$__")
 59
 60    return ret
 61
 62
 63# pylint: disable=too-many-instance-attributes,too-many-public-methods
 64class SourceUnit:
 65    """SourceUnit class"""
 66
 67    def __init__(self, compilation_unit: "CompilationUnit", filename: Filename):
 68
 69        self.filename = filename
 70        self.compilation_unit: "CompilationUnit" = compilation_unit
 71
 72        # ABI, bytecode and srcmap are indexed by contract_name
 73        self._abis: Dict = {}
 74        self._runtime_bytecodes: Dict = {}
 75        self._init_bytecodes: Dict = {}
 76        self._hashes: Dict = {}
 77        self._events: Dict = {}
 78        self._srcmaps: Dict[str, List[str]] = {}
 79        self._srcmaps_runtime: Dict[str, List[str]] = {}
 80        self.ast: Dict = {}
 81
 82        # Natspec
 83        self._natspec: Dict[str, Natspec] = {}
 84
 85        # Libraries used by the contract
 86        # contract_name -> (library, pattern)
 87        self._libraries: Dict[str, List[Tuple[str, str]]] = {}
 88
 89        # set containing all the contract names
 90        self._contracts_name: List[str] = []
 91
 92        # set containing all the contract name without the libraries
 93        self._contracts_name_without_libraries: Optional[List[str]] = None
 94
 95    # region ABI
 96    ###################################################################################
 97    ###################################################################################
 98
 99    @property
100    def abis(self) -> Dict:
101        """Return the ABIs
102
103        Returns:
104            Dict: ABIs (solc/vyper format) (contract name -> ABI)
105        """
106        return self._abis
107
108    def abi(self, name: str) -> Dict:
109        """Get the ABI from a contract
110
111        Args:
112            name (str): Contract name
113
114        Returns:
115            Dict: ABI (solc/vyper format)
116        """
117        return self._abis.get(name, None)
118
119    # endregion
120    ###################################################################################
121    ###################################################################################
122    # region Bytecode
123    ###################################################################################
124    ###################################################################################
125
126    @property
127    def bytecodes_runtime(self) -> Dict[str, str]:
128        """Return the runtime bytecodes
129
130        Returns:
131            Dict[str, str]: contract => runtime bytecode
132        """
133        return self._runtime_bytecodes
134
135    @bytecodes_runtime.setter
136    def bytecodes_runtime(self, bytecodes: Dict[str, str]) -> None:
137        """Set the bytecodes runtime
138
139        Args:
140            bytecodes (Dict[str, str]): New bytecodes runtime
141        """
142        self._runtime_bytecodes = bytecodes
143
144    @property
145    def bytecodes_init(self) -> Dict[str, str]:
146        """Return the init bytecodes
147
148        Returns:
149            Dict[str, str]: contract => init bytecode
150        """
151        return self._init_bytecodes
152
153    @bytecodes_init.setter
154    def bytecodes_init(self, bytecodes: Dict[str, str]) -> None:
155        """Set the bytecodes init
156
157        Args:
158            bytecodes (Dict[str, str]): New bytecodes init
159        """
160        self._init_bytecodes = bytecodes
161
162    def bytecode_runtime(self, name: str, libraries: Optional[Dict[str, int]] = None) -> str:
163        """Return the runtime bytecode of the contract.
164        If library is provided, patch the bytecode
165
166        Args:
167            name (str): contract name
168            libraries (Optional[Dict[str, str]], optional): lib_name => address. Defaults to None.
169
170        Returns:
171            str: runtime bytecode
172        """
173        runtime = self._runtime_bytecodes.get(name, None)
174        return self._update_bytecode_with_libraries(runtime, libraries)
175
176    def bytecode_init(self, name: str, libraries: Optional[Dict[str, int]] = None) -> str:
177        """Return the init bytecode of the contract.
178        If library is provided, patch the bytecode
179
180        Args:
181            name (str): contract name
182            libraries (Optional[Dict[str, int]], optional): lib_name => address. Defaults to None.
183
184        Returns:
185            str: init bytecode
186        """
187        init = self._init_bytecodes.get(name, None)
188        return self._update_bytecode_with_libraries(init, libraries)
189
190    # endregion
191    ###################################################################################
192    ###################################################################################
193    # region Source mapping
194    ###################################################################################
195    ###################################################################################
196
197    @property
198    def srcmaps_init(self) -> Dict[str, List[str]]:
199        """Return the srcmaps init
200
201        Returns:
202            Dict[str, List[str]]: Srcmaps init (solc/vyper format)
203        """
204        return self._srcmaps
205
206    @property
207    def srcmaps_runtime(self) -> Dict[str, List[str]]:
208        """Return the srcmaps runtime
209
210        Returns:
211            Dict[str, List[str]]: Srcmaps runtime (solc/vyper format)
212        """
213        return self._srcmaps_runtime
214
215    def srcmap_init(self, name: str) -> List[str]:
216        """Return the srcmap init of a contract
217
218        Args:
219            name (str): name of the contract
220
221        Returns:
222            List[str]: Srcmap init (solc/vyper format)
223        """
224        return self._srcmaps.get(name, [])
225
226    def srcmap_runtime(self, name: str) -> List[str]:
227        """Return the srcmap runtime of a contract
228
229        Args:
230            name (str): name of the contract
231
232        Returns:
233            List[str]: Srcmap runtime (solc/vyper format)
234        """
235        return self._srcmaps_runtime.get(name, [])
236
237    # endregion
238    ###################################################################################
239    ###################################################################################
240    # region Libraries
241    ###################################################################################
242    ###################################################################################
243
244    @property
245    def libraries(self) -> Dict[str, List[Tuple[str, str]]]:
246        """Return the libraries used
247
248        Returns:
249            Dict[str, List[Tuple[str, str]]]:  (contract_name -> [(library, pattern))])
250        """
251        return self._libraries
252
253    def _convert_libraries_names(self, libraries: Dict[str, int]) -> Dict[str, int]:
254        """Convert the libraries names
255        The name in the argument can be the library name, or filename:library_name
256        The returned dict contains all the names possible with the different solc versions
257
258        Args:
259            libraries (Dict[str, int]): lib_name => address
260
261        Returns:
262            Dict[str, int]: lib_name => address
263        """
264        new_names = {}
265        for (lib, addr) in libraries.items():
266            # Prior solidity 0.5
267            # libraries were on the format __filename:contract_name_____
268            # From solidity 0.5,
269            # libraries are on the format __$keccak(filename:contract_name)[34]$__
270            # https://solidity.readthedocs.io/en/v0.5.7/050-breaking-changes.html#command-line-and-json-interfaces
271
272            lib_4 = "__" + lib + "_" * (38 - len(lib))
273
274            sha3_result = keccak.new(digest_bits=256)
275            sha3_result.update(lib.encode("utf-8"))
276            lib_5 = "__$" + sha3_result.hexdigest()[:34] + "$__"
277
278            new_names[lib] = addr
279            new_names[lib_4] = addr
280            new_names[lib_5] = addr
281
282            for lib_filename, contract_names in self.compilation_unit.filename_to_contracts.items():
283                for contract_name in contract_names:
284                    if contract_name != lib:
285                        continue
286
287                    for candidate in get_library_candidate(lib_filename, lib):
288                        new_names[candidate] = addr
289
290        return new_names
291
292    def _library_name_lookup(
293        self, lib_name: str, original_contract: str
294    ) -> Optional[Tuple[str, str]]:
295        """Do a lookup on a library name to its name used in contracts
296        The library can be:
297        - the original contract name
298        - __X__ following Solidity 0.4 format
299        - __$..$__ following Solidity 0.5 format
300
301        Args:
302            lib_name (str): library name
303            original_contract (str): original contract name
304
305        Returns:
306            Optional[Tuple[str, str]]: contract_name, library_name
307        """
308
309        for filename, contract_names in self.compilation_unit.filename_to_contracts.items():
310            for name in contract_names:
311                if name == lib_name:
312                    return name, name
313
314                for candidate in get_library_candidate(filename, name):
315                    if candidate == lib_name:
316                        return name, candidate
317
318        # handle specific case of collision for Solidity <0.4
319        # We can only detect that the second contract is meant to be the library
320        # if there is only two contracts in the codebase
321        if len(self._contracts_name) == 2:
322            return next(
323                (
324                    (c, "__" + c + "_" * (38 - len(c)))
325                    for c in self._contracts_name
326                    if c != original_contract
327                ),
328                None,
329            )
330
331        return None
332
333    def libraries_names(self, name: str) -> List[str]:
334        """Return the names of the libraries used by the contract
335
336        Args:
337            name (str): contract name
338
339        Returns:
340            List[str]: libraries used
341        """
342
343        if name not in self._libraries:
344            init = re.findall(r"__.{36}__", self.bytecode_init(name))
345            runtime = re.findall(r"__.{36}__", self.bytecode_runtime(name))
346            libraires = [self._library_name_lookup(x, name) for x in set(init + runtime)]
347            self._libraries[name] = [lib for lib in libraires if lib]
348        return [name for (name, _) in self._libraries[name]]
349
350    def libraries_names_and_patterns(self, name: str) -> List[Tuple[str, str]]:
351        """Return the names and the patterns of the libraries used by the contract
352
353        Args:
354            name (str): contract name
355
356        Returns:
357            List[Tuple[str, str]]: (lib_name, pattern)
358        """
359
360        if name not in self._libraries:
361            init = re.findall(r"__.{36}__", self.bytecode_init(name))
362            runtime = re.findall(r"__.{36}__", self.bytecode_runtime(name))
363            libraires = [self._library_name_lookup(x, name) for x in set(init + runtime)]
364            self._libraries[name] = [lib for lib in libraires if lib]
365        return self._libraries[name]
366
367    def _update_bytecode_with_libraries(
368        self, bytecode: str, libraries: Union[None, Dict[str, int]]
369    ) -> str:
370        """Update the bytecode with the libraries address
371
372        Args:
373            bytecode (str): bytecode to patch
374            libraries (Union[None, Dict[str, int]]): pattern => address
375
376        Returns:
377            str: Patched bytecode
378        """
379        if libraries:
380            libraries = self._convert_libraries_names(libraries)
381            for library_found in re.findall(r"__.{36}__", bytecode):
382                if library_found in libraries:
383                    bytecode = re.sub(
384                        re.escape(library_found),
385                        f"{libraries[library_found]:0>40x}",
386                        bytecode,
387                    )
388        return bytecode
389
390    # endregion
391    ###################################################################################
392    ###################################################################################
393    # region Natspec
394    ###################################################################################
395    ###################################################################################
396
397    @property
398    def natspec(self) -> Dict[str, Natspec]:
399        """Return the natspec of the contracts
400
401        Returns:
402            Dict[str, Natspec]: Contract name -> Natspec
403        """
404        return self._natspec
405
406    # endregion
407    ###################################################################################
408    ###################################################################################
409    # region Contract Names
410    ###################################################################################
411    ###################################################################################
412
413    @property
414    def contracts_names(self) -> List[str]:
415        """Return the contracts names
416
417        Returns:
418            List[str]: List of the contracts names
419        """
420        return self._contracts_name
421
422    @contracts_names.setter
423    def contracts_names(self, names: List[str]) -> None:
424        """Set the contract names
425
426        Args:
427            names (List[str]): New contracts names
428        """
429        self._contracts_name = names
430
431    def add_contract_name(self, name: str) -> None:
432        """Add name to contracts_names, if not already present
433
434        Args:
435            name (str): Name to add to the list
436        """
437        if name not in self.contracts_names:
438            self.contracts_names.append(name)
439
440    @property
441    def contracts_names_without_libraries(self) -> List[str]:
442        """Return the contracts names without the librairies
443
444        Returns:
445            List[str]: List of contracts
446        """
447        if self._contracts_name_without_libraries is None:
448            libraries: List[str] = []
449            for contract_name in self._contracts_name:
450                libraries += self.libraries_names(contract_name)
451            self._contracts_name_without_libraries = [
452                l for l in self._contracts_name if l not in set(libraries)
453            ]
454        return self._contracts_name_without_libraries
455
456    # endregion
457    ###################################################################################
458    ###################################################################################
459    # region Hashes
460    ###################################################################################
461    ###################################################################################
462
463    def hashes(self, name: str) -> Dict[str, int]:
464        """Return the hashes of the functions
465
466        Args:
467            name (str): contract name
468
469        Returns:
470            Dict[str, int]: (function name => signature)
471        """
472        if not name in self._hashes:
473            self._compute_hashes(name)
474        return self._hashes[name]
475
476    def _compute_hashes(self, name: str) -> None:
477        """Compute the function hashes
478
479        Args:
480            name (str): contract name
481        """
482        self._hashes[name] = {}
483        for sig in self.abi(name):
484            if "type" in sig:
485                if sig["type"] == "function":
486                    sig_name = sig["name"]
487                    arguments = ",".join([x["type"] for x in sig["inputs"]])
488                    sig = f"{sig_name}({arguments})"
489                    sha3_result = keccak.new(digest_bits=256)
490                    sha3_result.update(sig.encode("utf-8"))
491                    self._hashes[name][sig] = int("0x" + sha3_result.hexdigest()[:8], 16)
492
493    # endregion
494    ###################################################################################
495    ###################################################################################
496    # region Events
497    ###################################################################################
498    ###################################################################################
499
500    def events_topics(self, name: str) -> Dict[str, Tuple[int, List[bool]]]:
501        """Return the topics of the contract's events
502
503        Args:
504            name (str): contract name
505
506        Returns:
507            Dict[str, Tuple[int, List[bool]]]: event signature => topic hash, [is_indexed for each parameter]
508        """
509        if not name in self._events:
510            self._compute_topics_events(name)
511        return self._events[name]
512
513    def _compute_topics_events(self, name: str) -> None:
514        """Compute the topics of the contract's events
515
516        Args:
517            name (str): contract name
518        """
519        self._events[name] = {}
520        for sig in self.abi(name):
521            if "type" in sig:
522                if sig["type"] == "event":
523                    sig_name = sig["name"]
524                    arguments = ",".join([x["type"] for x in sig["inputs"]])
525                    indexes = [x.get("indexed", False) for x in sig["inputs"]]
526                    sig = f"{sig_name}({arguments})"
527                    sha3_result = keccak.new(digest_bits=256)
528                    sha3_result.update(sig.encode("utf-8"))
529
530                    self._events[name][sig] = (int("0x" + sha3_result.hexdigest()[:8], 16), indexes)
531
532    # endregion
533    ###################################################################################
534    ###################################################################################
535    # region Metadata
536    ###################################################################################
537    ###################################################################################
538
539    def metadata_of(self, name: str) -> Dict[str, Union[str, bool]]:
540        """Return the parsed metadata of a contract by name
541
542        Args:
543            name (str): contract name
544
545        Raises:
546            ValueError: If no contract/library with that name exists
547
548        Returns:
549            Dict[str, Union[str, bool]]: fielname => value
550        """
551        # the metadata is at the end of the runtime(!) bytecode
552        try:
553            bytecode = self._runtime_bytecodes[name]
554            print("runtime bytecode", bytecode)
555        except:
556            raise ValueError(  # pylint: disable=raise-missing-from
557                f"contract {name} does not exist"
558            )
559
560        # the last two bytes contain the length of the preceding metadata.
561        metadata_length = int(f"0x{bytecode[-4:]}", base=16)
562        # extract the metadata
563        metadata = bytecode[-(metadata_length * 2 + 4) :]
564        metadata_decoded = cbor2.loads(bytearray.fromhex(metadata))
565
566        for k, v in metadata_decoded.items():
567            if len(v) == 1:
568                metadata_decoded[k] = bool(v)
569            elif k == "solc":
570                metadata_decoded[k] = ".".join([str(d) for d in v])
571            else:
572                # there might be nested items or other unforeseen errors
573                try:
574                    metadata_decoded[k] = v.hex()
575                except:  # pylint: disable=bare-except
576                    pass
577
578        return metadata_decoded
579
580    def remove_metadata(self) -> None:
581        """Remove init bytecode
582        See
583        http://solidity.readthedocs.io/en/v0.4.24/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode
584        """
585        # the metadata is at the end of the runtime(!) bytecode of each contract
586        for (key, bytecode) in self._runtime_bytecodes.items():
587            if not bytecode or bytecode == "0x":
588                continue
589            # the last two bytes contain the length of the preceding metadata.
590            metadata_length = int(f"0x{bytecode[-4:]}", base=16)
591            # store the metadata here so we can remove it from the init bytecode later on
592            metadata = bytecode[-(metadata_length * 2 + 4) :]
593            # remove the metadata from the runtime bytecode, '+ 4' for the two length-indication bytes at the end
594            self._runtime_bytecodes[key] = bytecode[0 : -(metadata_length * 2 + 4)]
595            # remove the metadata from the init bytecode
596            self._init_bytecodes[key] = self._init_bytecodes[key].replace(metadata, "")
597
598    # endregion
599    ###################################################################################
600    ###################################################################################
def get_library_candidate( filename: crytic_compile.utils.naming.Filename, contract_name: str) -> List[str]:
20def get_library_candidate(filename: Filename, contract_name: str) -> List[str]:
21    """
22    Return candidate name for library linking. A candidate is a str that might be found in other bytecodes
23
24    Args:
25        filename: filename of the contract
26        contract_name: contract name
27
28    Returns:
29        The list of candidates
30    """
31
32    # Some platform use only the contract name
33    # Some use fimename:contract_name
34
35    ret: List[str] = []
36
37    name_with_absolute_filename = filename.absolute + ":" + contract_name
38    name_with_used_filename = filename.used + ":" + contract_name
39
40    # Only 36 char were used in the past
41    # See https://docs.soliditylang.org/en/develop/using-the-compiler.html#library-linking
42    names_candidates = [
43        name_with_absolute_filename,
44        name_with_absolute_filename[0:36],
45        name_with_used_filename,
46        name_with_used_filename[0:36],
47    ]
48
49    # Solidity 0.4
50    ret.append("__" + contract_name + "_" * (38 - len(contract_name)))
51
52    for name_candidate in names_candidates:
53        # Solidity 0.4 with filename
54        ret.append("__" + name_candidate + "_" * (38 - len(name_candidate)))
55
56        # Solidity 0.5
57        sha3_result = keccak.new(digest_bits=256)
58        sha3_result.update(name_candidate.encode("utf-8"))
59        ret.append("__$" + sha3_result.hexdigest()[:34] + "$__")
60
61    return ret

Return candidate name for library linking. A candidate is a str that might be found in other bytecodes

Args: filename: filename of the contract contract_name: contract name

Returns: The list of candidates

class SourceUnit:
 65class SourceUnit:
 66    """SourceUnit class"""
 67
 68    def __init__(self, compilation_unit: "CompilationUnit", filename: Filename):
 69
 70        self.filename = filename
 71        self.compilation_unit: "CompilationUnit" = compilation_unit
 72
 73        # ABI, bytecode and srcmap are indexed by contract_name
 74        self._abis: Dict = {}
 75        self._runtime_bytecodes: Dict = {}
 76        self._init_bytecodes: Dict = {}
 77        self._hashes: Dict = {}
 78        self._events: Dict = {}
 79        self._srcmaps: Dict[str, List[str]] = {}
 80        self._srcmaps_runtime: Dict[str, List[str]] = {}
 81        self.ast: Dict = {}
 82
 83        # Natspec
 84        self._natspec: Dict[str, Natspec] = {}
 85
 86        # Libraries used by the contract
 87        # contract_name -> (library, pattern)
 88        self._libraries: Dict[str, List[Tuple[str, str]]] = {}
 89
 90        # set containing all the contract names
 91        self._contracts_name: List[str] = []
 92
 93        # set containing all the contract name without the libraries
 94        self._contracts_name_without_libraries: Optional[List[str]] = None
 95
 96    # region ABI
 97    ###################################################################################
 98    ###################################################################################
 99
100    @property
101    def abis(self) -> Dict:
102        """Return the ABIs
103
104        Returns:
105            Dict: ABIs (solc/vyper format) (contract name -> ABI)
106        """
107        return self._abis
108
109    def abi(self, name: str) -> Dict:
110        """Get the ABI from a contract
111
112        Args:
113            name (str): Contract name
114
115        Returns:
116            Dict: ABI (solc/vyper format)
117        """
118        return self._abis.get(name, None)
119
120    # endregion
121    ###################################################################################
122    ###################################################################################
123    # region Bytecode
124    ###################################################################################
125    ###################################################################################
126
127    @property
128    def bytecodes_runtime(self) -> Dict[str, str]:
129        """Return the runtime bytecodes
130
131        Returns:
132            Dict[str, str]: contract => runtime bytecode
133        """
134        return self._runtime_bytecodes
135
136    @bytecodes_runtime.setter
137    def bytecodes_runtime(self, bytecodes: Dict[str, str]) -> None:
138        """Set the bytecodes runtime
139
140        Args:
141            bytecodes (Dict[str, str]): New bytecodes runtime
142        """
143        self._runtime_bytecodes = bytecodes
144
145    @property
146    def bytecodes_init(self) -> Dict[str, str]:
147        """Return the init bytecodes
148
149        Returns:
150            Dict[str, str]: contract => init bytecode
151        """
152        return self._init_bytecodes
153
154    @bytecodes_init.setter
155    def bytecodes_init(self, bytecodes: Dict[str, str]) -> None:
156        """Set the bytecodes init
157
158        Args:
159            bytecodes (Dict[str, str]): New bytecodes init
160        """
161        self._init_bytecodes = bytecodes
162
163    def bytecode_runtime(self, name: str, libraries: Optional[Dict[str, int]] = None) -> str:
164        """Return the runtime bytecode of the contract.
165        If library is provided, patch the bytecode
166
167        Args:
168            name (str): contract name
169            libraries (Optional[Dict[str, str]], optional): lib_name => address. Defaults to None.
170
171        Returns:
172            str: runtime bytecode
173        """
174        runtime = self._runtime_bytecodes.get(name, None)
175        return self._update_bytecode_with_libraries(runtime, libraries)
176
177    def bytecode_init(self, name: str, libraries: Optional[Dict[str, int]] = None) -> str:
178        """Return the init bytecode of the contract.
179        If library is provided, patch the bytecode
180
181        Args:
182            name (str): contract name
183            libraries (Optional[Dict[str, int]], optional): lib_name => address. Defaults to None.
184
185        Returns:
186            str: init bytecode
187        """
188        init = self._init_bytecodes.get(name, None)
189        return self._update_bytecode_with_libraries(init, libraries)
190
191    # endregion
192    ###################################################################################
193    ###################################################################################
194    # region Source mapping
195    ###################################################################################
196    ###################################################################################
197
198    @property
199    def srcmaps_init(self) -> Dict[str, List[str]]:
200        """Return the srcmaps init
201
202        Returns:
203            Dict[str, List[str]]: Srcmaps init (solc/vyper format)
204        """
205        return self._srcmaps
206
207    @property
208    def srcmaps_runtime(self) -> Dict[str, List[str]]:
209        """Return the srcmaps runtime
210
211        Returns:
212            Dict[str, List[str]]: Srcmaps runtime (solc/vyper format)
213        """
214        return self._srcmaps_runtime
215
216    def srcmap_init(self, name: str) -> List[str]:
217        """Return the srcmap init of a contract
218
219        Args:
220            name (str): name of the contract
221
222        Returns:
223            List[str]: Srcmap init (solc/vyper format)
224        """
225        return self._srcmaps.get(name, [])
226
227    def srcmap_runtime(self, name: str) -> List[str]:
228        """Return the srcmap runtime of a contract
229
230        Args:
231            name (str): name of the contract
232
233        Returns:
234            List[str]: Srcmap runtime (solc/vyper format)
235        """
236        return self._srcmaps_runtime.get(name, [])
237
238    # endregion
239    ###################################################################################
240    ###################################################################################
241    # region Libraries
242    ###################################################################################
243    ###################################################################################
244
245    @property
246    def libraries(self) -> Dict[str, List[Tuple[str, str]]]:
247        """Return the libraries used
248
249        Returns:
250            Dict[str, List[Tuple[str, str]]]:  (contract_name -> [(library, pattern))])
251        """
252        return self._libraries
253
254    def _convert_libraries_names(self, libraries: Dict[str, int]) -> Dict[str, int]:
255        """Convert the libraries names
256        The name in the argument can be the library name, or filename:library_name
257        The returned dict contains all the names possible with the different solc versions
258
259        Args:
260            libraries (Dict[str, int]): lib_name => address
261
262        Returns:
263            Dict[str, int]: lib_name => address
264        """
265        new_names = {}
266        for (lib, addr) in libraries.items():
267            # Prior solidity 0.5
268            # libraries were on the format __filename:contract_name_____
269            # From solidity 0.5,
270            # libraries are on the format __$keccak(filename:contract_name)[34]$__
271            # https://solidity.readthedocs.io/en/v0.5.7/050-breaking-changes.html#command-line-and-json-interfaces
272
273            lib_4 = "__" + lib + "_" * (38 - len(lib))
274
275            sha3_result = keccak.new(digest_bits=256)
276            sha3_result.update(lib.encode("utf-8"))
277            lib_5 = "__$" + sha3_result.hexdigest()[:34] + "$__"
278
279            new_names[lib] = addr
280            new_names[lib_4] = addr
281            new_names[lib_5] = addr
282
283            for lib_filename, contract_names in self.compilation_unit.filename_to_contracts.items():
284                for contract_name in contract_names:
285                    if contract_name != lib:
286                        continue
287
288                    for candidate in get_library_candidate(lib_filename, lib):
289                        new_names[candidate] = addr
290
291        return new_names
292
293    def _library_name_lookup(
294        self, lib_name: str, original_contract: str
295    ) -> Optional[Tuple[str, str]]:
296        """Do a lookup on a library name to its name used in contracts
297        The library can be:
298        - the original contract name
299        - __X__ following Solidity 0.4 format
300        - __$..$__ following Solidity 0.5 format
301
302        Args:
303            lib_name (str): library name
304            original_contract (str): original contract name
305
306        Returns:
307            Optional[Tuple[str, str]]: contract_name, library_name
308        """
309
310        for filename, contract_names in self.compilation_unit.filename_to_contracts.items():
311            for name in contract_names:
312                if name == lib_name:
313                    return name, name
314
315                for candidate in get_library_candidate(filename, name):
316                    if candidate == lib_name:
317                        return name, candidate
318
319        # handle specific case of collision for Solidity <0.4
320        # We can only detect that the second contract is meant to be the library
321        # if there is only two contracts in the codebase
322        if len(self._contracts_name) == 2:
323            return next(
324                (
325                    (c, "__" + c + "_" * (38 - len(c)))
326                    for c in self._contracts_name
327                    if c != original_contract
328                ),
329                None,
330            )
331
332        return None
333
334    def libraries_names(self, name: str) -> List[str]:
335        """Return the names of the libraries used by the contract
336
337        Args:
338            name (str): contract name
339
340        Returns:
341            List[str]: libraries used
342        """
343
344        if name not in self._libraries:
345            init = re.findall(r"__.{36}__", self.bytecode_init(name))
346            runtime = re.findall(r"__.{36}__", self.bytecode_runtime(name))
347            libraires = [self._library_name_lookup(x, name) for x in set(init + runtime)]
348            self._libraries[name] = [lib for lib in libraires if lib]
349        return [name for (name, _) in self._libraries[name]]
350
351    def libraries_names_and_patterns(self, name: str) -> List[Tuple[str, str]]:
352        """Return the names and the patterns of the libraries used by the contract
353
354        Args:
355            name (str): contract name
356
357        Returns:
358            List[Tuple[str, str]]: (lib_name, pattern)
359        """
360
361        if name not in self._libraries:
362            init = re.findall(r"__.{36}__", self.bytecode_init(name))
363            runtime = re.findall(r"__.{36}__", self.bytecode_runtime(name))
364            libraires = [self._library_name_lookup(x, name) for x in set(init + runtime)]
365            self._libraries[name] = [lib for lib in libraires if lib]
366        return self._libraries[name]
367
368    def _update_bytecode_with_libraries(
369        self, bytecode: str, libraries: Union[None, Dict[str, int]]
370    ) -> str:
371        """Update the bytecode with the libraries address
372
373        Args:
374            bytecode (str): bytecode to patch
375            libraries (Union[None, Dict[str, int]]): pattern => address
376
377        Returns:
378            str: Patched bytecode
379        """
380        if libraries:
381            libraries = self._convert_libraries_names(libraries)
382            for library_found in re.findall(r"__.{36}__", bytecode):
383                if library_found in libraries:
384                    bytecode = re.sub(
385                        re.escape(library_found),
386                        f"{libraries[library_found]:0>40x}",
387                        bytecode,
388                    )
389        return bytecode
390
391    # endregion
392    ###################################################################################
393    ###################################################################################
394    # region Natspec
395    ###################################################################################
396    ###################################################################################
397
398    @property
399    def natspec(self) -> Dict[str, Natspec]:
400        """Return the natspec of the contracts
401
402        Returns:
403            Dict[str, Natspec]: Contract name -> Natspec
404        """
405        return self._natspec
406
407    # endregion
408    ###################################################################################
409    ###################################################################################
410    # region Contract Names
411    ###################################################################################
412    ###################################################################################
413
414    @property
415    def contracts_names(self) -> List[str]:
416        """Return the contracts names
417
418        Returns:
419            List[str]: List of the contracts names
420        """
421        return self._contracts_name
422
423    @contracts_names.setter
424    def contracts_names(self, names: List[str]) -> None:
425        """Set the contract names
426
427        Args:
428            names (List[str]): New contracts names
429        """
430        self._contracts_name = names
431
432    def add_contract_name(self, name: str) -> None:
433        """Add name to contracts_names, if not already present
434
435        Args:
436            name (str): Name to add to the list
437        """
438        if name not in self.contracts_names:
439            self.contracts_names.append(name)
440
441    @property
442    def contracts_names_without_libraries(self) -> List[str]:
443        """Return the contracts names without the librairies
444
445        Returns:
446            List[str]: List of contracts
447        """
448        if self._contracts_name_without_libraries is None:
449            libraries: List[str] = []
450            for contract_name in self._contracts_name:
451                libraries += self.libraries_names(contract_name)
452            self._contracts_name_without_libraries = [
453                l for l in self._contracts_name if l not in set(libraries)
454            ]
455        return self._contracts_name_without_libraries
456
457    # endregion
458    ###################################################################################
459    ###################################################################################
460    # region Hashes
461    ###################################################################################
462    ###################################################################################
463
464    def hashes(self, name: str) -> Dict[str, int]:
465        """Return the hashes of the functions
466
467        Args:
468            name (str): contract name
469
470        Returns:
471            Dict[str, int]: (function name => signature)
472        """
473        if not name in self._hashes:
474            self._compute_hashes(name)
475        return self._hashes[name]
476
477    def _compute_hashes(self, name: str) -> None:
478        """Compute the function hashes
479
480        Args:
481            name (str): contract name
482        """
483        self._hashes[name] = {}
484        for sig in self.abi(name):
485            if "type" in sig:
486                if sig["type"] == "function":
487                    sig_name = sig["name"]
488                    arguments = ",".join([x["type"] for x in sig["inputs"]])
489                    sig = f"{sig_name}({arguments})"
490                    sha3_result = keccak.new(digest_bits=256)
491                    sha3_result.update(sig.encode("utf-8"))
492                    self._hashes[name][sig] = int("0x" + sha3_result.hexdigest()[:8], 16)
493
494    # endregion
495    ###################################################################################
496    ###################################################################################
497    # region Events
498    ###################################################################################
499    ###################################################################################
500
501    def events_topics(self, name: str) -> Dict[str, Tuple[int, List[bool]]]:
502        """Return the topics of the contract's events
503
504        Args:
505            name (str): contract name
506
507        Returns:
508            Dict[str, Tuple[int, List[bool]]]: event signature => topic hash, [is_indexed for each parameter]
509        """
510        if not name in self._events:
511            self._compute_topics_events(name)
512        return self._events[name]
513
514    def _compute_topics_events(self, name: str) -> None:
515        """Compute the topics of the contract's events
516
517        Args:
518            name (str): contract name
519        """
520        self._events[name] = {}
521        for sig in self.abi(name):
522            if "type" in sig:
523                if sig["type"] == "event":
524                    sig_name = sig["name"]
525                    arguments = ",".join([x["type"] for x in sig["inputs"]])
526                    indexes = [x.get("indexed", False) for x in sig["inputs"]]
527                    sig = f"{sig_name}({arguments})"
528                    sha3_result = keccak.new(digest_bits=256)
529                    sha3_result.update(sig.encode("utf-8"))
530
531                    self._events[name][sig] = (int("0x" + sha3_result.hexdigest()[:8], 16), indexes)
532
533    # endregion
534    ###################################################################################
535    ###################################################################################
536    # region Metadata
537    ###################################################################################
538    ###################################################################################
539
540    def metadata_of(self, name: str) -> Dict[str, Union[str, bool]]:
541        """Return the parsed metadata of a contract by name
542
543        Args:
544            name (str): contract name
545
546        Raises:
547            ValueError: If no contract/library with that name exists
548
549        Returns:
550            Dict[str, Union[str, bool]]: fielname => value
551        """
552        # the metadata is at the end of the runtime(!) bytecode
553        try:
554            bytecode = self._runtime_bytecodes[name]
555            print("runtime bytecode", bytecode)
556        except:
557            raise ValueError(  # pylint: disable=raise-missing-from
558                f"contract {name} does not exist"
559            )
560
561        # the last two bytes contain the length of the preceding metadata.
562        metadata_length = int(f"0x{bytecode[-4:]}", base=16)
563        # extract the metadata
564        metadata = bytecode[-(metadata_length * 2 + 4) :]
565        metadata_decoded = cbor2.loads(bytearray.fromhex(metadata))
566
567        for k, v in metadata_decoded.items():
568            if len(v) == 1:
569                metadata_decoded[k] = bool(v)
570            elif k == "solc":
571                metadata_decoded[k] = ".".join([str(d) for d in v])
572            else:
573                # there might be nested items or other unforeseen errors
574                try:
575                    metadata_decoded[k] = v.hex()
576                except:  # pylint: disable=bare-except
577                    pass
578
579        return metadata_decoded
580
581    def remove_metadata(self) -> None:
582        """Remove init bytecode
583        See
584        http://solidity.readthedocs.io/en/v0.4.24/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode
585        """
586        # the metadata is at the end of the runtime(!) bytecode of each contract
587        for (key, bytecode) in self._runtime_bytecodes.items():
588            if not bytecode or bytecode == "0x":
589                continue
590            # the last two bytes contain the length of the preceding metadata.
591            metadata_length = int(f"0x{bytecode[-4:]}", base=16)
592            # store the metadata here so we can remove it from the init bytecode later on
593            metadata = bytecode[-(metadata_length * 2 + 4) :]
594            # remove the metadata from the runtime bytecode, '+ 4' for the two length-indication bytes at the end
595            self._runtime_bytecodes[key] = bytecode[0 : -(metadata_length * 2 + 4)]
596            # remove the metadata from the init bytecode
597            self._init_bytecodes[key] = self._init_bytecodes[key].replace(metadata, "")
598
599    # endregion
600    ###################################################################################
601    ###################################################################################

SourceUnit class

SourceUnit( compilation_unit: crytic_compile.compilation_unit.CompilationUnit, filename: crytic_compile.utils.naming.Filename)
68    def __init__(self, compilation_unit: "CompilationUnit", filename: Filename):
69
70        self.filename = filename
71        self.compilation_unit: "CompilationUnit" = compilation_unit
72
73        # ABI, bytecode and srcmap are indexed by contract_name
74        self._abis: Dict = {}
75        self._runtime_bytecodes: Dict = {}
76        self._init_bytecodes: Dict = {}
77        self._hashes: Dict = {}
78        self._events: Dict = {}
79        self._srcmaps: Dict[str, List[str]] = {}
80        self._srcmaps_runtime: Dict[str, List[str]] = {}
81        self.ast: Dict = {}
82
83        # Natspec
84        self._natspec: Dict[str, Natspec] = {}
85
86        # Libraries used by the contract
87        # contract_name -> (library, pattern)
88        self._libraries: Dict[str, List[Tuple[str, str]]] = {}
89
90        # set containing all the contract names
91        self._contracts_name: List[str] = []
92
93        # set containing all the contract name without the libraries
94        self._contracts_name_without_libraries: Optional[List[str]] = None
filename
ast: Dict
abis: Dict
100    @property
101    def abis(self) -> Dict:
102        """Return the ABIs
103
104        Returns:
105            Dict: ABIs (solc/vyper format) (contract name -> ABI)
106        """
107        return self._abis

Return the ABIs

Returns: Dict: ABIs (solc/vyper format) (contract name -> ABI)

def abi(self, name: str) -> Dict:
109    def abi(self, name: str) -> Dict:
110        """Get the ABI from a contract
111
112        Args:
113            name (str): Contract name
114
115        Returns:
116            Dict: ABI (solc/vyper format)
117        """
118        return self._abis.get(name, None)

Get the ABI from a contract

Args: name (str): Contract name

Returns: Dict: ABI (solc/vyper format)

bytecodes_runtime: Dict[str, str]
127    @property
128    def bytecodes_runtime(self) -> Dict[str, str]:
129        """Return the runtime bytecodes
130
131        Returns:
132            Dict[str, str]: contract => runtime bytecode
133        """
134        return self._runtime_bytecodes

Return the runtime bytecodes

Returns: Dict[str, str]: contract => runtime bytecode

bytecodes_init: Dict[str, str]
145    @property
146    def bytecodes_init(self) -> Dict[str, str]:
147        """Return the init bytecodes
148
149        Returns:
150            Dict[str, str]: contract => init bytecode
151        """
152        return self._init_bytecodes

Return the init bytecodes

Returns: Dict[str, str]: contract => init bytecode

def bytecode_runtime( self, name: str, libraries: Union[Dict[str, int], NoneType] = None) -> str:
163    def bytecode_runtime(self, name: str, libraries: Optional[Dict[str, int]] = None) -> str:
164        """Return the runtime bytecode of the contract.
165        If library is provided, patch the bytecode
166
167        Args:
168            name (str): contract name
169            libraries (Optional[Dict[str, str]], optional): lib_name => address. Defaults to None.
170
171        Returns:
172            str: runtime bytecode
173        """
174        runtime = self._runtime_bytecodes.get(name, None)
175        return self._update_bytecode_with_libraries(runtime, libraries)

Return the runtime bytecode of the contract. If library is provided, patch the bytecode

Args: name (str): contract name libraries (Optional[Dict[str, str]], optional): lib_name => address. Defaults to None.

Returns: str: runtime bytecode

def bytecode_init( self, name: str, libraries: Union[Dict[str, int], NoneType] = None) -> str:
177    def bytecode_init(self, name: str, libraries: Optional[Dict[str, int]] = None) -> str:
178        """Return the init bytecode of the contract.
179        If library is provided, patch the bytecode
180
181        Args:
182            name (str): contract name
183            libraries (Optional[Dict[str, int]], optional): lib_name => address. Defaults to None.
184
185        Returns:
186            str: init bytecode
187        """
188        init = self._init_bytecodes.get(name, None)
189        return self._update_bytecode_with_libraries(init, libraries)

Return the init bytecode of the contract. If library is provided, patch the bytecode

Args: name (str): contract name libraries (Optional[Dict[str, int]], optional): lib_name => address. Defaults to None.

Returns: str: init bytecode

srcmaps_init: Dict[str, List[str]]
198    @property
199    def srcmaps_init(self) -> Dict[str, List[str]]:
200        """Return the srcmaps init
201
202        Returns:
203            Dict[str, List[str]]: Srcmaps init (solc/vyper format)
204        """
205        return self._srcmaps

Return the srcmaps init

Returns: Dict[str, List[str]]: Srcmaps init (solc/vyper format)

srcmaps_runtime: Dict[str, List[str]]
207    @property
208    def srcmaps_runtime(self) -> Dict[str, List[str]]:
209        """Return the srcmaps runtime
210
211        Returns:
212            Dict[str, List[str]]: Srcmaps runtime (solc/vyper format)
213        """
214        return self._srcmaps_runtime

Return the srcmaps runtime

Returns: Dict[str, List[str]]: Srcmaps runtime (solc/vyper format)

def srcmap_init(self, name: str) -> List[str]:
216    def srcmap_init(self, name: str) -> List[str]:
217        """Return the srcmap init of a contract
218
219        Args:
220            name (str): name of the contract
221
222        Returns:
223            List[str]: Srcmap init (solc/vyper format)
224        """
225        return self._srcmaps.get(name, [])

Return the srcmap init of a contract

Args: name (str): name of the contract

Returns: List[str]: Srcmap init (solc/vyper format)

def srcmap_runtime(self, name: str) -> List[str]:
227    def srcmap_runtime(self, name: str) -> List[str]:
228        """Return the srcmap runtime of a contract
229
230        Args:
231            name (str): name of the contract
232
233        Returns:
234            List[str]: Srcmap runtime (solc/vyper format)
235        """
236        return self._srcmaps_runtime.get(name, [])

Return the srcmap runtime of a contract

Args: name (str): name of the contract

Returns: List[str]: Srcmap runtime (solc/vyper format)

libraries: Dict[str, List[Tuple[str, str]]]
245    @property
246    def libraries(self) -> Dict[str, List[Tuple[str, str]]]:
247        """Return the libraries used
248
249        Returns:
250            Dict[str, List[Tuple[str, str]]]:  (contract_name -> [(library, pattern))])
251        """
252        return self._libraries

Return the libraries used

Returns: Dict[str, List[Tuple[str, str]]]: (contract_name -> [(library, pattern))])

def libraries_names(self, name: str) -> List[str]:
334    def libraries_names(self, name: str) -> List[str]:
335        """Return the names of the libraries used by the contract
336
337        Args:
338            name (str): contract name
339
340        Returns:
341            List[str]: libraries used
342        """
343
344        if name not in self._libraries:
345            init = re.findall(r"__.{36}__", self.bytecode_init(name))
346            runtime = re.findall(r"__.{36}__", self.bytecode_runtime(name))
347            libraires = [self._library_name_lookup(x, name) for x in set(init + runtime)]
348            self._libraries[name] = [lib for lib in libraires if lib]
349        return [name for (name, _) in self._libraries[name]]

Return the names of the libraries used by the contract

Args: name (str): contract name

Returns: List[str]: libraries used

def libraries_names_and_patterns(self, name: str) -> List[Tuple[str, str]]:
351    def libraries_names_and_patterns(self, name: str) -> List[Tuple[str, str]]:
352        """Return the names and the patterns of the libraries used by the contract
353
354        Args:
355            name (str): contract name
356
357        Returns:
358            List[Tuple[str, str]]: (lib_name, pattern)
359        """
360
361        if name not in self._libraries:
362            init = re.findall(r"__.{36}__", self.bytecode_init(name))
363            runtime = re.findall(r"__.{36}__", self.bytecode_runtime(name))
364            libraires = [self._library_name_lookup(x, name) for x in set(init + runtime)]
365            self._libraries[name] = [lib for lib in libraires if lib]
366        return self._libraries[name]

Return the names and the patterns of the libraries used by the contract

Args: name (str): contract name

Returns: List[Tuple[str, str]]: (lib_name, pattern)

natspec: Dict[str, crytic_compile.utils.natspec.Natspec]
398    @property
399    def natspec(self) -> Dict[str, Natspec]:
400        """Return the natspec of the contracts
401
402        Returns:
403            Dict[str, Natspec]: Contract name -> Natspec
404        """
405        return self._natspec

Return the natspec of the contracts

Returns: Dict[str, Natspec]: Contract name -> Natspec

contracts_names: List[str]
414    @property
415    def contracts_names(self) -> List[str]:
416        """Return the contracts names
417
418        Returns:
419            List[str]: List of the contracts names
420        """
421        return self._contracts_name

Return the contracts names

Returns: List[str]: List of the contracts names

def add_contract_name(self, name: str) -> None:
432    def add_contract_name(self, name: str) -> None:
433        """Add name to contracts_names, if not already present
434
435        Args:
436            name (str): Name to add to the list
437        """
438        if name not in self.contracts_names:
439            self.contracts_names.append(name)

Add name to contracts_names, if not already present

Args: name (str): Name to add to the list

contracts_names_without_libraries: List[str]
441    @property
442    def contracts_names_without_libraries(self) -> List[str]:
443        """Return the contracts names without the librairies
444
445        Returns:
446            List[str]: List of contracts
447        """
448        if self._contracts_name_without_libraries is None:
449            libraries: List[str] = []
450            for contract_name in self._contracts_name:
451                libraries += self.libraries_names(contract_name)
452            self._contracts_name_without_libraries = [
453                l for l in self._contracts_name if l not in set(libraries)
454            ]
455        return self._contracts_name_without_libraries

Return the contracts names without the librairies

Returns: List[str]: List of contracts

def hashes(self, name: str) -> Dict[str, int]:
464    def hashes(self, name: str) -> Dict[str, int]:
465        """Return the hashes of the functions
466
467        Args:
468            name (str): contract name
469
470        Returns:
471            Dict[str, int]: (function name => signature)
472        """
473        if not name in self._hashes:
474            self._compute_hashes(name)
475        return self._hashes[name]

Return the hashes of the functions

Args: name (str): contract name

Returns: Dict[str, int]: (function name => signature)

def events_topics(self, name: str) -> Dict[str, Tuple[int, List[bool]]]:
501    def events_topics(self, name: str) -> Dict[str, Tuple[int, List[bool]]]:
502        """Return the topics of the contract's events
503
504        Args:
505            name (str): contract name
506
507        Returns:
508            Dict[str, Tuple[int, List[bool]]]: event signature => topic hash, [is_indexed for each parameter]
509        """
510        if not name in self._events:
511            self._compute_topics_events(name)
512        return self._events[name]

Return the topics of the contract's events

Args: name (str): contract name

Returns: Dict[str, Tuple[int, List[bool]]]: event signature => topic hash, [is_indexed for each parameter]

def metadata_of(self, name: str) -> Dict[str, Union[str, bool]]:
540    def metadata_of(self, name: str) -> Dict[str, Union[str, bool]]:
541        """Return the parsed metadata of a contract by name
542
543        Args:
544            name (str): contract name
545
546        Raises:
547            ValueError: If no contract/library with that name exists
548
549        Returns:
550            Dict[str, Union[str, bool]]: fielname => value
551        """
552        # the metadata is at the end of the runtime(!) bytecode
553        try:
554            bytecode = self._runtime_bytecodes[name]
555            print("runtime bytecode", bytecode)
556        except:
557            raise ValueError(  # pylint: disable=raise-missing-from
558                f"contract {name} does not exist"
559            )
560
561        # the last two bytes contain the length of the preceding metadata.
562        metadata_length = int(f"0x{bytecode[-4:]}", base=16)
563        # extract the metadata
564        metadata = bytecode[-(metadata_length * 2 + 4) :]
565        metadata_decoded = cbor2.loads(bytearray.fromhex(metadata))
566
567        for k, v in metadata_decoded.items():
568            if len(v) == 1:
569                metadata_decoded[k] = bool(v)
570            elif k == "solc":
571                metadata_decoded[k] = ".".join([str(d) for d in v])
572            else:
573                # there might be nested items or other unforeseen errors
574                try:
575                    metadata_decoded[k] = v.hex()
576                except:  # pylint: disable=bare-except
577                    pass
578
579        return metadata_decoded

Return the parsed metadata of a contract by name

Args: name (str): contract name

Raises: ValueError: If no contract/library with that name exists

Returns: Dict[str, Union[str, bool]]: fielname => value

def remove_metadata(self) -> None:
581    def remove_metadata(self) -> None:
582        """Remove init bytecode
583        See
584        http://solidity.readthedocs.io/en/v0.4.24/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode
585        """
586        # the metadata is at the end of the runtime(!) bytecode of each contract
587        for (key, bytecode) in self._runtime_bytecodes.items():
588            if not bytecode or bytecode == "0x":
589                continue
590            # the last two bytes contain the length of the preceding metadata.
591            metadata_length = int(f"0x{bytecode[-4:]}", base=16)
592            # store the metadata here so we can remove it from the init bytecode later on
593            metadata = bytecode[-(metadata_length * 2 + 4) :]
594            # remove the metadata from the runtime bytecode, '+ 4' for the two length-indication bytes at the end
595            self._runtime_bytecodes[key] = bytecode[0 : -(metadata_length * 2 + 4)]
596            # remove the metadata from the init bytecode
597            self._init_bytecodes[key] = self._init_bytecodes[key].replace(metadata, "")