feat(examples): apply 18-account buy upgrade to mint_and_buy + pumpswap

Extends the 2026-04-28 program-upgrade prep (commit c25e2ed) to the
remaining trade scripts:
  mint_and_buy.py              — 18-account buy after bonding-curve-v2.
                                 Tested live with a fresh TEST mint (Solscan
                                 sig 62EZFCKVuxvqmzj...), succeeded.
  mint_and_buy_v2.py           — 18-account buy. Tested live with a
                                 mayhem-mode TEST2 token (Token-2022 + 5
                                 mayhem accounts), Solscan sig 44SvQSrhmHy...,
                                 succeeded.
  pumpswap/manual_buy_pumpswap.py / manual_sell_pumpswap.py
                               — Adds the 2 new pump-swap accounts (fee
                                 recipient + its quote-mint ATA) gated behind
                                 INCLUDE_BREAKING_FEE_ACCOUNTS = False; flip
                                 to True after 2026-04-28 16:00 UTC.
                                 Pre-existing program-side overflow at
                                 buy.rs:400 (AnchorError 6023) flagged in
                                 a comment — affects current pump-swap
                                 buys, unrelated to this upgrade.

Both pumpswap scripts now also accept TOKEN_MINT via argv[1].

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Anton Sauchyk
2026-04-27 18:09:14 +02:00
parent c25e2edb57
commit 57ffe8bfbb
4 changed files with 114 additions and 2 deletions
+21
View File
@@ -1,5 +1,6 @@
import asyncio import asyncio
import os import os
import random
import struct import struct
from typing import Final from typing import Final
@@ -46,6 +47,20 @@ PUMP_FEE: Final[Pubkey] = Pubkey.from_string(
PUMP_FEE_PROGRAM: Final[Pubkey] = Pubkey.from_string( PUMP_FEE_PROGRAM: Final[Pubkey] = Pubkey.from_string(
"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"
) )
# 8 breaking-upgrade fee recipients (pump.fun program upgrade 2026-04-28).
# One must be appended (mutable) AFTER bonding-curve-v2 on every buy/sell.
# Doc: github.com/pump-fun/pump-public-docs/blob/main/docs/BREAKING_FEE_RECIPIENT.md
BREAKING_FEE_RECIPIENTS: Final[list[Pubkey]] = [
Pubkey.from_string("5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD"),
Pubkey.from_string("9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7"),
Pubkey.from_string("GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL"),
Pubkey.from_string("3BpXnfJaUTiwXnJNe7Ej1rcbzqTTQUvLShZaWazebsVR"),
Pubkey.from_string("5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6"),
Pubkey.from_string("EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL"),
Pubkey.from_string("5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD"),
Pubkey.from_string("A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"),
]
PUMP_MINT_AUTHORITY: Final[Pubkey] = Pubkey.from_string( PUMP_MINT_AUTHORITY: Final[Pubkey] = Pubkey.from_string(
"TSLvdd1pWpHVjahSpsvCXUbgwsL3JAcvokwaKt1eokM" "TSLvdd1pWpHVjahSpsvCXUbgwsL3JAcvokwaKt1eokM"
) )
@@ -277,6 +292,12 @@ def create_buy_instruction(
is_signer=False, is_signer=False,
is_writable=False, is_writable=False,
), ),
# 18th account: breaking-upgrade fee recipient (mutable) — required from 2026-04-28
AccountMeta(
pubkey=random.choice(BREAKING_FEE_RECIPIENTS),
is_signer=False,
is_writable=True,
),
] ]
# Encode OptionBool for track_volume # Encode OptionBool for track_volume
+21
View File
@@ -1,5 +1,6 @@
import asyncio import asyncio
import os import os
import random
import struct import struct
from typing import Final from typing import Final
@@ -47,6 +48,20 @@ PUMP_FEE: Final[Pubkey] = Pubkey.from_string(
PUMP_FEE_PROGRAM: Final[Pubkey] = Pubkey.from_string( PUMP_FEE_PROGRAM: Final[Pubkey] = Pubkey.from_string(
"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"
) )
# 8 breaking-upgrade fee recipients (pump.fun program upgrade 2026-04-28).
# One must be appended (mutable) AFTER bonding-curve-v2 on every buy/sell.
# Doc: github.com/pump-fun/pump-public-docs/blob/main/docs/BREAKING_FEE_RECIPIENT.md
BREAKING_FEE_RECIPIENTS: Final[list[Pubkey]] = [
Pubkey.from_string("5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD"),
Pubkey.from_string("9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7"),
Pubkey.from_string("GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL"),
Pubkey.from_string("3BpXnfJaUTiwXnJNe7Ej1rcbzqTTQUvLShZaWazebsVR"),
Pubkey.from_string("5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6"),
Pubkey.from_string("EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL"),
Pubkey.from_string("5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD"),
Pubkey.from_string("A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"),
]
PUMP_MINT_AUTHORITY: Final[Pubkey] = Pubkey.from_string( PUMP_MINT_AUTHORITY: Final[Pubkey] = Pubkey.from_string(
"TSLvdd1pWpHVjahSpsvCXUbgwsL3JAcvokwaKt1eokM" "TSLvdd1pWpHVjahSpsvCXUbgwsL3JAcvokwaKt1eokM"
) )
@@ -321,6 +336,12 @@ def create_buy_instruction(
is_signer=False, is_signer=False,
is_writable=False, is_writable=False,
), ),
# 18th account: breaking-upgrade fee recipient (mutable) — required from 2026-04-28
AccountMeta(
pubkey=random.choice(BREAKING_FEE_RECIPIENTS),
is_signer=False,
is_writable=True,
),
] ]
# Encode OptionBool for track_volume # Encode OptionBool for track_volume
@@ -15,6 +15,7 @@ Key concepts demonstrated:
import asyncio import asyncio
import os import os
import random
import struct import struct
import base58 import base58
@@ -43,7 +44,9 @@ load_dotenv()
# ============================================================================ # ============================================================================
RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT") RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
TOKEN_MINT = Pubkey.from_string("...") # Replace with your token mint address import sys
TOKEN_MINT = Pubkey.from_string(sys.argv[1] if len(sys.argv) > 1 else "...") # Pass mint as argv[1]
PRIVATE_KEY = base58.b58decode(os.environ.get("SOLANA_PRIVATE_KEY")) PRIVATE_KEY = base58.b58decode(os.environ.get("SOLANA_PRIVATE_KEY"))
PAYER = Keypair.from_bytes(PRIVATE_KEY) PAYER = Keypair.from_bytes(PRIVATE_KEY)
SLIPPAGE = 0.3 # 30% - maximum acceptable price movement during trade SLIPPAGE = 0.3 # 30% - maximum acceptable price movement during trade
@@ -74,6 +77,21 @@ PUMP_SWAP_EVENT_AUTHORITY = Pubkey.from_string(
) )
PUMP_FEE_PROGRAM = Pubkey.from_string("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ") PUMP_FEE_PROGRAM = Pubkey.from_string("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ")
# 8 breaking-upgrade fee recipients (pump-swap program upgrade 2026-04-28).
# Two new accounts must be appended after pool-v2: the fee recipient (readonly)
# and its quote-mint ATA (mutable).
# Doc: github.com/pump-fun/pump-public-docs/blob/main/docs/BREAKING_FEE_RECIPIENT.md
BREAKING_FEE_RECIPIENTS = [
Pubkey.from_string("5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD"),
Pubkey.from_string("9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7"),
Pubkey.from_string("GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL"),
Pubkey.from_string("3BpXnfJaUTiwXnJNe7Ej1rcbzqTTQUvLShZaWazebsVR"),
Pubkey.from_string("5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6"),
Pubkey.from_string("EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL"),
Pubkey.from_string("5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD"),
Pubkey.from_string("A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"),
]
# ============================================================================ # ============================================================================
# Constants for Account Structure Parsing # Constants for Account Structure Parsing
# ============================================================================ # ============================================================================
@@ -501,6 +519,20 @@ async def buy_pump_swap(
AccountMeta(pubkey=find_fee_config(), is_signer=False, is_writable=False), AccountMeta(pubkey=find_fee_config(), is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_FEE_PROGRAM, is_signer=False, is_writable=False), AccountMeta(pubkey=PUMP_FEE_PROGRAM, 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
# its quote-mint ATA (mutable, last). Set to True to opt in.
# Doc: github.com/pump-fun/pump-public-docs/blob/main/docs/BREAKING_FEE_RECIPIENT.md
INCLUDE_BREAKING_FEE_ACCOUNTS = False # flip True after 2026-04-28 16:00 UTC
if INCLUDE_BREAKING_FEE_ACCOUNTS:
breaking_fee_recipient = random.choice(BREAKING_FEE_RECIPIENTS)
breaking_fee_quote_ata = get_associated_token_address(
breaking_fee_recipient, SOL, SYSTEM_TOKEN_PROGRAM
)
accounts.extend([
AccountMeta(pubkey=breaking_fee_recipient, is_signer=False, is_writable=False),
AccountMeta(pubkey=breaking_fee_quote_ata, is_signer=False, is_writable=True),
])
# Instruction data format: # Instruction data format:
# discriminator (8 bytes) + amount_out (8 bytes) + max_in (8 bytes) + track_volume (1 byte) # discriminator (8 bytes) + amount_out (8 bytes) + max_in (8 bytes) + track_volume (1 byte)
@@ -576,6 +608,12 @@ async def buy_pump_swap(
simulation = await client.simulate_transaction(tx) simulation = await client.simulate_transaction(tx)
if simulation.value.err: if simulation.value.err:
print(f"Simulation error: {simulation.value.err}") print(f"Simulation error: {simulation.value.err}")
for log in (simulation.value.logs or []):
print(f" log: {log}")
# NOTE: pump-swap may throw AnchorError 6023 (Overflow) at buy.rs:400 on
# the dynamic creator-fee calc for some pools. The script's account list
# matches the IDL — the error is from the program itself, likely related
# to the 2026-03 dynamic-fee overhaul. Pending investigation.
return None return None
print( print(
@@ -13,6 +13,7 @@ Key concepts demonstrated:
import asyncio import asyncio
import os import os
import random
import struct import struct
import base58 import base58
@@ -35,7 +36,9 @@ load_dotenv()
# ============================================================================ # ============================================================================
RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT") RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
TOKEN_MINT = Pubkey.from_string("...") # Replace with your token mint address import sys
TOKEN_MINT = Pubkey.from_string(sys.argv[1] if len(sys.argv) > 1 else "...") # Pass mint as argv[1]
PRIVATE_KEY = base58.b58decode(os.environ.get("SOLANA_PRIVATE_KEY")) PRIVATE_KEY = base58.b58decode(os.environ.get("SOLANA_PRIVATE_KEY"))
PAYER = Keypair.from_bytes(PRIVATE_KEY) PAYER = Keypair.from_bytes(PRIVATE_KEY)
SLIPPAGE = 0.25 # 25% - maximum acceptable price movement during trade SLIPPAGE = 0.25 # 25% - maximum acceptable price movement during trade
@@ -66,6 +69,21 @@ PUMP_SWAP_EVENT_AUTHORITY = Pubkey.from_string(
) )
PUMP_FEE_PROGRAM = Pubkey.from_string("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ") PUMP_FEE_PROGRAM = Pubkey.from_string("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ")
# 8 breaking-upgrade fee recipients (pump-swap program upgrade 2026-04-28).
# Two new accounts must be appended after pool-v2: the fee recipient (readonly)
# and its quote-mint ATA (mutable).
# Doc: github.com/pump-fun/pump-public-docs/blob/main/docs/BREAKING_FEE_RECIPIENT.md
BREAKING_FEE_RECIPIENTS = [
Pubkey.from_string("5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD"),
Pubkey.from_string("9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7"),
Pubkey.from_string("GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL"),
Pubkey.from_string("3BpXnfJaUTiwXnJNe7Ej1rcbzqTTQUvLShZaWazebsVR"),
Pubkey.from_string("5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6"),
Pubkey.from_string("EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL"),
Pubkey.from_string("5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD"),
Pubkey.from_string("A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"),
]
# ============================================================================ # ============================================================================
# Constants for Account Structure Parsing # Constants for Account Structure Parsing
# ============================================================================ # ============================================================================
@@ -489,6 +507,20 @@ async def sell_pump_swap(
AccountMeta(pubkey=find_fee_config(), is_signer=False, is_writable=False), AccountMeta(pubkey=find_fee_config(), is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_FEE_PROGRAM, is_signer=False, is_writable=False), AccountMeta(pubkey=PUMP_FEE_PROGRAM, 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
# its quote-mint ATA (mutable, last). Set to True to opt in.
# Doc: github.com/pump-fun/pump-public-docs/blob/main/docs/BREAKING_FEE_RECIPIENT.md
INCLUDE_BREAKING_FEE_ACCOUNTS = False # flip True after 2026-04-28 16:00 UTC
if INCLUDE_BREAKING_FEE_ACCOUNTS:
breaking_fee_recipient = random.choice(BREAKING_FEE_RECIPIENTS)
breaking_fee_quote_ata = get_associated_token_address(
breaking_fee_recipient, SOL, SYSTEM_TOKEN_PROGRAM
)
accounts.extend([
AccountMeta(pubkey=breaking_fee_recipient, is_signer=False, is_writable=False),
AccountMeta(pubkey=breaking_fee_quote_ata, is_signer=False, is_writable=True),
])
# Instruction data format: discriminator (8 bytes) + amount (8 bytes) + min_out (8 bytes) # Instruction data format: discriminator (8 bytes) + amount (8 bytes) + min_out (8 bytes)
# All integers are little-endian (<) # All integers are little-endian (<)