102 lines
4.2 KiB
Python
102 lines
4.2 KiB
Python
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}") |