import asyncio import base64 import hashlib import json import os import struct 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.compute_budget import set_compute_unit_price from solders.instruction import AccountMeta, Instruction from solders.keypair import Keypair from solders.message import Message from solders.pubkey import Pubkey from solders.transaction import Transaction, VersionedTransaction from spl.token.instructions import get_associated_token_address # Here and later all the discriminators are precalculated. See learning-examples/discriminator.py EXPECTED_DISCRIMINATOR = struct.pack(" None: parsed = self._STRUCT.parse(data[8:]) self.__dict__.update(parsed) async def get_pump_curve_state( conn: AsyncClient, curve_address: Pubkey ) -> BondingCurveState: response = await conn.get_account_info(curve_address, encoding="base64") if not response.value or not response.value.data: raise ValueError("Invalid curve state: No data") data = response.value.data if data[:8] != EXPECTED_DISCRIMINATOR: raise ValueError("Invalid curve state discriminator") 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 ) 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(os.environ.get("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) amount_lamports = int(amount * LAMPORTS_PER_SOL) # Fetch the token price curve_state = await get_pump_curve_state(client, bonding_curve) token_price_sol = calculate_pump_curve_price(curve_state) token_amount = amount / token_price_sol # Calculate maximum SOL to spend with slippage max_amount_lamports = int(amount_lamports * (1 + slippage)) # Create associated token account with retries for ata_attempt in range(max_retries): try: account_info = await client.get_account_info(associated_token_account, encoding="base64") if account_info.value is None: 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 ) 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" ) print("Associated token account created.") 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}" ) break except Exception as e: print( f"Attempt {ata_attempt + 1} to create associated token account failed: {e!s}" ) if ata_attempt < max_retries - 1: 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." ) return # Continue with the buy transaction for attempt in range(max_retries): try: accounts = [ 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=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 ), ] discriminator = struct.pack("