#!/usr/bin/env python3 """ WETH Unwrap Script - Convert WETH back to ETH on Arbitrum Use this script if your WETH wrapping transaction failed or timed out Prerequisites: - Python 3.7+ - pip install web3 eth-account python-dotenv Instructions: 1. Ensure your .env file contains MAINNET_RPC_URL and PRIVATE_KEY 2. Run: python unwrap_weth.py 3. Follow the prompts to unwrap your WETH """ import os import sys import json import time # Try to import required libraries try: from web3 import Web3 from eth_account import Account except ImportError as e: print(f"[ERROR] Missing required library: {e}") print("Please install with: pip install web3 eth-account python-dotenv") sys.exit(1) try: from dotenv import load_dotenv except ImportError: print("[WARNING] python-dotenv not found, will use environment variables directly") def load_dotenv(override=True): pass def setup_logging(): """Setup logging for the unwrap script""" import logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler(), logging.FileHandler('unwrap_weth.log', encoding='utf-8') ] ) return logging.getLogger(__name__) logger = setup_logging() def get_weth_balance(w3, account_address): """Get current WETH balance""" weth_address = "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1" erc20_abi = json.loads(''' [ {"inputs": [], "name": "decimals", "outputs": [{"internalType": "uint8", "name": "", "type": "uint8"}], "stateMutability": "view", "type": "function"}, {"inputs": [], "name": "symbol", "outputs": [{"internalType": "string", "name": "", "type": "string"}], "stateMutability": "view", "type": "function"}, {"inputs": [{"internalType": "address", "name": "account", "type": "address"}], "name": "balanceOf", "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], "stateMutability": "view", "type": "function"} ] ''') try: weth_contract = w3.eth.contract(address=weth_address, abi=erc20_abi) balance = weth_contract.functions.balanceOf(account_address).call() decimals = weth_contract.functions.decimals().call() symbol = weth_contract.functions.symbol().call() return balance, decimals, symbol except Exception as e: logger.error(f"Error getting WETH balance: {e}") return 0, 18, "WETH" def get_eth_balance(w3, account_address): """Get current ETH balance""" try: return w3.eth.get_balance(account_address) except Exception as e: logger.error(f"Error getting ETH balance: {e}") return 0 def unwrap_weth(w3, account, amount_wei): """Unwrap WETH to ETH""" weth_address = "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1" weth_abi = json.loads(''' [ {"constant": false, "inputs": [{"name": "wad", "type": "uint256"}], "name": "withdraw", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function"} ] ''') try: weth_contract = w3.eth.contract(address=weth_address, abi=weth_abi) # Build transaction with higher gas parameters nonce = w3.eth.get_transaction_count(account.address) gas_price = w3.eth.gas_price txn = weth_contract.functions.withdraw(amount_wei).build_transaction({ 'from': account.address, 'nonce': nonce, 'gas': 150000, # Higher gas limit for safety 'maxFeePerGas': gas_price * 3, # 3x gas price for faster processing 'maxPriorityFeePerGas': w3.eth.max_priority_fee * 2, 'chainId': w3.eth.chain_id }) logger.info(f"Sending WETH unwrap transaction...") logger.info(f"Amount: {amount_wei / 10**18:.6f} WETH") logger.info(f"Gas Price: {gas_price / 10**9:.2f} gwei") logger.info(f"Max Fee: {txn['maxFeePerGas'] / 10**9:.2f} gwei") # Sign and send transaction signed_txn = w3.eth.account.sign_transaction(txn, private_key=account.key) tx_hash = w3.eth.send_raw_transaction(signed_txn.raw_transaction) logger.info(f"Transaction sent: {tx_hash.hex()}") logger.info(f"Arbiscan: https://arbiscan.io/tx/{tx_hash.hex()}") # Wait for confirmation with longer timeout logger.info("Waiting for transaction confirmation...") receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=600) # 10 minutes if receipt.status == 1: logger.info("[SUCCESS] WETH unwrap successful!") return True else: logger.error(f"[ERROR] Transaction failed. Status: {receipt.status}") return False except Exception as e: logger.error(f"[ERROR] Error during unwrap transaction: {str(e)}") return False def check_pending_transaction(w3, tx_hash_hex): """Check if a pending transaction exists and its status""" try: receipt = w3.eth.get_transaction_receipt(tx_hash_hex) return receipt.status if receipt else None except: return None def main(): logger.info("=== WETH Unwrap Script ===") logger.info("This script will convert your WETH back to ETH on Arbitrum") # Load environment variables load_dotenv(override=True) # Get configuration from environment rpc_url = os.environ.get("MAINNET_RPC_URL") private_key = os.environ.get("MAIN_WALLET_PRIVATE_KEY") or os.environ.get("PRIVATE_KEY") if not rpc_url or not private_key: logger.error("[ERROR] Missing RPC URL or Private Key") logger.error("Please ensure MAINNET_RPC_URL and PRIVATE_KEY are set in your .env file") logger.error("Example .env file:") logger.error("MAINNET_RPC_URL=https://arbitrum-one.public.blastapi.io") logger.error("PRIVATE_KEY=0x...") return # Connect to Arbitrum try: w3 = Web3(Web3.HTTPProvider(rpc_url)) if not w3.is_connected(): logger.error("[ERROR] Failed to connect to Arbitrum RPC") return logger.info(f"[SUCCESS] Connected to Chain ID: {w3.eth.chain_id}") except Exception as e: logger.error(f"[ERROR] Connection error: {e}") return # Setup account try: account = Account.from_key(private_key) w3.eth.default_account = account.address logger.info(f"Wallet: {account.address}") except Exception as e: logger.error(f"[ERROR] Account setup error: {e}") return # Check current balances weth_balance, weth_decimals, weth_symbol = get_weth_balance(w3, account.address) eth_balance = get_eth_balance(w3, account.address) logger.info(f"Current WETH Balance: {weth_balance / 10**weth_decimals:.6f} {weth_symbol}") logger.info(f"Current ETH Balance: {eth_balance / 10**18:.6f} ETH") if weth_balance == 0: logger.info("No WETH balance to unwrap. Exiting.") return # Check if there's a pending transaction from the error pending_tx = "0x12c38f98938481f89c556e32e652218d1e44e61c8ad320943368ad42b22cd591" logger.info(f"\nChecking your failed transaction: {pending_tx}") pending_status = check_pending_transaction(w3, pending_tx) if pending_status is not None: if pending_status == 1: logger.info("[SUCCESS] Your previous WETH wrap transaction actually succeeded!") logger.info("Your WETH balance should be available now.") else: logger.warning("[WARNING] Your previous transaction failed") else: logger.info("Transaction not found - it may still be pending") # Ask user how much to unwrap weth_amount_human = weth_balance / 10**weth_decimals print(f"\nYou have {weth_amount_human:.6f} WETH available") print("Options:") print("1. Unwrap all WETH") print("2. Unwrap specific amount") print("3. Exit") try: choice = input("\nEnter your choice (1, 2, or 3): ").strip() if choice == "3": logger.info("Exiting script") return elif choice == "1": amount_to_unwrap = weth_balance logger.info(f"Unwrapping all WETH: {amount_to_unwrap / 10**weth_decimals:.6f} WETH") elif choice == "2": amount_str = input(f"Enter amount to unwrap (max: {weth_amount_human:.6f}): ").strip() try: amount_float = float(amount_str) if amount_float <= 0: logger.error("[ERROR] Amount must be greater than 0") return amount_to_unwrap = int(amount_float * (10 ** weth_decimals)) if amount_to_unwrap > weth_balance: logger.error("[ERROR] Amount exceeds WETH balance") return except ValueError: logger.error("[ERROR] Invalid amount") return else: logger.error("[ERROR] Invalid choice") return except KeyboardInterrupt: logger.info("\nOperation cancelled by user") return except Exception as e: logger.error(f"[ERROR] Input error: {e}") return # Confirm before executing confirm = input(f"\nConfirm unwrap {amount_to_unwrap / 10**weth_decimals:.6f} WETH? (y/N): ").strip().lower() if confirm != 'y': logger.info("Operation cancelled") return # Execute unwrap try: success = unwrap_weth(w3, account, amount_to_unwrap) if success: # Check final balances time.sleep(5) # Brief pause to let blockchain update final_weth_balance, _, _ = get_weth_balance(w3, account.address) final_eth_balance = get_eth_balance(w3, account.address) logger.info(f"\nFinal WETH Balance: {final_weth_balance / 10**weth_decimals:.6f} WETH") logger.info(f"Final ETH Balance: {final_eth_balance / 10**18:.6f} ETH") logger.info("[SUCCESS] Unwrap operation completed successfully!") else: logger.error("[ERROR] Unwrap operation failed") except Exception as e: logger.error(f"[ERROR] Error during unwrap: {str(e)}") logger.error("This might be due to network issues or insufficient gas") if __name__ == "__main__": main()