mirror of
https://github.com/chainstacklabs/pumpfun-bonkfun-bot.git
synced 2026-08-16 00:38:05 +00:00
Implement computing associatedBondingCurve from bondingCurve
For reference #26
This commit is contained in:
@@ -8,7 +8,9 @@ Make sure you have the required packages installed `pip install -r requirements.
|
|||||||
|
|
||||||
Make sure you have your endpoints set up in `config.py`.
|
Make sure you have your endpoints set up in `config.py`.
|
||||||
|
|
||||||
Quick note on a couple of new scripts in `/learning-examples`:
|
Quick note on a couple on a few new scripts in `/learning-examples`:
|
||||||
|
|
||||||
|
*(this is basically a changelog now)*
|
||||||
|
|
||||||
## Bonding curve state check
|
## Bonding curve state check
|
||||||
|
|
||||||
@@ -29,3 +31,38 @@ Note that it's using the [blockSubscribe]([url](https://docs.chainstack.com/refe
|
|||||||
To run:
|
To run:
|
||||||
|
|
||||||
`python listen_to_raydium_migration.py`
|
`python listen_to_raydium_migration.py`
|
||||||
|
|
||||||
|
**The new two additions are based on this question [associatedBondingCurve #26](https://github.com/chainstacklabs/pump-fun-bot/issues/26)**
|
||||||
|
|
||||||
|
You can take the compute the associatedBondingCurve address following the [Solana docs PDA](https://solana.com/docs/core/pda) description logic. Take the following as input *as seed* (order seems to matter):
|
||||||
|
|
||||||
|
- bondingCurve address
|
||||||
|
- the Solana system token program address: `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`
|
||||||
|
- the token mint address
|
||||||
|
|
||||||
|
And compute against the Solana system associated token account program address: `ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL`.
|
||||||
|
|
||||||
|
The implications of this are kinda huge:
|
||||||
|
* you can now use `logsSubscribe` to snipe the tokens and you are not limited to the `blockSubscribe` method
|
||||||
|
* see which one is faster
|
||||||
|
* not every provider supports `blockSubscribe` on lower tier plans or at all, but everyone supports `logsSubscribe`
|
||||||
|
|
||||||
|
The following script showcase the implementation.
|
||||||
|
|
||||||
|
## Compute associated bonding curve
|
||||||
|
|
||||||
|
`compute_associated_bonding_curve.py` — computes the associated bonding curve for a given token.
|
||||||
|
|
||||||
|
To run:
|
||||||
|
|
||||||
|
`python compute_associated_bonding_curve.py` and then enter the token mint address.
|
||||||
|
|
||||||
|
## Listen to new direct full details
|
||||||
|
|
||||||
|
`listen_new_direct_full_details.py` — listens to the new direct full details events and prints the signature, the token address, and the bonding curve address.
|
||||||
|
|
||||||
|
To run:
|
||||||
|
|
||||||
|
`python listen_new_direct_full_details.py`
|
||||||
|
|
||||||
|
So now you can run `listen_create_from_blocksubscribe.py` and `listen_new_direct_full_details.py` at the same time and see which one is faster.
|
||||||
@@ -19,7 +19,7 @@ BUY_SLIPPAGE = 0.2 # 20% slippage tolerance for buying
|
|||||||
SELL_SLIPPAGE = 0.2 # 20% slippage tolerance for selling
|
SELL_SLIPPAGE = 0.2 # 20% slippage tolerance for selling
|
||||||
|
|
||||||
# Your nodes
|
# Your nodes
|
||||||
# You can also get a trader node https://docs.chainstack.com/docs/warp-transactions
|
# You can also get a trader node https://docs.chainstack.com/docs/solana-trader-nodes
|
||||||
RPC_ENDPOINT = "SOLANA_NODE_RPC_ENDPOINT"
|
RPC_ENDPOINT = "SOLANA_NODE_RPC_ENDPOINT"
|
||||||
WSS_ENDPOINT = "SOLANA_NODE_WSS_ENDPOINT"
|
WSS_ENDPOINT = "SOLANA_NODE_WSS_ENDPOINT"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from solders.pubkey import Pubkey
|
||||||
|
|
||||||
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
from config import PUMP_PROGRAM
|
||||||
|
|
||||||
|
def get_bonding_curve_address(mint: Pubkey, program_id: Pubkey) -> tuple[Pubkey, int]:
|
||||||
|
"""
|
||||||
|
Derives the bonding curve address for a given mint
|
||||||
|
"""
|
||||||
|
return Pubkey.find_program_address(
|
||||||
|
[
|
||||||
|
b"bonding-curve",
|
||||||
|
bytes(mint)
|
||||||
|
],
|
||||||
|
program_id
|
||||||
|
)
|
||||||
|
|
||||||
|
def find_associated_bonding_curve(mint: Pubkey, bonding_curve: Pubkey) -> Pubkey:
|
||||||
|
"""
|
||||||
|
Find the associated bonding curve for a given mint and bonding curve.
|
||||||
|
This uses the standard ATA derivation.
|
||||||
|
"""
|
||||||
|
from config import SYSTEM_TOKEN_PROGRAM as TOKEN_PROGRAM_ID
|
||||||
|
from config import SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM as ATA_PROGRAM_ID
|
||||||
|
|
||||||
|
derived_address, _ = Pubkey.find_program_address(
|
||||||
|
[
|
||||||
|
bytes(bonding_curve),
|
||||||
|
bytes(TOKEN_PROGRAM_ID),
|
||||||
|
bytes(mint),
|
||||||
|
],
|
||||||
|
ATA_PROGRAM_ID
|
||||||
|
)
|
||||||
|
return derived_address
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
mint_address = input("Enter the token mint address: ")
|
||||||
|
|
||||||
|
try:
|
||||||
|
mint = Pubkey.from_string(mint_address)
|
||||||
|
|
||||||
|
bonding_curve_address, bump = get_bonding_curve_address(mint, PUMP_PROGRAM)
|
||||||
|
|
||||||
|
# Calculate the associated bonding curve
|
||||||
|
associated_bonding_curve = find_associated_bonding_curve(mint, bonding_curve_address)
|
||||||
|
|
||||||
|
print("\nResults:")
|
||||||
|
print("-" * 50)
|
||||||
|
print(f"Token Mint: {mint}")
|
||||||
|
print(f"Bonding Curve: {bonding_curve_address}")
|
||||||
|
print(f"Associated Bonding Curve: {associated_bonding_curve}")
|
||||||
|
print(f"Bonding Curve Bump: {bump}")
|
||||||
|
print("-" * 50)
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"Error: Invalid address format - {str(e)}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import websockets
|
||||||
|
import base58
|
||||||
|
import base64
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from solders.pubkey import Pubkey
|
||||||
|
|
||||||
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
from config import (
|
||||||
|
WSS_ENDPOINT,
|
||||||
|
PUMP_PROGRAM,
|
||||||
|
SYSTEM_TOKEN_PROGRAM as TOKEN_PROGRAM_ID,
|
||||||
|
SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM as ATA_PROGRAM_ID
|
||||||
|
)
|
||||||
|
|
||||||
|
def find_associated_bonding_curve(mint: Pubkey, bonding_curve: Pubkey) -> Pubkey:
|
||||||
|
"""
|
||||||
|
Find the associated bonding curve for a given mint and bonding curve.
|
||||||
|
This uses the standard ATA derivation.
|
||||||
|
"""
|
||||||
|
derived_address, _ = Pubkey.find_program_address(
|
||||||
|
[
|
||||||
|
bytes(bonding_curve),
|
||||||
|
bytes(TOKEN_PROGRAM_ID),
|
||||||
|
bytes(mint),
|
||||||
|
],
|
||||||
|
ATA_PROGRAM_ID
|
||||||
|
)
|
||||||
|
return derived_address
|
||||||
|
|
||||||
|
# Load the IDL JSON file
|
||||||
|
with open('../idl/pump_fun_idl.json', 'r') as f:
|
||||||
|
idl = json.load(f)
|
||||||
|
|
||||||
|
# Extract the "create" instruction definition
|
||||||
|
create_instruction = next(instr for instr in idl['instructions'] if instr['name'] == 'create')
|
||||||
|
|
||||||
|
def parse_create_instruction(data):
|
||||||
|
if len(data) < 8:
|
||||||
|
return None
|
||||||
|
offset = 8
|
||||||
|
parsed_data = {}
|
||||||
|
|
||||||
|
# Parse fields based on CreateEvent structure
|
||||||
|
fields = [
|
||||||
|
('name', 'string'),
|
||||||
|
('symbol', 'string'),
|
||||||
|
('uri', 'string'),
|
||||||
|
('mint', 'publicKey'),
|
||||||
|
('bondingCurve', 'publicKey'),
|
||||||
|
('user', 'publicKey'),
|
||||||
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
for field_name, field_type in fields:
|
||||||
|
if field_type == 'string':
|
||||||
|
length = struct.unpack('<I', data[offset:offset+4])[0]
|
||||||
|
offset += 4
|
||||||
|
value = data[offset:offset+length].decode('utf-8')
|
||||||
|
offset += length
|
||||||
|
elif field_type == 'publicKey':
|
||||||
|
value = base58.b58encode(data[offset:offset+32]).decode('utf-8')
|
||||||
|
offset += 32
|
||||||
|
|
||||||
|
parsed_data[field_name] = value
|
||||||
|
|
||||||
|
return parsed_data
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def print_transaction_details(log_data):
|
||||||
|
print(f"Signature: {log_data.get('signature')}")
|
||||||
|
|
||||||
|
for log in log_data.get('logs', []):
|
||||||
|
if log.startswith("Program data:"):
|
||||||
|
try:
|
||||||
|
data = base58.b58decode(log.split(": ")[1]).decode('utf-8')
|
||||||
|
print(f"Data: {data}")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def listen_for_new_tokens():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
async with websockets.connect(WSS_ENDPOINT) as websocket:
|
||||||
|
subscription_message = json.dumps({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "logsSubscribe",
|
||||||
|
"params": [
|
||||||
|
{"mentions": [str(PUMP_PROGRAM)]},
|
||||||
|
{"commitment": "processed"}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
await websocket.send(subscription_message)
|
||||||
|
print(f"Listening for new token creations from program: {PUMP_PROGRAM}")
|
||||||
|
|
||||||
|
# Wait for subscription confirmation
|
||||||
|
response = await websocket.recv()
|
||||||
|
print(f"Subscription response: {response}")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
response = await websocket.recv()
|
||||||
|
data = json.loads(response)
|
||||||
|
|
||||||
|
if 'method' in data and data['method'] == 'logsNotification':
|
||||||
|
log_data = data['params']['result']['value']
|
||||||
|
logs = log_data.get('logs', [])
|
||||||
|
|
||||||
|
if any("Program log: Instruction: Create" in log for log in logs):
|
||||||
|
for log in logs:
|
||||||
|
if "Program data:" in log:
|
||||||
|
try:
|
||||||
|
encoded_data = log.split(": ")[1]
|
||||||
|
decoded_data = base64.b64decode(encoded_data)
|
||||||
|
parsed_data = parse_create_instruction(decoded_data)
|
||||||
|
if parsed_data and 'name' in parsed_data:
|
||||||
|
print("Signature:", log_data.get('signature'))
|
||||||
|
for key, value in parsed_data.items():
|
||||||
|
print(f"{key}: {value}")
|
||||||
|
|
||||||
|
# Calculate associated bonding curve
|
||||||
|
mint = Pubkey.from_string(parsed_data['mint'])
|
||||||
|
bonding_curve = Pubkey.from_string(parsed_data['bondingCurve'])
|
||||||
|
associated_curve = find_associated_bonding_curve(mint, bonding_curve)
|
||||||
|
print(f"Associated Bonding Curve: {associated_curve}")
|
||||||
|
print("##########################################################################################")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to decode: {log}")
|
||||||
|
print(f"Error: {str(e)}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An error occurred while processing message: {e}")
|
||||||
|
break
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Connection error: {e}")
|
||||||
|
print("Reconnecting in 5 seconds...")
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(listen_for_new_tokens())
|
||||||
Reference in New Issue
Block a user