Merge pull request #124 from chainstacklabs/feat/upd-buy-instr

Add global_volume_accumulator and user_volume_accumulator to buy instructions (bonding curve, amm)
This commit is contained in:
Anton
2025-08-01 05:33:16 +00:00
committed by GitHub
8 changed files with 2249 additions and 7 deletions
+1074 -2
View File
File diff suppressed because it is too large Load Diff
+1045 -2
View File
File diff suppressed because it is too large Load Diff
+23
View File
@@ -104,6 +104,27 @@ def _find_creator_vault(creator: Pubkey) -> Pubkey:
return derived_address
def _find_global_volume_accumulator() -> Pubkey:
derived_address, _ = Pubkey.find_program_address(
[
b"global_volume_accumulator"
],
PUMP_PROGRAM,
)
return derived_address
def _find_user_volume_accumulator(user: Pubkey) -> Pubkey:
derived_address, _ = Pubkey.find_program_address(
[
b"user_volume_accumulator",
bytes(user)
],
PUMP_PROGRAM,
)
return derived_address
async def buy_token(
mint: Pubkey,
bonding_curve: Pubkey,
@@ -153,6 +174,8 @@ async def buy_token(
pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False
),
AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(pubkey=_find_global_volume_accumulator(), is_signer=False, is_writable=True),
AccountMeta(pubkey=_find_user_volume_accumulator(payer.pubkey()), is_signer=False, is_writable=True),
]
discriminator = struct.pack("<Q", 16927863322537952870)
+23
View File
@@ -101,6 +101,27 @@ def find_creator_vault(creator: Pubkey) -> Pubkey:
return derived_address
def _find_global_volume_accumulator() -> Pubkey:
derived_address, _ = Pubkey.find_program_address(
[
b"global_volume_accumulator"
],
PUMP_PROGRAM,
)
return derived_address
def _find_user_volume_accumulator(user: Pubkey) -> Pubkey:
derived_address, _ = Pubkey.find_program_address(
[
b"user_volume_accumulator",
bytes(user)
],
PUMP_PROGRAM,
)
return derived_address
def create_pump_create_instruction(
mint: Pubkey,
mint_authority: Pubkey,
@@ -177,6 +198,8 @@ def create_buy_instruction(
AccountMeta(pubkey=creator_vault, is_signer=False, is_writable=True),
AccountMeta(pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False),
AccountMeta(pubkey=_find_global_volume_accumulator(), is_signer=False, is_writable=True),
AccountMeta(pubkey=_find_user_volume_accumulator(user), is_signer=False, is_writable=True),
]
data = (
@@ -20,16 +20,16 @@ from solana.rpc.commitment import Confirmed
from solana.rpc.types import MemcmpOpts, TxOpts
from solders.compute_budget import set_compute_unit_limit, set_compute_unit_price
from solders.instruction import AccountMeta, Instruction
from solders.system_program import TransferParams, transfer
from solders.keypair import Keypair
from solders.message import Message
from solders.pubkey import Pubkey
from solders.system_program import TransferParams, transfer
from solders.transaction import VersionedTransaction
from spl.token.instructions import (
SyncNativeParams,
create_idempotent_associated_token_account,
get_associated_token_address,
sync_native,
SyncNativeParams
)
load_dotenv()
@@ -165,6 +165,44 @@ def find_coin_creator_vault(coin_creator: Pubkey) -> Pubkey:
)
return derived_address
def find_global_volume_accumulator() -> Pubkey:
"""Derive the Program Derived Address (PDA) for the global volume accumulator.
Calculates the deterministic PDA that tracks global trading volume
across all pools in the PUMP AMM protocol.
Returns:
Pubkey of the derived global volume accumulator account
"""
derived_address, _ = Pubkey.find_program_address(
[
b"global_volume_accumulator"
],
PUMP_AMM_PROGRAM_ID,
)
return derived_address
def find_user_volume_accumulator(user: Pubkey) -> Pubkey:
"""Derive the Program Derived Address (PDA) for a user's volume accumulator.
Calculates the deterministic PDA that tracks trading volume
for a specific user in the PUMP AMM protocol.
Args:
user: Pubkey of the user account
Returns:
Pubkey of the derived user volume accumulator account
"""
derived_address, _ = Pubkey.find_program_address(
[
b"user_volume_accumulator",
bytes(user)
],
PUMP_AMM_PROGRAM_ID,
)
return derived_address
async def calculate_token_pool_price(client: AsyncClient, pool_base_token_account: Pubkey, pool_quote_token_account: Pubkey) -> float:
"""Calculate the current price of tokens in an AMM pool.
@@ -229,6 +267,10 @@ async def buy_pump_swap(client: AsyncClient, pump_fun_amm_market: Pubkey, payer:
print(f"Buying {base_amount_out / (10**TOKEN_DECIMALS):.10f} tokens")
print(f"Maximum SOL input: {max_sol_input / LAMPORTS_PER_SOL:.10f} SOL")
# Calculate required PDAs for volume tracking
global_volume_accumulator = find_global_volume_accumulator()
user_volume_accumulator = find_user_volume_accumulator(payer.pubkey())
# Define all accounts needed for the buy instruction
accounts = [
AccountMeta(pubkey=pump_fun_amm_market, is_signer=False, is_writable=False),
@@ -250,6 +292,8 @@ async def buy_pump_swap(client: AsyncClient, pump_fun_amm_market: Pubkey, payer:
AccountMeta(pubkey=PUMP_AMM_PROGRAM_ID, is_signer=False, is_writable=False),
AccountMeta(pubkey=coin_creator_vault_ata, is_signer=False, is_writable=True),
AccountMeta(pubkey=coin_creator_vault_authority, is_signer=False, is_writable=False),
AccountMeta(pubkey=global_volume_accumulator, is_signer=False, is_writable=True),
AccountMeta(pubkey=user_volume_accumulator, is_signer=False, is_writable=True),
]
data = BUY_DISCRIMINATOR + struct.pack("<Q", base_amount_out) + struct.pack("<Q", max_sol_input)
+1 -1
View File
@@ -86,7 +86,7 @@ async def start_bot(config_path: str):
"wait_before_new_token", 15
),
max_token_age=cfg.get("timing", {}).get("max_token_age", 0.001),
token_wait_timeout=cfg.get("timing", {}).get("token_wait_timeout", 30),
token_wait_timeout=cfg.get("timing", {}).get("token_wait_timeout", 120),
# Cleanup settings
cleanup_mode=cfg.get("cleanup", {}).get("mode", "disabled"),
cleanup_force_close_with_burn=cfg.get("cleanup", {}).get(
+31
View File
@@ -49,3 +49,34 @@ class PumpAddresses:
LIQUIDITY_MIGRATOR: Final[Pubkey] = Pubkey.from_string(
"39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg"
)
@staticmethod
def find_global_volume_accumulator() -> Pubkey:
"""
Derive the Program Derived Address (PDA) for the global volume accumulator.
Returns:
Pubkey of the derived global volume accumulator account
"""
derived_address, _ = Pubkey.find_program_address(
[b"global_volume_accumulator"],
PumpAddresses.PROGRAM,
)
return derived_address
@staticmethod
def find_user_volume_accumulator(user: Pubkey) -> Pubkey:
"""
Derive the Program Derived Address (PDA) for a user's volume accumulator.
Args:
user: Pubkey of the user account
Returns:
Pubkey of the derived user volume accumulator account
"""
derived_address, _ = Pubkey.find_program_address(
[b"user_volume_accumulator", bytes(user)],
PumpAddresses.PROGRAM,
)
return derived_address
+6
View File
@@ -195,6 +195,12 @@ class TokenBuyer(Trader):
AccountMeta(
pubkey=PumpAddresses.PROGRAM, is_signer=False, is_writable=False
),
AccountMeta(
pubkey=PumpAddresses.find_global_volume_accumulator(), is_signer=False, is_writable=True
),
AccountMeta(
pubkey=PumpAddresses.find_user_volume_accumulator(self.wallet.pubkey), is_signer=False, is_writable=True
),
]
# Prepare idempotent create ATA instruction: it will not fail if ATA already exists