Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: automatically handle solc configuration for Etherscan Platform #544

Merged
merged 16 commits into from
Mar 20, 2024

Conversation

shortdoom
Copy link
Contributor

@shortdoom shortdoom commented Jan 25, 2024

Related: #543

PoC as it only deals with the most common scenario when relative imports were originally structured around @package-name. For foundry projects it's most common to specify remappings string without the "@" and there are also more messier examples in the wild. I think it should be possible to cover most of them though.

What does it do?

After downloading and compiling code with the use of Etherscan platform it will output solc_remaps.txt file with "guessed" remappings strings, built out of absolute paths used internally by crytic-compile at first download. Then, it will save the file to the working directory of project (downloaded files)

How to use?

No additional command is needed. Run crytic-compile as usual with address on the network as the target. After it finishes

cd into the root of downloaded project (crytic-export/etherscan-contracts/0x1-Test) and run:

crytic-compile contracts/Target.sol --solc-remaps $(cat solc_remaps.txt)

Edit/Update: Changed approach from the original

Summary by CodeRabbit

  • New Features
    • Enhanced the compile method to include creation and export of metadata_config for Solidity compilation settings, improving project configuration and setup.
    • Added a new Etherscan test in the script ci_test_etherscan.sh to validate contract compilation and configuration file existence.

@0xalpharush
Copy link
Contributor

0xalpharush commented Jan 25, 2024

I think it's a great idea to automate this

What is the reason to guess the remapping instead of saving the ones downloaded from etherscan here in crytic-export/remappings.txt?

with open(path_filename_disk, "w", encoding="utf8") as file_desc:
file_desc.write(source_code["content"])
remappings = dict_source_code.get("settings", {}).get("remappings", None)
return list(filtered_paths), directory, _sanitize_remappings(remappings, directory)

We can probably automatically load the remappings.txt file and not require the user to pass --solc-remaps as well

Wdyt?

@shortdoom
Copy link
Contributor Author

Those remappings seem to be always empty (None) for me, no matter what the target when I was running manual testing. I verified most of this with slapping logging functions here and there through out the execution flow of CryticCompile => Etherscan => solc_standard_json.standalone_compile().

as far as I could understood, CompilationUnit actually just uses absolute paths from here to compile on the first run:

self._source_units: Dict[Filename, SourceUnit] = {}

hence why I also generated just absolute paths for the solc_remaps.txt. those seem more reliable than relative.

remappings variable seems to, for some reason, not be used or at least doesn't generate any usable remapping strings for next compilation

@elopez
Copy link
Member

elopez commented Jan 26, 2024

Hi @shortdoom! thanks for working on this!

I verified locally and the remapping variable does have proper remappings, is it possible you tested with a contract on etherscan that does not have the metadata? I printed the remappings like the following:

diff --git a/crytic_compile/platform/etherscan.py b/crytic_compile/platform/etherscan.py
index a8f54b4..cbec350 100644
--- a/crytic_compile/platform/etherscan.py
+++ b/crytic_compile/platform/etherscan.py
@@ -187,6 +187,10 @@ def _handle_multiple_files(
 
     remappings = dict_source_code.get("settings", {}).get("remappings", None)
 
+    print("Remappings:", remappings)
+    print("Directory:", directory)
+    print("Sanitized:", _sanitize_remappings(remappings, directory))
+
     return list(filtered_paths), directory, _sanitize_remappings(remappings, directory)
 

And fetching an address, I do get remappings:

% ./venv/bin/crytic-compile 0x2a311e451491091d2a1d3c43f4f5744bdb4e773a
Remappings: ['@0x/contracts-utils=/home/cluracan/code/0x-protocol/node_modules/@0x/contracts-utils', '@0x/contracts-erc20=/home/cluracan/code/0x-protocol/contracts/zero-ex/node_modules/@0x/contracts-erc20']
Directory: crytic-export/etherscan-contracts/0x2a311e451491091d2a1d3c43f4f5744bdb4e773a-NativeOrdersFeature
Sanitized: ['@0x/contracts-utils=home/cluracan/code/0x-protocol/node_modules/@0x/contracts-utils/', '@0x/contracts-erc20=home/cluracan/code/0x-protocol/contracts/zero-ex/node_modules/@0x/contracts-erc20/']
INFO:CryticCompile:'solc --standard-json --allow-paths /Users/emilio/git/crytic-compile/crytic-export/etherscan-contracts/0x2a311e451491091d2a1d3c43f4f5744bdb4e773a-NativeOrdersFeature' running

@shortdoom
Copy link
Contributor Author

Huh, yes! It seems that targets I was running it against were without the metadata.

That definitely changes the purpose of this PR, happy to hear how you would want to proceed.

Ps. Nice target address for testing path resolution edge cases :)

