mirror of
https://github.com/chainstacklabs/pumpfun-bonkfun-bot.git
synced 2026-07-29 08:17:45 +00:00
02343b775b
Refresh the vendored IDLs from pump-fun/pump-public-docs @ 9c82f61 and move all pump.fun trading onto the v2 instruction interface. This is required, not optional: legacy buy/sell cannot trade coins paired against a quote asset other than SOL, and USDC is already whitelisted in the on-chain Global account. Protocol changes absorbed: - buy_v2 (27 accounts) / sell_v2 (26 accounts) replace the legacy instructions. Every account is mandatory and the order is identical for all coins, so the conditional cashback/mayhem account lists are gone. Legacy remains available via PumpFunInstructionBuilder(use_legacy_instructions=True). - BondingCurve is 151 bytes: virtual_sol_reserves -> virtual_quote_reserves, real_sol_reserves -> real_quote_reserves, plus quote_mint at offset 83. Old field names are kept as aliases so existing callers keep working. - v2 instruction data drops the track_volume OptionBool; amounts are in the quote mint's raw units rather than always lamports. - create_v2 carries a non-SOL quote mint as optional remaining accounts 17-19, and CreateEvent gained quote_mint, so extreme_fast_mode can resolve the quote asset without an extra fetch. USDC support: new trade.quote_amounts and filters.allowed_quote_mints config, accepting "sol"/"usdc" aliases or raw mints. Amounts are per-quote-mint because 1 USDC and 1 SOL are not interchangeable. A coin whose quote mint has no configured amount is skipped rather than traded at the wrong size, so SOL-only configs are unaffected. Bug fixes found while verifying: - The logs and blocks listeners set no websocket max_size, so any frame over 1 MiB closed the connection with 1009 and the token in it was lost. Raised to 32 MiB. - PumpSwap priced against the raw quote vault balance, ignoring the new Pool.virtual_quote_reserves (i128 at offset 245; live pools are 301 bytes). Upstream's note that this field is 0 everywhere is out of date: a live pool carries 17.58 SOL against a 148 SOL vault, a 10.15% price error. - The seller read curve state once at confirmed commitment and silently fell back to create-time values, risking a stale creator_vault and ConstraintSeeds. It now retries at processed, matching the buyer. - Account cleanup would burn wrapped SOL when force_burn was set, destroying value that closing the account returns. WSOL is now closed without burning. - The mint scripts treated a landed transaction as a successful one, so a reverted buy printed as success. They now assert the on-chain result. Compute unit limits retuned from mainnet measurements: buy 100k -> 180k, sell 60k -> 120k. Mint-and-buy is no longer atomic, because create_v2 plus buy_v2 exceeds the 1232-byte transaction limit; both mint scripts send two transactions. Adds learning-examples/pump_v2.py as one shared, standalone v2 toolkit for the example scripts, and three verification scripts: an offline layout check against the IDL, a no-funds mainnet simulation, and a live listener matrix that buys, sells and closes the ATA per listener. Verified on mainnet: all four listeners (geyser, logs, blocks, pumpportal) and all eight example scripts completed a real buy, sell and ATA close, each confirmed by reading the transaction result back rather than trusting confirmation alone. The USDC path is verified structurally only; no USDC-paired coin could be found on-chain to exercise it. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
503 lines
17 KiB
Python
503 lines
17 KiB
Python
"""Self-contained pump.fun v2 trade helpers for the learning examples.
|
|
|
|
The bonding-curve program's `buy_v2` / `sell_v2` instructions take 27 and 26
|
|
mandatory accounts in a fixed order, identical for every coin — SOL-paired or
|
|
USDC-paired, mayhem or not, cashback or not. Encoding that once here keeps the
|
|
example scripts from each carrying their own copy of the list, which is how they
|
|
drift out of sync with the program.
|
|
|
|
Deliberately standalone: it imports nothing from `src/`, so the examples stay
|
|
readable on their own. `learning-examples/verify_v2_account_layout.py` checks the
|
|
layout below against `idl/pump_fun_idl.json`.
|
|
|
|
Docs: BUY.md, SELL.md and COIN_CREATION.md under docs/instructions in
|
|
github.com/pump-fun/pump-public-docs
|
|
"""
|
|
|
|
import secrets
|
|
import struct
|
|
|
|
from construct import Flag, Int64ul, Struct
|
|
from solders.instruction import AccountMeta, Instruction
|
|
from solders.pubkey import Pubkey
|
|
|
|
# Programs and well-known accounts
|
|
PUMP_PROGRAM = Pubkey.from_string("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P")
|
|
PUMP_GLOBAL = Pubkey.from_string("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf")
|
|
PUMP_EVENT_AUTHORITY = Pubkey.from_string(
|
|
"Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1"
|
|
)
|
|
PUMP_FEE_PROGRAM = Pubkey.from_string("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ")
|
|
SYSTEM_PROGRAM = Pubkey.from_string("11111111111111111111111111111111")
|
|
TOKEN_PROGRAM = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
|
|
TOKEN_2022_PROGRAM = Pubkey.from_string("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb")
|
|
ASSOCIATED_TOKEN_PROGRAM = Pubkey.from_string(
|
|
"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
|
|
)
|
|
|
|
# Quote mints. bonding_curve.quote_mint is Pubkey::default() for SOL-paired
|
|
# coins, but the v2 instructions expect wrapped SOL to be passed explicitly.
|
|
DEFAULT_PUBKEY = Pubkey.from_string("11111111111111111111111111111111")
|
|
WSOL_MINT = Pubkey.from_string("So11111111111111111111111111111111111111112")
|
|
USDC_MINT = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
|
|
QUOTE_DECIMALS = {WSOL_MINT: 9, USDC_MINT: 6}
|
|
|
|
TOKEN_DECIMALS = 6
|
|
LAMPORTS_PER_SOL = 1_000_000_000
|
|
|
|
# Account discriminator for the BondingCurve account.
|
|
BONDING_CURVE_DISCRIMINATOR = struct.pack("<Q", 6966180631402821399)
|
|
|
|
# Instruction discriminators (first 8 bytes of sha256("global:<name>")).
|
|
BUY_V2_DISCRIMINATOR = bytes([184, 23, 238, 97, 103, 197, 211, 61])
|
|
SELL_V2_DISCRIMINATOR = bytes([93, 246, 130, 60, 231, 233, 64, 178])
|
|
|
|
# Fee recipients: 8 normal (non-mayhem coins), 8 reserved (mayhem coins),
|
|
# 8 buyback (every coin). See FEE_RECIPIENTS.md in the pump-fun public docs.
|
|
NORMAL_FEE_RECIPIENTS = [
|
|
Pubkey.from_string("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV"),
|
|
Pubkey.from_string("7VtfL8fvgNfhz17qKRMjzQEXgbdpnHHHQRh54R9jP2RJ"),
|
|
Pubkey.from_string("7hTckgnGnLQR6sdH7YkqFTAA7VwTfYFaZ6EhEsU3saCX"),
|
|
Pubkey.from_string("9rPYyANsfQZw3DnDmKE3YCQF5E8oD89UXoHn9JFEhJUz"),
|
|
Pubkey.from_string("AVmoTthdrX6tKt4nDjco2D775W2YK3sDhxPcMmzUAmTY"),
|
|
Pubkey.from_string("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM"),
|
|
Pubkey.from_string("FWsW1xNtWscwNmKv6wVsU1iTzRN6wmmk3MjxRP5tT7hz"),
|
|
Pubkey.from_string("G5UZAVbAf46s7cKWoyKu8kYTip9DGTpbLZ2qa9Aq69dP"),
|
|
]
|
|
RESERVED_FEE_RECIPIENTS = [
|
|
Pubkey.from_string("GesfTA3X2arioaHp8bbKdjG9vJtskViWACZoYvxp4twS"),
|
|
Pubkey.from_string("4budycTjhs9fD6xw62VBducVTNgMgJJ5BgtKq7mAZwn6"),
|
|
Pubkey.from_string("8SBKzEQU4nLSzcwF4a74F2iaUDQyTfjGndn6qUWBnrpR"),
|
|
Pubkey.from_string("4UQeTP1T39KZ9Sfxzo3WR5skgsaP6NZa87BAkuazLEKH"),
|
|
Pubkey.from_string("8sNeir4QsLsJdYpc9RZacohhK1Y5FLU3nC5LXgYB4aa6"),
|
|
Pubkey.from_string("Fh9HmeLNUMVCvejxCtCL2DbYaRyBFVJ5xrWkLnMH6fdk"),
|
|
Pubkey.from_string("463MEnMeGyJekNZFQSTUABBEbLnvMTALbT6ZmsxAbAdq"),
|
|
Pubkey.from_string("6AUH3WEHucYZyC61hqpqYUWVto5qA5hjHuNQ32GNnNxA"),
|
|
]
|
|
BUYBACK_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"),
|
|
]
|
|
|
|
|
|
def normalize_quote_mint(quote_mint: Pubkey | None) -> Pubkey:
|
|
"""Map a curve's raw quote_mint onto the mint to pass to the instruction.
|
|
|
|
Args:
|
|
quote_mint: Raw value read from the bonding curve, or None
|
|
|
|
Returns:
|
|
Wrapped SOL for SOL-paired coins, otherwise the mint unchanged
|
|
"""
|
|
if quote_mint is None or quote_mint == DEFAULT_PUBKEY:
|
|
return WSOL_MINT
|
|
return quote_mint
|
|
|
|
|
|
def is_sol_paired(quote_mint: Pubkey | None) -> bool:
|
|
"""Whether a coin settles in native SOL.
|
|
|
|
Args:
|
|
quote_mint: Raw or normalized quote mint
|
|
|
|
Returns:
|
|
True if the coin is SOL-paired
|
|
"""
|
|
return normalize_quote_mint(quote_mint) == WSOL_MINT
|
|
|
|
|
|
def quote_units(quote_mint: Pubkey) -> int:
|
|
"""Raw units per whole unit of a quote mint.
|
|
|
|
Args:
|
|
quote_mint: Quote mint address
|
|
|
|
Returns:
|
|
1e9 for SOL, 1e6 for USDC
|
|
"""
|
|
return 10 ** QUOTE_DECIMALS.get(quote_mint, 9)
|
|
|
|
|
|
def quote_token_program(quote_mint: Pubkey) -> Pubkey:
|
|
"""Token program owning a quote mint.
|
|
|
|
Args:
|
|
quote_mint: Quote mint address
|
|
|
|
Returns:
|
|
Token program id (both current quote mints are legacy SPL Token)
|
|
"""
|
|
return TOKEN_PROGRAM
|
|
|
|
|
|
def find_bonding_curve(mint: Pubkey) -> Pubkey:
|
|
"""Derive the bonding curve PDA.
|
|
|
|
Args:
|
|
mint: Base token mint
|
|
|
|
Returns:
|
|
Bonding curve address
|
|
"""
|
|
return Pubkey.find_program_address([b"bonding-curve", bytes(mint)], PUMP_PROGRAM)[0]
|
|
|
|
|
|
def find_creator_vault(creator: Pubkey) -> Pubkey:
|
|
"""Derive the creator vault PDA.
|
|
|
|
Args:
|
|
creator: Coin creator
|
|
|
|
Returns:
|
|
Creator vault address
|
|
"""
|
|
return Pubkey.find_program_address(
|
|
[b"creator-vault", bytes(creator)], PUMP_PROGRAM
|
|
)[0]
|
|
|
|
|
|
def find_global_volume_accumulator() -> Pubkey:
|
|
"""Derive the global volume accumulator PDA.
|
|
|
|
Returns:
|
|
Global volume accumulator address
|
|
"""
|
|
return Pubkey.find_program_address([b"global_volume_accumulator"], PUMP_PROGRAM)[0]
|
|
|
|
|
|
def find_user_volume_accumulator(user: Pubkey) -> Pubkey:
|
|
"""Derive a user's volume accumulator PDA.
|
|
|
|
Args:
|
|
user: User wallet
|
|
|
|
Returns:
|
|
User volume accumulator address
|
|
"""
|
|
return Pubkey.find_program_address(
|
|
[b"user_volume_accumulator", bytes(user)], PUMP_PROGRAM
|
|
)[0]
|
|
|
|
|
|
def find_fee_config() -> Pubkey:
|
|
"""Derive the fee config PDA (under the pump fees program).
|
|
|
|
Returns:
|
|
Fee config address
|
|
"""
|
|
return Pubkey.find_program_address(
|
|
[b"fee_config", bytes(PUMP_PROGRAM)], PUMP_FEE_PROGRAM
|
|
)[0]
|
|
|
|
|
|
def find_sharing_config(base_mint: Pubkey) -> Pubkey:
|
|
"""Derive the creator-fee sharing config PDA (under the pump fees program).
|
|
|
|
Mandatory on buy_v2/sell_v2.
|
|
|
|
Args:
|
|
base_mint: Base token mint
|
|
|
|
Returns:
|
|
Sharing config address
|
|
"""
|
|
return Pubkey.find_program_address(
|
|
[b"sharing-config", bytes(base_mint)], PUMP_FEE_PROGRAM
|
|
)[0]
|
|
|
|
|
|
def find_associated_token_account(
|
|
owner: Pubkey, mint: Pubkey, token_program: Pubkey
|
|
) -> Pubkey:
|
|
"""Derive an associated token account address.
|
|
|
|
Args:
|
|
owner: ATA owner (may be a PDA)
|
|
mint: Token mint
|
|
token_program: Token program owning the mint
|
|
|
|
Returns:
|
|
Associated token account address
|
|
"""
|
|
return Pubkey.find_program_address(
|
|
[bytes(owner), bytes(token_program), bytes(mint)],
|
|
ASSOCIATED_TOKEN_PROGRAM,
|
|
)[0]
|
|
|
|
|
|
def pick_fee_recipient(*, is_mayhem_mode: bool) -> Pubkey:
|
|
"""Pick a fee_recipient from the set the program expects for this coin.
|
|
|
|
Args:
|
|
is_mayhem_mode: Whether the coin is in mayhem mode
|
|
|
|
Returns:
|
|
A fee recipient address
|
|
"""
|
|
pool = RESERVED_FEE_RECIPIENTS if is_mayhem_mode else NORMAL_FEE_RECIPIENTS
|
|
return secrets.choice(pool)
|
|
|
|
|
|
def pick_buyback_fee_recipient() -> Pubkey:
|
|
"""Pick a buyback fee recipient, required on every v2 trade.
|
|
|
|
Returns:
|
|
A buyback fee recipient address
|
|
"""
|
|
return secrets.choice(BUYBACK_FEE_RECIPIENTS)
|
|
|
|
|
|
class BondingCurveState:
|
|
"""Parsed pump.fun BondingCurve account.
|
|
|
|
The account is 151 bytes: the 115-byte documented struct followed by
|
|
reserved padding. The SOL-named reserve fields were renamed to quote fields
|
|
when non-SOL quote assets landed; the old names are kept as aliases.
|
|
"""
|
|
|
|
_STRUCT = Struct(
|
|
"virtual_token_reserves" / Int64ul,
|
|
"virtual_quote_reserves" / Int64ul,
|
|
"real_token_reserves" / Int64ul,
|
|
"real_quote_reserves" / Int64ul,
|
|
"token_total_supply" / Int64ul,
|
|
"complete" / Flag,
|
|
)
|
|
# Byte offsets from the start of the account, discriminator included.
|
|
_CREATOR_OFFSET = 49
|
|
_MAYHEM_OFFSET = 81
|
|
_CASHBACK_OFFSET = 82
|
|
_QUOTE_MINT_OFFSET = 83
|
|
|
|
def __init__(self, data: bytes) -> None:
|
|
"""Parse bonding curve account data.
|
|
|
|
Args:
|
|
data: Raw account data including the 8-byte discriminator
|
|
|
|
Raises:
|
|
ValueError: If the discriminator is wrong or the data is truncated
|
|
"""
|
|
if len(data) < 8:
|
|
raise ValueError("Data too short to contain discriminator")
|
|
if data[:8] != BONDING_CURVE_DISCRIMINATOR:
|
|
raise ValueError("Invalid curve state discriminator")
|
|
|
|
self.__dict__.update(self._STRUCT.parse(data[8:]))
|
|
|
|
# Aliases for the pre-rename field names.
|
|
self.virtual_sol_reserves = self.virtual_quote_reserves
|
|
self.real_sol_reserves = self.real_quote_reserves
|
|
|
|
self.creator = self._read_pubkey(data, self._CREATOR_OFFSET)
|
|
self.is_mayhem_mode = self._read_flag(data, self._MAYHEM_OFFSET)
|
|
self.is_cashback_coin = self._read_flag(data, self._CASHBACK_OFFSET)
|
|
raw_quote_mint = self._read_pubkey(data, self._QUOTE_MINT_OFFSET)
|
|
self.quote_mint = normalize_quote_mint(raw_quote_mint)
|
|
self.is_sol_paired = is_sol_paired(raw_quote_mint)
|
|
|
|
@staticmethod
|
|
def _read_pubkey(data: bytes, offset: int) -> Pubkey | None:
|
|
"""Read a 32-byte pubkey if the data extends that far.
|
|
|
|
Args:
|
|
data: Raw account data
|
|
offset: Byte offset
|
|
|
|
Returns:
|
|
Pubkey, or None if the field is absent
|
|
"""
|
|
if len(data) < offset + 32:
|
|
return None
|
|
return Pubkey.from_bytes(data[offset : offset + 32])
|
|
|
|
@staticmethod
|
|
def _read_flag(data: bytes, offset: int) -> bool:
|
|
"""Read a single-byte bool if the data extends that far.
|
|
|
|
Args:
|
|
data: Raw account data
|
|
offset: Byte offset
|
|
|
|
Returns:
|
|
Flag value, or False if the field is absent
|
|
"""
|
|
return bool(data[offset]) if len(data) > offset else False
|
|
|
|
def price_per_token(self) -> float:
|
|
"""Current price in whole quote units per whole token.
|
|
|
|
Returns:
|
|
Price, or 0.0 if reserves are empty
|
|
"""
|
|
if not self.virtual_token_reserves or not self.virtual_quote_reserves:
|
|
return 0.0
|
|
return (
|
|
self.virtual_quote_reserves / 10 ** QUOTE_DECIMALS.get(self.quote_mint, 9)
|
|
) / (self.virtual_token_reserves / 10**TOKEN_DECIMALS)
|
|
|
|
|
|
def build_v2_accounts(
|
|
*,
|
|
base_mint: Pubkey,
|
|
creator: Pubkey,
|
|
user: Pubkey,
|
|
quote_mint: Pubkey,
|
|
base_token_program: Pubkey,
|
|
is_mayhem_mode: bool,
|
|
include_global_volume_accumulator: bool,
|
|
) -> list[AccountMeta]:
|
|
"""Build the ordered account list shared by buy_v2 and sell_v2.
|
|
|
|
Args:
|
|
base_mint: Coin being traded
|
|
creator: Coin creator, from bonding_curve.creator
|
|
user: Signer / trader
|
|
quote_mint: Normalized quote mint (wrapped SOL for SOL-paired coins)
|
|
base_token_program: Token program owning base_mint
|
|
is_mayhem_mode: Selects which fee recipient set to draw from
|
|
include_global_volume_accumulator: True for buy_v2, False for sell_v2
|
|
|
|
Returns:
|
|
Ordered AccountMeta list (27 entries for buy_v2, 26 for sell_v2)
|
|
"""
|
|
quote_program = quote_token_program(quote_mint)
|
|
bonding_curve = find_bonding_curve(base_mint)
|
|
creator_vault = find_creator_vault(creator)
|
|
user_volume_accumulator = find_user_volume_accumulator(user)
|
|
fee_recipient = pick_fee_recipient(is_mayhem_mode=is_mayhem_mode)
|
|
buyback_fee_recipient = pick_buyback_fee_recipient()
|
|
|
|
def ata(owner: Pubkey, mint: Pubkey, program: Pubkey) -> Pubkey:
|
|
return find_associated_token_account(owner, mint, program)
|
|
|
|
accounts = [
|
|
(PUMP_GLOBAL, False),
|
|
(base_mint, False),
|
|
(quote_mint, False),
|
|
(base_token_program, False),
|
|
(quote_program, False),
|
|
(ASSOCIATED_TOKEN_PROGRAM, False),
|
|
(fee_recipient, True),
|
|
(ata(fee_recipient, quote_mint, quote_program), True),
|
|
(buyback_fee_recipient, True),
|
|
(ata(buyback_fee_recipient, quote_mint, quote_program), True),
|
|
(bonding_curve, True),
|
|
(ata(bonding_curve, base_mint, base_token_program), True),
|
|
(ata(bonding_curve, quote_mint, quote_program), True),
|
|
(user, True),
|
|
(ata(user, base_mint, base_token_program), True),
|
|
(ata(user, quote_mint, quote_program), True),
|
|
(creator_vault, True),
|
|
(ata(creator_vault, quote_mint, quote_program), True),
|
|
(find_sharing_config(base_mint), False),
|
|
]
|
|
if include_global_volume_accumulator:
|
|
accounts.append((find_global_volume_accumulator(), False))
|
|
accounts += [
|
|
(user_volume_accumulator, True),
|
|
(ata(user_volume_accumulator, quote_mint, quote_program), True),
|
|
(find_fee_config(), False),
|
|
(PUMP_FEE_PROGRAM, False),
|
|
(SYSTEM_PROGRAM, False),
|
|
(PUMP_EVENT_AUTHORITY, False),
|
|
(PUMP_PROGRAM, False),
|
|
]
|
|
|
|
return [
|
|
AccountMeta(pubkey=pubkey, is_signer=pubkey == user, is_writable=writable)
|
|
for pubkey, writable in accounts
|
|
]
|
|
|
|
|
|
def build_buy_v2_instruction(
|
|
*,
|
|
base_mint: Pubkey,
|
|
creator: Pubkey,
|
|
user: Pubkey,
|
|
token_amount_raw: int,
|
|
max_quote_cost_raw: int,
|
|
quote_mint: Pubkey = WSOL_MINT,
|
|
base_token_program: Pubkey = TOKEN_2022_PROGRAM,
|
|
is_mayhem_mode: bool = False,
|
|
) -> Instruction:
|
|
"""Build a buy_v2 instruction.
|
|
|
|
Args:
|
|
base_mint: Coin to buy
|
|
creator: Coin creator, from bonding_curve.creator
|
|
user: Buyer / signer
|
|
token_amount_raw: Base tokens to buy, in raw units
|
|
max_quote_cost_raw: Spend cap in the quote mint's raw units
|
|
quote_mint: Normalized quote mint
|
|
base_token_program: Token program owning base_mint
|
|
is_mayhem_mode: Whether the coin is in mayhem mode
|
|
|
|
Returns:
|
|
The buy_v2 instruction
|
|
"""
|
|
return Instruction(
|
|
program_id=PUMP_PROGRAM,
|
|
data=BUY_V2_DISCRIMINATOR
|
|
+ struct.pack("<Q", token_amount_raw)
|
|
+ struct.pack("<Q", max_quote_cost_raw),
|
|
accounts=build_v2_accounts(
|
|
base_mint=base_mint,
|
|
creator=creator,
|
|
user=user,
|
|
quote_mint=quote_mint,
|
|
base_token_program=base_token_program,
|
|
is_mayhem_mode=is_mayhem_mode,
|
|
include_global_volume_accumulator=True,
|
|
),
|
|
)
|
|
|
|
|
|
def build_sell_v2_instruction(
|
|
*,
|
|
base_mint: Pubkey,
|
|
creator: Pubkey,
|
|
user: Pubkey,
|
|
token_amount_raw: int,
|
|
min_quote_output_raw: int,
|
|
quote_mint: Pubkey = WSOL_MINT,
|
|
base_token_program: Pubkey = TOKEN_2022_PROGRAM,
|
|
is_mayhem_mode: bool = False,
|
|
) -> Instruction:
|
|
"""Build a sell_v2 instruction.
|
|
|
|
Args:
|
|
base_mint: Coin to sell
|
|
creator: Coin creator, from bonding_curve.creator
|
|
user: Seller / signer
|
|
token_amount_raw: Base tokens to sell, in raw units
|
|
min_quote_output_raw: Minimum acceptable payout in raw quote units
|
|
quote_mint: Normalized quote mint
|
|
base_token_program: Token program owning base_mint
|
|
is_mayhem_mode: Whether the coin is in mayhem mode
|
|
|
|
Returns:
|
|
The sell_v2 instruction
|
|
"""
|
|
return Instruction(
|
|
program_id=PUMP_PROGRAM,
|
|
data=SELL_V2_DISCRIMINATOR
|
|
+ struct.pack("<Q", token_amount_raw)
|
|
+ struct.pack("<Q", min_quote_output_raw),
|
|
accounts=build_v2_accounts(
|
|
base_mint=base_mint,
|
|
creator=creator,
|
|
user=user,
|
|
quote_mint=quote_mint,
|
|
base_token_program=base_token_program,
|
|
is_mayhem_mode=is_mayhem_mode,
|
|
include_global_volume_accumulator=False,
|
|
),
|
|
)
|