Files
hyper/clp_auto_hedger/unwrap_weth.py

276 lines
10 KiB
Python

#!/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()