@elopez
Copy link
Member

elopez commented Jan 26, 2024

yeah that's a cool address to test things with, there's other ones on the ci_etherscan_test.sh file if you want to try them out :)

by the way, regarding the address you were using to test on Slack (0x29d2bcf0d70f95ce16697e645e2b76d218d66109), it seems that one doesn't require any remappings to build; that likely explains why it works while there's no remappings on the metadata.

emilio@mbpro 0x29d2bcf0d70f95ce16697e645e2b76d218d66109-OxODexPool % crytic-compile contracts/OxODexPool.sol
INFO:CryticCompile:'solc --version' running
INFO:CryticCompile:'solc contracts/OxODexPool.sol --combined-json abi,ast,bin,bin-runtime,srcmap,srcmap-runtime,userdoc,devdoc,hashes --allow-paths .,/Users/emilio/git/crytic-compile/crytic-export/etherscan-contracts/0x29d2bcf0d70f95ce16697e645e2b76d218d66109-OxODexPool/contracts' running
INFO:CryticCompile:Compilation warnings/errors on contracts/OxODexPool.sol:
Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> contracts/interfaces/IWETH9.sol

Warning: Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.
  --> contracts/lib/LSAG.sol:21:18:
   |
21 |         returns (uint256[2] memory)
   |                  ^^^^^^^^^^^^^^^^^

Warning: Unused local variable.
   --> contracts/OxODexPool.sol:337:24:
    |
337 |         (bool success, bytes memory data) = _recipient.call{value: _amount - relayerFee}("");
    |                        ^^^^^^^^^^^^^^^^^

On an unrelated note, it seems globbing of files is not working as expected @0xalpharush , I had to patch it as follows to build all the files at the same time. I saw there's a TODO on that area so I'm bringing it to your attention here.

diff --git a/crytic_compile/crytic_compile.py b/crytic_compile/crytic_compile.py
index 3996150..8f2b896 100644
--- a/crytic_compile/crytic_compile.py
+++ b/crytic_compile/crytic_compile.py
@@ -717,8 +717,8 @@ def compile_all(target: str, **kwargs: str) -> List[CryticCompile]:
         else:
             compilations.append(CryticCompile(target, **kwargs))
     elif os.path.isdir(target):
-        solidity_filenames = glob.glob(os.path.join(target, "*.sol"))
-        vyper_filenames = glob.glob(os.path.join(target, "*.vy"))
+        solidity_filenames = glob.glob(os.path.join(target, "**", "*.sol"), recursive=True)
+        vyper_filenames = glob.glob(os.path.join(target, "**", "*.vy"), recursive=True)
         # Determine if we're using --standard-solc option to
         # aggregate many files into a single compilation.
         if use_solc_standard_json:

With that change you can build all the files from 0x29d2bcf0d70f95ce16697e645e2b76d218d66109 at once after the initial download:

emilio@mbpro 0x29d2bcf0d70f95ce16697e645e2b76d218d66109-OxODexPool % ../../../venv/bin/crytic-compile . --solc-standard-json
INFO:CryticCompile:'solc --version' running
INFO:CryticCompile:'solc --standard-json --allow-paths /Users/emilio/git/crytic-compile/crytic-export/etherscan-contracts/0x29d2bcf0d70f95ce16697e645e2b76d218d66109-OxODexPool' running
WARNING:CryticCompile:Warning: Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> ./contracts/interfaces/IWETH9.sol


Warning: Warning: Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.
  --> ./contracts/lib/LSAG.sol:21:18:
   |
