""" Buy operations for pump.fun tokens. """ import struct from typing import Final from solders.instruction import AccountMeta, Instruction from solders.pubkey import Pubkey from spl.token.instructions import create_idempotent_associated_token_account from core.client import SolanaClient from core.curve import BondingCurveManager from core.priority_fee.manager import PriorityFeeManager from core.pubkeys import ( LAMPORTS_PER_SOL, TOKEN_DECIMALS, PumpAddresses, SystemAddresses, ) from core.wallet import Wallet from trading.base import TokenInfo, Trader, TradeResult from utils.logger import get_logger logger = get_logger(__name__) # Discriminator for the buy instruction EXPECTED_DISCRIMINATOR: Final[bytes] = struct.pack(" TradeResult: """Execute buy operation. Args: token_info: Token information Returns: TradeResult with buy outcome """ try: # Convert amount to lamports amount_lamports = int(self.amount * LAMPORTS_PER_SOL) if self.extreme_fast_mode: # Skip the wait and directly calculate the amount token_amount = self.extreme_fast_token_amount token_price_sol = self.amount / token_amount logger.info(f"EXTREME FAST Mode: Buying {token_amount} tokens.") else: # Regular behavior with RPC call curve_state = await self.curve_manager.get_curve_state(token_info.bonding_curve) token_price_sol = curve_state.calculate_price() token_amount = self.amount / token_price_sol # Calculate maximum SOL to spend with slippage max_amount_lamports = int(amount_lamports * (1 + self.slippage)) associated_token_account = self.wallet.get_associated_token_address( token_info.mint ) tx_signature = await self._send_buy_transaction( token_info, associated_token_account, token_amount, max_amount_lamports, ) logger.info( f"Buying {token_amount:.6f} tokens at {token_price_sol:.8f} SOL per token" ) logger.info( f"Total cost: {self.amount:.6f} SOL (max: {max_amount_lamports / LAMPORTS_PER_SOL:.6f} SOL)" ) success = await self.client.confirm_transaction(tx_signature) if success: logger.info(f"Buy transaction confirmed: {tx_signature}") return TradeResult( success=True, tx_signature=tx_signature, amount=token_amount, price=token_price_sol, ) else: return TradeResult( success=False, error_message=f"Transaction failed to confirm: {tx_signature}", ) except Exception as e: logger.error(f"Buy operation failed: {e!s}") return TradeResult(success=False, error_message=str(e)) async def _send_buy_transaction( self, token_info: TokenInfo, associated_token_account: Pubkey, token_amount: float, max_amount_lamports: int, ) -> str: """Send buy transaction. Args: token_info: Token information associated_token_account: User's token account token_amount: Amount of tokens to buy max_amount_lamports: Maximum SOL to spend in lamports Returns: Transaction signature Raises: Exception: If transaction fails after all retries """ accounts = [ AccountMeta( pubkey=PumpAddresses.GLOBAL, is_signer=False, is_writable=False ), AccountMeta(pubkey=PumpAddresses.FEE, is_signer=False, is_writable=True), AccountMeta(pubkey=token_info.mint, is_signer=False, is_writable=False), AccountMeta( pubkey=token_info.bonding_curve, is_signer=False, is_writable=True ), AccountMeta( pubkey=token_info.associated_bonding_curve, is_signer=False, is_writable=True, ), AccountMeta( pubkey=associated_token_account, is_signer=False, is_writable=True ), AccountMeta(pubkey=self.wallet.pubkey, is_signer=True, is_writable=True), AccountMeta( pubkey=SystemAddresses.PROGRAM, is_signer=False, is_writable=False ), AccountMeta( pubkey=SystemAddresses.TOKEN_PROGRAM, is_signer=False, is_writable=False ), AccountMeta( pubkey=SystemAddresses.RENT, is_signer=False, is_writable=False ), AccountMeta( pubkey=PumpAddresses.EVENT_AUTHORITY, is_signer=False, is_writable=False ), AccountMeta( pubkey=PumpAddresses.PROGRAM, is_signer=False, is_writable=False ), ] # Prepare idempotent create ATA instruction: it will not fail if ATA already exists idempotent_ata_ix = create_idempotent_associated_token_account( self.wallet.pubkey, self.wallet.pubkey, token_info.mint, SystemAddresses.TOKEN_PROGRAM ) # Prepare buy instruction data token_amount_raw = int(token_amount * 10**TOKEN_DECIMALS) data = ( EXPECTED_DISCRIMINATOR + struct.pack("