diff --git a/README.md b/README.md index 51e9eb7..5c137a3 100644 --- a/README.md +++ b/README.md @@ -7,41 +7,10 @@ Diff your Ethereum smart contracts code from GitHub against Blockchain explorer verified source code. -## Prerequisites +## Install -This project was developed using these dependencies with their exact versions listed below: - -- Python 3.12 -- Poetry 1.8 - -Other versions may work as well but were not tested at all. - -## Setup - -1. Install Poetry - -Use the following command to install poetry: - -```shell -pip install --user poetry~=1.8 -``` - -alternatively, you could proceed with `pipx`: - -```shell -pipx install poetry~=1.8 -``` - -2. Activate poetry virtual environment, - -```shell -poetry shell -``` - -3. Install Python dependencies - -```shell -poetry install +```bash +pipx install git+https://github.com/lidofinance/diffyscan ``` ## Usage @@ -61,7 +30,7 @@ export GITHUB_API_TOKEN= Start script with one of the examples provided ```bash -python3 main.py config_samples/lido_dao_sepolia_config.json +diffyscan config_samples/lido_dao_sepolia_config.json ``` Alternatively, create a new config file named `config.json`, @@ -92,7 +61,7 @@ Alternatively, create a new config file named `config.json`, Start the script ```bash -python3 main.py +dyffyscan ``` > Note: Brownie verification tooling might rewrite the imports in the source submission. It transforms relative paths to imported contracts into flat paths ('./folder/contract.sol' -> 'contract.sol'), which makes Diffyscan unable to find a contract for verification. @@ -100,7 +69,51 @@ python3 main.py For contracts whose sources were verified by brownie tooling: ```bash -python3 main.py --support-brownie +diffyscan --support-brownie ``` ℹ️ See more config examples inside the [config_samples](./config_samples/) dir. + +## Development setup + +### Prerequisites + +This project was developed using these dependencies with their exact versions listed below: + +- Python 3.12 +- Poetry 1.8 + +Other versions may work as well but were not tested at all. + +### Setup + +1. Install Poetry + +Use the following command to install poetry: + +```bash +pip install --user poetry~=1.8 +``` + +alternatively, you could proceed with `pipx`: + +```bash +pipx install poetry~=1.8 +``` + +2. Activate poetry virtual environment, + +```bash +poetry shell +``` + +3. Install [poetry-dynamic-versioning](https://github.com/mtkennerly/poetry-dynamic-versioning?tab=readme-ov-file#installation) + +- In most cases: `poetry self add "poetry-dynamic-versioning[plugin]"` +- If you installed Poetry with Pipx: `pipx inject poetry "poetry-dynamic-versioning[plugin]"` + +4. Install Python dependencies + +```bash +poetry install +``` diff --git a/main.py b/diffyscan/diffyscan.py similarity index 88% rename from main.py rename to diffyscan/diffyscan.py index 85d8c16..9ddb6e4 100644 --- a/main.py +++ b/diffyscan/diffyscan.py @@ -4,15 +4,16 @@ import argparse import os -from utils.common import load_config, load_env -from utils.constants import DIFFS_DIR, START_TIME, DEFAULT_CONFIG_PATH -from utils.explorer import get_contract_from_explorer -from utils.github import get_file_from_github, get_file_from_github_recursive, resolve_dep -from utils.helpers import create_dirs -from utils.logger import logger +from .utils.common import load_config, load_env +from .utils.constants import DIFFS_DIR, START_TIME, DEFAULT_CONFIG_PATH +from .utils.explorer import get_contract_from_explorer +from .utils.github import get_file_from_github, get_file_from_github_recursive, resolve_dep +from .utils.helpers import create_dirs +from .utils.logger import logger -GITHUB_API_TOKEN = load_env("GITHUB_API_TOKEN", masked=True) +__version__ = "0.0.0" + g_skip_user_input: bool = False @@ -83,7 +84,7 @@ def run_diff(config, name, address, explorer_api_token, github_api_token, recurs github_file = get_file_from_github_recursive(github_api_token, repo, path_to_file, dep_name) else: github_file = get_file_from_github(github_api_token, repo, path_to_file, dep_name) - + if not github_file: github_file = "" file_found = False @@ -126,6 +127,8 @@ def run_diff(config, name, address, explorer_api_token, github_api_token, recurs def process_config(path: str, recursive_parsing: bool): logger.info(f"Loading config {path}...") config = load_config(path) + + github_api_token = load_env("GITHUB_API_TOKEN", masked=True) explorer_token = None if "explorer_token_env_var" in config: explorer_token = load_env(config["explorer_token_env_var"], masked=True, required=False) @@ -133,11 +136,12 @@ def process_config(path: str, recursive_parsing: bool): contracts = config["contracts"] logger.info(f"Running diff for contracts from config {contracts}...") for address, name in config["contracts"].items(): - run_diff(config, name, address, explorer_token, GITHUB_API_TOKEN, recursive_parsing) + run_diff(config, name, address, explorer_token, github_api_token, recursive_parsing) def parse_arguments(): parser = argparse.ArgumentParser() + parser.add_argument("--version", "-V", action="store_true", help="Display version information") parser.add_argument("path", nargs="?", default=None, help="Path to config or directory with configs") parser.add_argument("--yes", "-y", help="If set don't ask for input before validating each contract", action="store_true") parser.add_argument( @@ -154,6 +158,10 @@ def main(): args = parse_arguments() g_skip_user_input = args.yes + if args.version: + print(f"Diffyscan {__version__}") + return + logger.info("Welcome to Diffyscan!") logger.divider() diff --git a/utils/common.py b/diffyscan/utils/common.py similarity index 93% rename from utils/common.py rename to diffyscan/utils/common.py index fdb710a..afae8fe 100644 --- a/utils/common.py +++ b/diffyscan/utils/common.py @@ -5,8 +5,8 @@ import requests -from utils.logger import logger -from utils.types import Config +from .logger import logger +from .types import Config def load_env(variable_name, required=True, masked=False): @@ -32,7 +32,7 @@ def load_config(path: str) -> Config: def fetch(url, headers={}): - logger.log(f"fetch: {url}") + logger.log(f"Fetch: {url}") response = requests.get(url, headers=headers) if response.status_code == 404: diff --git a/utils/constants.py b/diffyscan/utils/constants.py similarity index 100% rename from utils/constants.py rename to diffyscan/utils/constants.py diff --git a/utils/explorer.py b/diffyscan/utils/explorer.py similarity index 97% rename from utils/explorer.py rename to diffyscan/utils/explorer.py index ab3be06..a3fa6f6 100644 --- a/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -1,8 +1,8 @@ import json import sys -from utils.common import fetch -from utils.logger import logger +from .common import fetch +from .logger import logger def _errorNoSourceCodeAndExit(address): diff --git a/utils/github.py b/diffyscan/utils/github.py similarity index 98% rename from utils/github.py rename to diffyscan/utils/github.py index 850e129..97d8128 100644 --- a/utils/github.py +++ b/diffyscan/utils/github.py @@ -1,6 +1,7 @@ import base64 -from utils.common import fetch, parse_repo_link -from utils.logger import logger + +from .common import fetch, parse_repo_link +from .logger import logger def get_file_from_github(github_api_token, dependency_repo, path_to_file, dep_name): diff --git a/utils/helpers.py b/diffyscan/utils/helpers.py similarity index 100% rename from utils/helpers.py rename to diffyscan/utils/helpers.py diff --git a/utils/logger.py b/diffyscan/utils/logger.py similarity index 96% rename from utils/logger.py rename to diffyscan/utils/logger.py index 7895207..256ef90 100644 --- a/utils/logger.py +++ b/diffyscan/utils/logger.py @@ -1,6 +1,7 @@ import termtables -from utils.constants import LOGS_PATH -from utils.helpers import create_dirs + +from .constants import LOGS_PATH +from .helpers import create_dirs CYAN = "\033[96m" PURPLE = "\033[95m" @@ -107,7 +108,7 @@ def color_row(self, row): hlcolor = GREEN file_found = row[2] - diffs_found = row[3] != None and row[3] > 0 + diffs_found = row[3] is not None and row[3] > 0 if not file_found: hlcolor = RED diff --git a/diffyscan/utils/types.py b/diffyscan/utils/types.py new file mode 100644 index 0000000..7a3d570 --- /dev/null +++ b/diffyscan/utils/types.py @@ -0,0 +1,8 @@ +from typing import TypedDict + + +class Config(TypedDict): + contracts: dict[str, str] + network: str + github_repo: str + dependencies: dict[str, str] diff --git a/poetry.lock b/poetry.lock index eca2544..ba9c5a6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -315,4 +315,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<4" -content-hash = "9c5e3c7048e15a4e540b4604f7fa4138387fc3fd4ec827d39776dc559da5e2b5" +content-hash = "a3bffa1b0b06580edf3675779901c02a1b182f2bb204ce923c02da33faf8a458" diff --git a/pyproject.toml b/pyproject.toml index 94c6fc8..5315b3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,25 +1,37 @@ [tool.poetry] -name = "explorer-github-diff-checker" -version = "0.2.0" +name = "diffyscan" +version = "0.0.0" description = "Diff your Ethereum smart contracts code from GitHub against Blockchain explorer verified source code." authors = ["Azat Serikov "] license = "MIT" readme = "README.md" repository = "https://github.com/lidofinance/diffyscan" - keywords = ["ethereum", "diff", "sources"] -package-mode = false + +package-mode = true +packages = [{ include = "diffyscan"}] +exclude = ["config_samples"] + +[tool.poetry.scripts] +diffyscan = "diffyscan.diffyscan:main" [tool.poetry.dependencies] python = ">=3.10,<4" requests = "^2.32.2" termtables = "^0.2.4" -[tool.poetry.dev-dependencies] - [tool.poetry.group.dev.dependencies] black = "^24.4.2" +[tool.poetry-dynamic-versioning] +enable = true +vcs = "git" +style = "semver" +metadata = false + +[tool.poetry-dynamic-versioning.substitution] +files = ["diffyscan/diffyscan.py"] + [build-system] -requires = ["poetry-core>=1.9.0"] -build-backend = "poetry.core.masonry.api" +requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"] +build-backend = "poetry_dynamic_versioning.backend" diff --git a/utils/types.py b/utils/types.py deleted file mode 100644 index e0cfd32..0000000 --- a/utils/types.py +++ /dev/null @@ -1,8 +0,0 @@ -from typing import Dict, TypedDict - - -class Config(TypedDict): - contract: str - network: str - github_repo: str - dependencies: Dict[str, str]