21 |         returns (uint256[2] memory)
   |                  ^^^^^^^^^^^^^^^^^


Warning: Warning: Unused local variable.
   --> ./contracts/OxODexPool.sol:337:24:
    |
337 |         (bool success, bytes memory data) = _recipient.call{value: _amount - relayerFee}("");
    |                        ^^^^^^^^^^^^^^^^^

Going back to the PR @shortdoom, I think it would be handy to preserve more of the metadata we fetch. From experience, some etherscan contracts don't build without using the same optimization/compiler flags (e.g. via-ir, or the evm target version). I think writing a suitable foundry.toml file would be an easy way to achieve storage and make it easily usable, although it might require adding one new contracts folder in the storage path, what do you all think? Is there a more generic way to register this metadata?

@shortdoom
Copy link
Contributor Author

I dug a little deeper as some of targets were still failing for me, including 0x2a311e451491091d2a1d3c43f4f5744bdb4e773a from cli_tests_etherscan.sh.

Can you try first downloading and then cd into the directory to re-execute compilation of Target.sol and see if it succeeds or complains about import paths?

I found few more targets like this, each having node_modules directory involved.

0x19c7d0fbf906c282dedb5543d098f43dfe9f856f also from cli_tests_etherscan.sh.

However, for the above - in contrary to all of the other examples - it helps to manually provide remappings as argument crytic-compile contracts/SNDMN.sol --solc-remaps "@openzeppelin/=node_modules/@openzeppelin/"

And, two more, also with node_modules mapping

0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD - fails to re-compile
0x841AF31c35FcE829DbB939466239Dc32F96840ae - fails to re-compile

Coming back to how to built it into the better automation I think we could just use remappings from here

with open(path_filename_disk, "w", encoding="utf8") as file_desc:
file_desc.write(source_code["content"])
remappings = dict_source_code.get("settings", {}).get("remappings", None)
return list(filtered_paths), directory, _sanitize_remappings(remappings, directory)
and output them to the remappings.txt file then crytic-compile could just check for existence of this file and and compile accordingly, as @0xalpharush suggested.

@elopez
Copy link
Member

elopez commented Jan 26, 2024

@shortdoom I see 0x2a311e451491091d2a1d3c43f4f5744bdb4e773a does have remappings on its metadata:

Remappings: ['@0x/contracts-utils=/home/cluracan/code/0x-protocol/node_modules/@0x/contracts-utils', '@0x/contracts-erc20=/home/cluracan/code/0x-protocol/contracts/zero-ex/node_modules/@0x/contracts-erc20']
Directory: crytic-export/etherscan-contracts/0x2a311e451491091d2a1d3c43f4f5744bdb4e773a-NativeOrdersFeature
Sanitized: ['@0x/contracts-utils=home/cluracan/code/0x-protocol/node_modules/@0x/contracts-utils/', '@0x/contracts-erc20=home/cluracan/code/0x-protocol/contracts/zero-ex/node_modules/@0x/contracts-erc20/']

It is also a good example why other metadata such as the solc version is important; even though the contracts claim solc ^0.6.5, they don't build with that version, but do with 0.6.12 (the one in the etherscan metadata).

