diff --git a/bots/bot-sniper-1-geyser.yaml b/bots/bot-sniper-1-geyser.yaml index ec05ef4..94812ce 100644 --- a/bots/bot-sniper-1-geyser.yaml +++ b/bots/bot-sniper-1-geyser.yaml @@ -8,11 +8,11 @@ rpc_endpoint: "${SOLANA_NODE_RPC_ENDPOINT}" wss_endpoint: "${SOLANA_NODE_WSS_ENDPOINT}" private_key: "${SOLANA_PRIVATE_KEY}" -enabled: false # You can turn off the bot w/o removing its config +enabled: true # You can turn off the bot w/o removing its config separate_process: true # Options: "pump_fun" (default), "lets_bonk" -platform: "pump_fun" +platform: "lets_bonk" # Geyser configuration (fastest method for getting updates) geyser: diff --git a/bots/bot-sniper-2-logs.yaml b/bots/bot-sniper-2-logs.yaml index 46562e5..47780b8 100644 --- a/bots/bot-sniper-2-logs.yaml +++ b/bots/bot-sniper-2-logs.yaml @@ -8,7 +8,7 @@ rpc_endpoint: "${SOLANA_NODE_RPC_ENDPOINT}" wss_endpoint: "${SOLANA_NODE_WSS_ENDPOINT}" private_key: "${SOLANA_PRIVATE_KEY}" -enabled: true # You can turn off the bot w/o removing its config +enabled: false # You can turn off the bot w/o removing its config separate_process: true # Options: "pump_fun" (default), "lets_bonk" diff --git a/src/platforms/letsbonk/address_provider.py b/src/platforms/letsbonk/address_provider.py index e9ee899..647f093 100644 --- a/src/platforms/letsbonk/address_provider.py +++ b/src/platforms/letsbonk/address_provider.py @@ -102,20 +102,16 @@ class LetsBonkAddressProvider(AddressProvider): """ accounts = {} - # Add pool state if available + # Add pool state - must be present in token_info if token_info.pool_state: accounts["pool_state"] = token_info.pool_state - # Add vault addresses if available + # Add vault addresses - must be present in token_info if token_info.base_vault: accounts["base_vault"] = token_info.base_vault if token_info.quote_vault: accounts["quote_vault"] = token_info.quote_vault - # Derive pool state if not provided - if not token_info.pool_state: - accounts["pool_state"] = self.derive_pool_address(token_info.mint) - # Derive authority PDA accounts["authority"] = self.derive_authority_pda() @@ -178,15 +174,20 @@ class LetsBonkAddressProvider(AddressProvider): """ additional_accounts = self.get_additional_accounts(token_info) + # Vault addresses must be present in token_info + if not token_info.base_vault or not token_info.quote_vault: + raise ValueError(f"Missing required vault addresses for token {token_info.mint}. " + f"base_vault: {token_info.base_vault}, quote_vault: {token_info.quote_vault}") + return { "payer": user, "authority": additional_accounts["authority"], "global_config": LetsBonkAddresses.GLOBAL_CONFIG, "platform_config": LetsBonkAddresses.PLATFORM_CONFIG, - "pool_state": additional_accounts["pool_state"], + "pool_state": token_info.pool_state, "user_base_token": self.derive_user_token_account(user, token_info.mint), - "base_vault": additional_accounts.get("base_vault", token_info.base_vault), - "quote_vault": additional_accounts.get("quote_vault", token_info.quote_vault), + "base_vault": token_info.base_vault, + "quote_vault": token_info.quote_vault, "base_token_mint": token_info.mint, "quote_token_mint": SystemAddresses.SOL_MINT, "base_token_program": SystemAddresses.TOKEN_PROGRAM, @@ -207,15 +208,20 @@ class LetsBonkAddressProvider(AddressProvider): """ additional_accounts = self.get_additional_accounts(token_info) + # Vault addresses must be present in token_info + if not token_info.base_vault or not token_info.quote_vault: + raise ValueError(f"Missing required vault addresses for token {token_info.mint}. " + f"base_vault: {token_info.base_vault}, quote_vault: {token_info.quote_vault}") + return { "payer": user, "authority": additional_accounts["authority"], "global_config": LetsBonkAddresses.GLOBAL_CONFIG, "platform_config": LetsBonkAddresses.PLATFORM_CONFIG, - "pool_state": additional_accounts["pool_state"], + "pool_state": token_info.pool_state, "user_base_token": self.derive_user_token_account(user, token_info.mint), - "base_vault": additional_accounts.get("base_vault", token_info.base_vault), - "quote_vault": additional_accounts.get("quote_vault", token_info.quote_vault), + "base_vault": token_info.base_vault, + "quote_vault": token_info.quote_vault, "base_token_mint": token_info.mint, "quote_token_mint": SystemAddresses.SOL_MINT, "base_token_program": SystemAddresses.TOKEN_PROGRAM, diff --git a/src/platforms/letsbonk/event_parser.py b/src/platforms/letsbonk/event_parser.py index c5f6f90..21e4d0e 100644 --- a/src/platforms/letsbonk/event_parser.py +++ b/src/platforms/letsbonk/event_parser.py @@ -104,14 +104,29 @@ class LetsBonkEventParser(EventParser): if not base_mint_param: return None - # Extract account information based on IDL account order - creator = get_account_key(1) # creator account + # Extract account information based on IDL account order for initialize instruction + # From the manual example, the account order is: + # 0: creator (signer) + # 1: creator_ata (not needed for TokenInfo) + # 2: global_config + # 3: platform_config + # 4: creator + # 5: pool_state + # 6: base_mint + # 7: quote_mint (WSOL) + # 8: base_vault + # 9: quote_vault + # ... other accounts + + creator = get_account_key(0) # First signer account (creator) pool_state = get_account_key(5) # pool_state account base_mint = get_account_key(6) # base_mint account base_vault = get_account_key(8) # base_vault account quote_vault = get_account_key(9) # quote_vault account if not all([creator, pool_state, base_mint, base_vault, quote_vault]): + logger.debug(f"Missing required accounts: creator={creator}, pool_state={pool_state}, " + f"base_mint={base_mint}, base_vault={base_vault}, quote_vault={quote_vault}") return None return TokenInfo( diff --git a/src/platforms/letsbonk/instruction_builder.py b/src/platforms/letsbonk/instruction_builder.py index b16a6bd..8fba465 100644 --- a/src/platforms/letsbonk/instruction_builder.py +++ b/src/platforms/letsbonk/instruction_builder.py @@ -104,28 +104,28 @@ class LetsBonkInstructionBuilder(InstructionBuilder): initialize_wsol_ix = self._create_initialize_account_instruction( wsol_account, SystemAddresses.SOL_MINT, - user, - address_provider + user ) instructions.append(initialize_wsol_ix) - # 4. Build buy_exact_in instruction + # 4. Build buy_exact_in instruction with correct account ordering + # Based on the IDL and manual examples, the account order should be: buy_accounts = [ - AccountMeta(pubkey=accounts_info["payer"], is_signer=True, is_writable=True), - AccountMeta(pubkey=accounts_info["authority"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["global_config"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["platform_config"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["pool_state"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["user_base_token"], is_signer=False, is_writable=True), - AccountMeta(pubkey=wsol_account, is_signer=False, is_writable=True), # user_quote_token - AccountMeta(pubkey=accounts_info["base_vault"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["quote_vault"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["base_token_mint"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["quote_token_mint"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["base_token_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["quote_token_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["event_authority"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["program"], is_signer=False, is_writable=False), + AccountMeta(pubkey=user, is_signer=True, is_writable=True), # payer + AccountMeta(pubkey=accounts_info["authority"], is_signer=False, is_writable=False), # authority + AccountMeta(pubkey=accounts_info["global_config"], is_signer=False, is_writable=False), # global_config + AccountMeta(pubkey=accounts_info["platform_config"], is_signer=False, is_writable=False), # platform_config + AccountMeta(pubkey=accounts_info["pool_state"], is_signer=False, is_writable=True), # pool_state + AccountMeta(pubkey=accounts_info["user_base_token"], is_signer=False, is_writable=True), # user_base_token + AccountMeta(pubkey=wsol_account, is_signer=False, is_writable=True), # user_quote_token (WSOL account) + AccountMeta(pubkey=accounts_info["base_vault"], is_signer=False, is_writable=True), # base_vault + AccountMeta(pubkey=accounts_info["quote_vault"], is_signer=False, is_writable=True), # quote_vault + AccountMeta(pubkey=token_info.mint, is_signer=False, is_writable=False), # base_token_mint + AccountMeta(pubkey=SystemAddresses.SOL_MINT, is_signer=False, is_writable=False), # quote_token_mint + AccountMeta(pubkey=SystemAddresses.TOKEN_PROGRAM, is_signer=False, is_writable=False), # base_token_program + AccountMeta(pubkey=SystemAddresses.TOKEN_PROGRAM, is_signer=False, is_writable=False), # quote_token_program + AccountMeta(pubkey=accounts_info["event_authority"], is_signer=False, is_writable=False), # event_authority + AccountMeta(pubkey=accounts_info["program"], is_signer=False, is_writable=False), # program ] # Build instruction data: discriminator + amount_in + minimum_amount_out + share_fee_rate @@ -148,8 +148,7 @@ class LetsBonkInstructionBuilder(InstructionBuilder): close_wsol_ix = self._create_close_account_instruction( wsol_account, user, - user, - address_provider + user ) instructions.append(close_wsol_ix) @@ -204,28 +203,27 @@ class LetsBonkInstructionBuilder(InstructionBuilder): initialize_wsol_ix = self._create_initialize_account_instruction( wsol_account, SystemAddresses.SOL_MINT, - user, - address_provider + user ) instructions.append(initialize_wsol_ix) - # 3. Build sell_exact_in instruction + # 3. Build sell_exact_in instruction with correct account ordering sell_accounts = [ - AccountMeta(pubkey=accounts_info["payer"], is_signer=True, is_writable=True), - AccountMeta(pubkey=accounts_info["authority"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["global_config"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["platform_config"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["pool_state"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["user_base_token"], is_signer=False, is_writable=True), - AccountMeta(pubkey=wsol_account, is_signer=False, is_writable=True), # user_quote_token - AccountMeta(pubkey=accounts_info["base_vault"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["quote_vault"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["base_token_mint"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["quote_token_mint"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["base_token_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["quote_token_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["event_authority"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["program"], is_signer=False, is_writable=False), + AccountMeta(pubkey=user, is_signer=True, is_writable=True), # payer + AccountMeta(pubkey=accounts_info["authority"], is_signer=False, is_writable=False), # authority + AccountMeta(pubkey=accounts_info["global_config"], is_signer=False, is_writable=False), # global_config + AccountMeta(pubkey=accounts_info["platform_config"], is_signer=False, is_writable=False), # platform_config + AccountMeta(pubkey=accounts_info["pool_state"], is_signer=False, is_writable=True), # pool_state + AccountMeta(pubkey=accounts_info["user_base_token"], is_signer=False, is_writable=True), # user_base_token (tokens being sold) + AccountMeta(pubkey=wsol_account, is_signer=False, is_writable=True), # user_quote_token (WSOL received) + AccountMeta(pubkey=accounts_info["base_vault"], is_signer=False, is_writable=True), # base_vault (receives tokens) + AccountMeta(pubkey=accounts_info["quote_vault"], is_signer=False, is_writable=True), # quote_vault (sends WSOL) + AccountMeta(pubkey=token_info.mint, is_signer=False, is_writable=False), # base_token_mint + AccountMeta(pubkey=SystemAddresses.SOL_MINT, is_signer=False, is_writable=False), # quote_token_mint + AccountMeta(pubkey=SystemAddresses.TOKEN_PROGRAM, is_signer=False, is_writable=False), # base_token_program + AccountMeta(pubkey=SystemAddresses.TOKEN_PROGRAM, is_signer=False, is_writable=False), # quote_token_program + AccountMeta(pubkey=accounts_info["event_authority"], is_signer=False, is_writable=False), # event_authority + AccountMeta(pubkey=accounts_info["program"], is_signer=False, is_writable=False), # program ] # Build instruction data: discriminator + amount_in + minimum_amount_out + share_fee_rate @@ -248,8 +246,7 @@ class LetsBonkInstructionBuilder(InstructionBuilder): close_wsol_ix = self._create_close_account_instruction( wsol_account, user, - user, - address_provider + user ) instructions.append(close_wsol_ix) @@ -278,8 +275,8 @@ class LetsBonkInstructionBuilder(InstructionBuilder): accounts_info["user_base_token"], accounts_info["base_vault"], accounts_info["quote_vault"], - accounts_info["base_token_mint"], - accounts_info["quote_token_mint"], + token_info.mint, + SystemAddresses.SOL_MINT, accounts_info["program"], ] @@ -306,8 +303,8 @@ class LetsBonkInstructionBuilder(InstructionBuilder): accounts_info["user_base_token"], accounts_info["base_vault"], accounts_info["quote_vault"], - accounts_info["base_token_mint"], - accounts_info["quote_token_mint"], + token_info.mint, + SystemAddresses.SOL_MINT, accounts_info["program"], ] @@ -328,8 +325,7 @@ class LetsBonkInstructionBuilder(InstructionBuilder): self, account: Pubkey, mint: Pubkey, - owner: Pubkey, - address_provider: AddressProvider + owner: Pubkey ) -> Instruction: """Create an InitializeAccount instruction for the Token Program. @@ -337,7 +333,6 @@ class LetsBonkInstructionBuilder(InstructionBuilder): account: The account to initialize mint: The token mint owner: The account owner - address_provider: Platform address provider Returns: Instruction for initializing the account @@ -362,8 +357,7 @@ class LetsBonkInstructionBuilder(InstructionBuilder): self, account: Pubkey, destination: Pubkey, - owner: Pubkey, - address_provider: AddressProvider + owner: Pubkey ) -> Instruction: """Create a CloseAccount instruction for the Token Program. @@ -371,7 +365,6 @@ class LetsBonkInstructionBuilder(InstructionBuilder): account: The account to close destination: Where to send the remaining lamports owner: The account owner (must sign) - address_provider: Platform address provider Returns: Instruction for closing the account diff --git a/src/platforms/pumpfun/instruction_builder.py b/src/platforms/pumpfun/instruction_builder.py index 474dfdc..8fba465 100644 --- a/src/platforms/pumpfun/instruction_builder.py +++ b/src/platforms/pumpfun/instruction_builder.py @@ -1,14 +1,17 @@ """ -Pump.Fun implementation of InstructionBuilder interface. +LetsBonk implementation of InstructionBuilder interface. -This module builds pump.fun-specific buy and sell instructions +This module builds LetsBonk (Raydium LaunchLab) specific buy and sell instructions by implementing the InstructionBuilder interface with IDL-based discriminators. """ +import hashlib import struct +import time from solders.instruction import AccountMeta, Instruction from solders.pubkey import Pubkey +from solders.system_program import CreateAccountWithSeedParams, create_account_with_seed from spl.token.instructions import create_idempotent_associated_token_account from core.pubkeys import TOKEN_DECIMALS, SystemAddresses @@ -19,28 +22,28 @@ from utils.logger import get_logger logger = get_logger(__name__) -class PumpFunInstructionBuilder(InstructionBuilder): - """Pump.Fun implementation of InstructionBuilder interface with IDL-based discriminators.""" +class LetsBonkInstructionBuilder(InstructionBuilder): + """LetsBonk (Raydium LaunchLab) implementation of InstructionBuilder interface with IDL-based discriminators.""" def __init__(self, idl_parser: IDLParser): - """Initialize pump.fun instruction builder with injected IDL parser. + """Initialize LetsBonk instruction builder with injected IDL parser. Args: - idl_parser: Pre-loaded IDL parser for pump.fun platform + idl_parser: Pre-loaded IDL parser for LetsBonk platform """ self._idl_parser = idl_parser # Get discriminators from injected IDL parser discriminators = self._idl_parser.get_instruction_discriminators() - self._buy_discriminator = discriminators["buy"] - self._sell_discriminator = discriminators["sell"] + self._buy_exact_in_discriminator = discriminators["buy_exact_in"] + self._sell_exact_in_discriminator = discriminators["sell_exact_in"] - logger.info("Pump.Fun instruction builder initialized with injected IDL parser") + logger.info("LetsBonk instruction builder initialized with injected IDL parser") @property def platform(self) -> Platform: """Get the platform this builder serves.""" - return Platform.PUMP_FUN + return Platform.LETS_BONK async def build_buy_instruction( self, @@ -50,7 +53,7 @@ class PumpFunInstructionBuilder(InstructionBuilder): minimum_amount_out: int, address_provider: AddressProvider ) -> list[Instruction]: - """Build buy instruction(s) for pump.fun. + """Build buy instruction(s) for LetsBonk using buy_exact_in. Args: token_info: Token information @@ -67,38 +70,71 @@ class PumpFunInstructionBuilder(InstructionBuilder): # Get all required accounts accounts_info = address_provider.get_buy_instruction_accounts(token_info, user) - # 1. Create idempotent ATA instruction (won't fail if ATA already exists) + # 1. Create idempotent ATA for base token ata_instruction = create_idempotent_associated_token_account( user, # payer - user, # owner + user, # owner token_info.mint, # mint SystemAddresses.TOKEN_PROGRAM, # token program ) instructions.append(ata_instruction) - # 2. Build buy instruction + # 2. Create WSOL account with seed (temporary account for the transaction) + wsol_seed = self._generate_wsol_seed(user) + wsol_account = address_provider.create_wsol_account_with_seed(user, wsol_seed) + + # Account creation cost + amount to spend + account_creation_lamports = 2_039_280 # Standard account creation cost + total_lamports = amount_in + account_creation_lamports + + create_wsol_ix = create_account_with_seed( + CreateAccountWithSeedParams( + from_pubkey=user, + to_pubkey=wsol_account, + base=user, + seed=wsol_seed, + lamports=total_lamports, + space=165, # Size of a token account + owner=SystemAddresses.TOKEN_PROGRAM + ) + ) + instructions.append(create_wsol_ix) + + # 3. Initialize WSOL account + initialize_wsol_ix = self._create_initialize_account_instruction( + wsol_account, + SystemAddresses.SOL_MINT, + user + ) + instructions.append(initialize_wsol_ix) + + # 4. Build buy_exact_in instruction with correct account ordering + # Based on the IDL and manual examples, the account order should be: buy_accounts = [ - AccountMeta(pubkey=accounts_info["global"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["fee"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["mint"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["bonding_curve"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["associated_bonding_curve"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["user_token_account"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["user"], is_signer=True, is_writable=True), - AccountMeta(pubkey=accounts_info["system_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["token_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["creator_vault"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["event_authority"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["global_volume_accumulator"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["user_volume_accumulator"], is_signer=False, is_writable=True), + AccountMeta(pubkey=user, is_signer=True, is_writable=True), # payer + AccountMeta(pubkey=accounts_info["authority"], is_signer=False, is_writable=False), # authority + AccountMeta(pubkey=accounts_info["global_config"], is_signer=False, is_writable=False), # global_config + AccountMeta(pubkey=accounts_info["platform_config"], is_signer=False, is_writable=False), # platform_config + AccountMeta(pubkey=accounts_info["pool_state"], is_signer=False, is_writable=True), # pool_state + AccountMeta(pubkey=accounts_info["user_base_token"], is_signer=False, is_writable=True), # user_base_token + AccountMeta(pubkey=wsol_account, is_signer=False, is_writable=True), # user_quote_token (WSOL account) + AccountMeta(pubkey=accounts_info["base_vault"], is_signer=False, is_writable=True), # base_vault + AccountMeta(pubkey=accounts_info["quote_vault"], is_signer=False, is_writable=True), # quote_vault + AccountMeta(pubkey=token_info.mint, is_signer=False, is_writable=False), # base_token_mint + AccountMeta(pubkey=SystemAddresses.SOL_MINT, is_signer=False, is_writable=False), # quote_token_mint + AccountMeta(pubkey=SystemAddresses.TOKEN_PROGRAM, is_signer=False, is_writable=False), # base_token_program + AccountMeta(pubkey=SystemAddresses.TOKEN_PROGRAM, is_signer=False, is_writable=False), # quote_token_program + AccountMeta(pubkey=accounts_info["event_authority"], is_signer=False, is_writable=False), # event_authority + AccountMeta(pubkey=accounts_info["program"], is_signer=False, is_writable=False), # program ] - # Build instruction data: discriminator + token_amount + max_sol_cost + # Build instruction data: discriminator + amount_in + minimum_amount_out + share_fee_rate + SHARE_FEE_RATE = 0 # No sharing fee instruction_data = ( - self._buy_discriminator + - struct.pack(" list[Instruction]: - """Build sell instruction(s) for pump.fun. + """Build sell instruction(s) for LetsBonk using sell_exact_in. Args: token_info: Token information @@ -135,27 +179,60 @@ class PumpFunInstructionBuilder(InstructionBuilder): # Get all required accounts accounts_info = address_provider.get_sell_instruction_accounts(token_info, user) - # Build sell instruction accounts + # 1. Create WSOL account with seed (to receive SOL) + wsol_seed = self._generate_wsol_seed(user) + wsol_account = address_provider.create_wsol_account_with_seed(user, wsol_seed) + + # Minimal account creation cost + account_creation_lamports = 2_039_280 + + create_wsol_ix = create_account_with_seed( + CreateAccountWithSeedParams( + from_pubkey=user, + to_pubkey=wsol_account, + base=user, + seed=wsol_seed, + lamports=account_creation_lamports, + space=165, # Size of a token account + owner=SystemAddresses.TOKEN_PROGRAM + ) + ) + instructions.append(create_wsol_ix) + + # 2. Initialize WSOL account + initialize_wsol_ix = self._create_initialize_account_instruction( + wsol_account, + SystemAddresses.SOL_MINT, + user + ) + instructions.append(initialize_wsol_ix) + + # 3. Build sell_exact_in instruction with correct account ordering sell_accounts = [ - AccountMeta(pubkey=accounts_info["global"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["fee"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["mint"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["bonding_curve"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["associated_bonding_curve"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["user_token_account"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["user"], is_signer=True, is_writable=True), - AccountMeta(pubkey=accounts_info["system_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["creator_vault"], is_signer=False, is_writable=True), - AccountMeta(pubkey=accounts_info["token_program"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["event_authority"], is_signer=False, is_writable=False), - AccountMeta(pubkey=accounts_info["program"], is_signer=False, is_writable=False), + AccountMeta(pubkey=user, is_signer=True, is_writable=True), # payer + AccountMeta(pubkey=accounts_info["authority"], is_signer=False, is_writable=False), # authority + AccountMeta(pubkey=accounts_info["global_config"], is_signer=False, is_writable=False), # global_config + AccountMeta(pubkey=accounts_info["platform_config"], is_signer=False, is_writable=False), # platform_config + AccountMeta(pubkey=accounts_info["pool_state"], is_signer=False, is_writable=True), # pool_state + AccountMeta(pubkey=accounts_info["user_base_token"], is_signer=False, is_writable=True), # user_base_token (tokens being sold) + AccountMeta(pubkey=wsol_account, is_signer=False, is_writable=True), # user_quote_token (WSOL received) + AccountMeta(pubkey=accounts_info["base_vault"], is_signer=False, is_writable=True), # base_vault (receives tokens) + AccountMeta(pubkey=accounts_info["quote_vault"], is_signer=False, is_writable=True), # quote_vault (sends WSOL) + AccountMeta(pubkey=token_info.mint, is_signer=False, is_writable=False), # base_token_mint + AccountMeta(pubkey=SystemAddresses.SOL_MINT, is_signer=False, is_writable=False), # quote_token_mint + AccountMeta(pubkey=SystemAddresses.TOKEN_PROGRAM, is_signer=False, is_writable=False), # base_token_program + AccountMeta(pubkey=SystemAddresses.TOKEN_PROGRAM, is_signer=False, is_writable=False), # quote_token_program + AccountMeta(pubkey=accounts_info["event_authority"], is_signer=False, is_writable=False), # event_authority + AccountMeta(pubkey=accounts_info["program"], is_signer=False, is_writable=False), # program ] - # Build instruction data: discriminator + token_amount + min_sol_output + # Build instruction data: discriminator + amount_in + minimum_amount_out + share_fee_rate + SHARE_FEE_RATE = 0 # No sharing fee instruction_data = ( - self._sell_discriminator + - struct.pack(" str: + """Generate a unique seed for WSOL account creation. + + Args: + user: User's wallet address + + Returns: + Unique seed string for WSOL account + """ + # Generate a unique seed based on timestamp and user pubkey + seed_data = f"{int(time.time())}{user!s}" + return hashlib.sha256(seed_data.encode()).hexdigest()[:32] + + def _create_initialize_account_instruction( + self, + account: Pubkey, + mint: Pubkey, + owner: Pubkey + ) -> Instruction: + """Create an InitializeAccount instruction for the Token Program. + + Args: + account: The account to initialize + mint: The token mint + owner: The account owner + + Returns: + Instruction for initializing the account + """ + accounts = [ + AccountMeta(pubkey=account, is_signer=False, is_writable=True), + AccountMeta(pubkey=mint, is_signer=False, is_writable=False), + AccountMeta(pubkey=owner, is_signer=False, is_writable=False), + AccountMeta(pubkey=SystemAddresses.RENT, is_signer=False, is_writable=False), + ] + + # InitializeAccount instruction discriminator (instruction 1 in Token Program) + data = bytes([1]) + + return Instruction( + program_id=SystemAddresses.TOKEN_PROGRAM, + data=data, + accounts=accounts + ) + + def _create_close_account_instruction( + self, + account: Pubkey, + destination: Pubkey, + owner: Pubkey + ) -> Instruction: + """Create a CloseAccount instruction for the Token Program. + + Args: + account: The account to close + destination: Where to send the remaining lamports + owner: The account owner (must sign) + + Returns: + Instruction for closing the account + """ + accounts = [ + AccountMeta(pubkey=account, is_signer=False, is_writable=True), + AccountMeta(pubkey=destination, is_signer=False, is_writable=True), + AccountMeta(pubkey=owner, is_signer=True, is_writable=False), + ] + + # CloseAccount instruction discriminator (instruction 9 in Token Program) + data = bytes([9]) + + return Instruction( + program_id=SystemAddresses.TOKEN_PROGRAM, + data=data, + accounts=accounts + ) + def calculate_token_amount_raw(self, token_amount_decimal: float) -> int: """Convert decimal token amount to raw token units. diff --git a/trades/trades.log b/trades/trades.log index d226794..f1df671 100644 --- a/trades/trades.log +++ b/trades/trades.log @@ -1,2 +1,4 @@ {"timestamp": "2025-04-24T20:30:13.087092", "action": "buy", "token_address": "DWMUmRQUZPCBA1gwdDxTJuz6JHnQkREiWMyQpsKWGp9v", "symbol": "U8", "price": 5e-06, "amount": 20, "tx_hash": "3JvdfCep45PUB6rCcH4dB2NuwvFP8n67SCUxqJMt4MuN5ekHYc6J27aCUfwNUK3hh5rSyKNYAWXya5vQAT2qQivB"} {"timestamp": "2025-04-24T20:30:32.759177", "action": "sell", "token_address": "DWMUmRQUZPCBA1gwdDxTJuz6JHnQkREiWMyQpsKWGp9v", "symbol": "U8", "price": 3.805530050663904e-08, "amount": 20.0, "tx_hash": "5cveLfU7XhPNCPMCZfTXyugJpmAQNmi7zr81PSqs8DsP1T2swYFjJwaB5hNSf3kFPfRzgzd7QZBVaZLd5MqsJevB"} +{"timestamp": "2025-08-02T15:37:04.403139", "action": "buy", "platform": "lets_bonk", "token_address": "7o5FtYXxpX6sqtcJJ3ES4DiWt4C9HnHzKuHrZPpjbonk", "symbol": "pants", "price": 5e-06, "amount": 20, "tx_hash": "26Uu4rZ1PcnzioHwWAh3Reca4MMNZjgiQbmihjisiurpv4xcHFtvSptZoV3zUkS5bdqZ4zpLeGb46J4bWyczu6FY"} +{"timestamp": "2025-08-02T15:37:21.984944", "action": "sell", "platform": "lets_bonk", "token_address": "7o5FtYXxpX6sqtcJJ3ES4DiWt4C9HnHzKuHrZPpjbonk", "symbol": "pants", "price": 2.7959121193874663e-08, "amount": 3712.914779, "tx_hash": "5zXxjxHZuWXxGih1Aca6HoFkybiyHT4jPghhXCg3NbkyD3nuzzaJtmjg4mnoEQDeTn64b1cUZMawhmYbeq3cwjfj"}