diff --git a/.gitignore b/.gitignore index 238eac7..666b8fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea/ -venv/ \ No newline at end of file +venv/ +wallets/ \ No newline at end of file diff --git a/wallet.py b/wallet.py index 39bb787..4755707 100644 --- a/wallet.py +++ b/wallet.py @@ -1,6 +1,7 @@ import os import json import getpass +from pathlib import Path from web3 import Web3 from Crypto.Cipher import AES from Crypto.Protocol.KDF import PBKDF2 @@ -8,7 +9,6 @@ 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" @@ -25,6 +25,7 @@ def encrypt_wallet(private_key, password): 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(), @@ -42,7 +43,7 @@ def decrypt_wallet(data, password): def connect_to_network(): global web3 - rpc_url = Prompt.ask("[bold cyan]Введите RPC URL[/]") + rpc_url = Prompt.ask("[bold cyan]Введите RPC URL[/]", default="https://ethereum-rpc.publicnode.com") web3 = Web3(Web3.HTTPProvider(rpc_url)) if web3.is_connected(): print("[green]Успешное подключение к сети Ethereum[/]") @@ -54,12 +55,27 @@ def connect_to_network(): 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") + save_wallet(acct) + + +def import_wallet(): + print("[bold cyan]Введите приватный ключ: [/]", end='') + private_key = getpass.getpass(prompt="") + account = Account.from_key(private_key) + save_wallet(account) + + +def save_wallet(account): + print("[bold cyan]Введите пароль для шифрования кошелька: [/]", end='') + password = getpass.getpass(prompt="") + + encrypted = encrypt_wallet(account.key.hex(), password) + + filename = os.path.join(WALLETS_DIR, f"{account.address}.json") + with open(filename, 'w') as f: - json.dump({"address": acct.address, "data": encrypted}, f) - print(f"[green]Кошелек создан и сохранен: {acct.address}[/]") + json.dump({"address": account.address, "data": encrypted}, f) + print(f"[green]Кошелек сохранен: {account.address}[/]") def select_wallet(): @@ -68,9 +84,16 @@ def select_wallet(): if not files: print("[red]Нет доступных кошельков.[/]") return + for i, fname in enumerate(files): - print(f"[{i}] {fname}") - choice = int(Prompt.ask("Выберите номер кошелька")) + print(f"[{i}] {Path(fname).stem}") + + try: + choice = int(Prompt.ask("Выберите номер кошелька")) + except: + print("[red]Неверный номер.[/]") + return + with open(os.path.join(WALLETS_DIR, files[choice])) as f: wallet_data = json.load(f) selected_wallet = wallet_data['address'] @@ -85,45 +108,47 @@ def get_balance(): 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("Введите пароль от кошелька: ") + + print("[bold cyan]Введите пароль от кошелька: [/]", end='') + password = getpass.getpass(prompt="") + 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}") + to = Prompt.ask("[bold cyan]Введите адрес получателя[/]") + value = float(Prompt.ask("[bold cyan]Введите сумму в ETH[/]")) + + base_fee = web3.eth.fee_history(1, 'latest')['baseFeePerGas'][-1] + priority_fee_gwei = float(Prompt.ask("[bold cyan]Введите приоритетную комиссию в GWEI[/]", default="2")) + max_priority_fee = web3.to_wei(priority_fee_gwei, 'gwei') + max_fee = base_fee + max_priority_fee * 2 + + print(f"[yellow]Максимальная комиссия за транзакцию: {float(max_fee) / 10**9} gwei[/]") tx = { + 'chainId': web3.eth.chain_id, 'nonce': web3.eth.get_transaction_count(selected_wallet), 'to': to, 'value': web3.to_wei(value, 'ether'), 'gas': 21000, - 'gasPrice': gas_price + 'maxFeePerGas': max_fee, + 'maxPriorityFeePerGas': max_priority_fee } - 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()}[/]") + + try: + signed = web3.eth.account.sign_transaction(tx, private_key) + tx_hash = web3.eth.send_raw_transaction(signed.raw_transaction) + + print(f"[green]Транзакция отправлена! TX Hash: {tx_hash.hex()}[/]") + except Exception as error: + print(f"[red]{error.message}[/]") def main(): @@ -134,6 +159,7 @@ def main(): w. Выбор кошелька b. Просмотр баланса s. Отправка средств + i. Импорт существующего кошелька q. Выход """) @@ -154,7 +180,9 @@ def main(): if web3 and selected_wallet: send_transaction() else: - print("[red]Необходимо подключение и выбор кошелька[/]") + print("[red]Необходимо подключиться к сети и выбрать кошелек[/]") + elif choice == 'i': + import_wallet() elif choice == 'q': break else: