fix(examples): add pool-v2 account to pumpswap, repair geyser listener

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) <noreply@anthropic.com>
This commit is contained in:
Anton Sauchyk
2026-04-27 18:35:13 +02:00
parent 57ffe8bfbb
commit ad6d2cc755
3 changed files with 35 additions and 6 deletions
+8 -3
View File
@@ -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"])
@@ -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
@@ -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