651 lines
26 KiB
Python
651 lines
26 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Interactive PacaBotManager CLI Tool
|
||
==================================
|
||
|
||
An interactive command-line tool for managing PACA stakes and vestings
|
||
through the PacaBotManager contract on BSC mainnet.
|
||
|
||
Usage: python python_scripts/interactive_bot_manager.py
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
from web3 import Web3
|
||
from eth_account import Account
|
||
from dotenv import load_dotenv
|
||
import re
|
||
|
||
from web3.middleware import ExtraDataToPOAMiddleware
|
||
|
||
# Load environment variables
|
||
load_dotenv()
|
||
|
||
class InteractivePacaBotManager:
|
||
def __init__(self, chain_id=None):
|
||
print("🐍 Interactive PacaBotManager CLI")
|
||
print("=" * 40)
|
||
|
||
# Chain configurations
|
||
self.chains = {
|
||
"bsc": {
|
||
"name": "BSC Mainnet",
|
||
"rpc_url": "https://bsc-dataseed1.binance.org",
|
||
"chain_id": 56,
|
||
"currency": "BNB",
|
||
"bot_manager": "0x4E5d3cD7743934b61041ba2ac3E9df39a0A26dcC",
|
||
"paca_contract": "0x3fF44D639a4982A4436f6d737430141aBE68b4E1",
|
||
"explorer": "https://bscscan.com"
|
||
},
|
||
"base": {
|
||
"name": "Base Mainnet",
|
||
"rpc_url": "https://virtual.base.us-east.rpc.tenderly.co/0552c4f5-a0ca-4b15-860f-fc73a3cb7983",
|
||
"chain_id": 8453,
|
||
"currency": "ETH",
|
||
"bot_manager": "0x811e82b299F58649f1e0AAD33d6ba49Fa87EA969",
|
||
"paca_contract": "0xDf2027318D27c4eD1C047B4d6247A7a705bb407b", # Correct Base PACA proxy
|
||
"explorer": "https://basescan.org"
|
||
},
|
||
"sonic": {
|
||
"name": "Sonic Network",
|
||
"rpc_url": "https://rpc.soniclabs.com",
|
||
"chain_id": 146,
|
||
"currency": "SONIC",
|
||
"bot_manager": "0x5a9A8bE051282dd5505222b9c539EB1898BB5C06",
|
||
"paca_contract": "0xa26F8128Ecb2FF2FC5618498758cC82Cf1FDad5F", # Correct Sonic PACA proxy
|
||
"explorer": "https://sonicscan.org"
|
||
}
|
||
}
|
||
|
||
# If chain_id provided, use it directly, otherwise prompt user
|
||
if chain_id:
|
||
self.current_chain = chain_id
|
||
else:
|
||
self.current_chain = self.select_chain()
|
||
|
||
if not self.current_chain:
|
||
print("❌ No chain selected. Exiting.")
|
||
sys.exit(1)
|
||
|
||
# Set up connection for selected chain
|
||
self.setup_chain_connection()
|
||
|
||
def select_chain(self):
|
||
"""Prompt user to select which blockchain to use"""
|
||
print("\n🌐 Select Blockchain Network:")
|
||
print("=" * 30)
|
||
print("1. 🟡 BSC Mainnet (Binance Smart Chain)")
|
||
print("2. 🔵 Base Mainnet")
|
||
print("3. ⚡ Sonic Network")
|
||
print("4. 🚪 Exit")
|
||
print("-" * 30)
|
||
|
||
while True:
|
||
choice = input("Select network (1-4): ").strip()
|
||
|
||
if choice == "1":
|
||
return "bsc"
|
||
elif choice == "2":
|
||
return "base"
|
||
elif choice == "3":
|
||
return "sonic"
|
||
elif choice == "4" or choice.lower() in ['exit', 'quit', 'q']:
|
||
return None
|
||
else:
|
||
print("❌ Invalid choice. Please select 1-4.")
|
||
|
||
def setup_chain_connection(self):
|
||
"""Set up Web3 connection for the selected chain"""
|
||
chain_config = self.chains[self.current_chain]
|
||
|
||
print(f"\n🔗 Connecting to {chain_config['name']}...")
|
||
|
||
# Set up RPC connection
|
||
self.rpc_url = chain_config['rpc_url']
|
||
self.w3 = Web3(Web3.HTTPProvider(self.rpc_url))
|
||
|
||
# Add middleware for BSC (POA networks)
|
||
if self.current_chain == "bsc":
|
||
self.w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
|
||
|
||
# Set contract addresses for current chain
|
||
self.bot_manager_address = chain_config['bot_manager']
|
||
self.paca_contract_address = chain_config['paca_contract']
|
||
self.currency = chain_config['currency']
|
||
self.explorer_url = chain_config['explorer']
|
||
|
||
self.private_key = os.getenv('PRIVATE_KEY')
|
||
if not self.private_key:
|
||
print("❌ Error: PRIVATE_KEY not found in environment variables")
|
||
print("Please add PRIVATE_KEY=your_private_key_here to your .env file")
|
||
sys.exit(1)
|
||
|
||
# Set up account
|
||
self.account = Account.from_key(self.private_key)
|
||
chain_config = self.chains[self.current_chain]
|
||
|
||
print(f"🔑 Connected as: {self.account.address}")
|
||
|
||
try:
|
||
balance = self.w3.eth.get_balance(self.account.address)
|
||
print(f"💰 Balance: {self.w3.from_wei(balance, 'ether'):.4f} {self.currency}")
|
||
except Exception as e:
|
||
print(f"❌ Connection failed: {e}")
|
||
sys.exit(1)
|
||
|
||
print(f"🌐 Network: {chain_config['name']}")
|
||
print(f"🤖 BotManager: {self.bot_manager_address}")
|
||
print(f"🔗 PACA Contract: {self.paca_contract_address}")
|
||
print()
|
||
|
||
# Contract ABIs
|
||
self.bot_manager_abi = [
|
||
{
|
||
"inputs": [
|
||
{"internalType": "address", "name": "pacaContract", "type": "address"},
|
||
{"internalType": "address", "name": "user", "type": "address"}
|
||
],
|
||
"name": "clearStakes",
|
||
"outputs": [],
|
||
"stateMutability": "nonpayable",
|
||
"type": "function"
|
||
},
|
||
{
|
||
"inputs": [
|
||
{"internalType": "address", "name": "pacaContract", "type": "address"},
|
||
{"internalType": "address", "name": "user", "type": "address"}
|
||
],
|
||
"name": "clearVesting",
|
||
"outputs": [],
|
||
"stateMutability": "nonpayable",
|
||
"type": "function"
|
||
},
|
||
{
|
||
"inputs": [
|
||
{
|
||
"components": [
|
||
{"internalType": "address", "name": "target", "type": "address"},
|
||
{"internalType": "bytes", "name": "callData", "type": "bytes"}
|
||
],
|
||
"internalType": "struct PacaBotManager.Call[]",
|
||
"name": "calls",
|
||
"type": "tuple[]"
|
||
}
|
||
],
|
||
"name": "multiCallAtomic",
|
||
"outputs": [
|
||
{"internalType": "bytes[]", "name": "returnData", "type": "bytes[]"}
|
||
],
|
||
"stateMutability": "nonpayable",
|
||
"type": "function"
|
||
},
|
||
{
|
||
"inputs": [],
|
||
"name": "owner",
|
||
"outputs": [
|
||
{"internalType": "address", "name": "", "type": "address"}
|
||
],
|
||
"stateMutability": "view",
|
||
"type": "function"
|
||
}
|
||
]
|
||
|
||
self.paca_abi = [
|
||
{
|
||
"inputs": [
|
||
{"internalType": "address", "name": "user", "type": "address"}
|
||
],
|
||
"name": "getStakes",
|
||
"outputs": [
|
||
{
|
||
"components": [
|
||
{"internalType": "uint256", "name": "amount", "type": "uint256"},
|
||
{"internalType": "uint256", "name": "lastClaimed", "type": "uint256"},
|
||
{"internalType": "uint256", "name": "dailyRewardRate", "type": "uint256"},
|
||
{"internalType": "uint256", "name": "unlockTime", "type": "uint256"},
|
||
{"internalType": "bool", "name": "complete", "type": "bool"}
|
||
],
|
||
"internalType": "struct PacaFinanceWithBoostAndScheduleBsc.Stake[]",
|
||
"name": "",
|
||
"type": "tuple[]"
|
||
}
|
||
],
|
||
"stateMutability": "view",
|
||
"type": "function"
|
||
},
|
||
{
|
||
"inputs": [
|
||
{"internalType": "address", "name": "user", "type": "address"}
|
||
],
|
||
"name": "getVestings",
|
||
"outputs": [
|
||
{
|
||
"components": [
|
||
{"internalType": "uint256", "name": "amount", "type": "uint256"},
|
||
{"internalType": "uint256", "name": "bonus", "type": "uint256"},
|
||
{"internalType": "uint256", "name": "lockedUntil", "type": "uint256"},
|
||
{"internalType": "uint256", "name": "claimedAmount", "type": "uint256"},
|
||
{"internalType": "uint256", "name": "claimedBonus", "type": "uint256"},
|
||
{"internalType": "uint256", "name": "lastClaimed", "type": "uint256"},
|
||
{"internalType": "uint256", "name": "createdAt", "type": "uint256"},
|
||
{"internalType": "address", "name": "token", "type": "address"},
|
||
{"internalType": "bool", "name": "complete", "type": "bool"},
|
||
{"internalType": "uint256", "name": "usdAmount", "type": "uint256"}
|
||
],
|
||
"internalType": "struct PacaFinanceWithBoostAndScheduleBsc.Vesting[]",
|
||
"name": "",
|
||
"type": "tuple[]"
|
||
}
|
||
],
|
||
"stateMutability": "view",
|
||
"type": "function"
|
||
},
|
||
{
|
||
"inputs": [],
|
||
"name": "owner",
|
||
"outputs": [
|
||
{"internalType": "address", "name": "", "type": "address"}
|
||
],
|
||
"stateMutability": "view",
|
||
"type": "function"
|
||
}
|
||
]
|
||
|
||
# Create contract instances
|
||
self.bot_manager = self.w3.eth.contract(
|
||
address=self.bot_manager_address,
|
||
abi=self.bot_manager_abi
|
||
)
|
||
|
||
self.paca_contract = self.w3.eth.contract(
|
||
address=self.paca_contract_address,
|
||
abi=self.paca_abi
|
||
)
|
||
|
||
def validate_address(self, address):
|
||
"""Validate Ethereum address format"""
|
||
if not address:
|
||
return False
|
||
|
||
# Remove 0x prefix if present
|
||
if address.startswith('0x') or address.startswith('0X'):
|
||
address = address[2:]
|
||
|
||
# Check if it's 40 hex characters
|
||
if len(address) != 40:
|
||
return False
|
||
|
||
# Check if all characters are hex
|
||
return re.match(r'^[0-9a-fA-F]{40}$', address) is not None
|
||
|
||
def get_user_address(self):
|
||
"""Prompt user for an Ethereum address"""
|
||
while True:
|
||
address = input("📍 Enter user address (0x...): ").strip()
|
||
|
||
if address.lower() in ['quit', 'exit', 'q']:
|
||
return None
|
||
|
||
if self.validate_address(address):
|
||
# Ensure proper format
|
||
if not address.startswith('0x'):
|
||
address = '0x' + address
|
||
return Web3.to_checksum_address(address)
|
||
else:
|
||
print("❌ Invalid address format. Please enter a valid Ethereum address.")
|
||
print(" Example: 0x41970Ce76b656030A79E7C1FA76FC4EB93980255")
|
||
print(" (or type 'quit' to exit)")
|
||
|
||
def send_transaction(self, tx_dict):
|
||
"""Send a transaction and wait for confirmation"""
|
||
try:
|
||
# Estimate gas first
|
||
print("⏳ Estimating gas...")
|
||
estimated_gas = 2_000_000 # self.w3.eth.estimate_gas(tx_dict)
|
||
|
||
# Add 50% buffer for safety (especially important for large operations)
|
||
gas_limit = int(estimated_gas * 1.5)
|
||
print(f"⛽ Estimated gas: {estimated_gas:,}")
|
||
print(f"⛽ Gas limit (with buffer): {gas_limit:,}")
|
||
|
||
# Add gas and nonce
|
||
tx_dict['gas'] = gas_limit
|
||
gas_price = self.w3.eth.gas_price
|
||
tx_dict['maxFeePerGas'] = int(gas_price * 1) # 20% buffer on gas price
|
||
tx_dict['maxPriorityFeePerGas'] = int(gas_price)
|
||
tx_dict['nonce'] = self.w3.eth.get_transaction_count(self.account.address)
|
||
tx_dict['type'] = 2
|
||
|
||
# Calculate total cost
|
||
max_cost = gas_limit * tx_dict['maxFeePerGas']
|
||
|
||
print(f"⛽ Max Fee Per Gas: {self.w3.from_wei(tx_dict['maxFeePerGas'], 'gwei'):.2f} gwei")
|
||
print(f"💰 Max Transaction Cost: {self.w3.from_wei(max_cost, 'ether'):.6f} {self.currency}")
|
||
|
||
# Ask for confirmation if this is an expensive transaction
|
||
if max_cost > self.w3.to_wei(0.01, 'ether'): # If more than 0.01 BNB
|
||
confirm = input(f"\n⚠️ High gas cost transaction! Continue? (yes/no): ")
|
||
if confirm.lower() not in ['yes', 'y']:
|
||
print("❌ Transaction cancelled")
|
||
return None
|
||
|
||
# Sign and send
|
||
signed = self.w3.eth.account.sign_transaction(tx_dict, self.private_key)
|
||
tx_hash = self.w3.eth.send_raw_transaction(signed.raw_transaction)
|
||
|
||
print(f"🧾 Transaction sent: {tx_hash.hex()}")
|
||
print("⏳ Waiting for confirmation...")
|
||
|
||
# Wait for confirmation
|
||
receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=300)
|
||
|
||
if receipt.status == 1:
|
||
print("✅ Transaction successful!")
|
||
print(f"⛽ Gas used: {receipt.gasUsed:,}")
|
||
print(f"🔗 Explorer: {self.explorer_url}/tx/{tx_hash.hex()}")
|
||
return receipt
|
||
else:
|
||
print("❌ Transaction failed!")
|
||
return None
|
||
|
||
except Exception as e:
|
||
print(f"❌ Transaction failed: {e}")
|
||
return None
|
||
|
||
def get_stakes(self, user_address):
|
||
"""Get and display stakes for a user"""
|
||
print(f"\n📊 Getting stakes for: {user_address}")
|
||
|
||
try:
|
||
stakes = self.paca_contract.functions.getStakes(user_address).call()
|
||
|
||
if len(stakes) == 0:
|
||
print("📭 No stakes found for this user")
|
||
return stakes
|
||
|
||
print(f"📈 Found {len(stakes)} stakes:")
|
||
|
||
total_amount = 0
|
||
active_stakes = 0
|
||
|
||
for i, stake in enumerate(stakes):
|
||
amount = stake[0]
|
||
last_claimed = stake[1]
|
||
daily_reward_rate = stake[2]
|
||
unlock_time = stake[3]
|
||
complete = stake[4]
|
||
|
||
is_active = not complete and amount > 0
|
||
if is_active:
|
||
active_stakes += 1
|
||
total_amount += amount
|
||
|
||
print(f"\n 📌 Stake {i + 1}:")
|
||
print(f" Amount: {self.w3.from_wei(amount, 'ether'):.6f} ETH")
|
||
print(f" Daily Reward: {self.w3.from_wei(daily_reward_rate, 'ether'):.6f} ETH")
|
||
print(f" Complete: {complete}")
|
||
print(f" Status: {'🟢 ACTIVE' if is_active else '🔴 COMPLETED'}")
|
||
|
||
if unlock_time > 0:
|
||
import datetime
|
||
unlock_date = datetime.datetime.fromtimestamp(unlock_time)
|
||
print(f" Unlock: {unlock_date.strftime('%Y-%m-%d %H:%M:%S')}")
|
||
|
||
print(f"\n💎 Summary:")
|
||
print(f" Total Stakes: {len(stakes)}")
|
||
print(f" Active Stakes: {active_stakes}")
|
||
print(f" Total Active: {self.w3.from_wei(total_amount, 'ether'):.6f} ETH")
|
||
|
||
return stakes
|
||
|
||
except Exception as e:
|
||
print(f"❌ Error getting stakes: {e}")
|
||
return []
|
||
|
||
def get_vestings(self, user_address):
|
||
"""Get and display vestings for a user"""
|
||
print(f"\n🔮 Getting vestings for: {user_address}")
|
||
|
||
try:
|
||
vestings = self.paca_contract.functions.getVestings(user_address).call()
|
||
|
||
if len(vestings) == 0:
|
||
print("📭 No vestings found for this user")
|
||
return vestings
|
||
|
||
print(f"📈 Found {len(vestings)} vestings:")
|
||
|
||
total_amount = 0
|
||
active_vestings = 0
|
||
|
||
for i, vesting in enumerate(vestings):
|
||
amount = vesting[0] # amount
|
||
bonus = vesting[1] # bonus
|
||
locked_until = vesting[2] # lockedUntil
|
||
claimed_amount = vesting[3] # claimedAmount
|
||
claimed_bonus = vesting[4] # claimedBonus
|
||
last_claimed = vesting[5] # lastClaimed
|
||
created_at = vesting[6] # createdAt
|
||
token = vesting[7] # token
|
||
complete = vesting[8] # complete
|
||
usd_amount = vesting[9] # usdAmount
|
||
|
||
is_active = not complete and amount > 0
|
||
if is_active:
|
||
active_vestings += 1
|
||
total_amount += amount
|
||
|
||
print(f"\n 📌 Vesting {i + 1}:")
|
||
print(f" Amount: {self.w3.from_wei(amount, 'ether'):.6f} tokens")
|
||
print(f" Bonus: {self.w3.from_wei(bonus, 'ether'):.6f} tokens")
|
||
print(f" Claimed: {self.w3.from_wei(claimed_amount, 'ether'):.6f} tokens")
|
||
print(f" USD Value: ${self.w3.from_wei(usd_amount, 'ether'):.2f}")
|
||
print(f" Token: {token}")
|
||
print(f" Complete: {complete}")
|
||
print(f" Status: {'🟢 ACTIVE' if is_active else '🔴 COMPLETED'}")
|
||
|
||
if locked_until > 0:
|
||
import datetime
|
||
unlock_date = datetime.datetime.fromtimestamp(locked_until)
|
||
print(f" Locked Until: {unlock_date.strftime('%Y-%m-%d %H:%M:%S')}")
|
||
if created_at > 0:
|
||
import datetime
|
||
created_date = datetime.datetime.fromtimestamp(created_at)
|
||
print(f" Created: {created_date.strftime('%Y-%m-%d %H:%M:%S')}")
|
||
|
||
print(f"\n💎 Summary:")
|
||
print(f" Total Vestings: {len(vestings)}")
|
||
print(f" Active Vestings: {active_vestings}")
|
||
print(f" Total Active: {self.w3.from_wei(total_amount, 'ether'):.6f} tokens")
|
||
|
||
return vestings
|
||
|
||
except Exception as e:
|
||
print(f"❌ Error getting vestings: {e}")
|
||
return []
|
||
|
||
def clear_stakes(self, user_address):
|
||
"""Clear all stakes for a user"""
|
||
print(f"\n🔥 Preparing to clear stakes for: {user_address}")
|
||
|
||
# First show what will be cleared
|
||
stakes = self.get_stakes(user_address)
|
||
if not stakes:
|
||
return
|
||
|
||
active_stakes = sum(1 for stake in stakes if not stake[4] and stake[0] > 0)
|
||
if active_stakes == 0:
|
||
print("⚠️ No active stakes to clear!")
|
||
return
|
||
|
||
print(f"\n⚠️ WARNING: This will clear {active_stakes} active stakes!")
|
||
confirm = input("Type 'CONFIRM' to proceed: ")
|
||
|
||
if confirm != 'CONFIRM':
|
||
print("❌ Operation cancelled")
|
||
return
|
||
|
||
print("🔥 Executing clearStakes...")
|
||
tx_dict = self.bot_manager.functions.clearStakes(
|
||
self.paca_contract_address,
|
||
user_address
|
||
).build_transaction({'from': self.account.address})
|
||
|
||
return self.send_transaction(tx_dict)
|
||
|
||
def clear_vesting(self, user_address):
|
||
"""Clear all vestings for a user"""
|
||
print(f"\n🔮 Preparing to clear vestings for: {user_address}")
|
||
|
||
# First show what will be cleared
|
||
vestings = self.get_vestings(user_address)
|
||
if not vestings:
|
||
return
|
||
|
||
active_vestings = sum(1 for vesting in vestings if not vesting[8] and vesting[0] > 0)
|
||
if active_vestings == 0:
|
||
print("⚠️ No active vestings to clear!")
|
||
return
|
||
|
||
print(f"\n⚠️ WARNING: This will clear {active_vestings} active vestings!")
|
||
confirm = input("Type 'CONFIRM' to proceed: ")
|
||
|
||
if confirm != 'CONFIRM':
|
||
print("❌ Operation cancelled")
|
||
return
|
||
|
||
print("🔮 Executing clearVesting...")
|
||
tx_dict = self.bot_manager.functions.clearVesting(
|
||
self.paca_contract_address,
|
||
user_address
|
||
).build_transaction({'from': self.account.address, 'gas': 1000000})
|
||
|
||
return self.send_transaction(tx_dict)
|
||
|
||
def clear_both(self, user_address):
|
||
"""Clear both stakes and vestings atomically"""
|
||
print(f"\n🔥🔮 Preparing to clear BOTH stakes AND vestings for: {user_address}")
|
||
|
||
# Show both stakes and vestings
|
||
stakes = self.get_stakes(user_address)
|
||
vestings = self.get_vestings(user_address)
|
||
|
||
active_stakes = sum(1 for stake in stakes if not stake[4] and stake[0] > 0) if stakes else 0
|
||
active_vestings = sum(1 for vesting in vestings if not vesting[8] and vesting[0] > 0) if vestings else 0
|
||
|
||
if active_stakes == 0 and active_vestings == 0:
|
||
print("⚠️ No active stakes or vestings to clear!")
|
||
return
|
||
|
||
print(f"\n⚠️ WARNING: This will clear:")
|
||
print(f" - {active_stakes} active stakes")
|
||
print(f" - {active_vestings} active vestings")
|
||
print(" ⚠️ This operation is ATOMIC - both will be cleared together!")
|
||
|
||
confirm = input("Type 'CONFIRM' to proceed: ")
|
||
|
||
if confirm != 'CONFIRM':
|
||
print("❌ Operation cancelled")
|
||
return
|
||
|
||
print("🔥🔮 Executing atomic clear...")
|
||
|
||
# Prepare multi-call
|
||
calls = [
|
||
{
|
||
'target': self.paca_contract_address,
|
||
'callData': self.w3.keccak(text="clearStakes(address)")[:4] +
|
||
self.w3.eth.codec.encode(['address'], [user_address])
|
||
},
|
||
{
|
||
'target': self.paca_contract_address,
|
||
'callData': self.w3.keccak(text="clearVesting(address)")[:4] +
|
||
self.w3.eth.codec.encode(['address'], [user_address])
|
||
}
|
||
]
|
||
|
||
tx_dict = self.bot_manager.functions.multiCallAtomic(calls).build_transaction({
|
||
'from': self.account.address,
|
||
'gas': 1000000
|
||
})
|
||
|
||
return self.send_transaction(tx_dict)
|
||
|
||
def show_menu(self):
|
||
"""Display the main menu"""
|
||
chain_config = self.chains[self.current_chain]
|
||
print("\n" + "="*60)
|
||
print(f"🤖 PacaBotManager Operations - {chain_config['name']}")
|
||
print("="*60)
|
||
print("1. 📊 View Stakes")
|
||
print("2. 🔮 View Vestings")
|
||
print("3. 🔥 Clear Stakes")
|
||
print("4. 🔮 Clear Vestings")
|
||
print("5. 🔥🔮 Clear BOTH (Atomic)")
|
||
print("6. 🌐 Switch Chain")
|
||
print("7. 🏃 Exit")
|
||
print("-"*60)
|
||
|
||
def run(self):
|
||
"""Main interactive loop"""
|
||
print("🚀 Interactive PacaBotManager Ready!")
|
||
|
||
while True:
|
||
# try:
|
||
self.show_menu()
|
||
|
||
choice = input("Select operation (1-7): ").strip()
|
||
|
||
if choice == '7' or choice.lower() in ['exit', 'quit', 'q']:
|
||
print("👋 Goodbye!")
|
||
break
|
||
|
||
elif choice == '6':
|
||
# Switch chain
|
||
new_chain = self.select_chain()
|
||
if new_chain and new_chain != self.current_chain:
|
||
print(f"🔄 Switching from {self.chains[self.current_chain]['name']} to {self.chains[new_chain]['name']}...")
|
||
self.current_chain = new_chain
|
||
self.setup_chain_connection()
|
||
print("✅ Chain switched successfully!")
|
||
elif new_chain == self.current_chain:
|
||
print(f"ℹ️ Already connected to {self.chains[self.current_chain]['name']}")
|
||
continue
|
||
|
||
elif choice in ['1', '2', '3', '4', '5']:
|
||
user_address = self.get_user_address()
|
||
if user_address is None:
|
||
continue
|
||
|
||
if choice == '1':
|
||
self.get_stakes(user_address)
|
||
elif choice == '2':
|
||
self.get_vestings(user_address)
|
||
elif choice == '3':
|
||
self.clear_stakes(user_address)
|
||
elif choice == '4':
|
||
self.clear_vesting(user_address)
|
||
elif choice == '5':
|
||
self.clear_both(user_address)
|
||
|
||
else:
|
||
print("❌ Invalid choice. Please select 1-7.")
|
||
|
||
# except KeyboardInterrupt:
|
||
# print("\n\n👋 Interrupted by user. Goodbye!")
|
||
# break
|
||
# except Exception as e:
|
||
# print(f"❌ Error: {e}")
|
||
# continue
|
||
|
||
def main():
|
||
"""Main entry point"""
|
||
# try:
|
||
manager = InteractivePacaBotManager()
|
||
manager.run()
|
||
# except Exception as e:
|
||
# print(f"💥 Fatal error: {e}")
|
||
# sys.exit(1)
|
||
|
||
if __name__ == "__main__":
|
||
main() |