addLiquidityETH, отправляемых на роутер PancakeSwap. При обнаружении такой транзакции бот извлекает адрес нового токена и немедленно формирует, подписывает и отправляет свою собственную транзакцию swapExactETHForTokensSupportingFeeOnTransferTokens с более высокой ценой газа (gas price), чтобы опередить транзакцию добавления ликвидности и купить токен по начальной цене.Исходный код
Представленный ниже Python-скрипт демонстрирует базовую реализацию алгоритма снайпинга токенов на PancakeSwap. Он подключается к WebSocket RPC для мониторинга Mempool сети Binance Smart Chain (BSC) в реальном времени. Когда обнаруживается транзакция addLiquidityETH, отправленная на роутер PancakeSwap, скрипт пытается декодировать ее, извлечь адрес нового токена и немедленно отправить свою собственную транзакцию на покупку этого токена с использованием BNB.
Важные замечания:
- Риск
amountOutMin: В целях демонстрацииamountOutMinустановлен в0. Это означает, что вы готовы принять любое количество токенов, что крайне рискованно в реальных условиях и может привести к значительным потерям из-за проскальзывания (slippage) или MEV-атак. В продакшене необходимо реализовать сложную логику для точного расчетаamountOutMinна основе ожидаемой цены и допустимого проскальзывания. - Надежность RPC: Для эффективного снайпинга критически важен доступ к надежному и быстрому RPC-узлу, который предоставляет полный и своевременный поток транзакций Mempool. Публичные RPC-узлы часто имеют ограничения по скорости, задержки или не показывают все pending-транзакции. Рекомендуется использовать выделенные узлы или специализированные сервисы.
- Скорость: Скорость выполнения кода, сетевая задержка и скорость ответа RPC-узла являются ключевыми факторами успеха.
- Безопасность: Никогда не храните приватные ключи непосредственно в коде. Используйте переменные окружения или другие безопасные методы хранения.
import asyncio
import os
import json
from web3 import Web3
from web3.middleware import geth_poa_middleware
from web3.exceptions import TransactionNotFound
from eth_abi import decode_abi
# --- Configuration ---
# Используйте переменные окружения для чувствительных данных
PRIVATE_KEY = os.getenv("PRIVATE_KEY")
if not PRIVATE_KEY:
raise ValueError("PRIVATE_KEY environment variable not set.")
# BSC RPC URL (например, от QuickNode, Alchemy, или публичный узел)
# Для мониторинга Mempool рекомендуется выделенный узел или сервис.
# Публичные узлы могут иметь ограничения по скорости или ненадежно показывать pending-транзакции.
BSC_RPC_WSS = os.getenv("BSC_RPC_WSS", "wss://bsc-ws-node.nariox.org:443") # Пример публичного WSS
BSC_RPC_HTTP = os.getenv("BSC_RPC_HTTP", "https://bsc-dataseed.binance.org/") # Пример публичного HTTP
# Адрес PancakeSwap Router V2
ROUTER_ADDRESS = Web3.to_checksum_address("0x10ED43C718714eb63d5aA57B78B54704E256024E")
# Адрес WBNB
WBNB_ADDRESS = Web3.to_checksum_address("0xbb4CdB9eD5B8D4B0AD8e592a0d0Bf17346401d78")
# Ваш адрес кошелька
YOUR_ADDRESS = Web3.to_checksum_address(Web3.eth.account.from_key(PRIVATE_KEY).address)
# Параметры снайпинга
GAS_PRICE_MULTIPLIER = 1.5 # Умножить обнаруженную цену газа на этот фактор
GAS_LIMIT = 3000000 # Максимальный лимит газа для снайперской транзакции
SLIPPAGE = 0.05 # 5% проскальзывания (0.05) - НЕ ИСПОЛЬЗУЕТСЯ ПРИ amountOutMin=0
AMOUNT_BNB_TO_SNIPE = Web3.to_wei(0.01, 'ether') # Количество BNB для использования в снайперской транзакции
# --- ABIs (упрощенные для релевантных функций) ---
# Полный ABI для PancakeSwap Router V2 можно найти на BscScan
ROUTER_ABI = [
{
"inputs": [
{"internalType": "address", "name": "token", "type": "address"},
{"internalType": "uint256", "name": "amountTokenDesired", "type": "uint256"},
{"internalType": "uint256", "name": "amountTokenMin", "type": "uint256"},
{"internalType": "uint256", "name": "amountETHMin", "type": "uint256"},
{"internalType": "address", "name": "to", "type": "address"},
{"internalType": "uint256", "name": "deadline", "type": "uint256"},
],
"name": "addLiquidityETH",
"outputs": [
{"internalType": "uint256", "name": "amountToken", "type": "uint256"},
{"internalType": "uint256", "name": "amountETH", "type": "uint256"},
{"internalType": "uint256", "name": "liquidity", "type": "uint256"},
],
"stateMutability": "payable",
"type": "function",
},
{
"inputs": [
{"internalType": "uint256", "name": "amountOutMin", "type": "uint256"},
{"internalType": "address[]", "name": "path", "type": "address[]"},
{"internalType": "address", "name": "to", "type": "address"},
{"internalType": "uint256", "name": "deadline", "type": "uint256"},
],
"name": "swapExactETHForTokensSupportingFeeOnTransferTokens",
"outputs": [],
"stateMutability": "payable",
"type": "function",
},
]
# --- Web3 Setup ---
w3_http = Web3(Web3.HTTPProvider(BSC_RPC_HTTP))
w3_http.middleware_onion.inject(geth_poa_middleware, layer=0) # Для совместимости с BSC
router_contract = w3_http.eth.contract(address=ROUTER_ADDRESS, abi=ROUTER_ABI)
# Селекторы функций для быстрого сравнения
ADD_LIQUIDITY_ETH_SELECTOR = router_contract.functions.addLiquidityETH.selector.hex()
SWAP_ETH_FOR_TOKENS_SELECTOR = router_contract.functions.swapExactETHForTokensSupportingFeeOnTransferTokens.selector.hex()
async def handle_pending_transaction(tx_hash_hex, w3_ws):
"""Обрабатывает обнаруженную pending-транзакцию."""
try:
# Получаем полную информацию о транзакции
tx = await w3_ws.eth.get_transaction(tx_hash_hex)
# Проверяем, является ли транзакция вызовом роутера PancakeSwap
if tx and tx['to'] == ROUTER_ADDRESS:
function_selector = tx['input'][:10]
# Если это транзакция добавления ликвидности ETH
if function_selector == ADD_LIQUIDITY_ETH_SELECTOR:
print(f"Обнаружена транзакция addLiquidityETH: {tx_hash_hex}")
try:
# Декодируем входные данные для получения адреса токена
# Типы для addLiquidityETH: (address, uint256, uint256, uint256, address, uint256)
# Нам нужен первый адрес (token)
decoded_data = decode_abi(
['address', 'uint256', 'uint256', 'uint256', 'address', 'uint256'],
Web3.to_bytes(hexstr=tx['input'][10:])
)
token_address = Web3.to_checksum_address(decoded_data[0])
print(f" Токен: {token_address}, Отправитель LP: {tx['from']}")
# --- Логика снайпинга ---
# amountOutMin: Это критически важный параметр. Установка его в 0
# означает, что вы готовы принять любое количество токенов, что
# крайне рискованно и может привести к значительным потерям.
# В реальном снайпере здесь должна быть сложная логика для
# расчета минимального количества токенов, которое вы готовы получить,
# исходя из ожидаемой цены и допустимого проскальзывания.
# Для демонстрации мы устанавливаем 0, но это НЕ РЕКОМЕНДУЕТСЯ для продакшена.
amount_out_min = 0 # КРАЙНЕ РИСКОВАННО В ПРОДАКШЕНЕ!
print(f" amountOutMin установлен в {amount_out_min} (КРАЙНЕ РИСКОВАННО).")
# Формируем снайперскую транзакцию
nonce = w3_http.eth.get_transaction_count(YOUR_ADDRESS)
# Используем цену газа из pending-транзакции LP, умноженную на множитель
gas_price = int(tx['gasPrice'] * GAS_PRICE_MULTIPLIER)
# Убедимся, что цена газа не слишком низкая (опционально)
current_avg_gas_price = w3_http.eth.gas_price
if gas_price < current_avg_gas_price:
gas_price = int(current_avg_gas_price * GAS_PRICE_MULTIPLIER)
sniping_tx = router_contract.functions.swapExactETHForTokensSupportingFeeOnTransferTokens(
amountOutMin=amount_out_min,
path=[WBNB_ADDRESS, token_address],
to=YOUR_ADDRESS,
deadline=int(asyncio.time() + 300) # Дедлайн через 5 минут
).build_transaction({
'from': YOUR_ADDRESS,
'value': AMOUNT_BNB_TO_SNIPE,
'gas': GAS_LIMIT,
'gasPrice': gas_price,
'nonce': nonce,
})
# Подписываем и отправляем транзакцию
signed_tx = w3_http.eth.account.sign_transaction(sniping_tx, private_key=PRIVATE_KEY)
tx_hash_snipe = w3_http.eth.send_raw_transaction(signed_tx.rawTransaction)
print(f" Снайперская транзакция отправлена! Хеш: {tx_hash_snipe.hex()}")
# Опционально: ожидание подтверждения транзакции
# try:
# receipt = w3_http.eth.wait_for_transaction_receipt(tx_hash_snipe, timeout=300)
# if receipt.status == 1:
# print(f" Снайперская транзакция {tx_hash_snipe.hex()} успешно выполнена!")
# else:
# print(f" Снайперская транзакция {tx_hash_snipe.hex()} завершилась неудачей!")
# except TransactionNotFound:
# print(f" Снайперская транзакция {tx_hash_snipe.hex()} не найдена после таймаута.")
# except Exception as e:
# print(f" Ошибка при ожидании квитанции снайперской транзакции: {e}")
except Exception as e:
print(f" Ошибка при декодировании или обработке транзакции addLiquidityETH {tx_hash_hex}: {e}")
except TransactionNotFound:
# Транзакция могла быть уже добыта или отброшена до того, как мы успели ее получить
pass
except Exception as e:
print(f"Ошибка при обработке транзакции {tx_hash_hex}: {e}")
async def listen_for_pending_transactions():
"""Подключается к WebSocket и слушает новые pending-транзакции."""
print("Подключение к WebSocket...")
w3_ws = Web3(Web3.WebsocketProvider(BSC_RPC_WSS))
w3_ws.middleware_onion.inject(geth_poa_middleware, layer=0)
if not w3_ws.is_connected():
print("Не удалось подключиться к WebSocket RPC. Выход.")
return
print("Успешное подключение к WebSocket.")
# Подписываемся на новые pending-транзакции
# Подписка 'newPendingTransactions' возвращает только хеши транзакций.
# Затем нам нужно получить полные детали транзакции с помощью eth_getTransaction.
subscription_id = w3_ws.eth.subscribe('newPendingTransactions')
print(f"Подписан на новые pending-транзакции. ID подписки: {subscription_id}")
while True:
try:
message = w3_ws.ws.recv()
response = json.loads(message)
if 'params' in response and 'result' in response['params']:
tx_hash_hex = response['params']['result']
# Запускаем обработку транзакции как отдельную задачу, чтобы не блокировать слушатель
asyncio.create_task(handle_pending_transaction(tx_hash_hex, w3_ws))
except asyncio.CancelledError:
print("Слушатель отменен.")
break
except Exception as e:
print(f"Ошибка в слушателе WebSocket: {e}")
# Попытка переподключиться или обработать ошибку
await asyncio.sleep(5) # Ждем перед повторной попыткой
async def main():
"""Основная функция бота."""
print("Запуск бота для снайпинга Mempool...")
print(f"Мониторинг PancakeSwap Router V2 ({ROUTER_ADDRESS}) на предмет транзакций addLiquidityETH.")
print(f"Ваш адрес: {YOUR_ADDRESS}")
print(f"Количество BNB для снайпинга: {Web3.from_wei(AMOUNT_BNB_TO_SNIPE, 'ether')} BNB")
await listen_for_pending_transactions()
if __name__ == "__main__":
# Для Windows, ProactorEventLoop часто лучше для асинхронного ввода/вывода
if os.name == 'nt':
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
try:
asyncio.run(main())
except KeyboardInterrupt:
print("Бот остановлен вручную.")
except Exception as e:
print(f"Произошла необработанная ошибка: {e}")
Разбор параметров
PRIVATE_KEY: Ваш приватный ключ кошелька, с которого будут отправляться транзакции. Критически важно: храните его в переменных окружения или другом безопасном месте, никогда не вставляйте напрямую в код.BSC_RPC_WSS: WebSocket URL для RPC-узла Binance Smart Chain. Используется для подписки на новые pending-транзакции в Mempool. Для продакшена рекомендуется использовать приватный или высокопроизводительный узел.BSC_RPC_HTTP: HTTP URL для RPC-узла Binance Smart Chain. Используется для отправки транзакций и получения информации о состоянии сети (например, nonce, gas price).ROUTER_ADDRESS: Адрес контракта PancakeSwap Router V2 на BSC. Это основной контракт, через который происходят обмены и добавление ликвидности.WBNB_ADDRESS: Адрес обернутого BNB (Wrapped BNB) на BSC. Используется в качестве одной из сторон торговой пары.YOUR_ADDRESS: Ваш публичный адрес кошелька, автоматически вычисляется из приватного ключа.GAS_PRICE_MULTIPLIER: Множитель, применяемый к цене газа обнаруженной транзакцииaddLiquidityETH. Увеличение этого значения помогает вашей снайперской транзакции быть обработанной быстрее (front-run).GAS_LIMIT: Максимальный лимит газа для вашей снайперской транзакции. Установите достаточно высокое значение, чтобы транзакция не провалилась из-за нехватки газа, но не чрезмерно высокое, чтобы избежать лишних трат.SLIPPAGE: Допустимое проскальзывание в процентах (например,0.05для 5%). ВНИМАНИЕ: В текущей реализации сamountOutMin=0этот параметр не используется для защиты от проскальзывания.AMOUNT_BNB_TO_SNIPE: Количество BNB, которое вы хотите использовать для покупки нового токена.
Как запустить
Для запуска бота выполните следующие шаги:
-
Установите Python: Убедитесь, что у вас установлен Python 3.8 или выше. Вы можете скачать его с официального сайта python.org.
-
Установите необходимые библиотеки: Откройте терминал или командную строку и выполните команду:
pip install web3 eth-abi -
Настройте переменные окружения: Создайте файл
.envв корневой директории вашего проекта или установите переменные окружения напрямую в вашей системе. Это критически важно для безопасности вашего приватного ключа. Пример для.env:PRIVATE_KEY="ВАШ_ПРИВАТНЫЙ_КЛЮЧ_ЗДЕСЬ" BSC_RPC_WSS="wss://ваш_wss_rpc_url" BSC_RPC_HTTP="https://ваш_http_rpc_url"Замените заполнители на ваш реальный приватный ключ и URL-ы RPC. Для получения приватного ключа из MetaMask, например, вы можете следовать инструкциям в нашей статье Подпись API-запросов ECDSA для dYdX V4 на Python: Руководство, где объясняется работа с приватными ключами.
-
Сохраните код: Скопируйте предоставленный выше Python-код и сохраните его в файл, например,
sniper_bot.py. -
Запустите бота: Откройте терминал в директории, где вы сохранили файл
sniper_bot.py, и выполните команду:python sniper_bot.pyБот начнет мониторинг Mempool и будет выводить информацию о найденных транзакциях и отправленных снайперских сделках.
Предупреждение: Снайпинг токенов — это высокорискованная деятельность, требующая глубокого понимания механики блокчейна, смарт-контрактов и рыночных условий. Этот скрипт является демонстрационным примером и не предназначен для использования в продакшене без значительных доработок и тестирования. Убедитесь, что вы понимаете все риски, включая возможность потери средств из-за проскальзывания, неудачных транзакций или MEV-атак. Также учтите, что комиссии за транзакции могут быть значительными, как обсуждается в нашей статье Расчет комиссий мейкера/тейкера (Fee Tiers) в CCXT Python.
Для изучения других продвинутых торговых стратегий, таких как арбитраж, вы можете ознакомиться с нашей статьей Треугольный арбитраж на OKX: Python скрипт для Triangular Arbitrage.




