From 27929e97be591c7b145a31dc41d9334f5f9e5a15 Mon Sep 17 00:00:00 2001 From: Anton Sauchyk Date: Tue, 28 Apr 2026 22:11:37 +0200 Subject: [PATCH] perf(extreme-fast-mode): use processed commitment + tight retry for BC refresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before: BC fetch in extreme_fast_mode used default (confirmed) commitment and retried up to 10 times with growing backoff (~10s total). Geyser/logs fire on processed, so the BC isn't yet at confirmed commitment when the bot reads it — most fetches failed and burned the full retry budget, adding ~10s to detect→confirmed. After: read at processed commitment to match the listener event horizon, and cap retries at 4 with 150ms gaps (~600ms ceiling). Most reads succeed on the first attempt. Mainnet benchmark (n=4 successful buys, geyser listener): buy_slot − create_slot: min=2 median=3 max=9 (~0.8s — 3.6s on chain) wall detect→confirmed: median 2-3s (1s resolution) Best case 2 slots = next slot after creation + tx propagation. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/core/client.py | 13 +++++++++---- src/platforms/pumpfun/curve_manager.py | 11 +++++++++-- src/trading/platform_aware.py | 13 +++++++------ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/core/client.py b/src/core/client.py index ab8e9d4..0d57067 100644 --- a/src/core/client.py +++ b/src/core/client.py @@ -147,11 +147,15 @@ class SolanaClient: return result["result"] return None - async def get_account_info(self, pubkey: Pubkey) -> dict[str, Any]: + async def get_account_info( + self, pubkey: Pubkey, commitment: str | None = None + ) -> dict[str, Any]: """Get account info from the blockchain. Args: pubkey: Public key of the account + commitment: Optional commitment override (e.g., "processed" for + fresh state right after a geyser event; default "confirmed") Returns: Account info response @@ -161,9 +165,10 @@ class SolanaClient: """ await self._rate_limiter.acquire() client = await self.get_client() - response = await client.get_account_info( - pubkey, encoding="base64" - ) # base64 encoding for account data by default + kwargs: dict[str, Any] = {"encoding": "base64"} + if commitment is not None: + kwargs["commitment"] = commitment + response = await client.get_account_info(pubkey, **kwargs) if not response.value: raise ValueError(f"Account {pubkey} not found") return response.value diff --git a/src/platforms/pumpfun/curve_manager.py b/src/platforms/pumpfun/curve_manager.py index 6b107bc..74fd188 100644 --- a/src/platforms/pumpfun/curve_manager.py +++ b/src/platforms/pumpfun/curve_manager.py @@ -38,17 +38,24 @@ class PumpFunCurveManager(CurveManager): """Get the platform this manager serves.""" return Platform.PUMP_FUN - async def get_pool_state(self, pool_address: Pubkey) -> dict[str, Any]: + async def get_pool_state( + self, pool_address: Pubkey, commitment: str | None = None + ) -> dict[str, Any]: """Get the current state of a pump.fun bonding curve. Args: pool_address: Address of the bonding curve + commitment: Optional commitment override. Pass "processed" right + after a geyser/logs event so the BC is readable in the same + slot it was created (default: confirmed, ~1-2 slot lag). Returns: Dictionary containing bonding curve state data """ try: - account = await self.client.get_account_info(pool_address) + account = await self.client.get_account_info( + pool_address, commitment=commitment + ) if not account.data: raise ValueError(f"No data in bonding curve account {pool_address}") diff --git a/src/trading/platform_aware.py b/src/trading/platform_aware.py index bb8df2c..1ac8ea8 100644 --- a/src/trading/platform_aware.py +++ b/src/trading/platform_aware.py @@ -76,18 +76,19 @@ class PlatformAwareBuyer(Trader): ) 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): + # Use processed commitment — geyser/logs fire on processed + # so the BC is typically readable in the same slot. Most + # listeners only need 1 attempt; pumpportal occasionally + # races the on-chain commit, so allow a few quick retries. + for attempt in range(4): try: pool_state = await curve_manager.get_pool_state( - pool_address + pool_address, commitment="processed" ) break except Exception as inner: # noqa: BLE001 last_err = inner - await asyncio.sleep(min(0.3 + 0.3 * attempt, 1.5)) + await asyncio.sleep(0.15) if pool_state is None: raise last_err or RuntimeError( "pool_state unavailable after retries"