crytic-compile
crytic-compile is an abstraction layer for smart contract build systems, facilitating the compilation of Solidity and Vyper projects across various environments. It supports direct `solc` compilation as well as frameworks like Foundry, Hardhat, Truffle, and fetching verified contracts from block explorers like Etherscan and Sourcify. The library is actively maintained by Trail of Bits and receives frequent minor updates.
Common errors
-
crytic_compile.platform.exceptions.InvalidCompilation: Solidity version not found: ERROR:root:None
cause This error typically indicates that the required Solidity compiler version is not installed or `solc-select` is not correctly configured or found in the PATH.fixEnsure `solc-select` is installed (`pip install solc-select`) and the correct Solidity version for your project is installed via `solc-select install <version>` (e.g., `solc-select install 0.8.19`). Also, verify `solc-select` is in your system's PATH. If using a framework like Foundry, ensure the `solc_version` is correctly specified in `foundry.toml`. -
crytic_compile.platform.exceptions.InvalidCompilation: Unknown file: contracts/interfaces/callbacks/ITransferValidator.sol
cause This usually occurs when `crytic-compile` cannot locate an imported file, often in complex project structures with submodules, monorepos, or incorrect remappings, or if the target specified is a single file that relies on external imports not discoverable by the default mechanism.fixVerify that all necessary dependencies (e.g., node_modules, submodules) are correctly installed and accessible relative to the project root. Ensure remappings are properly configured. If compiling a single file with external imports, ensure the compilation context allows for discovery of those imports, or compile the entire project directory instead. -
Stack too deep. Try compiling with `--via-ir` (cli) or the equivalent `viaIR: true` (standard JSON) while enabling the optimizer.
cause This is a Solidity compiler error indicating that a function's local variables or stack usage exceeds the EVM's stack depth limit. `crytic-compile` passes this error through from `solc`.fixModify your Solidity contract to reduce stack usage, for example by refactoring complex functions or using structs. Alternatively, enable the `--via-ir` option if your `solc` version supports it, which can sometimes mitigate this issue by optimizing stack usage. This can often be done via `crytic-compile` arguments if exposed, or by adjusting your project's `solc` configuration. -
crytic_compile.platform.exceptions.InvalidCompilation: target is not a file or a directory: /path/to/nonexistent/project
cause The specified compilation target (file or directory path) does not exist or is inaccessible. This also occurs if `--export-format` or `--export-dir` are reused as `args` flags.fixEnsure the `target` path provided to `crytic-compile` (e.g., `CryticCompile('./my_project')`) is correct and points to an existing file or directory. For project compilation, ensure you are running `crytic-compile` from the root of your project or pointing directly to it. Avoid reusing `--export-format` and `--export-dir` in the `args` parameter if they are already handled by the compilation process.
Warnings
- breaking Starting from version 0.3.5, `crytic-compile` no longer compiles tests and script files by default for Foundry projects. This change affects users of tools like Echidna and Medusa.
- gotcha Python 3.12.0 is specifically excluded from compatibility due to a known bug on Windows. While other Python 3.12.x versions might work, it's safer to avoid 3.12.0.
- breaking In version 0.3.2, there was a breaking API change where lists are used instead of sets for `filenames` and `contracts_names`, and filename ordering might have changed. This can impact tools consuming `crytic-compile`'s output.
- gotcha Be aware of typosquatting attacks. The legitimate package is `crytic-compile`. There was a malicious package named `crytic-compilers` found on PyPI, attempting to mimic the official library and execute malware on Windows systems.
Install
-
pip install crytic-compile
Imports
- CryticCompile
from crytic_compile import CryticCompile
- compile_all
from crytic_compile import compile_all
Quickstart
import os
from crytic_compile import compile_all
# Assuming a simple Solidity project in 'my_contract_project/'
# with a 'contracts/MyContract.sol' file.
# For Foundry, ensure foundry.toml is present.
project_path = './my_contract_project'
if not os.path.exists(project_path):
os.makedirs(os.path.join(project_path, 'contracts'), exist_ok=True)
with open(os.path.join(project_path, 'contracts', 'MyContract.sol'), 'w') as f:
f.write("""
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MyContract {
uint public value;
constructor(uint _value) {
value = _value;
}
function setValue(uint _newValue) public {
value = _newValue;
}
}
""")
# Simulate a foundry.toml for automatic detection
with open(os.path.join(project_path, 'foundry.toml'), 'w') as f:
f.write("[profile.default]\nsolc = '0.8.19'\n")
print(f"Compiling project at: {project_path}")
try:
compilations = compile_all(project_path)
for compilation_unit in compilations:
print(f" Compiled project type: {compilation_unit.platform.NAME}")
for source_unit in compilation_unit.source_units.values():
for contract_name in source_unit.contracts_names:
print(f" Contract: {contract_name}")
# Access ABI, bytecode, etc.
# abi = source_unit.abis[contract_name]
# bytecode = source_unit.bytecode_runtime(contract_name)
# print(f" ABI: {abi[:50]}...")
# print(f" Bytecode: {bytecode[:50]}...")
except Exception as e:
print(f"Error during compilation: {e}")
# Example of direct Etherscan compilation (requires an API key for rate limits)
# For a public Etherscan contract, you can use:
# etherscan_target = "etherscan:0x514910771af9ca656af84075aa92a562ae978e2b" # LINK token
# try:
# print(f"\nCompiling from Etherscan: {etherscan_target}")
# etherscan_compilation = compile_all(etherscan_target)[0]
# for source_unit in etherscan_compilation.source_units.values():
# for contract_name in source_unit.contracts_names:
# print(f" Etherscan Contract: {contract_name}")
# except Exception as e:
# print(f"Error during Etherscan compilation: {e}")