Compilation abstraction layer for smart contracts. Provides a unified interface to compile Solidity/Vyper projects across multiple build systems (solc, Foundry, Hardhat, Truffle, etc.) and fetch verified contracts from on-chain sources (Etherscan, Sourcify).
Used by Slither, Echidna, Manticore, and other Trail of Bits tools.
crytic_compile/
├── crytic_compile.py # Main CryticCompile class, compile_all()
├── compilation_unit.py # CompilationUnit - one compiler invocation
├── source_unit.py # SourceUnit - one source file's compiled data
├── compiler/ # CompilerVersion metadata
├── platform/ # Platform implementations
│ ├── abstract_platform.py # AbstractPlatform base class
│ ├── types.py # Type enum (platform priority)
│ ├── solc.py # Direct solc compilation
│ ├── foundry.py # Foundry/Forge
│ ├── hardhat.py # Hardhat (v2 and v3)
│ ├── truffle.py # Truffle
│ ├── etherscan.py # Etherscan API
│ ├── sourcify.py # Sourcify API
│ └── ... # Other platforms
├── utils/
│ ├── naming.py # Filename dataclass (path normalization)
│ ├── natspec.py # NatSpec comment handling
│ └── zip.py # Archive import/export
└── cryticparser/ # CLI argument parsing
| tool | purpose |
|---|---|
uv |
deps & venv |
ruff |
lint & format |
ty |
type check |
pytest |
tests |
# Setup (using uv)
uv sync --extra dev
# Linting & formatting
uv run ruff check crytic_compile/
uv run ruff format --check .
uv run ty check crytic_compile/
# Auto-fix
uv run ruff check --fix crytic_compile/
uv run ruff format .
# Tests
uv run pytest --cov=crytic_compile tests/
# Or use pip if preferred
pip install -e ".[dev]"
ruff check crytic_compile/
ty check crytic_compile/
pytest tests/# Find platform implementations
ast-grep --pattern 'class $NAME(AbstractPlatform): $$$' --lang py crytic_compile/platform
# Find where platforms are detected
rg "is_supported" crytic_compile/platform
# Find compilation flow
ast-grep --pattern 'def compile($$$): $$$' --lang py crytic_compile
# Trace data structures
rg "class CompilationUnit" crytic_compile
rg "class SourceUnit" crytic_compile- No speculative features - Don't add "might be useful" functionality
- Minimal dependencies - Only pycryptodome, cbor2, solc-select
- Platform abstraction - All frameworks produce identical
CompilationUnitoutput - Path normalization - Use
Filenamedataclass, not raw strings
- Type hints required - Parameters, returns, class variables, lists, sets. Dictionaries when possible.
- Google-style docstrings - For non-obvious operations
- Comments - No commented-out code. Code should be self-documenting.
- Errors - Raise
InvalidCompilationfor compilation failures with clear messages.
- 100-char line length
- No relative (
..) imports - Type hints on function signatures (enforced by ty)
Uses ruff for linting/formatting and ty for type checking:
ruff check crytic_compile/ # Lint
ruff format --check . # Check formatting
ty check crytic_compile/ # Type checkConfig in pyproject.toml:
- ruff: E, F, W, I, UP rules (ignores E501 line length)
- ty: Python 3.10 target
- Work from
masterbranch (main development trunk) - One logical change per PR
- Minimize formatting changes in unrelated code
- Large changes should be split into smaller PRs
-
Add type to
platform/types.py:class Type(IntEnum): MY_PLATFORM = 14 # Next available def priority(self) -> int: if self == Type.MY_PLATFORM: return 250 # Lower = higher priority
-
Create
platform/myplatform.py:class MyPlatform(AbstractPlatform): NAME = "MyPlatform" PROJECT_URL = "https://..." TYPE = Type.MY_PLATFORM @staticmethod def is_supported(target: str, **kwargs: str) -> bool: """Detect via marker file""" return os.path.isfile(os.path.join(target, "myconfig.toml")) def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None: """Run build and populate compilation units""" ... def clean(self, **kwargs: str) -> None: ... def is_dependency(self, path: str) -> bool: return "node_modules" in Path(path).parts def _guessed_tests(self) -> list[str]: return ["myplatform test"]
-
Import in
platform/all_platforms.pyand add to__all__
Tests live in /tests/. Run specific framework tests via CI scripts in /scripts/ci_*.sh.
pytest tests/test_library_linking.py -v # Specific test
pytest -k metadata # Pattern matchCryticCompile
└── compilation_units: dict[str, CompilationUnit]
└── source_units: dict[Filename, SourceUnit]
├── abis: dict[str, dict] # Contract ABIs
├── bytecodes_init: dict[str, str] # Creation bytecode
├── bytecodes_runtime: dict[str, str]
├── srcmaps_init: dict[str, list[str]]
├── srcmaps_runtime: dict[str, list[str]]
├── ast: dict # Compiler AST
└── natspec: dict[str, Natspec]
- CryticCompile (
crytic_compile.py) - Entry point. Detects platform, orchestrates compilation. - CompilationUnit (
compilation_unit.py) - One compiler invocation. Multiple possible per project (e.g., different solc versions). - SourceUnit (
source_unit.py) - Compiled data for one file. Access bytecodes, ABIs, source maps. - Filename (
utils/naming.py) - Path normalization. Use instead of raw strings.
--compile-force-frameworkflag checked first- Platforms sorted by priority (Foundry 100 > Hardhat 200 > Truffle 300 > others 1000)
- Each platform's
is_supported(target)called in order - First match wins, or fallback to Solc
Iterate all contracts:
cc = CryticCompile(target)
for compilation_unit in cc.compilation_units.values():
for source_unit in compilation_unit.source_units.values():
for contract_name in source_unit.contracts_names:
abi = source_unit.abis[contract_name]
bytecode = source_unit.bytecode_runtime(contract_name)Get bytecode with libraries linked:
libraries = {"SafeMath": "0xdeadbeef..."}
bytecode = source_unit.bytecode_runtime(contract_name, libraries)Access source mappings:
srcmap = source_unit.srcmaps_runtime[contract_name] # list[str]
# Each entry: "start:length:file_index:jump_type"- standard - crytic-compile JSON format
- solc - solc JSON output format
- truffle - Truffle artifact format
cc.export(export_format="standard", export_dir="crytic-export")Platforms can provide config() to extract settings (for Slither to use raw solc):
config = Foundry.config(working_dir)
# Returns: PlatformConfig(solc_version, optimizer, remappings, ...)Python version: 3.10+ (3.12.0 excluded due to Windows bug)
Build system: uv_build (pyproject.toml)
Dependencies: Minimal - pycryptodome (keccak), cbor2 (metadata), solc-select (compiler management)
Lockfile: uv.lock for reproducible builds
Don't push until asked.