diff --git a/pyproject.toml b/pyproject.toml index fae28c2..a041678 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "solc_detect" -version = "0.0.6" +version = "0.0.7" authors = [{name="Ta Quang Trung"},] description = "A tool to detect Solidity compiler version required by smart contracts" dynamic = ["dependencies"] diff --git a/solc_detect/__main__.py b/solc_detect/__main__.py index fd5da4e..11d4ac0 100644 --- a/solc_detect/__main__.py +++ b/solc_detect/__main__.py @@ -29,10 +29,10 @@ def configure_cli_arguments(): help="", ) - # Print verbose + # Quiet mode, print only the best version. arg_parser.add_argument( - "-v", - "--verbose", + "-q", + "--quiet", action="store_true", default=False, help="", @@ -74,11 +74,11 @@ def main(): arg_parser.exit() pragma_version = solc_detect.find_pragma_solc_version(input_file) - if args.verbose: + if not args.quiet: print("Detected pragmas:", pragma_version) best_version = solc_detect.find_best_solc_version_for_pragma(pragma_version) - if args.verbose: + if not args.quiet: print("Best version:", best_version) else: print(best_version) diff --git a/solc_detect/solc_detect.py b/solc_detect/solc_detect.py index eedfdba..fda87f4 100644 --- a/solc_detect/solc_detect.py +++ b/solc_detect/solc_detect.py @@ -4,32 +4,33 @@ Module define some utility variables and functions. """ -from typing import List, Union +from typing import List, Optional import nodesemver import semantic_version from solc_detect import pragma_parser -# from semantic_version import NpmSpec, Version +# Enumerate all Solidity versions based on information in: +# https://blog.soliditylang.org/category/release +solc_0_4 = ["0.4.%d" % i for i in range(27)] # 0.4.0 --> 0.4.26 +solc_0_5 = ["0.5.%d" % i for i in range(18)] # 0.5.0 --> 0.5.17 +solc_0_6 = ["0.6.%d" % i for i in range(13)] # 0.6.0 --> 0.6.12 +solc_0_7 = ["0.7.%d" % i for i in range(7)] # 0.7.0 --> 0.7.6 +solc_0_8 = ["0.8.%d" % i for i in range(20)] # 0.8.0 --> 0.8.19 -def init_all_solidity_versions() -> List[str]: - """Enumerate all Solidity versions. - - All Solidity releases: https://blog.soliditylang.org/category/releases/""" - - solidity_0_4 = ["0.4.%d" % i for i in range(27)] # 0.4.0 --> 0.4.26 - solidity_0_5 = ["0.5.%d" % i for i in range(18)] # 0.5.0 --> 0.5.17 - solidity_0_6 = ["0.6.%d" % i for i in range(13)] # 0.6.0 --> 0.6.12 - solidity_0_7 = ["0.7.%d" % i for i in range(7)] # 0.7.0 --> 0.7.6 - solidity_0_8 = ["0.8.%d" % i for i in range(20)] # 0.8.0 --> 0.8.19 - - all_versions = ( - solidity_0_4 + solidity_0_5 + solidity_0_6 + solidity_0_7 + solidity_0_8 - ) - - return all_versions +def enumerate_and_group_solc_version_by_minor_version() -> List[List[str]]: + """Enumerate all Solc versions, group by MINOR version, in each group, sort + by PATCH version, according to semantic versioning: https://semver.org/.""" + solc_versions = [ + solc_0_4, + solc_0_5, + solc_0_6, + solc_0_7, + solc_0_8, + ] + return solc_versions def find_pragma_solc_version(input_file) -> List[str]: @@ -38,23 +39,32 @@ def find_pragma_solc_version(input_file) -> List[str]: return pragma_versions -def find_best_solc_version_for_pragma(pragma_versions) -> Union[str, None]: - """Find the best version of Solc compiler for a pragma version.""" - all_versions = init_all_solidity_versions() +def find_best_solc_version_for_pragma(pragma_versions) -> Optional[str]: + """Find the best version of Solc compiler for a pragma version. This version + is the latest patch of the lowest minor version satisfying the required + pragmas.""" + + version_groups = enumerate_and_group_solc_version_by_minor_version() constraint = " ".join(pragma_versions) - try: - # Use `semantic_version` to find the best version first - all_semvers = [semantic_version.Version(v) for v in all_versions] - version_spec = semantic_version.NpmSpec(constraint) - best_version = version_spec.select(all_semvers) - return str(best_version) - except ValueError: - # If errors occur, use `node_semver` to find the best version - best_version = nodesemver.max_satisfying(all_versions, constraint) - return str(best_version) - - -def find_best_solc_version(input_file) -> Union[str, None]: - """Find the best version of Solc compiler for a smart contract.""" + for group in version_groups: + try: + # First, use `semantic_version` module to find the best version + all_semvers = [semantic_version.Version(v) for v in group] + version_spec = semantic_version.NpmSpec(constraint) + if (best_version := version_spec.select(all_semvers)): + return str(best_version) + except ValueError: + # If errors occur, then use `node_semver` module to find it + if (best_version := nodesemver.max_satisfying(group, constraint)): + return str(best_version) + + # Unable to find a suitable version + return None + + +def find_best_solc_version(input_file) -> Optional[str]: + """Find the best version of Solc compiler for a smart contract. This version + is the latest patch of the lowest minor version satisfying the required + pragmas.""" pragma_versions = find_pragma_solc_version(input_file) return find_best_solc_version_for_pragma(pragma_versions)