mirror of
https://github.com/chainstacklabs/pumpfun-bonkfun-bot.git
synced 2026-07-27 23:37:45 +00:00
fix(trading): refresh BC state on extreme_fast_mode buy + letsbonk pumpportal token-program default
Two related fixes from mainnet validation of geyser/logs/blocks/pumpportal: 1. extreme_fast_mode buys previously skipped the BC fetch entirely. After the 2026-04-28 cutover, fee_recipient (mayhem mode) and creator_vault (PFEE-delegated BC.creator) cannot be derived from listener-side data alone — the program rejects with NotAuthorized (0x1770) / ConstraintSeeds (0x7d6). Add a short retry loop that fetches the curve state once before submitting the buy, so the instruction builder picks the correct fee_recipient and creator_vault. The ~150ms cost is small relative to a failed tx and 5s sell wait. 2. LetsBonkPumpPortalProcessor returned TokenInfo without token_program_id. The universal builder then defaulted to Token-2022 and the buy failed with IncorrectProgramId at GetAccountDataSize on the user-ATA create ix. LetsBonk tokens are predominantly regular SPL Token, so default to TOKEN_PROGRAM in the processor. Note: the pumpportal listener still has unrelated data-race issues (the PumpPortal stream sometimes fires before the on-chain BC is RPC-readable, and bondingCurveKey occasionally references a different mint than the detected token). These predate the 2026-04-28 cutover and need a separate fix (e.g., verifying mint+BC consistency on the tx side before submit). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ File: src/platforms/letsbonk/pumpportal_processor.py
|
||||
|
||||
from solders.pubkey import Pubkey
|
||||
|
||||
from core.pubkeys import SystemAddresses
|
||||
from interfaces.core import Platform, TokenInfo
|
||||
from platforms.letsbonk.address_provider import LetsBonkAddressProvider
|
||||
from utils.logger import get_logger
|
||||
@@ -113,6 +114,12 @@ class LetsBonkPumpPortalProcessor:
|
||||
quote_vault=quote_vault,
|
||||
user=user,
|
||||
creator=creator,
|
||||
# PumpPortal data does not carry the token program. LetsBonk
|
||||
# tokens are predominantly regular SPL (Token), not Token-2022.
|
||||
# Default here so the ATA-create ix uses the correct program;
|
||||
# without this the universal builder defaults to Token-2022 and
|
||||
# the buy fails with IncorrectProgramId on GetAccountDataSize.
|
||||
token_program_id=SystemAddresses.TOKEN_PROGRAM,
|
||||
)
|
||||
|
||||
except Exception:
|
||||
|
||||
@@ -3,6 +3,8 @@ Platform-aware trader implementations that use the interface system.
|
||||
Final cleanup removing all platform-specific hardcoding.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from solders.pubkey import Pubkey
|
||||
|
||||
from core.client import SolanaClient
|
||||
@@ -61,6 +63,61 @@ class PlatformAwareBuyer(Trader):
|
||||
# Skip the wait and directly calculate the amount
|
||||
token_amount = self.extreme_fast_token_amount
|
||||
token_price_sol = self.amount / token_amount if token_amount > 0 else 0
|
||||
# Even in extreme_fast_mode, refresh mayhem/cashback/creator from
|
||||
# chain — listeners (especially pumpportal) often don't carry
|
||||
# these, and the program rejects with NotAuthorized (0x1770) /
|
||||
# ConstraintSeeds (0x7d6) when fee_recipient or creator_vault
|
||||
# is wrong. PumpPortal often notifies before the BC account is
|
||||
# readable, so retry briefly. One handful of RPC calls is cheap
|
||||
# relative to a failed buy.
|
||||
try:
|
||||
pool_address = self._get_pool_address(
|
||||
token_info, address_provider
|
||||
)
|
||||
pool_state = None
|
||||
last_err: Exception | None = None
|
||||
# Up to ~6s of retries — pumpportal frequently fires before
|
||||
# the BC account is RPC-readable. Without a settled BC the
|
||||
# bot can't pick the right fee_recipient or creator_vault.
|
||||
for attempt in range(10):
|
||||
try:
|
||||
pool_state = await curve_manager.get_pool_state(
|
||||
pool_address
|
||||
)
|
||||
break
|
||||
except Exception as inner: # noqa: BLE001
|
||||
last_err = inner
|
||||
await asyncio.sleep(min(0.3 + 0.3 * attempt, 1.5))
|
||||
if pool_state is None:
|
||||
raise last_err or RuntimeError(
|
||||
"pool_state unavailable after retries"
|
||||
)
|
||||
token_info.is_mayhem_mode = pool_state.get(
|
||||
"is_mayhem_mode", token_info.is_mayhem_mode
|
||||
)
|
||||
token_info.is_cashback_coin = pool_state.get(
|
||||
"is_cashback_coin", token_info.is_cashback_coin
|
||||
)
|
||||
fresh_creator = pool_state.get("creator")
|
||||
if fresh_creator and hasattr(
|
||||
address_provider, "derive_creator_vault"
|
||||
):
|
||||
from solders.pubkey import Pubkey as _Pubkey
|
||||
|
||||
new_creator = (
|
||||
_Pubkey.from_string(fresh_creator)
|
||||
if isinstance(fresh_creator, str)
|
||||
else fresh_creator
|
||||
)
|
||||
token_info.creator = new_creator
|
||||
token_info.creator_vault = (
|
||||
address_provider.derive_creator_vault(new_creator)
|
||||
)
|
||||
except Exception as e: # noqa: BLE001
|
||||
logger.warning(
|
||||
f"extreme_fast_mode buy: could not refresh curve flags "
|
||||
f"({e}); proceeding with token_info defaults"
|
||||
)
|
||||
else:
|
||||
# Get pool address based on platform using platform-agnostic method
|
||||
pool_address = self._get_pool_address(token_info, address_provider)
|
||||
|
||||
Reference in New Issue
Block a user