-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathpumpswap.py
More file actions
133 lines (108 loc) · 4.71 KB
/
pumpswap.py
File metadata and controls
133 lines (108 loc) · 4.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""PumpSwap AMM pool discovery, parsing, and price calculation."""
import struct
from solders.pubkey import Pubkey
from spl.token.instructions import get_associated_token_address
from pumpfun_cli.protocol.address import derive_amm_pool
from pumpfun_cli.protocol.client import RpcClient
from pumpfun_cli.protocol.contracts import (
GLOBALCONFIG_PROTOCOL_FEE_RECIPIENT_OFFSET,
PUMP_SWAP_GLOBAL_CONFIG,
STANDARD_PUMPSWAP_FEE_RECIPIENT,
TOKEN_PROGRAM,
WSOL_MINT,
)
async def get_pool_by_mint(client: RpcClient, mint: Pubkey) -> tuple[Pubkey, bytes]:
"""Find the PumpSwap pool for a given token mint.
Derives the pool address deterministically via PDA, then fetches
the account data with a single getAccountInfo call.
Returns (pool_address, pool_data) or raises RuntimeError if not found.
"""
pool_address = derive_amm_pool(mint)
try:
resp = await client.get_account_info(pool_address)
except Exception as exc:
raise RuntimeError(f"Failed to fetch PumpSwap pool {pool_address}: {exc}") from exc
if not resp.value:
raise RuntimeError(f"No PumpSwap pool found for {mint}")
return pool_address, bytes(resp.value.data)
def parse_pool_data(data: bytes) -> dict:
"""Parse binary pool account data into a dict.
Layout (after 8-byte discriminator):
u8 pool_bump offset 8
u16 index offset 9
pub creator offset 11
pub base_mint offset 43
pub quote_mint offset 75
pub lp_mint offset 107
pub pool_base_token offset 139
pub pool_quote_token offset 171
u64 lp_supply offset 203
pub coin_creator offset 211
"""
pool_bump = data[8]
index = struct.unpack_from("<H", data, 9)[0]
creator = Pubkey.from_bytes(data[11:43])
base_mint = Pubkey.from_bytes(data[43:75])
quote_mint = Pubkey.from_bytes(data[75:107])
lp_mint = Pubkey.from_bytes(data[107:139])
pool_base_token_account = Pubkey.from_bytes(data[139:171])
pool_quote_token_account = Pubkey.from_bytes(data[171:203])
lp_supply = struct.unpack_from("<Q", data, 203)[0]
coin_creator = Pubkey.from_bytes(data[211:243])
return {
"pool_bump": pool_bump,
"index": index,
"creator": creator,
"base_mint": base_mint,
"quote_mint": quote_mint,
"lp_mint": lp_mint,
"pool_base_token_account": pool_base_token_account,
"pool_quote_token_account": pool_quote_token_account,
"lp_supply": lp_supply,
"coin_creator": coin_creator,
}
async def get_fee_recipients(
client: RpcClient,
pool_data: bytes,
) -> tuple[Pubkey, Pubkey]:
"""Determine fee recipient and its WSOL ATA.
Always reads the protocol_fee_recipient from the GlobalConfig account.
Falls back to the standard fee recipient if GlobalConfig is unavailable.
Returns (fee_recipient, fee_recipient_wsol_ata).
"""
resp = await client.get_account_info(PUMP_SWAP_GLOBAL_CONFIG)
if resp.value:
config_data = bytes(resp.value.data)
off = GLOBALCONFIG_PROTOCOL_FEE_RECIPIENT_OFFSET
fee_recipient = Pubkey.from_bytes(config_data[off : off + 32])
else:
fee_recipient = STANDARD_PUMPSWAP_FEE_RECIPIENT
fee_recipient_ata = get_associated_token_address(fee_recipient, WSOL_MINT, TOKEN_PROGRAM)
return fee_recipient, fee_recipient_ata
async def get_token_program_id(client: RpcClient, mint: Pubkey) -> Pubkey:
"""Determine the token program (Token or Token2022) for a given mint."""
from pumpfun_cli.protocol.contracts import TOKEN_2022_PROGRAM
resp = await client.get_account_info(mint)
if not resp.value:
raise RuntimeError(f"Mint account not found: {mint}")
owner = resp.value.owner
if owner == TOKEN_PROGRAM:
return TOKEN_PROGRAM
if owner == TOKEN_2022_PROGRAM:
return TOKEN_2022_PROGRAM
raise RuntimeError(f"Unknown token program owner: {owner}")
async def get_pool_balances(client: RpcClient, pool: dict) -> tuple[int, int]:
"""Fetch base and quote token balances from pool accounts.
Returns (base_balance, quote_balance) as raw integers.
"""
base_balance = await client.get_token_account_balance(pool["pool_base_token_account"])
quote_balance = await client.get_token_account_balance(pool["pool_quote_token_account"])
return base_balance, quote_balance
async def get_pool_price(client: RpcClient, pool: dict) -> float:
"""Calculate current token price in SOL from pool reserves.
Returns price as SOL per token (float).
"""
base_balance, quote_balance = await get_pool_balances(client, pool)
if base_balance == 0:
return 0.0
return quote_balance / base_balance