commit 02ed2416d00b320cc5ef1123bffa114f38dd2d48 Author: Maksim Harbacheuski Date: Mon Jul 14 10:19:19 2025 +0300 Initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..238eac7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +venv/ \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4f0c680 Binary files /dev/null and b/requirements.txt differ diff --git a/wallet.py b/wallet.py new file mode 100644 index 0000000..39bb787 --- /dev/null +++ b/wallet.py @@ -0,0 +1,165 @@ +import os +import json +import getpass +from web3 import Web3 +from Crypto.Cipher import AES +from Crypto.Protocol.KDF import PBKDF2 +from Crypto.Random import get_random_bytes +from rich import print +from rich.prompt import Prompt +from eth_account import Account +import requests + +WALLETS_DIR = "wallets" + +web3 = None +selected_wallet = None +wallet_data = None + +if not os.path.exists(WALLETS_DIR): + os.makedirs(WALLETS_DIR) + + +def encrypt_wallet(private_key, password): + salt = get_random_bytes(16) + key = PBKDF2(password, salt, dkLen=32) + cipher = AES.new(key, AES.MODE_GCM) + ciphertext, tag = cipher.encrypt_and_digest(private_key.encode()) + return { + 'ciphertext': ciphertext.hex(), + 'salt': salt.hex(), + 'nonce': cipher.nonce.hex(), + 'tag': tag.hex() + } + + +def decrypt_wallet(data, password): + salt = bytes.fromhex(data['salt']) + key = PBKDF2(password, salt, dkLen=32) + cipher = AES.new(key, AES.MODE_GCM, nonce=bytes.fromhex(data['nonce'])) + return cipher.decrypt_and_verify(bytes.fromhex(data['ciphertext']), bytes.fromhex(data['tag'])).decode() + + +def connect_to_network(): + global web3 + rpc_url = Prompt.ask("[bold cyan]Введите RPC URL[/]") + web3 = Web3(Web3.HTTPProvider(rpc_url)) + if web3.is_connected(): + print("[green]Успешное подключение к сети Ethereum[/]") + else: + print("[red]Ошибка подключения к сети![/]") + web3 = None + + +def create_wallet(): + Account.enable_unaudited_hdwallet_features() + acct = Account.create() + password = getpass.getpass("Введите пароль для шифрования кошелька: ") + encrypted = encrypt_wallet(acct.key.hex(), password) + filename = os.path.join(WALLETS_DIR, f"{acct.address}.json") + with open(filename, 'w') as f: + json.dump({"address": acct.address, "data": encrypted}, f) + print(f"[green]Кошелек создан и сохранен: {acct.address}[/]") + + +def select_wallet(): + global selected_wallet, wallet_data + files = os.listdir(WALLETS_DIR) + if not files: + print("[red]Нет доступных кошельков.[/]") + return + for i, fname in enumerate(files): + print(f"[{i}] {fname}") + choice = int(Prompt.ask("Выберите номер кошелька")) + with open(os.path.join(WALLETS_DIR, files[choice])) as f: + wallet_data = json.load(f) + selected_wallet = wallet_data['address'] + print(f"[green]Кошелек выбран: {selected_wallet}[/]") + + +def get_balance(): + if not selected_wallet: + print("[red]Кошелек не выбран.[/]") + return + balance = web3.eth.get_balance(selected_wallet) + print(f"Баланс: {web3.from_wei(balance, 'ether')} ETH") + + +def fetch_gas_data(): + try: + res = requests.get("https://ethgas.watch/api/gas") + data = res.json() + fast = data['fast'] / 10 + gas_price_wei = web3.to_wei(fast, 'gwei') + eth_price = requests.get("https://api.coinbase.com/v2/prices/ETH-USD/spot").json()['data']['amount'] + gas_usd = web3.from_wei(gas_price_wei * 21000, 'ether') * float(eth_price) + return fast, gas_price_wei, round(gas_usd, 2) + except: + return None, None, None + + +def send_transaction(): + if not selected_wallet: + print("[red]Кошелек не выбран.[/]") + return + password = getpass.getpass("Введите пароль от кошелька: ") + try: + private_key = decrypt_wallet(wallet_data['data'], password) + except: + print("[red]Неверный пароль![/]") + return + + to = Prompt.ask("Введите адрес получателя") + value = float(Prompt.ask("Введите сумму в ETH")) + _, gas_price, _ = fetch_gas_data() + print(f"[yellow]Текущая цена за газ: {gas_price}") + + tx = { + 'nonce': web3.eth.get_transaction_count(selected_wallet), + 'to': to, + 'value': web3.to_wei(value, 'ether'), + 'gas': 21000, + 'gasPrice': gas_price + } + signed = web3.eth.account.sign_transaction(tx, private_key) + tx_hash = web3.eth.send_raw_transaction(signed.rawTransaction) + print(f"[green]Транзакция отправлена! TX Hash: {tx_hash.hex()}[/]") + + +def main(): + print(""" + [bold blue]Меню:[/] + a. Подключение к сети Ethereum + c. Создание кошелька + w. Выбор кошелька + b. Просмотр баланса + s. Отправка средств + q. Выход + """) + + while True: + choice = Prompt.ask("Выберите действие").lower() + if choice == 'a': + connect_to_network() + elif choice == 'c': + create_wallet() + elif choice == 'w': + select_wallet() + elif choice == 'b': + if web3 and selected_wallet: + get_balance() + else: + print("[red]Необходимо подключиться к сети и выбрать кошелек[/]") + elif choice == 's': + if web3 and selected_wallet: + send_transaction() + else: + print("[red]Необходимо подключение и выбор кошелька[/]") + elif choice == 'q': + break + else: + print("[red]Неверный выбор[/]") + + +if __name__ == "__main__": + main()