mirror of
https://github.com/chainstacklabs/pumpfun-bonkfun-bot.git
synced 2026-08-13 23:38:04 +00:00
feat(examples): add 18-account buy / 16-17-account sell for 2026-04-28 upgrade
pump.fun program upgrade scheduled for 2026-04-28 16:00 UTC adds one new
mutable account (one of 8 fee recipients) at the end of buy/sell ixs,
after bonding-curve-v2. The new format is already accepted on mainnet
ahead of the cutover, so applying now keeps scripts working on both
sides of the upgrade.
Doc: github.com/pump-fun/pump-public-docs/blob/main/docs/BREAKING_FEE_RECIPIENT.md
Per file:
manual_buy.py — 18-account buy. Also fixes a pre-existing
listener crash on Address-Lookup-Table create
txs (skips them) and adds bool/OptionBool
handling to decode_create_instruction so
create_v2 doesn't raise on its trailing args.
manual_buy_cu_optimized.py — 18-account buy. Bumps the CU-optimization
account-data limit to 16MB (tested) — older
values trigger MaxLoadedAccountsDataSizeExceeded
on Token-2022/cashback coins. Same listener
ALT-skip and decoder bool fixes.
manual_buy_geyser.py — 18-account buy. Re-enables price calculation
and mayhem-mode-aware fee_recipient detection
(was scaffolded out for testing).
manual_sell.py — 16-account non-cashback / 17-account cashback
sell. Reads is_cashback_coin from BC byte 82
and inserts user_volume_accumulator before
bonding-curve-v2 when needed. TOKEN_MINT now
accepts argv[1] override.
Live-verified on mainnet:
buy manual_buy.py 3AVnLC3sdBD598cs3... + 3bWU9bB8U9aemA2BFhS2...
buy manual_buy_cu_optimized 2PfH5rHw62o3KkQ3N4z8...
sell manual_sell.py 3E7dyPBPBRe95BtPQXmb... + 5L4wWSzPV36m6XqXAswB...
All four show the expected 18 (buy) / 16 (sell non-cashback) account count
with one of the 8 BREAKING_FEE_RECIPIENTS as the trailing mutable account.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
9b4841890c
commit
c25e2edb57
@@ -3,6 +3,7 @@ import base64
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import struct
|
||||
|
||||
import base58
|
||||
@@ -43,6 +44,20 @@ SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM = Pubkey.from_string(
|
||||
SOL = Pubkey.from_string("So11111111111111111111111111111111111111112")
|
||||
LAMPORTS_PER_SOL = 1_000_000_000
|
||||
|
||||
# 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 = [
|
||||
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"),
|
||||
]
|
||||
|
||||
# RPC ENDPOINTS
|
||||
RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
|
||||
RPC_WEBSOCKET = os.environ.get("SOLANA_NODE_WSS_ENDPOINT")
|
||||
@@ -295,6 +310,12 @@ async def buy_token(
|
||||
is_signer=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,
|
||||
),
|
||||
]
|
||||
|
||||
discriminator = struct.pack("<Q", 16927863322537952870)
|
||||
@@ -359,16 +380,24 @@ def decode_create_instruction(ix_data, ix_def, accounts):
|
||||
offset = 8 # Skip 8-byte discriminator
|
||||
|
||||
for arg in ix_def["args"]:
|
||||
if arg["type"] == "string":
|
||||
t = arg["type"]
|
||||
if t == "string":
|
||||
length = struct.unpack_from("<I", ix_data, offset)[0]
|
||||
offset += 4
|
||||
value = ix_data[offset : offset + length].decode("utf-8")
|
||||
offset += length
|
||||
elif arg["type"] == "pubkey":
|
||||
elif t == "pubkey":
|
||||
value = base58.b58encode(ix_data[offset : offset + 32]).decode("utf-8")
|
||||
offset += 32
|
||||
elif t == "bool":
|
||||
value = bool(ix_data[offset])
|
||||
offset += 1
|
||||
elif isinstance(t, dict) and "defined" in t:
|
||||
# OptionBool = struct { bool } = 1 byte
|
||||
value = bool(ix_data[offset])
|
||||
offset += 1
|
||||
else:
|
||||
raise ValueError(f"Unsupported type: {arg['type']}")
|
||||
raise ValueError(f"Unsupported type: {t}")
|
||||
|
||||
args[arg["name"]] = value
|
||||
|
||||
@@ -457,12 +486,14 @@ async def listen_for_create_transaction():
|
||||
for instr in idl["instructions"]
|
||||
if instr["name"] == instruction_name
|
||||
)
|
||||
# Skip txs that use Address Lookup Tables — their
|
||||
# instruction account indices reference ALT-loaded keys
|
||||
# not present in transaction.message.account_keys.
|
||||
static_keys = transaction.message.account_keys
|
||||
if any(idx >= len(static_keys) for idx in ix.accounts):
|
||||
continue
|
||||
account_keys = [
|
||||
str(
|
||||
transaction.message.account_keys[
|
||||
index
|
||||
]
|
||||
)
|
||||
str(static_keys[index])
|
||||
for index in ix.accounts
|
||||
]
|
||||
decoded_args = (
|
||||
|
||||
Reference in New Issue
Block a user