From ad6d2cc75590186307978691ad158188bf1e45cb Mon Sep 17 00:00:00 2001 From: Anton Sauchyk Date: Mon, 27 Apr 2026 18:35:13 +0200 Subject: [PATCH] fix(examples): add pool-v2 account to pumpswap, repair geyser listener MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PumpSwap (manual_buy_pumpswap.py / manual_sell_pumpswap.py) - Real on-chain pump-swap buys use 24 accounts; the IDL is incomplete (lists 23). Missing 24th account is pool-v2 PDA, derived from seed ["pool-v2", base_mint] under the pump-amm program. - Without it, pump-amm throws AnchorError 6023 (Overflow) at buy.rs:400/414 after the trade transfers complete (the source of the earlier "pre-existing overflow" — was actually a missing account, not a math bug). - find_pool_v2(base_mint) added; appended to both buy and sell account lists after fee_program. The April-28 breaking-fee accounts (still gated behind INCLUDE_BREAKING_FEE_ACCOUNTS) come after pool-v2 — matching the doc's "All accounts till bonding-curve-v2 and pool-v2 remain the same." - AsyncClient timeout bumped to 120s so the heavy getProgramAccounts pool lookup doesn't time out on slower RPC paths. - Live-verified on mainnet with 9PwadsGz...pump: buy 2xgdJJsDMzoMyxJzrCATKwGfpX7M... err=None (24 accounts) sell 4vdBkGpCMQYEWncj5k1suGzKPUMQ... err=None (24 accounts) manual_buy_geyser.py - Fixes IndexError when create tx references ALT-loaded keys (matches the same fix applied to manual_buy.py). - Re-enables the 15s sleep + on-chain price/fee_recipient lookup that were commented out for testing — needed for buys on real pools. - Live-verified: 45wSgX2Pu1LLZWLgu3e9qdhRaGJjqwMqQP5HdeV7zMBqNXziB2uEgxQd5bAqvAhBLFnAqzbuK3LAWowhxZxdemJ (18-account buy, err=None) on Token-2022 cashback-enabled INSIDER mint. Co-Authored-By: Claude Opus 4.7 (1M context) --- learning-examples/manual_buy_geyser.py | 11 ++++++++--- .../pumpswap/manual_buy_pumpswap.py | 16 ++++++++++++++-- .../pumpswap/manual_sell_pumpswap.py | 14 +++++++++++++- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/learning-examples/manual_buy_geyser.py b/learning-examples/manual_buy_geyser.py index 42d07cb..a43ec99 100644 --- a/learning-examples/manual_buy_geyser.py +++ b/learning-examples/manual_buy_geyser.py @@ -344,6 +344,11 @@ async def listen_for_create_transaction_geyser(): else: continue + # Skip txs whose create ix references ALT-loaded accounts not in + # msg.account_keys (indices >= len(msg.account_keys)). + if any(idx >= len(msg.account_keys) for idx in ix.accounts): + continue + # Found a create instruction token_data = decode_create_instruction_geyser( ix.data, msg.account_keys, ix.accounts @@ -513,9 +518,9 @@ async def main(): print("New token created:") print(json.dumps(token_data, indent=2)) - # sleep_duration_sec = 15 - # print(f"Waiting for {sleep_duration_sec} seconds for things to stabilize...") - # await asyncio.sleep(sleep_duration_sec) + sleep_duration_sec = 15 + print(f"Waiting {sleep_duration_sec}s for the bonding curve account to propagate...") + await asyncio.sleep(sleep_duration_sec) mint = Pubkey.from_string(token_data["mint"]) bonding_curve = Pubkey.from_string(token_data["bondingCurve"]) diff --git a/learning-examples/pumpswap/manual_buy_pumpswap.py b/learning-examples/pumpswap/manual_buy_pumpswap.py index 85b4b70..fc3c671 100644 --- a/learning-examples/pumpswap/manual_buy_pumpswap.py +++ b/learning-examples/pumpswap/manual_buy_pumpswap.py @@ -283,6 +283,16 @@ def find_fee_config() -> Pubkey: return derived_address +def find_pool_v2(base_mint: Pubkey) -> Pubkey: + """Derive the PDA for the pool-v2 account (per-base-mint), required as the + last "pre-upgrade" account on every pump-swap buy/sell.""" + derived_address, _ = Pubkey.find_program_address( + [b"pool-v2", bytes(base_mint)], + PUMP_AMM_PROGRAM_ID, + ) + return derived_address + + # ============================================================================ # Mayhem Mode Fee Handling # ============================================================================ @@ -518,6 +528,8 @@ async def buy_pump_swap( AccountMeta(pubkey=user_volume_accumulator, is_signer=False, is_writable=True), AccountMeta(pubkey=find_fee_config(), is_signer=False, is_writable=False), AccountMeta(pubkey=PUMP_FEE_PROGRAM, is_signer=False, is_writable=False), + # pool-v2 PDA (per-base-mint) — the last "pre-upgrade" account. + AccountMeta(pubkey=find_pool_v2(base_mint), is_signer=False, is_writable=False), ] # 2 new accounts required from 2026-04-28 16:00 UTC pump-swap program upgrade, # AFTER pool-v2: breaking-upgrade fee recipient (readonly, second-to-last) and @@ -643,9 +655,9 @@ async def buy_pump_swap( async def main() -> None: """Execute the complete buy flow.""" - sol_amount_to_spend = 0.000001 # Amount of SOL to spend on the purchase + sol_amount_to_spend = 0.001 # Amount of SOL to spend on the purchase - async with AsyncClient(RPC_ENDPOINT) as client: + async with AsyncClient(RPC_ENDPOINT, timeout=120) as client: # Step 1: Find the pool address for our token market_address = await get_market_address_by_base_mint( client, TOKEN_MINT, PUMP_AMM_PROGRAM_ID diff --git a/learning-examples/pumpswap/manual_sell_pumpswap.py b/learning-examples/pumpswap/manual_sell_pumpswap.py index b9e175c..7b126da 100644 --- a/learning-examples/pumpswap/manual_sell_pumpswap.py +++ b/learning-examples/pumpswap/manual_sell_pumpswap.py @@ -236,6 +236,16 @@ def find_fee_config() -> Pubkey: return derived_address +def find_pool_v2(base_mint: Pubkey) -> Pubkey: + """Derive the PDA for the pool-v2 account (per-base-mint), required as the + last "pre-upgrade" account on every pump-swap buy/sell.""" + derived_address, _ = Pubkey.find_program_address( + [b"pool-v2", bytes(base_mint)], + PUMP_AMM_PROGRAM_ID, + ) + return derived_address + + # ============================================================================ # Mayhem Mode Fee Handling # ============================================================================ @@ -506,6 +516,8 @@ async def sell_pump_swap( ), AccountMeta(pubkey=find_fee_config(), is_signer=False, is_writable=False), AccountMeta(pubkey=PUMP_FEE_PROGRAM, is_signer=False, is_writable=False), + # pool-v2 PDA (per-base-mint) — the last "pre-upgrade" account. + AccountMeta(pubkey=find_pool_v2(base_mint), is_signer=False, is_writable=False), ] # 2 new accounts required from 2026-04-28 16:00 UTC pump-swap program upgrade, # AFTER pool-v2: breaking-upgrade fee recipient (readonly, second-to-last) and @@ -571,7 +583,7 @@ async def sell_pump_swap( async def main() -> None: """Execute the complete sell flow.""" - async with AsyncClient(RPC_ENDPOINT) as client: + async with AsyncClient(RPC_ENDPOINT, timeout=120) as client: # Step 1: Find the pool address for our token market_address = await get_market_address_by_base_mint( client, TOKEN_MINT, PUMP_AMM_PROGRAM_ID