Skip to content

Commit 9c086fc

Browse files
authored
Merge branch 'master' into implementation_address
2 parents 24bc098 + ac316ec commit 9c086fc

File tree

14 files changed

+150
-42
lines changed

14 files changed

+150
-42
lines changed

.github/workflows/doc.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ jobs:
3737
- run: pip install -e ".[doc]"
3838
- run: pdoc -o html/ crytic_compile
3939
- name: Upload artifact
40-
uses: actions/upload-pages-artifact@v1
40+
uses: actions/upload-pages-artifact@v2
4141
with:
4242
# Upload the doc
4343
path: './html/'
4444
- name: Deploy to GitHub Pages
4545
id: deployment
46-
uses: actions/deploy-pages@v1
46+
uses: actions/deploy-pages@v2

.github/workflows/publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ jobs:
4545
path: dist/
4646

4747
- name: publish
48-
uses: pypa/[email protected].6
48+
uses: pypa/[email protected].8
4949

5050
- name: sign
51-
uses: sigstore/gh-action-sigstore-python@v1.2.3
51+
uses: sigstore/gh-action-sigstore-python@v2.0.0
5252
with:
5353
inputs: ./dist/*.tar.gz ./dist/*.whl
5454
release-signing-artifacts: true

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ If you're unsure where to start, we recommend our [`good first issue`](https://g
77
Bug reports and feature suggestions can be submitted to our issue tracker. For bug reports, attaching the contract that caused the bug will help us in debugging and resolving the issue quickly. If you find a security vulnerability, do not open an issue; email [email protected] instead.
88

99
## Questions
10-
Questions can be submitted to the issue tracker, but you may get a faster response if you ask in our [chat room](https://empireslacking.herokuapp.com/) (in the #ethereum channel).
10+
Questions can be submitted to the issue tracker, but you may get a faster response if you ask in our [chat room](https://slack.empirehacking.nyc/) (in the #ethereum channel).
1111

1212
## Code
1313
crytic-compile uses the pull request contribution model. Please make an account on Github, fork this repo, and submit code contributions via pull request. For more documentation, look [here](https://guides.github.com/activities/forking/).

README.md

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
# Crytic-compile
2-
[![Build Status](https://img.shields.io/github/workflow/status/crytic/crytic-compile/CI/master)](https://github.com/crytic/crytic-compile/actions?query=workflow%3ACI)
3-
[![Slack Status](https://empireslacking.herokuapp.com/badge.svg)](https://empireslacking.herokuapp.com)
2+
[![Build Status](https://img.shields.io/github/actions/workflow/status/crytic/crytic-compile/ci.yml?branch=master)](https://github.com/crytic/crytic-compile/actions?query=workflow%3ACI)
3+
[![Slack Status](https://slack.empirehacking.nyc/badge.svg)](https://slack.empirehacking.nyc)
44
[![PyPI version](https://badge.fury.io/py/crytic-compile.svg)](https://badge.fury.io/py/crytic-compile)
55

66
Library to help smart contract compilation. It includes support for:
77
- Direct solc compilation
8+
- [Foundry](https://github.com/foundry-rs/foundry/)
9+
- [Hardhat](https://github.com/nomiclabs/hardhat)
810
- [Brownie](https://github.com/iamdefinitelyahuman/brownie)
911
- [Buidler](https://github.com/nomiclabs/buidler)
1012
- [Dapp](https://dapp.tools/dapp/)
1113
- [Embark](https://embark.status.im/)
1214
- [Etherlime](https://github.com/LimeChain/etherlime)
1315
- [Etherscan](https://etherscan.io/) (including several alt-chain explorers and testnets)
14-
- [Foundry](https://github.com/foundry-rs/foundry/)
15-
- [Hardhat](https://github.com/nomiclabs/hardhat)
1616
- [Truffle](https://truffleframework.com/)
1717
- [Waffle](https://github.com/EthWorks/Waffle)
1818

19+
To force compilation with a specific framework, use the `--compile-force-framework` flag. For example, to force compilation with Hardhat:
20+
21+
```shell
22+
crytic-compile . --compile-force-framework hardhat
23+
```
24+
1925
See the [Configuration](https://github.com/crytic/crytic-compile/wiki/Configuration) documentation for advanced usages.
2026

2127
The plugin is used in Trail of Bits tools, including:
@@ -27,21 +33,33 @@ The plugin is used in Trail of Bits tools, including:
2733

2834
## Installation
2935

30-
```bash
36+
```shell
3137
pip3 install crytic-compile
3238
```
3339

3440
## Usage
3541

36-
### Standalone
37-
```bash
42+
In the root directory of your project e.g. same directory as `hardhat.config.js` or `foundry.toml`, run:
43+
44+
```shell
3845
crytic-compile .
3946
```
4047

4148
Crytic-compile will generate `crytic-export/contracts.json` containing the AST/ABI and bytecodes of the contracts.
4249

4350
Run `crytic-compile --help` for more options.
4451

52+
## Library Linking
53+
54+
If your project uses [libraries](https://docs.soliditylang.org/en/latest/contracts.html#libraries) with external functions, they can be linked to their deployed address with the `--compile-libraries` flag. For example, if you have a library `SafeMath` deployed at `0xff`, you can link it with:
55+
56+
57+
```shell
58+
crytic-compile . --compile-libraries "(SafeMath, 0xff)"
59+
```
60+
61+
If you are fuzzing with Echidna or Medusa, follow this [tutorial on linking libraries](https://secure-contracts.com/program-analysis/echidna/advanced/working-with-libraries.html?highlight=library#linking-libraries).
62+
4563
### As a library
4664

4765
See the [library documentation](https://github.com/crytic/crytic-compile/wiki/Library-Documentation).

crytic_compile/crytic_compile.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@
3636

3737

3838
def get_platforms() -> List[Type[AbstractPlatform]]:
39-
"""Return the available platforms classes
39+
"""Return the available platforms classes in order of preference
4040
4141
Returns:
4242
List[Type[AbstractPlatform]]: Available platforms
4343
"""
4444
platforms = [getattr(all_platforms, name) for name in dir(all_platforms)]
4545
platforms = [d for d in platforms if inspect.isclass(d) and issubclass(d, AbstractPlatform)]
46-
return sorted(platforms, key=lambda platform: platform.TYPE)
46+
return sorted(platforms, key=lambda platform: (platform.TYPE.priority(), platform.TYPE))
4747

4848

4949
def is_supported(target: str) -> bool:
@@ -63,13 +63,14 @@ def _extract_libraries(libraries_str: Optional[str]) -> Optional[Dict[str, int]]
6363

6464
if not libraries_str:
6565
return None
66-
67-
pattern = r"\((?P<name>\w+),\s*(?P<value1>0x[0-9a-fA-F]{2})\),?"
66+
# Extract tuple like (libname1, 0x00)
67+
pattern = r"\((?P<name>\w+),\s*(?P<value1>0x[0-9a-fA-F]{2,40})\),?"
6868
matches = re.findall(pattern, libraries_str)
6969

7070
if not matches:
71-
logging.info(f"Libraries {libraries_str} could not be parsed")
72-
return None
71+
raise ValueError(
72+
f"Invalid library linking directive\nGot:\n{libraries_str}\nExpected format:\n(libname1, 0x00),(libname2, 0x02)"
73+
)
7374

7475
ret: Dict[str, int] = {}
7576
for key, value in matches:

crytic_compile/platform/etherscan.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None:
299299
LOGGER.error("Incorrect etherscan request")
300300
raise InvalidCompilation("Incorrect etherscan request " + etherscan_url)
301301

302+
if not info["message"].startswith("OK") and "Invalid API Key" in info["result"]:
303+
LOGGER.error("Invalid etherscan API Key")
304+
raise InvalidCompilation("Invalid etherscan API Key: " + etherscan_url)
305+
302306
if not info["message"].startswith("OK"):
303307
LOGGER.error("Contract has no public source code")
304308
raise InvalidCompilation("Contract has no public source code: " + etherscan_url)

crytic_compile/platform/hardhat.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,10 @@ def is_supported(target: str, **kwargs: str) -> bool:
212212
if hardhat_ignore:
213213
return False
214214

215-
# If there is both foundry and hardhat, foundry takes priority
216-
if os.path.isfile(os.path.join(target, "foundry.toml")):
217-
return False
218-
219-
return os.path.isfile(os.path.join(target, "hardhat.config.js")) | os.path.isfile(
220-
os.path.join(target, "hardhat.config.ts")
215+
return (
216+
os.path.isfile(os.path.join(target, "hardhat.config.js"))
217+
or os.path.isfile(os.path.join(target, "hardhat.config.ts"))
218+
or os.path.isfile(os.path.join(target, "hardhat.config.cjs"))
221219
)
222220

223221
def is_dependency(self, path: str) -> bool:

crytic_compile/platform/solc.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@
3434
def _build_contract_data(compilation_unit: "CompilationUnit") -> Dict:
3535
contracts = {}
3636

37-
libraries = compilation_unit.crytic_compile.libraries
37+
libraries_to_update = compilation_unit.crytic_compile.libraries
3838

3939
for filename, source_unit in compilation_unit.source_units.items():
4040
for contract_name in source_unit.contracts_names:
41+
libraries = source_unit.libraries_names_and_patterns(contract_name)
4142
abi = str(source_unit.abi(contract_name))
4243
abi = abi.replace("'", '"')
4344
abi = abi.replace("True", "true")
@@ -48,10 +49,11 @@ def _build_contract_data(compilation_unit: "CompilationUnit") -> Dict:
4849
"srcmap": ";".join(source_unit.srcmap_init(contract_name)),
4950
"srcmap-runtime": ";".join(source_unit.srcmap_runtime(contract_name)),
5051
"abi": abi,
51-
"bin": source_unit.bytecode_init(contract_name, libraries),
52-
"bin-runtime": source_unit.bytecode_runtime(contract_name, libraries),
52+
"bin": source_unit.bytecode_init(contract_name, libraries_to_update),
53+
"bin-runtime": source_unit.bytecode_runtime(contract_name, libraries_to_update),
5354
"userdoc": source_unit.natspec[contract_name].userdoc.export(),
5455
"devdoc": source_unit.natspec[contract_name].devdoc.export(),
56+
"libraries": dict(libraries) if libraries else {},
5557
}
5658
return contracts
5759

@@ -519,16 +521,21 @@ def _run_solc(
519521

520522
additional_kwargs: Dict = {"cwd": working_dir} if working_dir else {}
521523
if not compiler_version.version in [f"0.4.{x}" for x in range(0, 11)]:
522-
# Add . as default allowed path
524+
# Add --allow-paths argument, if it isn't already specified
525+
# We allow the CWD as well as the directory that contains the file
523526
if "--allow-paths" not in cmd:
524527
file_dir_start = os.path.normpath(os.path.dirname(filename))
528+
# Paths in the --allow-paths arg can't contain commas, since this is the delimeter
529+
# Try using absolute path; if it contains a comma, try using relative path instead
525530
file_dir = os.path.abspath(file_dir_start)
526531
if "," in file_dir:
527532
try:
528533
file_dir = os.path.relpath(file_dir_start)
529534
except ValueError:
535+
# relpath can fail if, for example, we're on Windows and the directory is on a different drive than CWD
530536
pass
531537

538+
# Even the relative path might have a comma in it, so we want to make sure first
532539
if "," not in file_dir:
533540
cmd += ["--allow-paths", ".," + file_dir]
534541
else:

crytic_compile/platform/truffle.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -305,12 +305,6 @@ def is_supported(target: str, **kwargs: str) -> bool:
305305
if truffle_ignore:
306306
return False
307307

308-
# Avoid conflicts with hardhat
309-
if os.path.isfile(os.path.join(target, "hardhat.config.js")) | os.path.isfile(
310-
os.path.join(target, "hardhat.config.ts")
311-
):
312-
return False
313-
314308
return os.path.isfile(os.path.join(target, "truffle.js")) or os.path.isfile(
315309
os.path.join(target, "truffle-config.js")
316310
)

crytic_compile/platform/types.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,24 @@ def __str__(self) -> str: # pylint: disable=too-many-branches
6666
if self == Type.FOUNDRY:
6767
return "Foundry"
6868
raise ValueError
69+
70+
def priority(self) -> int:
71+
"""Return the priority for a certain platform.
72+
73+
A lower priority means the platform is more preferable. This is used to
74+
consistently select a platform when two or more are available.
75+
76+
Returns:
77+
int: priority number
78+
"""
79+
80+
if self == Type.FOUNDRY:
81+
return 100
82+
83+
if self == Type.HARDHAT:
84+
return 200
85+
86+
if self in [Type.TRUFFLE, Type.WAFFLE]:
87+
return 300
88+
89+
return 1000

0 commit comments

Comments
 (0)