├── .gitignore ├── contracts └── @cryptosvinarnik.zip ├── requirements.txt ├── README.md ├── config.py ├── utils ├── scan_api.py ├── deployment.py └── saving.py ├── app.py ├── app_cmd.py └── templates └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /contracts/@cryptosvinarnik.zip: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.11.1 2 | eth_account==0.5.6 3 | Flask==2.2.2 4 | httpx==0.23.0 5 | loguru==0.5.3 6 | requests==2.27.1 7 | web3==5.25.0 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # contract-parser 2 | Install requirements: pip install -r requirements.txt 3 | 4 | Run app.py for saver with local server or run app_cmd.py for cmd version. 5 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | API_KEY = "" # <--- input your API_KEY from etherscan.io 2 | 3 | 4 | # DON'T TOUCH THIS 5 | ENDPOINTS = {"1": "https://api.etherscan.io", "2": "https://api.bscscan.com", "3": "https://api.polygonscan.com", "4": "https://api-rinkeby.etherscan.io/"} 6 | SCANS = {"1": "https://etherscan.io", "2": "https://bscscan.com", "3": "https://polygonscan.com", "4": "https://rinkeby.etherscan.io/"} 7 | -------------------------------------------------------------------------------- /utils/scan_api.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | import requests 4 | 5 | 6 | @dataclass 7 | class EVMScan(): 8 | api_key: str 9 | endpoint: str 10 | 11 | def get_source_code(self, address: str) -> dict: 12 | return requests.get( 13 | f"{self.endpoint}/api", 14 | params={ 15 | "module": "contract", 16 | "action": "getsourcecode", 17 | "address": address, 18 | "apikey": self.api_key 19 | } 20 | ).json() 21 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, send_file 2 | 3 | from utils.saving import save_contract 4 | from utils.scan_api import EVMScan 5 | 6 | app = Flask(__name__, static_folder="contracts") 7 | 8 | 9 | with open("templates/index.html") as f: 10 | html = f.read() 11 | 12 | @app.route('/', methods=['GET', 'POST']) 13 | def index(): 14 | if request.method == "POST": 15 | api_key = request.form.get("api-key") 16 | contract = request.form.get("address") 17 | endpoint = request.form.get("endpoint") 18 | 19 | file = save_contract(EVMScan(api_key, endpoint), contract.lower()) 20 | 21 | return send_file(file, as_attachment=True) 22 | 23 | return html 24 | 25 | if __name__ == "__main__": 26 | app.run() 27 | -------------------------------------------------------------------------------- /utils/deployment.py: -------------------------------------------------------------------------------- 1 | from eth_account import Account 2 | from web3 import Web3 3 | from web3.middleware import geth_poa_middleware 4 | 5 | 6 | class Deployer(): 7 | 8 | def __init__(self, rpc_provider: str, private_key: str) -> None: 9 | self.w3 = Web3(Web3.HTTPProvider(rpc_provider)) 10 | self.w3.middleware_onion.inject(geth_poa_middleware, layer=0) 11 | self.private_key = private_key 12 | 13 | def deploy_contract(self, bytecode: str, gas: int, chain_id: int, gwei: float) -> str: 14 | tx = { 15 | "data": bytecode, 16 | "chainId": chain_id, 17 | "nonce": self.w3.eth.get_transaction_count(Account().from_key(self.private_key).address), 18 | "gas": gas, 19 | "gasPrice": self.w3.toWei(gwei, "gwei") 20 | } 21 | signed_tx = self.w3.eth.account.sign_transaction(tx, private_key=self.private_key) 22 | 23 | return self.w3.eth.sendRawTransaction(signed_tx.rawTransaction).hex() 24 | 25 | 26 | def deploy_contract(rpc: str, private_key: str, bytecode: str, chain_id: int, gas_limit: int, gwei: float) -> str: 27 | deployer = Deployer(rpc, private_key) 28 | 29 | return deployer.deploy_contract(bytecode, gas_limit, chain_id, gwei) 30 | -------------------------------------------------------------------------------- /app_cmd.py: -------------------------------------------------------------------------------- 1 | from loguru import logger 2 | 3 | from config import API_KEY, ENDPOINTS, SCANS 4 | from utils.deployment import deploy_contract 5 | from utils.saving import get_contract_bytecode, save_contract 6 | from utils.scan_api import EVMScan 7 | 8 | 9 | if __name__ == "__main__": 10 | action = input("Select an action (1 - save the contract, 2 - deploy to testnet): ") 11 | 12 | endpoint = input("Choise mainnet endpoint (1 - ether, 2 - bsc, 3 - polygon, 4 - rinkeby): ") 13 | address = input("Input address of a verified smart contract: ") 14 | 15 | if action.strip() == "1": 16 | save_contract(EVMScan(API_KEY, ENDPOINTS[endpoint]), address) 17 | 18 | elif action.strip() == "2": 19 | bytecode = get_contract_bytecode(SCANS[endpoint], address) 20 | 21 | chain_id = int(input(f"Choose chain_id (rinkeby - 4 , goerli - 5, kovan - 42, ropsten - 3, bsc_testnet - 97): ")) 22 | rpc = input("Enter the RPC http: ") 23 | private_key = input("Enter the private key: ") 24 | gas_limit = int(input("Enter the gas limit: ")) 25 | gwei = float(input("Enter the value of gwei: ")) 26 | 27 | txn_hash = deploy_contract(rpc, private_key, bytecode, chain_id, gas_limit, gwei) 28 | 29 | logger.success(f"Hash: {txn_hash}") 30 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Contract Parser 9 | 10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
32 | 33 |
34 |
35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /utils/saving.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import shutil 4 | from zipfile import ZipFile 5 | 6 | import httpx 7 | from bs4 import BeautifulSoup 8 | from loguru import logger 9 | 10 | from utils.scan_api import EVMScan 11 | 12 | 13 | def is_json(string: str) -> bool: 14 | try: 15 | json.loads(string) 16 | except ValueError as e: 17 | return False 18 | return True 19 | 20 | 21 | def get_contract_bytecode(endpoint: str, address: str) -> str: 22 | page = httpx.get(f"{endpoint}/address/{address}") 23 | soup = BeautifulSoup(page.text, "html.parser") 24 | 25 | return soup.find("div", {"id": "verifiedbytecode2"}).text 26 | 27 | 28 | def get_contract_abi(ether: EVMScan, address: str): 29 | source = ether.get_source_code(address) 30 | 31 | return source["result"][0]["ABI"] 32 | 33 | 34 | def save_contract(ether: EVMScan, address: str): 35 | source = ether.get_source_code(address) 36 | 37 | contract_name = source["result"][0]["ContractName"] 38 | abi = source["result"][0]["ABI"] 39 | 40 | if not os.path.exists(contract_name): 41 | os.mkdir(f"{contract_name}") 42 | 43 | # references with bad-json 44 | if source["result"][0]["SourceCode"].startswith("{{"): 45 | source_code = json.loads(source["result"][0]["SourceCode"][1:-1]) 46 | 47 | for dirs_with_file in source_code["sources"]: 48 | dirs = "/".join(dirs_with_file.split("/")[:-1]) 49 | filename = dirs_with_file.split("/")[-1] 50 | 51 | if not os.path.exists(f"{contract_name}/{dirs}"): 52 | os.makedirs(f"{contract_name}/{dirs}") 53 | 54 | with open(f"{contract_name}/{dirs}/{filename}", "w+", encoding="UTF-8") as f: 55 | f.write(source_code["sources"][dirs_with_file]["content"]) 56 | 57 | with open(f"{contract_name}/abi.json", "w+") as f: 58 | f.write(abi) 59 | # source code in single file 60 | elif not is_json(source["result"][0]["SourceCode"]): 61 | source_code = source["result"][0]["SourceCode"] 62 | 63 | with open(f"{contract_name}/{contract_name}.sol", "w+", encoding="UTF-8") as f: 64 | f.write(source_code) 65 | 66 | with open(f"{contract_name}/abi.json", "w+") as f: 67 | f.write(abi) 68 | 69 | with ZipFile(f"contracts/{contract_name}.zip", "w") as zip: 70 | for dirname, _, files in os.walk(contract_name): 71 | zip.write(dirname) 72 | for filename in files: 73 | zip.write(os.path.join(dirname, filename)) 74 | 75 | shutil.rmtree(contract_name) 76 | 77 | logger.success(f"{contract_name} saved!") 78 | 79 | return f"contracts/{contract_name}.zip" 80 | --------------------------------------------------------------------------------