import os import time import json import sys from decimal import Decimal from web3 import Web3 from web3.middleware import ExtraDataToPOAMiddleware from eth_account import Account from dotenv import load_dotenv # Add project root to path current_dir = os.path.dirname(os.path.abspath(__file__)) project_root = os.path.dirname(current_dir) sys.path.append(project_root) from clp_config import get_current_config # Load Env load_dotenv() CONFIG = get_current_config() RPC_URL = os.environ.get(CONFIG["RPC_ENV_VAR"]) PRIVATE_KEY = os.environ.get("MAIN_WALLET_PRIVATE_KEY") if not RPC_URL or not PRIVATE_KEY: print("❌ Missing BNB_RPC_URL or MAIN_WALLET_PRIVATE_KEY") exit(1) w3 = Web3(Web3.HTTPProvider(RPC_URL)) w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) account = Account.from_key(PRIVATE_KEY) print(f"🔗 Connected: {w3.is_connected()}") print(f"👤 Account: {account.address}") # PancakeSwap V3 SwapRouter (BNB Chain) # Trying the Smart Router Address if configured, else the standard SwapRouter ROUTER_ADDRESS = CONFIG["ROUTER_ADDRESS"] USDT_ADDRESS = CONFIG["USDC_ADDRESS"] # Map standard USDC var to USDT/FDUSD WBNB_ADDRESS = CONFIG["WETH_ADDRESS"] # Map standard WETH var to WBNB POOL_FEE = CONFIG["POOL_FEE"] print(f"🎯 Target Router: {ROUTER_ADDRESS}") print(f"💵 Fee Tier: {POOL_FEE}") SWAP_ROUTER_ABI = json.loads(''' [ {"inputs": [{"components": [{"internalType": "address", "name": "tokenIn", "type": "address"}, {"internalType": "address", "name": "tokenOut", "type": "address"}, {"internalType": "uint24", "name": "fee", "type": "uint24"}, {"internalType": "address", "name": "recipient", "type": "address"}, {"internalType": "uint256", "name": "deadline", "type": "uint256"}, {"internalType": "uint256", "name": "amountIn", "type": "uint256"}, {"internalType": "uint256", "name": "amountOutMinimum", "type": "uint256"}, {"internalType": "uint160", "name": "sqrtPriceLimitX96", "type": "uint160"}], "internalType": "struct ISwapRouter.ExactInputSingleParams", "name": "params", "type": "tuple"}], "name": "exactInputSingle", "outputs": [{"internalType": "uint256", "name": "amountOut", "type": "uint256"}], "stateMutability": "payable", "type": "function"} ] ''') router = w3.eth.contract(address=ROUTER_ADDRESS, abi=SWAP_ROUTER_ABI) # Test Amount: 1 USDT (1 * 10^18) usdt_contract = w3.eth.contract(address=USDT_ADDRESS, abi=json.loads('[{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"type":"function"}, {"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"type":"function"}]')) usdt_decimals = usdt_contract.functions.decimals().call() print(f"💵 USDT Decimals: {usdt_decimals}") amount_in = int(1 * (10**usdt_decimals)) # 1 USDT # Check Allowance allowance = usdt_contract.functions.allowance(account.address, ROUTER_ADDRESS).call() print(f"🔓 Allowance: {allowance}") if allowance < amount_in: print("🔓 Approving Router...") try: tx = usdt_contract.functions.approve(ROUTER_ADDRESS, 2**256 - 1).build_transaction({ 'from': account.address, 'nonce': w3.eth.get_transaction_count(account.address, 'pending'), 'gas': 100000, 'gasPrice': w3.eth.gas_price }) signed = account.sign_transaction(tx) tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction) print(f"⏳ Waiting for approval {tx_hash.hex()}...") w3.eth.wait_for_transaction_receipt(tx_hash) print("✅ Approved.") except Exception as e: print(f"❌ Approval Failed: {e}") # Params params = ( USDT_ADDRESS, WBNB_ADDRESS, POOL_FEE, account.address, int(time.time()) + 120, amount_in, 0, 0 ) print(f"🔄 Simulating Swap: 1 USDT -> WBNB...") print(f"📝 Params: {params}") try: # 1. Call (Simulation) res = router.functions.exactInputSingle(params).call({'from': account.address}) print(f"✅ Simulation SUCCESS! Output: {res}") except Exception as e: print(f"❌ Simulation FAILED: {e}")