fixed formatting

This commit is contained in:
smypmsa
2025-03-05 07:03:32 +00:00
parent faba3d8306
commit 8bf3700187
24 changed files with 1261 additions and 708 deletions
+3
View File
@@ -0,0 +1,3 @@
RPC_ENDPOINT=...
WSS_ENDPOINT=...
PRIVATE_KEY=...
+3
View File
@@ -1,3 +1,6 @@
.pylintrc
.ruff_cache
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
+12
View File
@@ -0,0 +1,12 @@
{
"python.languageServer": "Pylance",
"python.analysis.typeCheckingMode": "basic",
"ruff.lint.enable": true,
"ruff.format.enable": true,
"[python]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "charliermarsh.ruff"
}
}
+163 -85
View File
@@ -1,34 +1,32 @@
import asyncio
import json
import base64
import struct
import base58
import hashlib
import websockets
import json
import struct
import time
import base58
import spl.token.instructions as spl_token
import websockets
from construct import Flag, Int64ul, Struct
from solana.rpc.async_api import AsyncClient
from solana.transaction import Transaction
from solana.rpc.commitment import Confirmed
from solana.rpc.types import TxOpts
from solders.pubkey import Pubkey
from solana.transaction import Transaction
from solders.instruction import AccountMeta, Instruction
from solders.keypair import Keypair
from solders.instruction import Instruction, AccountMeta
from solders.pubkey import Pubkey
from solders.system_program import TransferParams, transfer
from solders.transaction import VersionedTransaction
from spl.token.instructions import get_associated_token_address
import spl.token.instructions as spl_token
from config import *
from construct import Struct, Int64ul, Flag
# Here and later all the discriminators are precalculated. See learning-examples/discriminator.py
EXPECTED_DISCRIMINATOR = struct.pack("<Q", 6966180631402821399)
TOKEN_DECIMALS = 6
class BondingCurveState:
_STRUCT = Struct(
"virtual_token_reserves" / Int64ul,
@@ -36,14 +34,17 @@ class BondingCurveState:
"real_token_reserves" / Int64ul,
"real_sol_reserves" / Int64ul,
"token_total_supply" / Int64ul,
"complete" / Flag
"complete" / Flag,
)
def __init__(self, data: bytes) -> None:
parsed = self._STRUCT.parse(data[8:])
self.__dict__.update(parsed)
async def get_pump_curve_state(conn: AsyncClient, curve_address: Pubkey) -> BondingCurveState:
async def get_pump_curve_state(
conn: AsyncClient, curve_address: Pubkey
) -> BondingCurveState:
response = await conn.get_account_info(curve_address)
if not response.value or not response.value.data:
raise ValueError("Invalid curve state: No data")
@@ -54,13 +55,24 @@ async def get_pump_curve_state(conn: AsyncClient, curve_address: Pubkey) -> Bond
return BondingCurveState(data)
def calculate_pump_curve_price(curve_state: BondingCurveState) -> float:
if curve_state.virtual_token_reserves <= 0 or curve_state.virtual_sol_reserves <= 0:
raise ValueError("Invalid reserve state")
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (curve_state.virtual_token_reserves / 10 ** TOKEN_DECIMALS)
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (
curve_state.virtual_token_reserves / 10**TOKEN_DECIMALS
)
async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curve: Pubkey, amount: float, slippage: float = 0.01, max_retries=5):
async def buy_token(
mint: Pubkey,
bonding_curve: Pubkey,
associated_bonding_curve: Pubkey,
amount: float,
slippage: float = 0.01,
max_retries=5,
):
private_key = base58.b58decode(PRIVATE_KEY)
payer = Keypair.from_bytes(private_key)
@@ -81,11 +93,11 @@ async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curv
try:
account_info = await client.get_account_info(associated_token_account)
if account_info.value is None:
print(f"Creating associated token account (Attempt {ata_attempt + 1})...")
print(
f"Creating associated token account (Attempt {ata_attempt + 1})..."
)
create_ata_ix = spl_token.create_associated_token_account(
payer=payer.pubkey(),
owner=payer.pubkey(),
mint=mint
payer=payer.pubkey(), owner=payer.pubkey(), mint=mint
)
create_ata_tx = Transaction()
create_ata_tx.add(create_ata_ix)
@@ -93,20 +105,28 @@ async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curv
create_ata_tx.recent_blockhash = recent_blockhash.value.blockhash
await client.send_transaction(create_ata_tx, payer)
print("Associated token account created.")
print(f"Associated token account address: {associated_token_account}")
print(
f"Associated token account address: {associated_token_account}"
)
break
else:
print("Associated token account already exists.")
print(f"Associated token account address: {associated_token_account}")
print(
f"Associated token account address: {associated_token_account}"
)
break
except Exception as e:
print(f"Attempt {ata_attempt + 1} to create associated token account failed: {str(e)}")
print(
f"Attempt {ata_attempt + 1} to create associated token account failed: {str(e)}"
)
if ata_attempt < max_retries - 1:
wait_time = 2 ** ata_attempt
wait_time = 2**ata_attempt
print(f"Retrying in {wait_time} seconds...")
await asyncio.sleep(wait_time)
else:
print("Max retries reached. Unable to create associated token account.")
print(
"Max retries reached. Unable to create associated token account."
)
return
# Continue with the buy transaction
@@ -116,19 +136,43 @@ async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curv
AccountMeta(pubkey=PUMP_GLOBAL, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_FEE, is_signer=False, is_writable=True),
AccountMeta(pubkey=mint, is_signer=False, is_writable=False),
AccountMeta(pubkey=bonding_curve, is_signer=False, is_writable=True),
AccountMeta(pubkey=associated_bonding_curve, is_signer=False, is_writable=True),
AccountMeta(pubkey=associated_token_account, is_signer=False, is_writable=True),
AccountMeta(pubkey=payer.pubkey(), is_signer=True, is_writable=True),
AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(pubkey=SYSTEM_TOKEN_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(
pubkey=bonding_curve, is_signer=False, is_writable=True
),
AccountMeta(
pubkey=associated_bonding_curve,
is_signer=False,
is_writable=True,
),
AccountMeta(
pubkey=associated_token_account,
is_signer=False,
is_writable=True,
),
AccountMeta(
pubkey=payer.pubkey(), is_signer=True, is_writable=True
),
AccountMeta(
pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False
),
AccountMeta(
pubkey=SYSTEM_TOKEN_PROGRAM, is_signer=False, is_writable=False
),
AccountMeta(pubkey=SYSTEM_RENT, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(
pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False
),
AccountMeta(
pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False
),
]
discriminator = struct.pack("<Q", 16927863322537952870)
data = discriminator + struct.pack("<Q", int(token_amount * 10**6)) + struct.pack("<Q", max_amount_lamports)
data = (
discriminator
+ struct.pack("<Q", int(token_amount * 10**6))
+ struct.pack("<Q", max_amount_lamports)
)
buy_ix = Instruction(PUMP_PROGRAM, data, accounts)
recent_blockhash = await client.get_latest_blockhash()
@@ -151,61 +195,66 @@ async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curv
except Exception as e:
print(f"Attempt {attempt + 1} failed: {str(e)}")
if attempt < max_retries - 1:
wait_time = 2 ** attempt
wait_time = 2**attempt
print(f"Retrying in {wait_time} seconds...")
await asyncio.sleep(wait_time)
else:
print("Max retries reached. Unable to complete the transaction.")
def load_idl(file_path):
with open(file_path, 'r') as f:
with open(file_path, "r") as f:
return json.load(f)
def decode_create_instruction(ix_data, ix_def, accounts):
args = {}
offset = 8 # Skip 8-byte discriminator
for arg in ix_def['args']:
if arg['type'] == 'string':
length = struct.unpack_from('<I', ix_data, offset)[0]
for arg in ix_def["args"]:
if arg["type"] == "string":
length = struct.unpack_from("<I", ix_data, offset)[0]
offset += 4
value = ix_data[offset:offset+length].decode('utf-8')
value = ix_data[offset : offset + length].decode("utf-8")
offset += length
elif arg['type'] == 'publicKey':
value = base64.b64encode(ix_data[offset:offset+32]).decode('utf-8')
elif arg["type"] == "publicKey":
value = base64.b64encode(ix_data[offset : offset + 32]).decode("utf-8")
offset += 32
else:
raise ValueError(f"Unsupported type: {arg['type']}")
args[arg['name']] = value
args[arg["name"]] = value
# Add accounts
args['mint'] = str(accounts[0])
args['bondingCurve'] = str(accounts[2])
args['associatedBondingCurve'] = str(accounts[3])
args['user'] = str(accounts[7])
args["mint"] = str(accounts[0])
args["bondingCurve"] = str(accounts[2])
args["associatedBondingCurve"] = str(accounts[3])
args["user"] = str(accounts[7])
return args
async def listen_for_create_transaction(websocket):
idl = load_idl('idl/pump_fun_idl.json')
idl = load_idl("idl/pump_fun_idl.json")
create_discriminator = 8576854823835016728
subscription_message = json.dumps({
"jsonrpc": "2.0",
"id": 1,
"method": "blockSubscribe",
"params": [
{"mentionsAccountOrProgram": str(PUMP_PROGRAM)},
{
"commitment": "confirmed",
"encoding": "base64",
"showRewards": False,
"transactionDetails": "full",
"maxSupportedTransactionVersion": 0
}
]
})
subscription_message = json.dumps(
{
"jsonrpc": "2.0",
"id": 1,
"method": "blockSubscribe",
"params": [
{"mentionsAccountOrProgram": str(PUMP_PROGRAM)},
{
"commitment": "confirmed",
"encoding": "base64",
"showRewards": False,
"transactionDetails": "full",
"maxSupportedTransactionVersion": 0,
},
],
}
)
await websocket.send(subscription_message)
print(f"Subscribed to blocks mentioning program: {PUMP_PROGRAM}")
@@ -221,27 +270,52 @@ async def listen_for_create_transaction(websocket):
response = await asyncio.wait_for(websocket.recv(), timeout=30)
data = json.loads(response)
if 'method' in data and data['method'] == 'blockNotification':
if 'params' in data and 'result' in data['params']:
block_data = data['params']['result']
if 'value' in block_data and 'block' in block_data['value']:
block = block_data['value']['block']
if 'transactions' in block:
for tx in block['transactions']:
if isinstance(tx, dict) and 'transaction' in tx:
tx_data_decoded = base64.b64decode(tx['transaction'][0])
transaction = VersionedTransaction.from_bytes(tx_data_decoded)
if "method" in data and data["method"] == "blockNotification":
if "params" in data and "result" in data["params"]:
block_data = data["params"]["result"]
if "value" in block_data and "block" in block_data["value"]:
block = block_data["value"]["block"]
if "transactions" in block:
for tx in block["transactions"]:
if isinstance(tx, dict) and "transaction" in tx:
tx_data_decoded = base64.b64decode(
tx["transaction"][0]
)
transaction = VersionedTransaction.from_bytes(
tx_data_decoded
)
for ix in transaction.message.instructions:
if str(transaction.message.account_keys[ix.program_id_index]) == str(PUMP_PROGRAM):
if str(
transaction.message.account_keys[
ix.program_id_index
]
) == str(PUMP_PROGRAM):
ix_data = bytes(ix.data)
discriminator = struct.unpack('<Q', ix_data[:8])[0]
discriminator = struct.unpack(
"<Q", ix_data[:8]
)[0]
if discriminator == create_discriminator:
create_ix = next(instr for instr in idl['instructions'] if instr['name'] == 'create')
account_keys = [str(transaction.message.account_keys[index]) for index in ix.accounts]
decoded_args = decode_create_instruction(ix_data, create_ix, account_keys)
create_ix = next(
instr
for instr in idl["instructions"]
if instr["name"] == "create"
)
account_keys = [
str(
transaction.message.account_keys[
index
]
)
for index in ix.accounts
]
decoded_args = (
decode_create_instruction(
ix_data, create_ix, account_keys
)
)
return decoded_args
except asyncio.TimeoutError:
print("No data received for 30 seconds, sending ping...")
@@ -251,6 +325,7 @@ async def listen_for_create_transaction(websocket):
print("WebSocket connection closed. Reconnecting...")
raise
async def main(yolo_mode=False):
if yolo_mode:
while True:
@@ -264,7 +339,9 @@ async def main(yolo_mode=False):
break
except Exception as e:
print(f"An error occurred: {e}")
print("Waiting for 5 seconds before looking for the next token...")
print(
"Waiting for 5 seconds before looking for the next token..."
)
await asyncio.sleep(5)
except Exception as e:
print(f"Connection error: {e}")
@@ -273,5 +350,6 @@ async def main(yolo_mode=False):
else:
await trade()
if __name__ == "__main__":
asyncio.run(main())
asyncio.run(main())
+23 -7
View File
@@ -1,14 +1,25 @@
import os
from dotenv import load_dotenv
from solders.pubkey import Pubkey
load_dotenv()
# System & pump.fun addresses
PUMP_PROGRAM = Pubkey.from_string("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P")
PUMP_GLOBAL = Pubkey.from_string("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf")
PUMP_EVENT_AUTHORITY = Pubkey.from_string("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1")
PUMP_EVENT_AUTHORITY = Pubkey.from_string(
"Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1"
)
PUMP_FEE = Pubkey.from_string("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM")
PUMP_LIQUIDITY_MIGRATOR = Pubkey.from_string("39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg")
PUMP_LIQUIDITY_MIGRATOR = Pubkey.from_string(
"39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg"
)
SYSTEM_PROGRAM = Pubkey.from_string("11111111111111111111111111111111")
SYSTEM_TOKEN_PROGRAM = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM = Pubkey.from_string("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")
SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM = Pubkey.from_string(
"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
)
SYSTEM_RENT = Pubkey.from_string("SysvarRent111111111111111111111111111111111")
SOL = Pubkey.from_string("So11111111111111111111111111111111111111112")
LAMPORTS_PER_SOL = 1_000_000_000
@@ -17,11 +28,16 @@ LAMPORTS_PER_SOL = 1_000_000_000
BUY_AMOUNT = 0.0001 # Amount of SOL to spend when buying
BUY_SLIPPAGE = 0.2 # 20% slippage tolerance for buying
SELL_SLIPPAGE = 0.2 # 20% slippage tolerance for selling
ENABLE_DYNAMIC_PRIORITY_FEE = (
True # getRecentPriorityFee is used to get current priority fee
)
EXTRA_PRIORITY_FEE = 0.1 # 10% increase in dynamic priority fee
# Your nodes
# You can also get a trader node https://docs.chainstack.com/docs/solana-trader-nodes
RPC_ENDPOINT = "SOLANA_NODE_RPC_ENDPOINT"
WSS_ENDPOINT = "SOLANA_NODE_WSS_ENDPOINT"
RPC_ENDPOINT = os.getenv("RPC_ENDPOINT")
WSS_ENDPOINT = os.getenv("WSS_ENDPOINT")
MAX_RPS = 25 # RPS of your node to avoid 429 errors
#Private key
PRIVATE_KEY = "SOLANA_PRIVATE_KEY"
# Private key
PRIVATE_KEY = os.getenv("PRIVATE_KEY")
@@ -3,36 +3,41 @@ import hashlib
import json
import os
import sys
import websockets
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from config import WSS_ENDPOINT, PUMP_PROGRAM
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from config import PUMP_PROGRAM, WSS_ENDPOINT
async def save_transaction(tx_data, tx_signature):
os.makedirs("blockSubscribe-transactions", exist_ok=True)
hashed_signature = hashlib.sha256(tx_signature.encode()).hexdigest()
file_path = os.path.join("blockSubscribe-transactions", f"{hashed_signature}.json")
with open(file_path, 'w') as f:
with open(file_path, "w") as f:
json.dump(tx_data, f, indent=2)
print(f"Saved transaction: {hashed_signature[:8]}...")
async def listen_for_transactions():
async with websockets.connect(WSS_ENDPOINT) as websocket:
subscription_message = json.dumps({
"jsonrpc": "2.0",
"id": 1,
"method": "blockSubscribe",
"params": [
{"mentionsAccountOrProgram": str(PUMP_PROGRAM)},
{
"commitment": "confirmed",
"encoding": "base64",
"showRewards": False,
"transactionDetails": "full",
"maxSupportedTransactionVersion": 0
}
]
})
subscription_message = json.dumps(
{
"jsonrpc": "2.0",
"id": 1,
"method": "blockSubscribe",
"params": [
{"mentionsAccountOrProgram": str(PUMP_PROGRAM)},
{
"commitment": "confirmed",
"encoding": "base64",
"showRewards": False,
"transactionDetails": "full",
"maxSupportedTransactionVersion": 0,
},
],
}
)
await websocket.send(subscription_message)
print(f"Subscribed to blocks mentioning program: {PUMP_PROGRAM}")
@@ -40,27 +45,36 @@ async def listen_for_transactions():
try:
response = await websocket.recv()
data = json.loads(response)
if 'method' in data and data['method'] == 'blockNotification':
if 'params' in data and 'result' in data['params']:
block_data = data['params']['result']
if 'value' in block_data and 'block' in block_data['value']:
block = block_data['value']['block']
if 'transactions' in block:
transactions = block['transactions']
if "method" in data and data["method"] == "blockNotification":
if "params" in data and "result" in data["params"]:
block_data = data["params"]["result"]
if "value" in block_data and "block" in block_data["value"]:
block = block_data["value"]["block"]
if "transactions" in block:
transactions = block["transactions"]
for tx in transactions:
if isinstance(tx, dict) and 'transaction' in tx:
if isinstance(tx['transaction'], list) and len(tx['transaction']) > 0:
tx_signature = tx['transaction'][0]
elif isinstance(tx['transaction'], dict) and 'signatures' in tx['transaction']:
tx_signature = tx['transaction']['signatures'][0]
if isinstance(tx, dict) and "transaction" in tx:
if (
isinstance(tx["transaction"], list)
and len(tx["transaction"]) > 0
):
tx_signature = tx["transaction"][0]
elif (
isinstance(tx["transaction"], dict)
and "signatures" in tx["transaction"]
):
tx_signature = tx["transaction"][
"signatures"
][0]
else:
continue
await save_transaction(tx, tx_signature)
elif 'result' in data:
elif "result" in data:
print(f"Subscription confirmed")
except Exception as e:
print(f"An error occurred: {str(e)}")
if __name__ == "__main__":
asyncio.run(listen_for_transactions())
asyncio.run(listen_for_transactions())
+9 -7
View File
@@ -5,21 +5,23 @@ import struct
# Set the instruction name here
instruction_name = "account:BondingCurve"
def calculate_discriminator(instruction_name):
# Create a SHA256 hash object
sha = hashlib.sha256()
# Update the hash with the instruction name
sha.update(instruction_name.encode('utf-8'))
sha.update(instruction_name.encode("utf-8"))
# Get the first 8 bytes of the hash
discriminator_bytes = sha.digest()[:8]
# Convert the bytes to a 64-bit unsigned integer (little-endian)
discriminator = struct.unpack('<Q', discriminator_bytes)[0]
discriminator = struct.unpack("<Q", discriminator_bytes)[0]
return discriminator
# Calculate the discriminator for the specified instruction
discriminator = calculate_discriminator(instruction_name)
@@ -28,4 +30,4 @@ print(f"Discriminator for '{instruction_name}' instruction: {discriminator}")
# global:buy discriminator - 16927863322537952870
# global:sell discriminator - 12502976635542562355
# global:create discriminator - 8576854823835016728
# account:BondingCurve discriminator - 6966180631402821399
# account:BondingCurve discriminator - 6966180631402821399
+43 -30
View File
@@ -1,19 +1,21 @@
import argparse
import asyncio
import os
import struct
import sys
from typing import Final
from construct import Struct, Int64ul, Flag
from construct import Flag, Int64ul, Struct
from solana.rpc.async_api import AsyncClient
from solders.pubkey import Pubkey
import argparse
import sys
import os
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from config import RPC_ENDPOINT, PUMP_PROGRAM
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from config import PUMP_PROGRAM, RPC_ENDPOINT
# Constants
EXPECTED_DISCRIMINATOR: Final[bytes] = struct.pack("<Q", 6966180631402821399)
class BondingCurveState:
_STRUCT = Struct(
"virtual_token_reserves" / Int64ul,
@@ -21,26 +23,26 @@ class BondingCurveState:
"real_token_reserves" / Int64ul,
"real_sol_reserves" / Int64ul,
"token_total_supply" / Int64ul,
"complete" / Flag
"complete" / Flag,
)
def __init__(self, data: bytes) -> None:
parsed = self._STRUCT.parse(data[8:])
self.__dict__.update(parsed)
def get_associated_bonding_curve_address(mint: Pubkey, program_id: Pubkey) -> tuple[Pubkey, int]:
def get_associated_bonding_curve_address(
mint: Pubkey, program_id: Pubkey
) -> tuple[Pubkey, int]:
"""
Derives the associated bonding curve address for a given mint
"""
return Pubkey.find_program_address(
[
b"bonding-curve",
bytes(mint)
],
program_id
)
return Pubkey.find_program_address([b"bonding-curve", bytes(mint)], program_id)
async def get_bonding_curve_state(conn: AsyncClient, curve_address: Pubkey) -> BondingCurveState:
async def get_bonding_curve_state(
conn: AsyncClient, curve_address: Pubkey
) -> BondingCurveState:
response = await conn.get_account_info(curve_address)
if not response.value or not response.value.data:
raise ValueError("Invalid curve state: No data")
@@ -51,46 +53,57 @@ async def get_bonding_curve_state(conn: AsyncClient, curve_address: Pubkey) -> B
return BondingCurveState(data)
async def check_token_status(mint_address: str) -> None:
try:
mint = Pubkey.from_string(mint_address)
# Get the associated bonding curve address
bonding_curve_address, bump = get_associated_bonding_curve_address(mint, PUMP_PROGRAM)
bonding_curve_address, bump = get_associated_bonding_curve_address(
mint, PUMP_PROGRAM
)
print("\nToken Status:")
print("-" * 50)
print(f"Token Mint: {mint}")
print(f"Associated Bonding Curve: {bonding_curve_address}")
print(f"Bump Seed: {bump}")
print("-" * 50)
# Check completion status
async with AsyncClient(RPC_ENDPOINT) as client:
try:
curve_state = await get_bonding_curve_state(client, bonding_curve_address)
curve_state = await get_bonding_curve_state(
client, bonding_curve_address
)
print("\nBonding Curve Status:")
print("-" * 50)
print(f"Completion Status: {'Completed' if curve_state.complete else 'Not Completed'}")
print(
f"Completion Status: {'Completed' if curve_state.complete else 'Not Completed'}"
)
if curve_state.complete:
print("\nNote: This bonding curve has completed and liquidity has been migrated to Raydium.")
print(
"\nNote: This bonding curve has completed and liquidity has been migrated to Raydium."
)
print("-" * 50)
except ValueError as e:
print(f"\nError accessing bonding curve: {e}")
except ValueError as e:
print(f"\nError: Invalid address format - {e}")
except Exception as e:
print(f"\nUnexpected error: {e}")
def main():
parser = argparse.ArgumentParser(description='Check token bonding curve status')
parser.add_argument('mint_address', help='The token mint address')
parser = argparse.ArgumentParser(description="Check token bonding curve status")
parser.add_argument("mint_address", help="The token mint address")
args = parser.parse_args()
asyncio.run(check_token_status(args.mint_address))
if __name__ == "__main__":
main()
main()
@@ -1,52 +1,51 @@
import sys
import os
import sys
from solders.pubkey import Pubkey
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from config import PUMP_PROGRAM
def get_bonding_curve_address(mint: Pubkey, program_id: Pubkey) -> tuple[Pubkey, int]:
"""
Derives the bonding curve address for a given mint
"""
return Pubkey.find_program_address(
[
b"bonding-curve",
bytes(mint)
],
program_id
)
return Pubkey.find_program_address([b"bonding-curve", bytes(mint)], program_id)
def find_associated_bonding_curve(mint: Pubkey, bonding_curve: Pubkey) -> Pubkey:
"""
Find the associated bonding curve for a given mint and bonding curve.
This uses the standard ATA derivation.
"""
from config import SYSTEM_TOKEN_PROGRAM as TOKEN_PROGRAM_ID
from config import SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM as ATA_PROGRAM_ID
from config import SYSTEM_TOKEN_PROGRAM as TOKEN_PROGRAM_ID
derived_address, _ = Pubkey.find_program_address(
[
bytes(bonding_curve),
bytes(TOKEN_PROGRAM_ID),
bytes(mint),
bytes(mint),
],
ATA_PROGRAM_ID
ATA_PROGRAM_ID,
)
return derived_address
def main():
def main():
mint_address = input("Enter the token mint address: ")
try:
mint = Pubkey.from_string(mint_address)
bonding_curve_address, bump = get_bonding_curve_address(mint, PUMP_PROGRAM)
# Calculate the associated bonding curve
associated_bonding_curve = find_associated_bonding_curve(mint, bonding_curve_address)
associated_bonding_curve = find_associated_bonding_curve(
mint, bonding_curve_address
)
print("\nResults:")
print("-" * 50)
print(f"Token Mint: {mint}")
@@ -54,9 +53,10 @@ def main():
print(f"Associated Bonding Curve: {associated_bonding_curve}")
print(f"Bonding Curve Bump: {bump}")
print("-" * 50)
except ValueError as e:
print(f"Error: Invalid address format - {str(e)}")
if __name__ == "__main__":
main()
+78 -61
View File
@@ -1,59 +1,65 @@
import base64
import hashlib
import json
import struct
import hashlib
from solana.transaction import Transaction
from solders.transaction import VersionedTransaction
from solders.pubkey import Pubkey
import sys
from solana.transaction import Transaction
from solders.pubkey import Pubkey
from solders.transaction import VersionedTransaction
def load_idl(file_path):
with open(file_path, 'r') as f:
with open(file_path, "r") as f:
return json.load(f)
def load_transaction(file_path):
with open(file_path, 'r') as f:
with open(file_path, "r") as f:
data = json.load(f)
return data
def decode_instruction(ix_data, ix_def):
args = {}
offset = 8 # Skip 8-byte discriminator
for arg in ix_def['args']:
if arg['type'] == 'u64':
value = struct.unpack_from('<Q', ix_data, offset)[0]
for arg in ix_def["args"]:
if arg["type"] == "u64":
value = struct.unpack_from("<Q", ix_data, offset)[0]
offset += 8
elif arg['type'] == 'publicKey':
value = ix_data[offset:offset+32].hex()
elif arg["type"] == "publicKey":
value = ix_data[offset : offset + 32].hex()
offset += 32
elif arg['type'] == 'string':
length = struct.unpack_from('<I', ix_data, offset)[0]
elif arg["type"] == "string":
length = struct.unpack_from("<I", ix_data, offset)[0]
offset += 4
value = ix_data[offset:offset+length].decode('utf-8')
value = ix_data[offset : offset + length].decode("utf-8")
offset += length
else:
raise ValueError(f"Unsupported type: {arg['type']}")
args[arg['name']] = value
args[arg["name"]] = value
return args
def calculate_discriminator(instruction_name):
sha = hashlib.sha256()
sha.update(instruction_name.encode('utf-8'))
sha.update(instruction_name.encode("utf-8"))
discriminator_bytes = sha.digest()[:8]
discriminator = struct.unpack('<Q', discriminator_bytes)[0]
discriminator = struct.unpack("<Q", discriminator_bytes)[0]
return discriminator
def decode_transaction(tx_data, idl):
decoded_instructions = []
# Decode the base64 transaction data
tx_data_decoded = base64.b64decode(tx_data['transaction'][0])
tx_data_decoded = base64.b64decode(tx_data["transaction"][0])
# Check if it's a versioned transaction
if tx_data.get('version') == 0:
if tx_data.get("version") == 0:
# Use solders library for versioned transactions
transaction = VersionedTransaction.from_bytes(tx_data_decoded)
instructions = transaction.message.instructions
@@ -65,68 +71,79 @@ def decode_transaction(tx_data, idl):
instructions = transaction.instructions
account_keys = transaction.message.account_keys
print("Legacy transaction detected")
print(f"Number of instructions: {len(instructions)}")
for idx, ix in enumerate(instructions):
program_id = str(account_keys[ix.program_id_index])
print(f"\nInstruction {idx}:")
print(f"Program ID: {program_id}")
print(f"IDL program address: {idl['metadata']['address']}")
if program_id == idl['metadata']['address']:
if program_id == idl["metadata"]["address"]:
ix_data = bytes(ix.data)
discriminator = struct.unpack('<Q', ix_data[:8])[0]
discriminator = struct.unpack("<Q", ix_data[:8])[0]
print(f"Discriminator: {discriminator:016x}")
for idl_ix in idl['instructions']:
for idl_ix in idl["instructions"]:
idl_discriminator = calculate_discriminator(f"global:{idl_ix['name']}")
print(f"Checking against IDL instruction: {idl_ix['name']} with discriminator {idl_discriminator:016x}")
print(
f"Checking against IDL instruction: {idl_ix['name']} with discriminator {idl_discriminator:016x}"
)
if discriminator == idl_discriminator:
decoded_args = decode_instruction(ix_data, idl_ix)
accounts = [str(account_keys[acc_idx]) for acc_idx in ix.accounts]
decoded_instructions.append({
'name': idl_ix['name'],
'args': decoded_args,
'accounts': accounts,
'program': program_id
})
decoded_instructions.append(
{
"name": idl_ix["name"],
"args": decoded_args,
"accounts": accounts,
"program": program_id,
}
)
break
else:
decoded_instructions.append({
'name': 'Unknown',
'data': ix_data.hex(),
'accounts': [str(account_keys[acc_idx]) for acc_idx in ix.accounts],
'program': program_id
})
decoded_instructions.append(
{
"name": "Unknown",
"data": ix_data.hex(),
"accounts": [
str(account_keys[acc_idx]) for acc_idx in ix.accounts
],
"program": program_id,
}
)
else:
instruction_name = 'External'
if program_id == 'ComputeBudget111111111111111111111111111111':
if ix.data[:1] == b'\x03':
instruction_name = 'ComputeBudget: Set compute unit limit'
elif ix.data[:1] == b'\x02':
instruction_name = 'ComputeBudget: Set compute unit price'
elif program_id == 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL':
instruction_name = 'Associated Token Account: Create'
decoded_instructions.append({
'name': instruction_name,
'programId': program_id,
'data': bytes(ix.data).hex(),
'accounts': [str(account_keys[acc_idx]) for acc_idx in ix.accounts]
})
instruction_name = "External"
if program_id == "ComputeBudget111111111111111111111111111111":
if ix.data[:1] == b"\x03":
instruction_name = "ComputeBudget: Set compute unit limit"
elif ix.data[:1] == b"\x02":
instruction_name = "ComputeBudget: Set compute unit price"
elif program_id == "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL":
instruction_name = "Associated Token Account: Create"
decoded_instructions.append(
{
"name": instruction_name,
"programId": program_id,
"data": bytes(ix.data).hex(),
"accounts": [str(account_keys[acc_idx]) for acc_idx in ix.accounts],
}
)
return decoded_instructions
if len(sys.argv) != 2:
print("Usage: python decode_fromBlock.py <transaction_file_path>")
sys.exit(1)
tx_file_path = sys.argv[1]
idl = load_idl('../idl/pump_fun_idl.json')
idl = load_idl("../idl/pump_fun_idl.json")
tx_data = load_transaction(tx_file_path)
decoded_instructions = decode_transaction(tx_data, idl)
print(json.dumps(decoded_instructions, indent=2))
print(json.dumps(decoded_instructions, indent=2))
@@ -1,12 +1,14 @@
import json
import base64
import json
import struct
from construct import Struct, Int64ul, Flag
from construct import Flag, Int64ul, Struct
LAMPORTS_PER_SOL = 1_000_000_000
TOKEN_DECIMALS = 6
EXPECTED_DISCRIMINATOR = struct.pack("<Q", 6966180631402821399)
class BondingCurveState:
_STRUCT = Struct(
"virtual_token_reserves" / Int64ul,
@@ -14,18 +16,22 @@ class BondingCurveState:
"real_token_reserves" / Int64ul,
"real_sol_reserves" / Int64ul,
"token_total_supply" / Int64ul,
"complete" / Flag
"complete" / Flag,
)
def __init__(self, data: bytes) -> None:
parsed = self._STRUCT.parse(data[8:])
self.__dict__.update(parsed)
def calculate_bonding_curve_price(curve_state: BondingCurveState) -> float:
if curve_state.virtual_token_reserves <= 0 or curve_state.virtual_sol_reserves <= 0:
raise ValueError("Invalid reserve state")
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (curve_state.virtual_token_reserves / 10 ** TOKEN_DECIMALS)
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (
curve_state.virtual_token_reserves / 10**TOKEN_DECIMALS
)
def decode_bonding_curve_data(raw_data: str) -> BondingCurveState:
decoded_data = base64.b64decode(raw_data)
@@ -33,12 +39,13 @@ def decode_bonding_curve_data(raw_data: str) -> BondingCurveState:
raise ValueError("Invalid curve state discriminator")
return BondingCurveState(decoded_data)
# Load the JSON data
with open('raw_bondingCurve_from_getAccountInfo.json', 'r') as file:
with open("raw_bondingCurve_from_getAccountInfo.json", "r") as file:
json_data = json.load(file)
# Extract the base64 encoded data
encoded_data = json_data['result']['value']['data'][0]
encoded_data = json_data["result"]["value"]["data"][0]
# Decode the data
bonding_curve_state = decode_bonding_curve_data(encoded_data)
@@ -53,4 +60,4 @@ print(f" Real Token Reserves: {bonding_curve_state.real_token_reserves}")
print(f" Real SOL Reserves: {bonding_curve_state.real_sol_reserves}")
print(f" Token Total Supply: {bonding_curve_state.token_total_supply}")
print(f" Complete: {bonding_curve_state.complete}")
print(f"\nToken Price: {token_price_sol:.10f} SOL")
print(f"\nToken Price: {token_price_sol:.10f} SOL")
+33 -31
View File
@@ -1,12 +1,11 @@
import base64
import json
import struct
import sys
import base58
from solana.transaction import Transaction
from solders.pubkey import Pubkey
import struct
import sys
import base64
import sys
if len(sys.argv) != 2:
print("Usage: python decode_getTransaction.py <transaction_file_path>")
@@ -15,18 +14,19 @@ if len(sys.argv) != 2:
tx_file_path = sys.argv[1]
# Load the IDL
with open('../idl/pump_fun_idl.json', 'r') as f:
with open("../idl/pump_fun_idl.json", "r") as f:
idl = json.load(f)
# Load the transaction log
with open(tx_file_path, 'r') as f:
with open(tx_file_path, "r") as f:
tx_log = json.load(f)
# Extract the transaction data
tx_data = tx_log['result']['transaction']
tx_data = tx_log["result"]["transaction"]
print(json.dumps(tx_data, indent=2))
def decode_create_instruction(data):
# The Create instruction has 3 string arguments: name, symbol, uri
offset = 8 # Skip the 8-byte discriminator
@@ -34,59 +34,61 @@ def decode_create_instruction(data):
for _ in range(3):
length = struct.unpack_from("<I", data, offset)[0]
offset += 4
string_data = data[offset:offset+length].decode('utf-8')
string_data = data[offset : offset + length].decode("utf-8")
results.append(string_data)
offset += length
return {
"name": results[0],
"symbol": results[1],
"uri": results[2]
}
return {"name": results[0], "symbol": results[1], "uri": results[2]}
def decode_buy_instruction(data):
# Assuming the buy instruction has a u64 argument for amount
amount = struct.unpack_from("<Q", data, 8)[0]
return {"amount": amount}
def decode_instruction_data(instruction, accounts, data):
if instruction['name'] == 'create':
if instruction["name"] == "create":
return decode_create_instruction(data)
elif instruction['name'] == 'buy':
elif instruction["name"] == "buy":
return decode_buy_instruction(data)
else:
return f"Unhandled instruction type: {instruction['name']}"
def find_matching_instruction(accounts, data):
if 'instructions' not in idl:
if "instructions" not in idl:
print("Warning: No instructions found in IDL")
return None
for instruction in idl['instructions']:
if len(instruction['accounts']) == len(accounts):
for instruction in idl["instructions"]:
if len(instruction["accounts"]) == len(accounts):
return instruction
return None
# Parse the transaction
tx_message = tx_data['message']
instructions = tx_message['instructions']
tx_message = tx_data["message"]
instructions = tx_message["instructions"]
for ix in instructions:
program_id = ix.get('programId')
accounts = ix.get('accounts', [])
data = ix.get('data', '')
if 'parsed' in ix:
program_id = ix.get("programId")
accounts = ix.get("accounts", [])
data = ix.get("data", "")
if "parsed" in ix:
print(f"Parsed instruction: {ix['program']} - {ix['parsed']['type']}")
print(f"Info: {json.dumps(ix['parsed']['info'], indent=2)}")
elif program_id == idl['metadata']['address']:
elif program_id == idl["metadata"]["address"]:
matching_instruction = find_matching_instruction(accounts, data)
if matching_instruction:
decoded_data = decode_instruction_data(matching_instruction, accounts, base58.b58decode(data))
decoded_data = decode_instruction_data(
matching_instruction, accounts, base58.b58decode(data)
)
print(f"Instruction: {matching_instruction['name']}")
print(f"Decoded data: {decoded_data}")
print("\nAccounts:")
for i, account in enumerate(accounts):
account_info = matching_instruction['accounts'][i]
account_info = matching_instruction["accounts"][i]
print(f" {account_info['name']}: {account}")
else:
print(f"Unable to match instruction for program {program_id}")
@@ -97,4 +99,4 @@ for ix in instructions:
print("\nTransaction Information:")
print(f"Blockhash: {tx_message['recentBlockhash']}")
print(f"Fee payer: {tx_message['accountKeys'][0]['pubkey']}")
print(f"Signature: {tx_data['signatures'][0]}")
print(f"Signature: {tx_data['signatures'][0]}")
+16 -7
View File
@@ -1,14 +1,14 @@
import asyncio
import os
import struct
import sys
import os
from typing import Final
from construct import Struct, Int64ul, Flag
from construct import Flag, Int64ul, Struct
from solana.rpc.async_api import AsyncClient
from solders.pubkey import Pubkey
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from config import RPC_ENDPOINT
LAMPORTS_PER_SOL: Final[int] = 1_000_000_000
@@ -18,6 +18,7 @@ CURVE_ADDRESS: Final[str] = "6GXfUqrmPM4VdN1NoDZsE155jzRegJngZRjMkGyby7do"
# Here and later all the discriminators are precalculated. See learning-examples/discriminator.py
EXPECTED_DISCRIMINATOR: Final[bytes] = struct.pack("<Q", 6966180631402821399)
class BondingCurveState:
_STRUCT = Struct(
"virtual_token_reserves" / Int64ul,
@@ -25,14 +26,17 @@ class BondingCurveState:
"real_token_reserves" / Int64ul,
"real_sol_reserves" / Int64ul,
"token_total_supply" / Int64ul,
"complete" / Flag
"complete" / Flag,
)
def __init__(self, data: bytes) -> None:
parsed = self._STRUCT.parse(data[8:])
self.__dict__.update(parsed)
async def get_bonding_curve_state(conn: AsyncClient, curve_address: Pubkey) -> BondingCurveState:
async def get_bonding_curve_state(
conn: AsyncClient, curve_address: Pubkey
) -> BondingCurveState:
response = await conn.get_account_info(curve_address)
if not response.value or not response.value.data:
raise ValueError("Invalid curve state: No data")
@@ -43,11 +47,15 @@ async def get_bonding_curve_state(conn: AsyncClient, curve_address: Pubkey) -> B
return BondingCurveState(data)
def calculate_bonding_curve_price(curve_state: BondingCurveState) -> float:
if curve_state.virtual_token_reserves <= 0 or curve_state.virtual_sol_reserves <= 0:
raise ValueError("Invalid reserve state")
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (curve_state.virtual_token_reserves / 10 ** TOKEN_DECIMALS)
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (
curve_state.virtual_token_reserves / 10**TOKEN_DECIMALS
)
async def main() -> None:
try:
@@ -63,5 +71,6 @@ async def main() -> None:
except Exception as e:
print(f"An unexpected error occurred: {e}")
if __name__ == "__main__":
asyncio.run(main())
asyncio.run(main())
@@ -1,68 +1,74 @@
import asyncio
import json
import websockets
import base64
import struct
import hashlib
import sys
import json
import os
from solders.transaction import VersionedTransaction
from solders.pubkey import Pubkey
import struct
import sys
import websockets
from solders.pubkey import Pubkey
from solders.transaction import VersionedTransaction
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from config import PUMP_PROGRAM, WSS_ENDPOINT
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from config import WSS_ENDPOINT, PUMP_PROGRAM
def load_idl(file_path):
with open(file_path, 'r') as f:
with open(file_path, "r") as f:
return json.load(f)
def decode_create_instruction(ix_data, ix_def, accounts):
args = {}
offset = 8 # Skip 8-byte discriminator
for arg in ix_def['args']:
if arg['type'] == 'string':
length = struct.unpack_from('<I', ix_data, offset)[0]
for arg in ix_def["args"]:
if arg["type"] == "string":
length = struct.unpack_from("<I", ix_data, offset)[0]
offset += 4
value = ix_data[offset:offset+length].decode('utf-8')
value = ix_data[offset : offset + length].decode("utf-8")
offset += length
elif arg['type'] == 'publicKey':
value = base64.b64encode(ix_data[offset:offset+32]).decode('utf-8')
elif arg["type"] == "publicKey":
value = base64.b64encode(ix_data[offset : offset + 32]).decode("utf-8")
offset += 32
else:
raise ValueError(f"Unsupported type: {arg['type']}")
args[arg['name']] = value
args[arg["name"]] = value
# Add accounts
args['mint'] = str(accounts[0])
args['bondingCurve'] = str(accounts[2])
args['associatedBondingCurve'] = str(accounts[3])
args['user'] = str(accounts[7])
args["mint"] = str(accounts[0])
args["bondingCurve"] = str(accounts[2])
args["associatedBondingCurve"] = str(accounts[3])
args["user"] = str(accounts[7])
return args
# Here and later all the discriminators are precalculated. See learning-examples/discriminator.py
async def listen_and_decode_create():
idl = load_idl('../idl/pump_fun_idl.json')
idl = load_idl("../idl/pump_fun_idl.json")
create_discriminator = 8576854823835016728
async with websockets.connect(WSS_ENDPOINT) as websocket:
subscription_message = json.dumps({
"jsonrpc": "2.0",
"id": 1,
"method": "blockSubscribe",
"params": [
{"mentionsAccountOrProgram": str(PUMP_PROGRAM)},
{
"commitment": "confirmed",
"encoding": "base64",
"showRewards": False,
"transactionDetails": "full",
"maxSupportedTransactionVersion": 0
}
]
})
subscription_message = json.dumps(
{
"jsonrpc": "2.0",
"id": 1,
"method": "blockSubscribe",
"params": [
{"mentionsAccountOrProgram": str(PUMP_PROGRAM)},
{
"commitment": "confirmed",
"encoding": "base64",
"showRewards": False,
"transactionDetails": "full",
"maxSupportedTransactionVersion": 0,
},
],
}
)
await websocket.send(subscription_message)
print(f"Subscribed to blocks mentioning program: {PUMP_PROGRAM}")
@@ -70,40 +76,78 @@ async def listen_and_decode_create():
try:
response = await websocket.recv()
data = json.loads(response)
if 'method' in data and data['method'] == 'blockNotification':
if 'params' in data and 'result' in data['params']:
block_data = data['params']['result']
if 'value' in block_data and 'block' in block_data['value']:
block = block_data['value']['block']
if 'transactions' in block:
for tx in block['transactions']:
if isinstance(tx, dict) and 'transaction' in tx:
tx_data_decoded = base64.b64decode(tx['transaction'][0])
transaction = VersionedTransaction.from_bytes(tx_data_decoded)
if "method" in data and data["method"] == "blockNotification":
if "params" in data and "result" in data["params"]:
block_data = data["params"]["result"]
if "value" in block_data and "block" in block_data["value"]:
block = block_data["value"]["block"]
if "transactions" in block:
for tx in block["transactions"]:
if isinstance(tx, dict) and "transaction" in tx:
tx_data_decoded = base64.b64decode(
tx["transaction"][0]
)
transaction = VersionedTransaction.from_bytes(
tx_data_decoded
)
for ix in transaction.message.instructions:
if str(transaction.message.account_keys[ix.program_id_index]) == str(PUMP_PROGRAM):
if str(
transaction.message.account_keys[
ix.program_id_index
]
) == str(PUMP_PROGRAM):
ix_data = bytes(ix.data)
discriminator = struct.unpack('<Q', ix_data[:8])[0]
if discriminator == create_discriminator:
create_ix = next(instr for instr in idl['instructions'] if instr['name'] == 'create')
account_keys = [str(transaction.message.account_keys[index]) for index in ix.accounts]
decoded_args = decode_create_instruction(ix_data, create_ix, account_keys)
print(json.dumps(decoded_args, indent=2))
discriminator = struct.unpack(
"<Q", ix_data[:8]
)[0]
if (
discriminator
== create_discriminator
):
create_ix = next(
instr
for instr in idl["instructions"]
if instr["name"] == "create"
)
account_keys = [
str(
transaction.message.account_keys[
index
]
)
for index in ix.accounts
]
decoded_args = (
decode_create_instruction(
ix_data,
create_ix,
account_keys,
)
)
print(
json.dumps(
decoded_args, indent=2
)
)
print("--------------------")
elif 'result' in data:
elif "result" in data:
print(f"Subscription confirmed")
else:
print(f"Received unexpected message type: {data.get('method', 'Unknown')}")
print(
f"Received unexpected message type: {data.get('method', 'Unknown')}"
)
except Exception as e:
print(f"An error occurred: {str(e)}")
print(f"Error details: {type(e).__name__}")
import traceback
traceback.print_exc()
print("WebSocket connection closed.")
if __name__ == "__main__":
asyncio.run(listen_and_decode_create())
asyncio.run(listen_and_decode_create())
+64 -43
View File
@@ -1,21 +1,25 @@
import asyncio
import json
import websockets
import base58
import base64
import json
import os
import struct
import sys
import os
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from config import WSS_ENDPOINT, PUMP_PROGRAM
import base58
import websockets
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from config import PUMP_PROGRAM, WSS_ENDPOINT
# Load the IDL JSON file
with open('../idl/pump_fun_idl.json', 'r') as f:
with open("../idl/pump_fun_idl.json", "r") as f:
idl = json.load(f)
# Extract the "create" instruction definition
create_instruction = next(instr for instr in idl['instructions'] if instr['name'] == 'create')
create_instruction = next(
instr for instr in idl["instructions"] if instr["name"] == "create"
)
def parse_create_instruction(data):
if len(data) < 8:
@@ -25,23 +29,23 @@ def parse_create_instruction(data):
# Parse fields based on CreateEvent structure
fields = [
('name', 'string'),
('symbol', 'string'),
('uri', 'string'),
('mint', 'publicKey'),
('bondingCurve', 'publicKey'),
('user', 'publicKey'),
("name", "string"),
("symbol", "string"),
("uri", "string"),
("mint", "publicKey"),
("bondingCurve", "publicKey"),
("user", "publicKey"),
]
try:
for field_name, field_type in fields:
if field_type == 'string':
length = struct.unpack('<I', data[offset:offset+4])[0]
if field_type == "string":
length = struct.unpack("<I", data[offset : offset + 4])[0]
offset += 4
value = data[offset:offset+length].decode('utf-8')
value = data[offset : offset + length].decode("utf-8")
offset += length
elif field_type == 'publicKey':
value = base58.b58encode(data[offset:offset+32]).decode('utf-8')
elif field_type == "publicKey":
value = base58.b58encode(data[offset : offset + 32]).decode("utf-8")
offset += 32
parsed_data[field_name] = value
@@ -50,30 +54,34 @@ def parse_create_instruction(data):
except:
return None
def print_transaction_details(log_data):
print(f"Signature: {log_data.get('signature')}")
for log in log_data.get('logs', []):
for log in log_data.get("logs", []):
if log.startswith("Program data:"):
try:
data = base58.b58decode(log.split(": ")[1]).decode('utf-8')
data = base58.b58decode(log.split(": ")[1]).decode("utf-8")
print(f"Data: {data}")
except:
pass
async def listen_for_new_tokens():
while True:
try:
async with websockets.connect(WSS_ENDPOINT) as websocket:
subscription_message = json.dumps({
"jsonrpc": "2.0",
"id": 1,
"method": "logsSubscribe",
"params": [
{"mentions": [str(PUMP_PROGRAM)]},
{"commitment": "processed"}
]
})
subscription_message = json.dumps(
{
"jsonrpc": "2.0",
"id": 1,
"method": "logsSubscribe",
"params": [
{"mentions": [str(PUMP_PROGRAM)]},
{"commitment": "processed"},
],
}
)
await websocket.send(subscription_message)
print(f"Listening for new token creations from program: {PUMP_PROGRAM}")
@@ -86,26 +94,38 @@ async def listen_for_new_tokens():
response = await websocket.recv()
data = json.loads(response)
if 'method' in data and data['method'] == 'logsNotification':
log_data = data['params']['result']['value']
logs = log_data.get('logs', [])
if any("Program log: Instruction: Create" in log for log in logs):
if "method" in data and data["method"] == "logsNotification":
log_data = data["params"]["result"]["value"]
logs = log_data.get("logs", [])
if any(
"Program log: Instruction: Create" in log
for log in logs
):
for log in logs:
if "Program data:" in log:
try:
encoded_data = log.split(": ")[1]
decoded_data = base64.b64decode(encoded_data)
parsed_data = parse_create_instruction(decoded_data)
if parsed_data and 'name' in parsed_data:
print("Signature:", log_data.get('signature'))
decoded_data = base64.b64decode(
encoded_data
)
parsed_data = parse_create_instruction(
decoded_data
)
if parsed_data and "name" in parsed_data:
print(
"Signature:",
log_data.get("signature"),
)
for key, value in parsed_data.items():
print(f"{key}: {value}")
print("##########################################################################################")
print(
"##########################################################################################"
)
except Exception as e:
print(f"Failed to decode: {log}")
print(f"Error: {str(e)}")
except Exception as e:
print(f"An error occurred while processing message: {e}")
break
@@ -115,5 +135,6 @@ async def listen_for_new_tokens():
print("Reconnecting in 5 seconds...")
await asyncio.sleep(5)
if __name__ == "__main__":
asyncio.run(listen_for_new_tokens())
asyncio.run(listen_for_new_tokens())
@@ -1,20 +1,24 @@
import asyncio
import json
import websockets
import base58
import base64
import json
import os
import struct
import sys
import os
import base58
import websockets
from solders.pubkey import Pubkey
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from config import (
WSS_ENDPOINT,
PUMP_PROGRAM,
SYSTEM_TOKEN_PROGRAM as TOKEN_PROGRAM_ID,
SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM as ATA_PROGRAM_ID
)
from config import SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM as ATA_PROGRAM_ID
from config import SYSTEM_TOKEN_PROGRAM as TOKEN_PROGRAM_ID
from config import (
WSS_ENDPOINT,
)
def find_associated_bonding_curve(mint: Pubkey, bonding_curve: Pubkey) -> Pubkey:
"""
@@ -27,16 +31,20 @@ def find_associated_bonding_curve(mint: Pubkey, bonding_curve: Pubkey) -> Pubkey
bytes(TOKEN_PROGRAM_ID),
bytes(mint),
],
ATA_PROGRAM_ID
ATA_PROGRAM_ID,
)
return derived_address
# Load the IDL JSON file
with open('../idl/pump_fun_idl.json', 'r') as f:
with open("../idl/pump_fun_idl.json", "r") as f:
idl = json.load(f)
# Extract the "create" instruction definition
create_instruction = next(instr for instr in idl['instructions'] if instr['name'] == 'create')
create_instruction = next(
instr for instr in idl["instructions"] if instr["name"] == "create"
)
def parse_create_instruction(data):
if len(data) < 8:
@@ -46,23 +54,23 @@ def parse_create_instruction(data):
# Parse fields based on CreateEvent structure
fields = [
('name', 'string'),
('symbol', 'string'),
('uri', 'string'),
('mint', 'publicKey'),
('bondingCurve', 'publicKey'),
('user', 'publicKey'),
("name", "string"),
("symbol", "string"),
("uri", "string"),
("mint", "publicKey"),
("bondingCurve", "publicKey"),
("user", "publicKey"),
]
try:
for field_name, field_type in fields:
if field_type == 'string':
length = struct.unpack('<I', data[offset:offset+4])[0]
if field_type == "string":
length = struct.unpack("<I", data[offset : offset + 4])[0]
offset += 4
value = data[offset:offset+length].decode('utf-8')
value = data[offset : offset + length].decode("utf-8")
offset += length
elif field_type == 'publicKey':
value = base58.b58encode(data[offset:offset+32]).decode('utf-8')
elif field_type == "publicKey":
value = base58.b58encode(data[offset : offset + 32]).decode("utf-8")
offset += 32
parsed_data[field_name] = value
@@ -71,30 +79,34 @@ def parse_create_instruction(data):
except:
return None
def print_transaction_details(log_data):
print(f"Signature: {log_data.get('signature')}")
for log in log_data.get('logs', []):
for log in log_data.get("logs", []):
if log.startswith("Program data:"):
try:
data = base58.b58decode(log.split(": ")[1]).decode('utf-8')
data = base58.b58decode(log.split(": ")[1]).decode("utf-8")
print(f"Data: {data}")
except:
pass
async def listen_for_new_tokens():
while True:
try:
async with websockets.connect(WSS_ENDPOINT) as websocket:
subscription_message = json.dumps({
"jsonrpc": "2.0",
"id": 1,
"method": "logsSubscribe",
"params": [
{"mentions": [str(PUMP_PROGRAM)]},
{"commitment": "processed"}
]
})
subscription_message = json.dumps(
{
"jsonrpc": "2.0",
"id": 1,
"method": "logsSubscribe",
"params": [
{"mentions": [str(PUMP_PROGRAM)]},
{"commitment": "processed"},
],
}
)
await websocket.send(subscription_message)
print(f"Listening for new token creations from program: {PUMP_PROGRAM}")
@@ -107,28 +119,50 @@ async def listen_for_new_tokens():
response = await websocket.recv()
data = json.loads(response)
if 'method' in data and data['method'] == 'logsNotification':
log_data = data['params']['result']['value']
logs = log_data.get('logs', [])
if any("Program log: Instruction: Create" in log for log in logs):
if "method" in data and data["method"] == "logsNotification":
log_data = data["params"]["result"]["value"]
logs = log_data.get("logs", [])
if any(
"Program log: Instruction: Create" in log
for log in logs
):
for log in logs:
if "Program data:" in log:
try:
encoded_data = log.split(": ")[1]
decoded_data = base64.b64decode(encoded_data)
parsed_data = parse_create_instruction(decoded_data)
if parsed_data and 'name' in parsed_data:
print("Signature:", log_data.get('signature'))
decoded_data = base64.b64decode(
encoded_data
)
parsed_data = parse_create_instruction(
decoded_data
)
if parsed_data and "name" in parsed_data:
print(
"Signature:",
log_data.get("signature"),
)
for key, value in parsed_data.items():
print(f"{key}: {value}")
# Calculate associated bonding curve
mint = Pubkey.from_string(parsed_data['mint'])
bonding_curve = Pubkey.from_string(parsed_data['bondingCurve'])
associated_curve = find_associated_bonding_curve(mint, bonding_curve)
print(f"Associated Bonding Curve: {associated_curve}")
print("##########################################################################################")
mint = Pubkey.from_string(
parsed_data["mint"]
)
bonding_curve = Pubkey.from_string(
parsed_data["bondingCurve"]
)
associated_curve = (
find_associated_bonding_curve(
mint, bonding_curve
)
)
print(
f"Associated Bonding Curve: {associated_curve}"
)
print(
"##########################################################################################"
)
except Exception as e:
print(f"Failed to decode: {log}")
print(f"Error: {str(e)}")
@@ -142,5 +176,6 @@ async def listen_for_new_tokens():
print("Reconnecting in 5 seconds...")
await asyncio.sleep(5)
if __name__ == "__main__":
asyncio.run(listen_for_new_tokens())
asyncio.run(listen_for_new_tokens())
+25 -14
View File
@@ -1,24 +1,25 @@
import websockets
import asyncio
import json
from datetime import datetime
import websockets
# PumpPortal WebSocket URL
WS_URL = "wss://pumpportal.fun/api/data"
def format_sol(value):
return f"{value:.6f} SOL"
def format_timestamp(timestamp):
return datetime.fromtimestamp(timestamp / 1000).strftime('%Y-%m-%d %H:%M:%S')
return datetime.fromtimestamp(timestamp / 1000).strftime("%Y-%m-%d %H:%M:%S")
async def listen_for_new_tokens():
async with websockets.connect(WS_URL) as websocket:
# Subscribe to new token events
await websocket.send(json.dumps({
"method": "subscribeNewToken",
"params": []
}))
await websocket.send(json.dumps({"method": "subscribeNewToken", "params": []}))
print("Listening for new token creations...")
@@ -27,23 +28,31 @@ async def listen_for_new_tokens():
message = await websocket.recv()
data = json.loads(message)
if 'method' in data and data['method'] == 'newToken':
token_info = data.get('params', [{}])[0]
elif 'signature' in data and 'mint' in data:
if "method" in data and data["method"] == "newToken":
token_info = data.get("params", [{}])[0]
elif "signature" in data and "mint" in data:
token_info = data
else:
continue
print("\n" + "=" * 50)
print(f"New token created: {token_info.get('name')} ({token_info.get('symbol')})")
print(
f"New token created: {token_info.get('name')} ({token_info.get('symbol')})"
)
print("=" * 50)
print(f"Address: {token_info.get('mint')}")
print(f"Creator: {token_info.get('traderPublicKey')}")
print(f"Initial Buy: {format_sol(token_info.get('initialBuy', 0))}")
print(f"Market Cap: {format_sol(token_info.get('marketCapSol', 0))}")
print(
f"Market Cap: {format_sol(token_info.get('marketCapSol', 0))}"
)
print(f"Bonding Curve: {token_info.get('bondingCurveKey')}")
print(f"Virtual SOL: {format_sol(token_info.get('vSolInBondingCurve', 0))}")
print(f"Virtual Tokens: {token_info.get('vTokensInBondingCurve', 0):,.0f}")
print(
f"Virtual SOL: {format_sol(token_info.get('vSolInBondingCurve', 0))}"
)
print(
f"Virtual Tokens: {token_info.get('vTokensInBondingCurve', 0):,.0f}"
)
print(f"Metadata URI: {token_info.get('uri')}")
print(f"Signature: {token_info.get('signature')}")
print("=" * 50)
@@ -55,6 +64,7 @@ async def listen_for_new_tokens():
except Exception as e:
print(f"\nAn error occurred: {e}")
async def main():
while True:
try:
@@ -64,5 +74,6 @@ async def main():
print("Reconnecting in 5 seconds...")
await asyncio.sleep(5)
if __name__ == "__main__":
asyncio.run(main())
asyncio.run(main())
@@ -1,57 +1,62 @@
import websockets
import asyncio
import json
import base64
from solders.pubkey import Pubkey
import sys
import json
import os
import sys
import websockets
from solders.pubkey import Pubkey
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from config import PUMP_LIQUIDITY_MIGRATOR, WSS_ENDPOINT
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from config import WSS_ENDPOINT, PUMP_LIQUIDITY_MIGRATOR
def process_initialize2_transaction(data):
"""Process and decode an initialize2 transaction"""
try:
signature = data['transaction']['signatures'][0]
account_keys = data['transaction']['message']['accountKeys']
signature = data["transaction"]["signatures"][0]
account_keys = data["transaction"]["message"]["accountKeys"]
# Check raydium_amm_idl.json for the account keys
# The token address is typically the 19th account (index 18)
# The liquidity pool address is typically the 3rd account (index 2)
if len(account_keys) > 18:
token_address = account_keys[18]
liquidity_address = account_keys[2]
print(f"\nSignature: {signature}")
print(f"Token Address: {token_address}")
print(f"Liquidity Address: {liquidity_address}")
print("=" * 50)
else:
print(f"\nError: Not enough account keys (found {len(account_keys)})")
except Exception as e:
print(f"\nError: {str(e)}")
async def listen_for_events():
while True:
try:
async with websockets.connect(WSS_ENDPOINT) as websocket:
subscription_message = json.dumps({
"jsonrpc": "2.0",
"id": 1,
"method": "blockSubscribe",
"params": [
{"mentionsAccountOrProgram": str(PUMP_LIQUIDITY_MIGRATOR)},
{
"commitment": "confirmed",
"encoding": "json",
"showRewards": False,
"transactionDetails": "full",
"maxSupportedTransactionVersion": 0
}
]
})
subscription_message = json.dumps(
{
"jsonrpc": "2.0",
"id": 1,
"method": "blockSubscribe",
"params": [
{"mentionsAccountOrProgram": str(PUMP_LIQUIDITY_MIGRATOR)},
{
"commitment": "confirmed",
"encoding": "json",
"showRewards": False,
"transactionDetails": "full",
"maxSupportedTransactionVersion": 0,
},
],
}
)
await websocket.send(subscription_message)
response = await websocket.recv()
print(f"Subscription response: {response}")
@@ -61,32 +66,43 @@ async def listen_for_events():
try:
response = await asyncio.wait_for(websocket.recv(), timeout=30)
data = json.loads(response)
if 'method' in data and data['method'] == 'blockNotification':
if 'params' in data and 'result' in data['params']:
block_data = data['params']['result']
if 'value' in block_data and 'block' in block_data['value']:
block = block_data['value']['block']
if 'transactions' in block:
for tx in block['transactions']:
logs = tx.get('meta', {}).get('logMessages', [])
if "method" in data and data["method"] == "blockNotification":
if "params" in data and "result" in data["params"]:
block_data = data["params"]["result"]
if (
"value" in block_data
and "block" in block_data["value"]
):
block = block_data["value"]["block"]
if "transactions" in block:
for tx in block["transactions"]:
logs = tx.get("meta", {}).get(
"logMessages", []
)
# Check for initialize2 instruction
for log in logs:
if "Program log: initialize2: InitializeInstruction2" in log:
print("Found initialize2 instruction!")
if (
"Program log: initialize2: InitializeInstruction2"
in log
):
print(
"Found initialize2 instruction!"
)
process_initialize2_transaction(tx)
break
except asyncio.TimeoutError:
print("\nChecking connection...")
print("Connection alive")
continue
except Exception as e:
print(f"\nConnection error: {str(e)}")
print("Retrying in 5 seconds...")
await asyncio.sleep(5)
if __name__ == "__main__":
asyncio.run(listen_for_events())
asyncio.run(listen_for_events())
+202 -105
View File
@@ -1,29 +1,26 @@
import asyncio
import json
import base64
import struct
import base58
import hashlib
import json
import os
import struct
from solana.transaction import Message
import base58
import spl.token.instructions as spl_token
import websockets
from construct import Flag, Int64ul, Struct
from solana.rpc.async_api import AsyncClient
from solana.rpc.commitment import Confirmed
from solana.rpc.types import TxOpts
from solders.pubkey import Pubkey
from solders.keypair import Keypair
from solders.instruction import Instruction, AccountMeta
from solders.system_program import TransferParams, transfer
from solders.transaction import VersionedTransaction, Transaction
from solana.transaction import Message
from solders.compute_budget import set_compute_unit_price
import spl.token.instructions as spl_token
from solders.instruction import AccountMeta, Instruction
from solders.keypair import Keypair
from solders.pubkey import Pubkey
from solders.system_program import TransferParams, transfer
from solders.transaction import Transaction, VersionedTransaction
from spl.token.instructions import get_associated_token_address
import websockets
import hashlib
from construct import Struct, Int64ul, Flag
# Here and later all the discriminators are precalculated. See learning-examples/discriminator.py
EXPECTED_DISCRIMINATOR = struct.pack("<Q", 6966180631402821399)
TOKEN_DECIMALS = 6
@@ -31,11 +28,15 @@ TOKEN_DECIMALS = 6
# Global constants
PUMP_PROGRAM = Pubkey.from_string("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P")
PUMP_GLOBAL = Pubkey.from_string("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf")
PUMP_EVENT_AUTHORITY = Pubkey.from_string("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1")
PUMP_EVENT_AUTHORITY = Pubkey.from_string(
"Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1"
)
PUMP_FEE = Pubkey.from_string("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM")
SYSTEM_PROGRAM = Pubkey.from_string("11111111111111111111111111111111")
SYSTEM_TOKEN_PROGRAM = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM = Pubkey.from_string("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")
SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM = Pubkey.from_string(
"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
)
SYSTEM_RENT = Pubkey.from_string("SysvarRent111111111111111111111111111111111")
SOL = Pubkey.from_string("So11111111111111111111111111111111111111112")
LAMPORTS_PER_SOL = 1_000_000_000
@@ -44,6 +45,7 @@ LAMPORTS_PER_SOL = 1_000_000_000
RPC_ENDPOINT = "ENTER_YOUR_CHAINSTACK_HTTP_ENDPOINT"
RPC_WEBSOCKET = "ENTER_YOUR_CHAINSTACK_WS_ENDPOINT"
class BondingCurveState:
_STRUCT = Struct(
"virtual_token_reserves" / Int64ul,
@@ -51,14 +53,17 @@ class BondingCurveState:
"real_token_reserves" / Int64ul,
"real_sol_reserves" / Int64ul,
"token_total_supply" / Int64ul,
"complete" / Flag
"complete" / Flag,
)
def __init__(self, data: bytes) -> None:
parsed = self._STRUCT.parse(data[8:])
self.__dict__.update(parsed)
async def get_pump_curve_state(conn: AsyncClient, curve_address: Pubkey) -> BondingCurveState:
async def get_pump_curve_state(
conn: AsyncClient, curve_address: Pubkey
) -> BondingCurveState:
response = await conn.get_account_info(curve_address)
if not response.value or not response.value.data:
raise ValueError("Invalid curve state: No data")
@@ -69,13 +74,24 @@ async def get_pump_curve_state(conn: AsyncClient, curve_address: Pubkey) -> Bond
return BondingCurveState(data)
def calculate_pump_curve_price(curve_state: BondingCurveState) -> float:
if curve_state.virtual_token_reserves <= 0 or curve_state.virtual_sol_reserves <= 0:
raise ValueError("Invalid reserve state")
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (curve_state.virtual_token_reserves / 10 ** TOKEN_DECIMALS)
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (
curve_state.virtual_token_reserves / 10**TOKEN_DECIMALS
)
async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curve: Pubkey, amount: float, slippage: float = 0.25, max_retries=5):
async def buy_token(
mint: Pubkey,
bonding_curve: Pubkey,
associated_bonding_curve: Pubkey,
amount: float,
slippage: float = 0.25,
max_retries=5,
):
private_key = base58.b58decode("ENTER_PRIVATE_KEY")
payer = Keypair.from_bytes(private_key)
@@ -96,36 +112,52 @@ async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curv
try:
account_info = await client.get_account_info(associated_token_account)
if account_info.value is None:
print(f"Creating associated token account (Attempt {ata_attempt + 1})...")
print(
f"Creating associated token account (Attempt {ata_attempt + 1})..."
)
create_ata_ix = spl_token.create_associated_token_account(
payer=payer.pubkey(),
owner=payer.pubkey(),
mint=mint
payer=payer.pubkey(), owner=payer.pubkey(), mint=mint
)
msg = Message([create_ata_ix], payer.pubkey())
tx_ata = await client.send_transaction(
Transaction([payer], msg, (await client.get_latest_blockhash()).value.blockhash),
opts=TxOpts(skip_preflight=True, preflight_commitment=Confirmed)
)
await client.confirm_transaction(tx_ata.value, commitment="confirmed")
Transaction(
[payer],
msg,
(await client.get_latest_blockhash()).value.blockhash,
),
opts=TxOpts(
skip_preflight=True, preflight_commitment=Confirmed
),
)
await client.confirm_transaction(
tx_ata.value, commitment="confirmed"
)
print("Associated token account created.")
print(f"Associated token account address: {associated_token_account}")
print(
f"Associated token account address: {associated_token_account}"
)
break
else:
print("Associated token account already exists.")
print(f"Associated token account address: {associated_token_account}")
print(
f"Associated token account address: {associated_token_account}"
)
break
except Exception as e:
print(f"Attempt {ata_attempt + 1} to create associated token account failed: {str(e)}")
print(
f"Attempt {ata_attempt + 1} to create associated token account failed: {str(e)}"
)
if ata_attempt < max_retries - 1:
wait_time = 2 ** ata_attempt
wait_time = 2**ata_attempt
print(f"Retrying in {wait_time} seconds...")
await asyncio.sleep(wait_time)
else:
print("Max retries reached. Unable to create associated token account.")
print(
"Max retries reached. Unable to create associated token account."
)
return
# Continue with the buy transaction
@@ -135,28 +167,58 @@ async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curv
AccountMeta(pubkey=PUMP_GLOBAL, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_FEE, is_signer=False, is_writable=True),
AccountMeta(pubkey=mint, is_signer=False, is_writable=False),
AccountMeta(pubkey=bonding_curve, is_signer=False, is_writable=True),
AccountMeta(pubkey=associated_bonding_curve, is_signer=False, is_writable=True),
AccountMeta(pubkey=associated_token_account, is_signer=False, is_writable=True),
AccountMeta(pubkey=payer.pubkey(), is_signer=True, is_writable=True),
AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(pubkey=SYSTEM_TOKEN_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(
pubkey=bonding_curve, is_signer=False, is_writable=True
),
AccountMeta(
pubkey=associated_bonding_curve,
is_signer=False,
is_writable=True,
),
AccountMeta(
pubkey=associated_token_account,
is_signer=False,
is_writable=True,
),
AccountMeta(
pubkey=payer.pubkey(), is_signer=True, is_writable=True
),
AccountMeta(
pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False
),
AccountMeta(
pubkey=SYSTEM_TOKEN_PROGRAM, is_signer=False, is_writable=False
),
AccountMeta(pubkey=SYSTEM_RENT, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(
pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False
),
AccountMeta(
pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False
),
]
discriminator = struct.pack("<Q", 16927863322537952870)
data = discriminator + struct.pack("<Q", int(token_amount * 10**6)) + struct.pack("<Q", max_amount_lamports)
data = (
discriminator
+ struct.pack("<Q", int(token_amount * 10**6))
+ struct.pack("<Q", max_amount_lamports)
)
buy_ix = Instruction(PUMP_PROGRAM, data, accounts)
msg = Message([set_compute_unit_price(1_000), buy_ix], payer.pubkey())
tx_buy = await client.send_transaction(
Transaction([payer], msg, (await client.get_latest_blockhash()).value.blockhash),
opts=TxOpts(skip_preflight=True, preflight_commitment=Confirmed)
)
Transaction(
[payer],
msg,
(await client.get_latest_blockhash()).value.blockhash,
),
opts=TxOpts(skip_preflight=True, preflight_commitment=Confirmed),
)
print(f"Transaction sent: https://explorer.solana.com/tx/{tx_buy.value}")
print(
f"Transaction sent: https://explorer.solana.com/tx/{tx_buy.value}"
)
await client.confirm_transaction(tx_buy.value, commitment="confirmed")
print("Transaction confirmed")
@@ -165,97 +227,129 @@ async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curv
except Exception as e:
print(f"Attempt {attempt + 1} failed: {str(e)[:50]}")
if attempt < max_retries - 1:
wait_time = 2 ** attempt
wait_time = 2**attempt
print(f"Retrying in {wait_time} seconds...")
await asyncio.sleep(wait_time)
else:
print("Max retries reached. Unable to complete the transaction.")
def load_idl(file_path):
with open(file_path, 'r') as f:
with open(file_path, "r") as f:
return json.load(f)
def calculate_discriminator(instruction_name):
sha = hashlib.sha256()
sha.update(instruction_name.encode('utf-8'))
return struct.unpack('<Q', sha.digest()[:8])[0]
sha.update(instruction_name.encode("utf-8"))
return struct.unpack("<Q", sha.digest()[:8])[0]
def decode_create_instruction(ix_data, ix_def, accounts):
args = {}
offset = 8 # Skip 8-byte discriminator
for arg in ix_def['args']:
if arg['type'] == 'string':
length = struct.unpack_from('<I', ix_data, offset)[0]
for arg in ix_def["args"]:
if arg["type"] == "string":
length = struct.unpack_from("<I", ix_data, offset)[0]
offset += 4
value = ix_data[offset:offset+length].decode('utf-8')
value = ix_data[offset : offset + length].decode("utf-8")
offset += length
elif arg['type'] == 'publicKey':
value = base64.b64encode(ix_data[offset:offset+32]).decode('utf-8')
elif arg["type"] == "publicKey":
value = base64.b64encode(ix_data[offset : offset + 32]).decode("utf-8")
offset += 32
else:
raise ValueError(f"Unsupported type: {arg['type']}")
args[arg['name']] = value
args[arg["name"]] = value
# Add accounts
args['mint'] = str(accounts[0])
args['bondingCurve'] = str(accounts[2])
args['associatedBondingCurve'] = str(accounts[3])
args['user'] = str(accounts[7])
args["mint"] = str(accounts[0])
args["bondingCurve"] = str(accounts[2])
args["associatedBondingCurve"] = str(accounts[3])
args["user"] = str(accounts[7])
return args
async def listen_for_create_transaction():
idl_path = os.path.join(os.path.dirname(__file__), '..', 'idl', 'pump_fun_idl.json')
idl_path = os.path.join(os.path.dirname(__file__), "..", "idl", "pump_fun_idl.json")
idl = load_idl(idl_path)
create_discriminator = calculate_discriminator("global:create")
async with websockets.connect(RPC_WEBSOCKET) as websocket:
subscription_message = json.dumps({
"jsonrpc": "2.0",
"id": 1,
"method": "blockSubscribe",
"params": [
{"mentionsAccountOrProgram": str(PUMP_PROGRAM)},
{
"commitment": "confirmed",
"encoding": "base64",
"showRewards": False,
"transactionDetails": "full",
"maxSupportedTransactionVersion": 0
}
]
})
subscription_message = json.dumps(
{
"jsonrpc": "2.0",
"id": 1,
"method": "blockSubscribe",
"params": [
{"mentionsAccountOrProgram": str(PUMP_PROGRAM)},
{
"commitment": "confirmed",
"encoding": "base64",
"showRewards": False,
"transactionDetails": "full",
"maxSupportedTransactionVersion": 0,
},
],
}
)
await websocket.send(subscription_message)
print(f"Subscribed to blocks mentioning program: {PUMP_PROGRAM}")
while True:
response = await websocket.recv()
data = json.loads(response)
if 'method' in data and data['method'] == 'blockNotification':
if 'params' in data and 'result' in data['params']:
block_data = data['params']['result']
if 'value' in block_data and 'block' in block_data['value']:
block = block_data['value']['block']
if 'transactions' in block:
for tx in block['transactions']:
if isinstance(tx, dict) and 'transaction' in tx:
tx_data_decoded = base64.b64decode(tx['transaction'][0])
transaction = VersionedTransaction.from_bytes(tx_data_decoded)
if "method" in data and data["method"] == "blockNotification":
if "params" in data and "result" in data["params"]:
block_data = data["params"]["result"]
if "value" in block_data and "block" in block_data["value"]:
block = block_data["value"]["block"]
if "transactions" in block:
for tx in block["transactions"]:
if isinstance(tx, dict) and "transaction" in tx:
tx_data_decoded = base64.b64decode(
tx["transaction"][0]
)
transaction = VersionedTransaction.from_bytes(
tx_data_decoded
)
for ix in transaction.message.instructions:
if str(transaction.message.account_keys[ix.program_id_index]) == str(PUMP_PROGRAM):
if str(
transaction.message.account_keys[
ix.program_id_index
]
) == str(PUMP_PROGRAM):
ix_data = bytes(ix.data)
discriminator = struct.unpack('<Q', ix_data[:8])[0]
discriminator = struct.unpack(
"<Q", ix_data[:8]
)[0]
if discriminator == create_discriminator:
create_ix = next(instr for instr in idl['instructions'] if instr['name'] == 'create')
account_keys = [str(transaction.message.account_keys[index]) for index in ix.accounts]
decoded_args = decode_create_instruction(ix_data, create_ix, account_keys)
create_ix = next(
instr
for instr in idl["instructions"]
if instr["name"] == "create"
)
account_keys = [
str(
transaction.message.account_keys[
index
]
)
for index in ix.accounts
]
decoded_args = (
decode_create_instruction(
ix_data, create_ix, account_keys
)
)
return decoded_args
async def main():
print("Waiting for a new token creation...")
token_data = await listen_for_create_transaction()
@@ -266,9 +360,9 @@ async def main():
print(f"Waiting for {sleep_duration_sec} seconds for things to stabilize...")
await asyncio.sleep(sleep_duration_sec)
mint = Pubkey.from_string(token_data['mint'])
bonding_curve = Pubkey.from_string(token_data['bondingCurve'])
associated_bonding_curve = Pubkey.from_string(token_data['associatedBondingCurve'])
mint = Pubkey.from_string(token_data["mint"])
bonding_curve = Pubkey.from_string(token_data["bondingCurve"])
associated_bonding_curve = Pubkey.from_string(token_data["associatedBondingCurve"])
# Fetch the token price
async with AsyncClient(RPC_ENDPOINT) as client:
@@ -281,8 +375,11 @@ async def main():
print(f"Bonding curve address: {bonding_curve}")
print(f"Token price: {token_price_sol:.10f} SOL")
print(f"Buying {amount:.6f} SOL worth of the new token with {slippage*100:.1f}% slippage tolerance...")
print(
f"Buying {amount:.6f} SOL worth of the new token with {slippage * 100:.1f}% slippage tolerance..."
)
await buy_token(mint, bonding_curve, associated_bonding_curve, amount, slippage)
if __name__ == "__main__":
asyncio.run(main())
asyncio.run(main())
+81 -29
View File
@@ -1,19 +1,20 @@
import asyncio
import json
import base64
import json
import struct
import base58
import spl.token.instructions as spl_token
from construct import Flag, Int64ul, Struct
from solana.rpc.async_api import AsyncClient
from solana.transaction import Transaction
from solana.rpc.commitment import Confirmed
from solders.pubkey import Pubkey
from solders.keypair import Keypair
from solders.instruction import Instruction, AccountMeta
from solana.rpc.types import TxOpts
from solana.transaction import Transaction
from solders.instruction import AccountMeta, Instruction
from solders.keypair import Keypair
from solders.pubkey import Pubkey
from solders.system_program import TransferParams, transfer
from spl.token.instructions import get_associated_token_address
import spl.token.instructions as spl_token
from construct import Struct, Int64ul, Flag
# Here and later all the discriminators are precalculated. See learning-examples/discriminator.py
EXPECTED_DISCRIMINATOR = struct.pack("<Q", 6966180631402821399)
@@ -22,11 +23,15 @@ TOKEN_DECIMALS = 6
# Global constants
PUMP_PROGRAM = Pubkey.from_string("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P")
PUMP_GLOBAL = Pubkey.from_string("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf")
PUMP_EVENT_AUTHORITY = Pubkey.from_string("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1")
PUMP_EVENT_AUTHORITY = Pubkey.from_string(
"Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1"
)
PUMP_FEE = Pubkey.from_string("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM")
SYSTEM_PROGRAM = Pubkey.from_string("11111111111111111111111111111111")
SYSTEM_TOKEN_PROGRAM = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM = Pubkey.from_string("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")
SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM = Pubkey.from_string(
"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
)
SYSTEM_RENT = Pubkey.from_string("SysvarRent111111111111111111111111111111111")
SOL = Pubkey.from_string("So11111111111111111111111111111111111111112")
LAMPORTS_PER_SOL = 1_000_000_000
@@ -36,6 +41,7 @@ UNIT_BUDGET = 100_000
# RPC endpoint
RPC_ENDPOINT = "SOLANA_NODE_RPC_ENDPOINT"
class BondingCurveState:
_STRUCT = Struct(
"virtual_token_reserves" / Int64ul,
@@ -43,14 +49,17 @@ class BondingCurveState:
"real_token_reserves" / Int64ul,
"real_sol_reserves" / Int64ul,
"token_total_supply" / Int64ul,
"complete" / Flag
"complete" / Flag,
)
def __init__(self, data: bytes) -> None:
parsed = self._STRUCT.parse(data[8:])
self.__dict__.update(parsed)
async def get_pump_curve_state(conn: AsyncClient, curve_address: Pubkey) -> BondingCurveState:
async def get_pump_curve_state(
conn: AsyncClient, curve_address: Pubkey
) -> BondingCurveState:
response = await conn.get_account_info(curve_address)
if not response.value or not response.value.data:
raise ValueError("Invalid curve state: No data")
@@ -61,11 +70,15 @@ async def get_pump_curve_state(conn: AsyncClient, curve_address: Pubkey) -> Bond
return BondingCurveState(data)
def calculate_pump_curve_price(curve_state: BondingCurveState) -> float:
if curve_state.virtual_token_reserves <= 0 or curve_state.virtual_sol_reserves <= 0:
raise ValueError("Invalid reserve state")
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (curve_state.virtual_token_reserves / 10 ** TOKEN_DECIMALS)
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (
curve_state.virtual_token_reserves / 10**TOKEN_DECIMALS
)
async def get_token_balance(conn: AsyncClient, associated_token_account: Pubkey):
response = await conn.get_token_account_balance(associated_token_account)
@@ -73,13 +86,20 @@ async def get_token_balance(conn: AsyncClient, associated_token_account: Pubkey)
return int(response.value.amount)
return 0
async def sell_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curve: Pubkey, slippage: float = 0.25, max_retries=5):
async def sell_token(
mint: Pubkey,
bonding_curve: Pubkey,
associated_bonding_curve: Pubkey,
slippage: float = 0.25,
max_retries=5,
):
private_key = base58.b58decode("SOLANA_PRIVATE_KEY")
payer = Keypair.from_bytes(private_key)
async with AsyncClient(RPC_ENDPOINT) as client:
associated_token_account = get_associated_token_address(payer.pubkey(), mint)
# Get token balance
token_balance = await get_token_balance(client, associated_token_account)
token_balance_decimal = token_balance / 10**TOKEN_DECIMALS
@@ -98,7 +118,7 @@ async def sell_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_cur
min_sol_output = float(token_balance_decimal) * float(token_price_sol)
slippage_factor = 1 - slippage
min_sol_output = int((min_sol_output * slippage_factor) * LAMPORTS_PER_SOL)
print(f"Selling {token_balance_decimal} tokens")
print(f"Minimum SOL output: {min_sol_output / LAMPORTS_PER_SOL:.10f} SOL")
@@ -109,19 +129,47 @@ async def sell_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_cur
AccountMeta(pubkey=PUMP_GLOBAL, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_FEE, is_signer=False, is_writable=True),
AccountMeta(pubkey=mint, is_signer=False, is_writable=False),
AccountMeta(pubkey=bonding_curve, is_signer=False, is_writable=True),
AccountMeta(pubkey=associated_bonding_curve, is_signer=False, is_writable=True),
AccountMeta(pubkey=associated_token_account, is_signer=False, is_writable=True),
AccountMeta(pubkey=payer.pubkey(), is_signer=True, is_writable=True),
AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(pubkey=SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(pubkey=SYSTEM_TOKEN_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(
pubkey=bonding_curve, is_signer=False, is_writable=True
),
AccountMeta(
pubkey=associated_bonding_curve,
is_signer=False,
is_writable=True,
),
AccountMeta(
pubkey=associated_token_account,
is_signer=False,
is_writable=True,
),
AccountMeta(
pubkey=payer.pubkey(), is_signer=True, is_writable=True
),
AccountMeta(
pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False
),
AccountMeta(
pubkey=SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM,
is_signer=False,
is_writable=False,
),
AccountMeta(
pubkey=SYSTEM_TOKEN_PROGRAM, is_signer=False, is_writable=False
),
AccountMeta(
pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False
),
AccountMeta(
pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False
),
]
discriminator = struct.pack("<Q", 12502976635542562355)
data = discriminator + struct.pack("<Q", amount) + struct.pack("<Q", min_sol_output)
data = (
discriminator
+ struct.pack("<Q", amount)
+ struct.pack("<Q", min_sol_output)
)
sell_ix = Instruction(PUMP_PROGRAM, data, accounts)
recent_blockhash = await client.get_latest_blockhash()
@@ -144,23 +192,27 @@ async def sell_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_cur
except Exception as e:
print(f"Attempt {attempt + 1} failed: {str(e)}")
if attempt < max_retries - 1:
wait_time = 2 ** attempt # Exponential backoff
wait_time = 2**attempt # Exponential backoff
print(f"Retrying in {wait_time} seconds...")
await asyncio.sleep(wait_time)
else:
print("Max retries reached. Unable to complete the transaction.")
async def main():
# Replace these with the actual values for the token you want to sell
mint = Pubkey.from_string("EyLuyWV5N1GVSqLLeumFDWYkmmSkLED5s2xqu37Lpump")
bonding_curve = Pubkey.from_string("AGZLYVwmGL9cXCLN4C3ki9hXGix1kXYjr59B2H7jwRMQ")
associated_bonding_curve = Pubkey.from_string("74H2bo2hjNnWgf9oDHzsVsu3mcsoiYYWJ3s9dVnL5erV")
associated_bonding_curve = Pubkey.from_string(
"74H2bo2hjNnWgf9oDHzsVsu3mcsoiYYWJ3s9dVnL5erV"
)
slippage = 0.25 # 25% slippage tolerance
print(f"Bonding curve address: {bonding_curve}")
print(f"Selling tokens with {slippage*100:.1f}% slippage tolerance...")
print(f"Selling tokens with {slippage * 100:.1f}% slippage tolerance...")
await sell_token(mint, bonding_curve, associated_bonding_curve, slippage)
if __name__ == "__main__":
asyncio.run(main())
asyncio.run(main())
+6
View File
@@ -0,0 +1,6 @@
[tool.black]
line-length = 88
target-version = ['py39']
[tool.isort]
profile = "black"
+6 -5
View File
@@ -1,7 +1,8 @@
base58>=2.1.1
borsh-construct>=0.1.0
construct>=2.10.68
construct-typing>=0.5.6
solana==0.34.3
solders>=0.21.0
websockets>=10.4
construct>=2.10.67
construct-typing>=0.5.2
solana==0.36.6
solders>=0.26.0
websockets>=15.0
python-dotenv>=1.0.1
+68 -28
View File
@@ -1,24 +1,21 @@
import asyncio
import json
import base64
import json
import struct
import base58
from typing import Final
import base58
import spl.token.instructions as spl_token
from construct import Flag, Int64ul, Struct
from solana.rpc.async_api import AsyncClient
from solana.transaction import Transaction
from solana.rpc.commitment import Confirmed
from solana.rpc.types import TxOpts
from solders.pubkey import Pubkey
from solana.transaction import Transaction
from solders.instruction import AccountMeta, Instruction
from solders.keypair import Keypair
from solders.instruction import Instruction, AccountMeta
from solders.pubkey import Pubkey
from solders.system_program import TransferParams, transfer
from spl.token.instructions import get_associated_token_address
import spl.token.instructions as spl_token
from construct import Struct, Int64ul, Flag
from config import *
@@ -26,6 +23,7 @@ from config import *
EXPECTED_DISCRIMINATOR: Final[bytes] = struct.pack("<Q", 6966180631402821399)
TOKEN_DECIMALS: Final[int] = 6
class BondingCurveState:
_STRUCT = Struct(
"virtual_token_reserves" / Int64ul,
@@ -33,14 +31,17 @@ class BondingCurveState:
"real_token_reserves" / Int64ul,
"real_sol_reserves" / Int64ul,
"token_total_supply" / Int64ul,
"complete" / Flag
"complete" / Flag,
)
def __init__(self, data: bytes) -> None:
parsed = self._STRUCT.parse(data[8:])
self.__dict__.update(parsed)
async def get_pump_curve_state(conn: AsyncClient, curve_address: Pubkey) -> BondingCurveState:
async def get_pump_curve_state(
conn: AsyncClient, curve_address: Pubkey
) -> BondingCurveState:
response = await conn.get_account_info(curve_address)
if not response.value or not response.value.data:
raise ValueError("Invalid curve state: No data")
@@ -51,11 +52,15 @@ async def get_pump_curve_state(conn: AsyncClient, curve_address: Pubkey) -> Bond
return BondingCurveState(data)
def calculate_pump_curve_price(curve_state: BondingCurveState) -> float:
if curve_state.virtual_token_reserves <= 0 or curve_state.virtual_sol_reserves <= 0:
raise ValueError("Invalid reserve state")
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (curve_state.virtual_token_reserves / 10 ** TOKEN_DECIMALS)
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (
curve_state.virtual_token_reserves / 10**TOKEN_DECIMALS
)
async def get_token_balance(conn: AsyncClient, associated_token_account: Pubkey):
response = await conn.get_token_account_balance(associated_token_account)
@@ -63,13 +68,20 @@ async def get_token_balance(conn: AsyncClient, associated_token_account: Pubkey)
return int(response.value.amount)
return 0
async def sell_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curve: Pubkey, slippage: float = 0.25, max_retries=5):
async def sell_token(
mint: Pubkey,
bonding_curve: Pubkey,
associated_bonding_curve: Pubkey,
slippage: float = 0.25,
max_retries=5,
):
private_key = base58.b58decode(PRIVATE_KEY)
payer = Keypair.from_bytes(private_key)
async with AsyncClient(RPC_ENDPOINT) as client:
associated_token_account = get_associated_token_address(payer.pubkey(), mint)
# Get token balance
token_balance = await get_token_balance(client, associated_token_account)
token_balance_decimal = token_balance / 10**TOKEN_DECIMALS
@@ -88,7 +100,7 @@ async def sell_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_cur
min_sol_output = float(token_balance_decimal) * float(token_price_sol)
slippage_factor = 1 - slippage
min_sol_output = int((min_sol_output * slippage_factor) * LAMPORTS_PER_SOL)
print(f"Selling {token_balance_decimal} tokens")
print(f"Minimum SOL output: {min_sol_output / LAMPORTS_PER_SOL:.10f} SOL")
@@ -98,19 +110,47 @@ async def sell_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_cur
AccountMeta(pubkey=PUMP_GLOBAL, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_FEE, is_signer=False, is_writable=True),
AccountMeta(pubkey=mint, is_signer=False, is_writable=False),
AccountMeta(pubkey=bonding_curve, is_signer=False, is_writable=True),
AccountMeta(pubkey=associated_bonding_curve, is_signer=False, is_writable=True),
AccountMeta(pubkey=associated_token_account, is_signer=False, is_writable=True),
AccountMeta(pubkey=payer.pubkey(), is_signer=True, is_writable=True),
AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(pubkey=SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(pubkey=SYSTEM_TOKEN_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(
pubkey=bonding_curve, is_signer=False, is_writable=True
),
AccountMeta(
pubkey=associated_bonding_curve,
is_signer=False,
is_writable=True,
),
AccountMeta(
pubkey=associated_token_account,
is_signer=False,
is_writable=True,
),
AccountMeta(
pubkey=payer.pubkey(), is_signer=True, is_writable=True
),
AccountMeta(
pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False
),
AccountMeta(
pubkey=SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM,
is_signer=False,
is_writable=False,
),
AccountMeta(
pubkey=SYSTEM_TOKEN_PROGRAM, is_signer=False, is_writable=False
),
AccountMeta(
pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False
),
AccountMeta(
pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False
),
]
discriminator = struct.pack("<Q", 12502976635542562355)
data = discriminator + struct.pack("<Q", amount) + struct.pack("<Q", min_sol_output)
data = (
discriminator
+ struct.pack("<Q", amount)
+ struct.pack("<Q", min_sol_output)
)
sell_ix = Instruction(PUMP_PROGRAM, data, accounts)
recent_blockhash = await client.get_latest_blockhash()
@@ -134,8 +174,8 @@ async def sell_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_cur
except Exception as e:
print(f"Attempt {attempt + 1} failed: {str(e)}")
if attempt < max_retries - 1:
wait_time = 2 ** attempt
wait_time = 2**attempt
print(f"Retrying in {wait_time} seconds...")
await asyncio.sleep(wait_time)
else:
print("Max retries reached. Unable to complete the transaction.")
print("Max retries reached. Unable to complete the transaction.")
+97 -43
View File
@@ -1,89 +1,107 @@
import asyncio
import json
import base64
import struct
import base58
import hashlib
import websockets
import os
import argparse
import asyncio
import base64
import hashlib
import json
import os
import struct
from datetime import datetime
import base58
import spl.token.instructions as spl_token
import websockets
from solana.rpc.async_api import AsyncClient
from solana.transaction import Transaction
from solana.rpc.commitment import Confirmed
from solana.rpc.types import TxOpts
from solders.pubkey import Pubkey
from solana.transaction import Transaction
from solders.instruction import AccountMeta, Instruction
from solders.keypair import Keypair
from solders.instruction import Instruction, AccountMeta
from solders.pubkey import Pubkey
from solders.system_program import TransferParams, transfer
from solders.transaction import VersionedTransaction
from spl.token.instructions import get_associated_token_address
import spl.token.instructions as spl_token
from buy import (
buy_token,
calculate_pump_curve_price,
get_pump_curve_state,
listen_for_create_transaction,
)
from config import *
# Import functions from buy.py
from buy import get_pump_curve_state, calculate_pump_curve_price, buy_token, listen_for_create_transaction
# Import functions from sell.py
from sell import sell_token
def log_trade(action, token_data, price, tx_hash):
os.makedirs("trades", exist_ok=True)
log_entry = {
"timestamp": datetime.utcnow().isoformat(),
"action": action,
"token_address": token_data['mint'],
"token_address": token_data["mint"],
"price": price,
"tx_hash": tx_hash
"tx_hash": tx_hash,
}
with open("trades/trades.log", 'a') as log_file:
with open("trades/trades.log", "a") as log_file:
json.dump(log_entry, log_file)
log_file.write("\n")
async def trade(websocket=None, match_string=None, bro_address=None, marry_mode=False, yolo_mode=False):
async def trade(
websocket=None,
match_string=None,
bro_address=None,
marry_mode=False,
yolo_mode=False,
):
if websocket is None:
async with websockets.connect(WSS_ENDPOINT) as websocket:
await _trade(websocket, match_string, bro_address, marry_mode, yolo_mode)
else:
await _trade(websocket, match_string, bro_address, marry_mode, yolo_mode)
async def _trade(websocket, match_string=None, bro_address=None, marry_mode=False, yolo_mode=False):
async def _trade(
websocket, match_string=None, bro_address=None, marry_mode=False, yolo_mode=False
):
while True:
print("Waiting for a new token creation...")
token_data = await listen_for_create_transaction(websocket)
print("New token created:")
print(json.dumps(token_data, indent=2))
if match_string and not (match_string.lower() in token_data['name'].lower() or match_string.lower() in token_data['symbol'].lower()):
if match_string and not (
match_string.lower() in token_data["name"].lower()
or match_string.lower() in token_data["symbol"].lower()
):
print(f"Token does not match the criteria '{match_string}'. Skipping...")
if not yolo_mode:
break
continue
if bro_address and token_data['user'] != bro_address:
print(f"Token not created by the specified user '{bro_address}'. Skipping...")
if bro_address and token_data["user"] != bro_address:
print(
f"Token not created by the specified user '{bro_address}'. Skipping..."
)
if not yolo_mode:
break
continue
# Save token information to a .txt file in the "trades" directory
mint_address = token_data['mint']
mint_address = token_data["mint"]
os.makedirs("trades", exist_ok=True)
file_name = os.path.join("trades", f"{mint_address}.txt")
with open(file_name, 'w') as file:
with open(file_name, "w") as file:
file.write(json.dumps(token_data, indent=2))
print(f"Token information saved to {file_name}")
print("Waiting for 15 seconds for things to stabilize...")
await asyncio.sleep(15)
mint = Pubkey.from_string(token_data['mint'])
bonding_curve = Pubkey.from_string(token_data['bondingCurve'])
associated_bonding_curve = Pubkey.from_string(token_data['associatedBondingCurve'])
mint = Pubkey.from_string(token_data["mint"])
bonding_curve = Pubkey.from_string(token_data["bondingCurve"])
associated_bonding_curve = Pubkey.from_string(
token_data["associatedBondingCurve"]
)
# Fetch the token price
async with AsyncClient(RPC_ENDPOINT) as client:
@@ -92,8 +110,12 @@ async def _trade(websocket, match_string=None, bro_address=None, marry_mode=Fals
print(f"Bonding curve address: {bonding_curve}")
print(f"Token price: {token_price_sol:.10f} SOL")
print(f"Buying {BUY_AMOUNT:.6f} SOL worth of the new token with {BUY_SLIPPAGE*100:.1f}% slippage tolerance...")
buy_tx_hash = await buy_token(mint, bonding_curve, associated_bonding_curve, BUY_AMOUNT, BUY_SLIPPAGE)
print(
f"Buying {BUY_AMOUNT:.6f} SOL worth of the new token with {BUY_SLIPPAGE * 100:.1f}% slippage tolerance..."
)
buy_tx_hash = await buy_token(
mint, bonding_curve, associated_bonding_curve, BUY_AMOUNT, BUY_SLIPPAGE
)
if buy_tx_hash:
log_trade("buy", token_data, token_price_sol, str(buy_tx_hash))
else:
@@ -103,8 +125,12 @@ async def _trade(websocket, match_string=None, bro_address=None, marry_mode=Fals
print("Waiting for 20 seconds before selling...")
await asyncio.sleep(20)
print(f"Selling tokens with {SELL_SLIPPAGE*100:.1f}% slippage tolerance...")
sell_tx_hash = await sell_token(mint, bonding_curve, associated_bonding_curve, SELL_SLIPPAGE)
print(
f"Selling tokens with {SELL_SLIPPAGE * 100:.1f}% slippage tolerance..."
)
sell_tx_hash = await sell_token(
mint, bonding_curve, associated_bonding_curve, SELL_SLIPPAGE
)
if sell_tx_hash:
log_trade("sell", token_data, token_price_sol, str(sell_tx_hash))
else:
@@ -115,6 +141,7 @@ async def _trade(websocket, match_string=None, bro_address=None, marry_mode=Fals
if not yolo_mode:
break
async def main(yolo_mode=False, match_string=None, bro_address=None, marry_mode=False):
if yolo_mode:
while True:
@@ -122,13 +149,21 @@ async def main(yolo_mode=False, match_string=None, bro_address=None, marry_mode=
async with websockets.connect(WSS_ENDPOINT) as websocket:
while True:
try:
await trade(websocket, match_string, bro_address, marry_mode, yolo_mode)
await trade(
websocket,
match_string,
bro_address,
marry_mode,
yolo_mode,
)
except websockets.exceptions.ConnectionClosed:
print("WebSocket connection closed. Reconnecting...")
break
except Exception as e:
print(f"An error occurred: {e}")
print("Waiting for 5 seconds before looking for the next token...")
print(
"Waiting for 5 seconds before looking for the next token..."
)
await asyncio.sleep(5)
except Exception as e:
print(f"Connection error: {e}")
@@ -139,19 +174,38 @@ async def main(yolo_mode=False, match_string=None, bro_address=None, marry_mode=
async with websockets.connect(WSS_ENDPOINT) as websocket:
await trade(websocket, match_string, bro_address, marry_mode, yolo_mode)
async def ping_websocket(websocket):
while True:
try:
await websocket.ping()
await asyncio.sleep(20) # Send a ping every 20 seconds
await asyncio.sleep(20) # Send a ping every 20 seconds
except:
break
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Trade tokens on Solana.")
parser.add_argument("--yolo", action="store_true", help="Run in YOLO mode (continuous trading)")
parser.add_argument("--match", type=str, help="Only trade tokens with names or symbols matching this string")
parser.add_argument("--bro", type=str, help="Only trade tokens created by this user address")
parser.add_argument("--marry", action="store_true", help="Only buy tokens, skip selling")
parser.add_argument(
"--yolo", action="store_true", help="Run in YOLO mode (continuous trading)"
)
parser.add_argument(
"--match",
type=str,
help="Only trade tokens with names or symbols matching this string",
)
parser.add_argument(
"--bro", type=str, help="Only trade tokens created by this user address"
)
parser.add_argument(
"--marry", action="store_true", help="Only buy tokens, skip selling"
)
args = parser.parse_args()
asyncio.run(main(yolo_mode=args.yolo, match_string=args.match, bro_address=args.bro, marry_mode=args.marry))
asyncio.run(
main(
yolo_mode=args.yolo,
match_string=args.match,
bro_address=args.bro,
marry_mode=args.marry,
)
)