mirror of
https://github.com/chainstacklabs/pumpfun-bonkfun-bot.git
synced 2026-08-17 09:18:05 +00:00
Use Transaction from solders
This commit is contained in:
+162
@@ -0,0 +1,162 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||||
|
.pdm.toml
|
||||||
|
.pdm-python
|
||||||
|
.pdm-build/
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
@@ -3,19 +3,25 @@ import json
|
|||||||
import base64
|
import base64
|
||||||
import struct
|
import struct
|
||||||
import base58
|
import base58
|
||||||
|
import os
|
||||||
|
|
||||||
|
from solana.transaction import Message
|
||||||
from solana.rpc.async_api import AsyncClient
|
from solana.rpc.async_api import AsyncClient
|
||||||
from solana.transaction import Transaction
|
|
||||||
from solana.rpc.commitment import Confirmed
|
from solana.rpc.commitment import Confirmed
|
||||||
|
from solana.rpc.types import TxOpts
|
||||||
|
|
||||||
from solders.pubkey import Pubkey
|
from solders.pubkey import Pubkey
|
||||||
from solders.keypair import Keypair
|
from solders.keypair import Keypair
|
||||||
from solders.instruction import Instruction, AccountMeta
|
from solders.instruction import Instruction, AccountMeta
|
||||||
from solana.rpc.types import TxOpts
|
|
||||||
from solders.system_program import TransferParams, transfer
|
from solders.system_program import TransferParams, transfer
|
||||||
|
from solders.transaction import VersionedTransaction, Transaction
|
||||||
|
from solders.compute_budget import set_compute_unit_price
|
||||||
|
|
||||||
|
import spl.token.instructions as spl_token
|
||||||
from spl.token.instructions import get_associated_token_address
|
from spl.token.instructions import get_associated_token_address
|
||||||
|
|
||||||
import websockets
|
import websockets
|
||||||
import hashlib
|
import hashlib
|
||||||
from solders.transaction import VersionedTransaction
|
|
||||||
import spl.token.instructions as spl_token
|
|
||||||
from construct import Struct, Int64ul, Flag
|
from construct import Struct, Int64ul, Flag
|
||||||
|
|
||||||
# Here and later all the discriminators are precalculated. See learning-examples/discriminator.py
|
# Here and later all the discriminators are precalculated. See learning-examples/discriminator.py
|
||||||
@@ -34,8 +40,9 @@ SYSTEM_RENT = Pubkey.from_string("SysvarRent111111111111111111111111111111111")
|
|||||||
SOL = Pubkey.from_string("So11111111111111111111111111111111111111112")
|
SOL = Pubkey.from_string("So11111111111111111111111111111111111111112")
|
||||||
LAMPORTS_PER_SOL = 1_000_000_000
|
LAMPORTS_PER_SOL = 1_000_000_000
|
||||||
|
|
||||||
# RPC endpoint
|
# RPC ENDPOINTS
|
||||||
RPC_ENDPOINT = "https://nd-789-855-895.p2pify.com/a24eec46e256346bd48e6a7ae9aab4c5"
|
RPC_ENDPOINT = "ENTER_YOUR_CHAINSTACK_HTTP_ENDPOINT"
|
||||||
|
RPC_WEBSOCKET = "ENTER_YOUR_CHAINSTACK_WS_ENDPOINT"
|
||||||
|
|
||||||
class BondingCurveState:
|
class BondingCurveState:
|
||||||
_STRUCT = Struct(
|
_STRUCT = Struct(
|
||||||
@@ -68,8 +75,8 @@ def calculate_pump_curve_price(curve_state: BondingCurveState) -> float:
|
|||||||
|
|
||||||
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (curve_state.virtual_token_reserves / 10 ** TOKEN_DECIMALS)
|
return (curve_state.virtual_sol_reserves / LAMPORTS_PER_SOL) / (curve_state.virtual_token_reserves / 10 ** TOKEN_DECIMALS)
|
||||||
|
|
||||||
async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curve: Pubkey, amount: float, slippage: float = 0.01, max_retries=5):
|
async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curve: Pubkey, amount: float, slippage: float = 0.25, max_retries=5):
|
||||||
private_key = base58.b58decode("5dhFD6KgGThDa2c8ytNquSVgpD1o1WuG4qGrSaT4yy3AbW812dYctgUEj8wV4XoWYfmdqFy6UaWLK8QZ7XzgnLZE")
|
private_key = base58.b58decode("ENTER_PRIVATE_KEY")
|
||||||
payer = Keypair.from_bytes(private_key)
|
payer = Keypair.from_bytes(private_key)
|
||||||
|
|
||||||
async with AsyncClient(RPC_ENDPOINT) as client:
|
async with AsyncClient(RPC_ENDPOINT) as client:
|
||||||
@@ -95,11 +102,15 @@ async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curv
|
|||||||
owner=payer.pubkey(),
|
owner=payer.pubkey(),
|
||||||
mint=mint
|
mint=mint
|
||||||
)
|
)
|
||||||
create_ata_tx = Transaction()
|
|
||||||
create_ata_tx.add(create_ata_ix)
|
msg = Message([create_ata_ix], payer.pubkey())
|
||||||
recent_blockhash = await client.get_latest_blockhash()
|
tx_ata = await client.send_transaction(
|
||||||
create_ata_tx.recent_blockhash = recent_blockhash.value.blockhash
|
Transaction([payer], msg, (await client.get_latest_blockhash()).value.blockhash),
|
||||||
await client.send_transaction(create_ata_tx, payer)
|
opts=TxOpts(skip_preflight=True, preflight_commitment=Confirmed)
|
||||||
|
)
|
||||||
|
|
||||||
|
await client.confirm_transaction(tx_ata.value, commitment="confirmed")
|
||||||
|
|
||||||
print("Associated token account created.")
|
print("Associated token account created.")
|
||||||
print(f"Associated token account address: {associated_token_account}")
|
print(f"Associated token account address: {associated_token_account}")
|
||||||
break
|
break
|
||||||
@@ -139,25 +150,20 @@ async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curv
|
|||||||
data = discriminator + struct.pack("<Q", int(token_amount * 10**6)) + struct.pack("<Q", max_amount_lamports)
|
data = discriminator + struct.pack("<Q", int(token_amount * 10**6)) + struct.pack("<Q", max_amount_lamports)
|
||||||
buy_ix = Instruction(PUMP_PROGRAM, data, accounts)
|
buy_ix = Instruction(PUMP_PROGRAM, data, accounts)
|
||||||
|
|
||||||
recent_blockhash = await client.get_latest_blockhash()
|
msg = Message([set_compute_unit_price(1_000), buy_ix], payer.pubkey())
|
||||||
transaction = Transaction()
|
tx_buy = await client.send_transaction(
|
||||||
transaction.add(buy_ix)
|
Transaction([payer], msg, (await client.get_latest_blockhash()).value.blockhash),
|
||||||
transaction.recent_blockhash = recent_blockhash.value.blockhash
|
opts=TxOpts(skip_preflight=True, preflight_commitment=Confirmed)
|
||||||
|
)
|
||||||
|
|
||||||
tx = await client.send_transaction(
|
print(f"Transaction sent: https://explorer.solana.com/tx/{tx_buy.value}")
|
||||||
transaction,
|
|
||||||
payer,
|
|
||||||
opts=TxOpts(skip_preflight=True, preflight_commitment=Confirmed),
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"Transaction sent: https://explorer.solana.com/tx/{tx.value}")
|
await client.confirm_transaction(tx_buy.value, commitment="confirmed")
|
||||||
|
|
||||||
await client.confirm_transaction(tx.value, commitment="confirmed")
|
|
||||||
print("Transaction confirmed")
|
print("Transaction confirmed")
|
||||||
return # Success, exit the function
|
return # Success, exit the function
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Attempt {attempt + 1} failed: {str(e)}")
|
print(f"Attempt {attempt + 1} failed: {str(e)[:50]}")
|
||||||
if attempt < max_retries - 1:
|
if attempt < max_retries - 1:
|
||||||
wait_time = 2 ** attempt
|
wait_time = 2 ** attempt
|
||||||
print(f"Retrying in {wait_time} seconds...")
|
print(f"Retrying in {wait_time} seconds...")
|
||||||
@@ -165,8 +171,6 @@ async def buy_token(mint: Pubkey, bonding_curve: Pubkey, associated_bonding_curv
|
|||||||
else:
|
else:
|
||||||
print("Max retries reached. Unable to complete the transaction.")
|
print("Max retries reached. Unable to complete the transaction.")
|
||||||
|
|
||||||
RPC_NODE_URL = "wss://ws-nd-789-855-895.p2pify.com/a24eec46e256346bd48e6a7ae9aab4c5"
|
|
||||||
|
|
||||||
def load_idl(file_path):
|
def load_idl(file_path):
|
||||||
with open(file_path, 'r') as f:
|
with open(file_path, 'r') as f:
|
||||||
return json.load(f)
|
return json.load(f)
|
||||||
@@ -203,10 +207,11 @@ def decode_create_instruction(ix_data, ix_def, accounts):
|
|||||||
return args
|
return args
|
||||||
|
|
||||||
async def listen_for_create_transaction():
|
async def listen_for_create_transaction():
|
||||||
idl = load_idl('../idl/pump_fun_idl.json')
|
idl_path = os.path.join(os.path.dirname(__file__), '..', 'idl', 'pump_fun_idl.json')
|
||||||
|
idl = load_idl(idl_path)
|
||||||
create_discriminator = calculate_discriminator("global:create")
|
create_discriminator = calculate_discriminator("global:create")
|
||||||
|
|
||||||
async with websockets.connect(RPC_NODE_URL) as websocket:
|
async with websockets.connect(RPC_WEBSOCKET) as websocket:
|
||||||
subscription_message = json.dumps({
|
subscription_message = json.dumps({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@@ -257,8 +262,9 @@ async def main():
|
|||||||
print("New token created:")
|
print("New token created:")
|
||||||
print(json.dumps(token_data, indent=2))
|
print(json.dumps(token_data, indent=2))
|
||||||
|
|
||||||
print("Waiting for 15 seconds for things to stabilize...")
|
sleep_duration_sec = 15
|
||||||
await asyncio.sleep(15)
|
print(f"Waiting for {sleep_duration_sec} seconds for things to stabilize...")
|
||||||
|
await asyncio.sleep(sleep_duration_sec)
|
||||||
|
|
||||||
mint = Pubkey.from_string(token_data['mint'])
|
mint = Pubkey.from_string(token_data['mint'])
|
||||||
bonding_curve = Pubkey.from_string(token_data['bondingCurve'])
|
bonding_curve = Pubkey.from_string(token_data['bondingCurve'])
|
||||||
|
|||||||
Reference in New Issue
Block a user