""" Pump.fun Token Buy Script with Compute Unit Optimization This script is identical to manual_buy.py but adds SetLoadedAccountsDataSizeLimit instruction. By default, Solana transactions can load up to 64MB of account data (costing 16k CU). By setting a lower limit (512KB), we reduce CU consumption and improve transaction priority. Key difference from manual_buy.py: - Adds set_loaded_accounts_data_size_limit(512_000) before other instructions NOTE: The CU savings from this optimization are NOT visible in transaction "consumed CU" metrics, which only show execution CU. The 16k CU loaded accounts overhead is counted separately for transaction priority/cost calculation. This makes the real impact hard to measure directly, but it improves priority. Reference: https://www.anza.xyz/blog/cu-optimization-with-setloadedaccountsdatasizelimit """ import asyncio import base64 import hashlib import json import os import struct import base58 import websockets from construct import Bytes, 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 ( create_idempotent_associated_token_account, get_associated_token_address, ) # Discriminators EXPECTED_DISCRIMINATOR = struct.pack(" None: """Parse bonding curve data.""" if data[:8] != EXPECTED_DISCRIMINATOR: raise ValueError("Invalid curve state discriminator") parsed = self._STRUCT.parse(data[8:]) self.__dict__.update(parsed) # Convert raw bytes to Pubkey for creator field if hasattr(self, "creator") and isinstance(self.creator, bytes): self.creator = Pubkey.from_bytes(self.creator) 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 ) def _find_creator_vault(creator: Pubkey) -> Pubkey: derived_address, _ = Pubkey.find_program_address( [b"creator-vault", bytes(creator)], PUMP_PROGRAM, ) return derived_address def _find_global_volume_accumulator() -> Pubkey: derived_address, _ = Pubkey.find_program_address( [b"global_volume_accumulator"], PUMP_PROGRAM, ) return derived_address def _find_user_volume_accumulator(user: Pubkey) -> Pubkey: derived_address, _ = Pubkey.find_program_address( [b"user_volume_accumulator", bytes(user)], PUMP_PROGRAM, ) return derived_address def _find_fee_config() -> Pubkey: derived_address, _ = Pubkey.find_program_address( [b"fee_config", bytes(PUMP_PROGRAM)], PUMP_FEE_PROGRAM, ) return derived_address def set_loaded_accounts_data_size_limit(bytes_limit: int) -> Instruction: """ Create SetLoadedAccountsDataSizeLimit instruction to reduce CU consumption. Solana defaults to 64MB loaded data limit (16k CU cost: 8 CU per 32KB). By setting a lower limit, you reduce CU consumption and improve tx priority. Args: bytes_limit: Max account data size in bytes (e.g., 512_000 = 512KB) Returns: Compute Budget instruction (discriminator 4) """ data = struct.pack("