Manually providing the remappings from the metadata does work [note I'm using the globbing patch from my earlier message still, to make things easier to build]

emilio@mbpro 0x2a311e451491091d2a1d3c43f4f5744bdb4e773a-NativeOrdersFeature % ../../../venv/bin/crytic-compile .  --solc-remaps '@0x/contracts-utils=home/cluracan/code/0x-protocol/node_modules/@0x/contracts-utils/ @0x/contracts-erc20=home/cluracan/code/0x-protocol/contracts/zero-ex/node_modules/@0x/contracts-erc20/'
INFO:CryticCompile:'solc --version' running
INFO:CryticCompile:'solc @0x/contracts-utils=home/cluracan/code/0x-protocol/node_modules/@0x/contracts-utils/ @0x/contracts-erc20=home/cluracan/code/0x-protocol/contracts/zero-ex/node_modules/@0x/contracts-erc20/ ./home/cluracan/code/0x-protocol/contracts/zero-ex/contracts/src/migrations/LibMigrate.sol --combined-json abi,ast,bin,bin-runtime,srcmap,srcmap-runtime,userdoc,devdoc,hashes,compact-format --allow-paths .,/Users/emilio/git/crytic-compile/crytic-export/etherscan-contracts/0x2a311e451491091d2a1d3c43f4f5744bdb4e773a-NativeOrdersFeature/home/cluracan/code/0x-protocol/contracts/zero-ex/contracts/src/migrations' running
INFO:CryticCompile:'solc --version' running
INFO:CryticCompile:'solc @0x/contracts-utils=home/cluracan/code/0x-protocol/node_modules/@0x/contracts-utils/ @0x/contracts-erc20=home/cluracan/code/0x-protocol/contracts/zero-ex/node_modules/@0x/contracts-erc20/ ./home/cluracan/code/0x-protocol/contracts/zero-ex/contracts/src/fixins/FixinTokenSpender.sol --combined-json abi,ast,bin,bin-runtime,srcmap,srcmap-runtime,userdoc,devdoc,hashes,compact-format --allow-paths .,/Users/emilio/git/crytic-compile/crytic-export/etherscan-contracts/0x2a311e451491091d2a1d3c43f4f5744bdb4e773a-NativeOrdersFeature/home/cluracan/code/0x-protocol/contracts/zero-ex/contracts/src/fixins' running
INFO:CryticCompile:'solc --version' running
INFO:CryticCompile:'solc @0x/contracts-utils=home/cluracan/code/0x-protocol/node_modules/@0x/contracts-utils/ @0x/contracts-erc20=home/cluracan/code/0x-protocol/contracts/zero-ex/node_modules/@0x/contracts-erc20/ ./home/cluracan/code/0x-protocol/contracts/zero-ex/contracts/src/fixins/FixinProtocolFees.sol --combined-json abi,ast,bin,bin-runtime,srcmap,srcmap-runtime,userdoc,devdoc,hashes,compact-format --allow-paths .,/Users/emilio/git/crytic-compile/crytic-export/etherscan-contracts/0x2a311e451491091d2a1d3c43f4f5744bdb4e773a-NativeOrdersFeature/home/cluracan/code/0x-protocol/contracts/zero-ex/contracts/src/fixins' running
INFO:CryticCompile:'solc --version' running
INFO:CryticCompile:'solc @0x/contracts-utils=home/cluracan/code/0x-protocol/node_modules/@0x/contracts-utils/ @0x/contracts-erc20=home/cluracan/code/0x-protocol/contracts/zero-ex/node_modules/@0x/contracts-erc20/ ./home/cluracan/code/0x-protocol/contracts/zero-ex/contracts/src/fixins/FixinEIP712.sol --combined-json abi,ast,bin,bin-runtime,srcmap,srcmap-runtime,userdoc,devdoc,hashes,compact-format --allow-paths .,/Users/emilio/git/crytic-compile/crytic-export/etherscan-contracts/0x2a311e451491091d2a1d3c43f4f5744bdb4e773a-NativeOrdersFeature/home/cluracan/code/0x-protocol/contracts/zero-ex/contracts/src/fixins' running
INFO:CryticCompile:'solc --version' running
INFO:CryticCompile:'solc @0x/contracts-utils=home/cluracan/code/0x-protocol/node_modules/@0x/contracts-utils/ @0x/contracts-erc20=home/cluracan/code/0x-protocol/contracts/zero-ex/node_modules/@0x/contracts-erc20/ ./home/cluracan/code/0x-protocol/contracts/zero-ex/contracts/src/fixins/FixinCommon.sol --combined-json abi,ast,bin,bin-runtime,srcmap,srcmap-runtime,userdoc,devdoc,hashes,compact-format --allow-paths .,/Users/emilio/git/crytic-compile/crytic-export/etherscan-contracts/0x2a311e451491091d2a1d3c43f4f5744bdb4e773a-NativeOrdersFeature/home/cluracan/code/0x-protocol/contracts/zero-ex/contracts/src/fixins' running
INFO:CryticCompile:'solc --version' running
INFO:CryticCompile:'solc @0x/contracts-utils=home/cluracan/code/0x-protocol/node_modules/@0x/contracts-utils/ @0x/contracts-erc20=home/cluracan/code/0x-protocol/contracts/zero-ex/node_modules/@0x/contracts-erc20/ ./home/cluracan/code/0x-protocol/contracts/zero-ex/contracts/src/features/INativeOrdersFeature.sol --combined-json abi,ast,bin,bin-runtime,srcmap,srcmap-runtime,userdoc,devdoc,hashes,compact-format --allow-paths .,/Users/emilio/git/crytic-compile/crytic-export/etherscan-contracts/0x2a311e451491091d2a1d3c43f4f5744bdb4e773a-NativeOrdersFeature/home/cluracan/code/0x-protocol/contracts/zero-ex/contracts/src/features' running
...

So yeah, I think just storing the (sanitized) remappings we get from etherscan should be sufficient for remapping purposes 👍

Copy link

coderabbitai bot commented Jan 28, 2024

Walkthrough

The recent update to the etherscan.py file introduces an enhancement in the Solidity compilation process by incorporating a metadata_config dictionary. This addition involves specifying various compilation settings, including remappings, the compiler version, EVM version, and optimization preferences, which are crucial for a tailored and efficient compilation. Furthermore, it extends its functionality by saving this configuration into a JSON file, streamlining the compilation process and ensuring consistency across builds.

Changes

File Path Change Summary
crytic_compile/platform/etherscan.py Added creation of metadata_config with Solidity compilation settings and saving it to a JSON file.

Related issues

  • Etherscan Platform remapping data #543: The changes in etherscan.py address the objectives outlined in this issue by capturing and outputting correct remapping data into a configuration file, facilitating subsequent re-compilations within the downloaded directory.

🐇✨
In the realm of code, under digital skies,
A rabbit hopped, with bright, keen eyes.
"A tweak, a twist, a clever guise,
Ensuring our Solidity flies!"
🌟📜

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit-tests for this file.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit tests for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository from git and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 2

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 1b2998e and b5f0d62.
Files selected for processing (1)
  • crytic_compile/platform/etherscan.py (1 hunks)

Comment on lines 427 to 432
metadata_config = {
"solc_remaps": remappings if remappings else {},
"solc_solcs_select": compiler_version,
"solc_args": ("--via-ir" if via_ir_enabled else "")
+ ("--optimize --optimize-runs " + str(optimize_runs) if optimize_runs else "")
+ ("--evm-version " + evm_version if evm_version else ""),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The construction of the metadata_config dictionary combines several conditional string concatenations for solc_args. This approach, while functional, can lead to maintenance challenges and readability issues. Consider refactoring this into a more modular approach, where each condition is checked and the corresponding string is appended to a list. This list can then be joined with spaces to form the final solc_args string. This would improve readability and make future modifications easier.

- "solc_args": ("--via-ir" if via_ir_enabled else "")
-     + ("--optimize --optimize-runs " + str(optimize_runs) if optimize_runs else "")
-     + ("--evm-version " + evm_version if evm_version else ""),
+ "solc_args": " ".join(filter(None, [
+     "--via-ir" if via_ir_enabled else "",
+     "--optimize --optimize-runs " + str(optimize_runs) if optimize_runs else "",
+     "--evm-version " + evm_version if evm_version else ""
+ ])),

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
metadata_config = {
"solc_remaps": remappings if remappings else {},
"solc_solcs_select": compiler_version,
"solc_args": ("--via-ir" if via_ir_enabled else "")
+ ("--optimize --optimize-runs " + str(optimize_runs) if optimize_runs else "")
+ ("--evm-version " + evm_version if evm_version else ""),
metadata_config = {
"solc_remaps": remappings if remappings else {},
"solc_solcs_select": compiler_version,
"solc_args": " ".join(filter(None, [
"--via-ir" if via_ir_enabled else "",
"--optimize --optimize-runs " + str(optimize_runs) if optimize_runs else "",
"--evm-version " + evm_version if evm_version else ""
])),

Comment on lines 435 to 439
with open(
os.path.join(working_dir if working_dir else export_dir, "crytic_compile.config.json"),
"w",
) as f:
json.dump(metadata_config, f)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing the metadata_config dictionary directly to a file without any error handling could lead to uncaught exceptions if there are issues with file access or disk space. It's recommended to wrap this operation in a try-except block to catch and handle potential IOError or PermissionError exceptions gracefully. This would improve the robustness of the code by ensuring that such errors are handled appropriately, possibly logging an error message or retrying the operation.

+ try:
  with open(
      os.path.join(working_dir if working_dir else export_dir, "crytic_compile.config.json"),
      "w",
  ) as f:
      json.dump(metadata_config, f)
+ except (IOError, PermissionError) as e:
+     LOGGER.error("Failed to write metadata_config to file: %s", e)

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
with open(
os.path.join(working_dir if working_dir else export_dir, "crytic_compile.config.json"),
"w",
) as f:
json.dump(metadata_config, f)
try:
with open(
os.path.join(working_dir if working_dir else export_dir, "crytic_compile.config.json"),
"w",
) as f:
json.dump(metadata_config, f)
except (IOError, PermissionError) as e:
LOGGER.error("Failed to write metadata_config to file: %s", e)

@shortdoom
Copy link
Contributor Author

I decided to output metadata directly to the crytic_compile.config.json file - that's automatically picked up by crytic-compile when re-compiling again from the downloaded directory and now no cli arguments are needed as it was the case with some sources. This is crytic-compile (solc) specific, for foundry/hardhat it would still be up to the user to build an appropriate config files.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 1

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between b5f0d62 and d47b73a.
Files selected for processing (2)
  • crytic_compile/platform/etherscan.py (1 hunks)
  • scripts/ci_test_etherscan.sh (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • crytic_compile/platform/etherscan.py

Comment on lines 103 to 132
# From crytic/crytic-compile#544
echo "::group::Etherscan #8"
crytic-compile 0x9AB6b21cDF116f611110b048987E58894786C244 --etherscan-apikey "$GITHUB_ETHERSCAN"

if [ $? -ne 0 ]
then
echo "Etherscan #8 test failed"
exit 255
fi

dir_name=$(ls crytic-export/etherscan-contracts/ | grep 0x9AB6b21cDF116f611110b048987E58894786C244)
cd crytic-export/etherscan-contracts/$dir_name

if [ ! -f crytic_compile.config.json ]; then
echo "crytic_compile.config.json does not exist"
exit 255
fi

# TODO: Globbing at crytic_compile.py:720 to run with '.'
crytic-compile 'contracts/InterestRates/InterestRatePositionManager.f.sol' --config-file crytic_compile.config.json

if [ $? -ne 0 ]
then
echo "crytic-compile command failed"
exit 255
fi

cd ../../../

echo "::endgroup::"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of Etherscan test #8 introduces a new test case for compiling a specific contract and checking for the existence of crytic_compile.config.json before running the compilation process. This is a positive step towards automating the generation of solc-remaps files for projects downloaded from Etherscan. However, there are a few areas that could be improved for clarity, maintainability, and robustness:

  1. Use of $? for Exit Status: Directly after running a command, the script checks $? for the exit status. This is a common practice but can lead to errors if additional commands are inadvertently inserted between the command and its status check. Consider capturing the exit status immediately in a variable to avoid such issues.

  2. Hardcoded Contract Address: The contract address 0x9AB6b21cDF116f611110b048987E58894786C244 is used twice, suggesting it has a specific significance for this test. It would be beneficial to define this as a variable at the start of the test section for clarity and ease of maintenance.

  3. Checking for Configuration File Existence: The script checks for the existence of crytic_compile.config.json but does not verify its content or structure. While the presence of the file is a good first step, ensuring it contains the expected configuration (even in a basic form) could prevent future errors.

  4. TODO Comment on Globbing: The TODO comment regarding globbing at crytic_compile.py:720 to run with '.' suggests an improvement or a fix that is pending. It's important to track these TODOs outside of the codebase, such as in an issue tracker, to ensure they are not forgotten.

  5. Directory Navigation: The script navigates into and out of directories using cd. While this works, it can be error-prone if the script fails or exits unexpectedly before reaching the cd ../../../. Consider using pushd and popd for more robust directory navigation, or running the commands in a subshell to avoid changing the working directory of the script.

  6. Error Handling and Messages: The error messages are clear but could include more context to aid in debugging. For example, specifying which part of the test failed or including the contract address in the error message could provide immediate clues without needing to dig through the script.

Improvements in these areas could enhance the script's readability, maintainability, and error resilience.

+ CONTRACT_ADDRESS="0x9AB6b21cDF116f611110b048987E58894786C244"
+ echo "Testing Etherscan compilation for contract $CONTRACT_ADDRESS"
- 105~
- 106~ if [ $? -ne 0 ]
+ COMPILE_STATUS=$?
+ if [ $COMPILE_STATUS -ne 0 ]
- 117~ # TODO: Globbing at crytic_compile.py:720 to run with '.'
+ # Consider tracking this TODO in an issue tracker
- 119~ if [ $? -ne 0 ]
+ COMPILE_STATUS=$?
+ if [ $COMPILE_STATUS -ne 0 ]
- 124~ cd ../../../
+ popd

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
# From crytic/crytic-compile#544
echo "::group::Etherscan #8"
crytic-compile 0x9AB6b21cDF116f611110b048987E58894786C244 --etherscan-apikey "$GITHUB_ETHERSCAN"
if [ $? -ne 0 ]
then
echo "Etherscan #8 test failed"
exit 255
fi
dir_name=$(ls crytic-export/etherscan-contracts/ | grep 0x9AB6b21cDF116f611110b048987E58894786C244)
cd crytic-export/etherscan-contracts/$dir_name
if [ ! -f crytic_compile.config.json ]; then
echo "crytic_compile.config.json does not exist"
exit 255
fi
# TODO: Globbing at crytic_compile.py:720 to run with '.'
crytic-compile 'contracts/InterestRates/InterestRatePositionManager.f.sol' --config-file crytic_compile.config.json
if [ $? -ne 0 ]
then
echo "crytic-compile command failed"
exit 255
fi
cd ../../../
echo "::endgroup::"
# From crytic/crytic-compile#544
echo "::group::Etherscan #8"
CONTRACT_ADDRESS="0x9AB6b21cDF116f611110b048987E58894786C244"
echo "Testing Etherscan compilation for contract $CONTRACT_ADDRESS"
crytic-compile $CONTRACT_ADDRESS --etherscan-apikey "$GITHUB_ETHERSCAN"
COMPILE_STATUS=$?
if [ $COMPILE_STATUS -ne 0 ]
then
echo "Etherscan #8 test failed"
exit 255
fi
dir_name=$(ls crytic-export/etherscan-contracts/ | grep $CONTRACT_ADDRESS)
cd crytic-export/etherscan-contracts/$dir_name
if [ ! -f crytic_compile.config.json ]; then
echo "crytic_compile.config.json does not exist"
exit 255
fi
# Consider tracking this TODO in an issue tracker
crytic-compile 'contracts/InterestRates/InterestRatePositionManager.f.sol' --config-file crytic_compile.config.json
COMPILE_STATUS=$?
if [ $COMPILE_STATUS -ne 0 ]
then
echo "crytic-compile command failed"
exit 255
fi
popd
echo "::endgroup::"

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between d47b73a and 6bdc8e3.
Files selected for processing (1)
  • scripts/ci_test_etherscan.sh (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • scripts/ci_test_etherscan.sh

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 6bdc8e3 and 9c397a7.
Files selected for processing (1)
  • crytic_compile/platform/etherscan.py (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • crytic_compile/platform/etherscan.py

@0xalpharush 0xalpharush changed the title feat: PoC of auto-generating solc-remaps file (for Etherscan Platform) feat: automatically handle solc configuratio for Etherscan Platform Mar 20, 2024
@0xalpharush 0xalpharush changed the title feat: automatically handle solc configuratio for Etherscan Platform feat: automatically handle solc configuration for Etherscan Platform Mar 20, 2024
@0xalpharush 0xalpharush changed the base branch from master to dev March 20, 2024 18:50
@0xalpharush 0xalpharush added this pull request to the merge queue Mar 20, 2024
Merged via the queue into crytic:dev with commit de160c4 Mar 20, 2024
50 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants