mirror of
https://github.com/chainstacklabs/pumpfun-bonkfun-bot.git
synced 2026-07-27 23:37:45 +00:00
Merge pull request #73 from chainstacklabs/feature/listen-to-migrations-script
Listen to migrations script (learning example)
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
import asyncio
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import struct
|
||||
|
||||
import base58
|
||||
import websockets
|
||||
from solders.pubkey import Pubkey
|
||||
|
||||
WSS_ENDPOINT = os.environ.get("SOLANA_NODE_WSS_ENDPOINT")
|
||||
MIGRATION_PROGRAM_ID = Pubkey.from_string("39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg")
|
||||
|
||||
|
||||
def parse_migrate_instruction(data):
|
||||
if len(data) < 8:
|
||||
print(f"[ERROR] Data length too short: {len(data)} bytes")
|
||||
return None
|
||||
|
||||
offset = 8
|
||||
parsed_data = {}
|
||||
|
||||
fields = [
|
||||
("timestamp", "i64"),
|
||||
("index", "u16"),
|
||||
("creator", "publicKey"),
|
||||
("baseMint", "publicKey"),
|
||||
("quoteMint", "publicKey"),
|
||||
("baseMintDecimals", "u8"),
|
||||
("quoteMintDecimals", "u8"),
|
||||
("baseAmountIn", "u64"),
|
||||
("quoteAmountIn", "u64"),
|
||||
("poolBaseAmount", "u64"),
|
||||
("poolQuoteAmount", "u64"),
|
||||
("minimumLiquidity", "u64"),
|
||||
("initialLiquidity", "u64"),
|
||||
("lpTokenAmountOut", "u64"),
|
||||
("poolBump", "u8"),
|
||||
("pool", "publicKey"),
|
||||
("lpMint", "publicKey"),
|
||||
("userBaseTokenAccount", "publicKey"),
|
||||
("userQuoteTokenAccount", "publicKey"),
|
||||
]
|
||||
|
||||
try:
|
||||
for field_name, field_type in fields:
|
||||
if field_type == "publicKey":
|
||||
value = data[offset:offset + 32]
|
||||
parsed_data[field_name] = base58.b58encode(value).decode("utf-8")
|
||||
offset += 32
|
||||
elif field_type in {"u64", "i64"}:
|
||||
value = struct.unpack("<Q", data[offset:offset + 8])[0] if field_type == "u64" else struct.unpack("<q", data[offset:offset + 8])[0]
|
||||
parsed_data[field_name] = value
|
||||
offset += 8
|
||||
elif field_type == "u16":
|
||||
value = struct.unpack("<H", data[offset:offset + 2])[0]
|
||||
parsed_data[field_name] = value
|
||||
offset += 2
|
||||
elif field_type == "u8":
|
||||
value = data[offset]
|
||||
parsed_data[field_name] = value
|
||||
offset += 1
|
||||
|
||||
return parsed_data
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to parse data at offset {offset}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def is_transaction_successful(logs):
|
||||
for log in logs:
|
||||
if "AnchorError thrown" in log or "Error" in log:
|
||||
print(f"[ERROR] Transaction failed: {log}")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def print_transaction_details(log_data):
|
||||
signature = log_data.get('signature', 'N/A')
|
||||
print(f"\n[INFO] Transaction Signature: {signature}")
|
||||
logs = log_data.get("logs", [])
|
||||
|
||||
for log in logs:
|
||||
if log.startswith("Program data:"):
|
||||
try:
|
||||
data = base64.b64decode(log.split(": ")[1])
|
||||
parsed_data = parse_migrate_instruction(data)
|
||||
if parsed_data:
|
||||
print("\n[INFO] Parsed Migration Instruction Data:")
|
||||
for key, value in parsed_data.items():
|
||||
print(f" {key}: {value}")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Base64 decode failed: {log}, Error: {e}")
|
||||
|
||||
def print_log_details(logs):
|
||||
print("\n[INFO] Processing logs:")
|
||||
for log in logs:
|
||||
print(f" Log: {log}")
|
||||
|
||||
|
||||
async def listen_for_migrations():
|
||||
while True:
|
||||
try:
|
||||
print("\n[INFO] Connecting to WebSocket ...")
|
||||
async with websockets.connect(WSS_ENDPOINT) as websocket:
|
||||
subscription_message = json.dumps(
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "logsSubscribe",
|
||||
"params": [
|
||||
{"mentions": [str(MIGRATION_PROGRAM_ID)]},
|
||||
{"commitment": "processed"},
|
||||
],
|
||||
}
|
||||
)
|
||||
await websocket.send(subscription_message)
|
||||
print(f"[INFO] Listening for migration instructions from program: {MIGRATION_PROGRAM_ID}")
|
||||
|
||||
response = await websocket.recv()
|
||||
print(f"[INFO] Subscription response: {response}")
|
||||
|
||||
while True:
|
||||
try:
|
||||
response = await asyncio.wait_for(websocket.recv(), timeout=60)
|
||||
data = json.loads(response)
|
||||
|
||||
if "method" in data and data["method"] == "logsNotification":
|
||||
log_data = data["params"]["result"]["value"]
|
||||
logs = log_data.get("logs", [])
|
||||
|
||||
if is_transaction_successful(logs):
|
||||
if not any("Program log: Instruction: Migrate" in log for log in logs):
|
||||
print("[INFO] Skipping: No Migrate instruction")
|
||||
continue
|
||||
|
||||
if any("Program log: Bonding curve already migrated" in log for log in logs):
|
||||
print("[INFO] Skipping: Bonding curve already migrated")
|
||||
continue
|
||||
|
||||
print("[INFO] Processing migration instruction!")
|
||||
print_transaction_details(log_data)
|
||||
else:
|
||||
print("[INFO] Skipping failed transaction.")
|
||||
except TimeoutError:
|
||||
print("[INFO] Timeout waiting for WebSocket message, retrying...")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] An error occurred: {e}")
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Connection error: {e}")
|
||||
print("[INFO] Reconnecting in 5 seconds...")
|
||||
await asyncio.sleep(5)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(listen_for_migrations())
|
||||
Reference in New Issue
Block a user