mirror of
https://github.com/chainstacklabs/pumpfun-bonkfun-bot.git
synced 2026-08-15 08:18:04 +00:00
Merge pull request #86 from chainstacklabs/feat/geyser-listener-for-bot
Add Geyser listener support to bot
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
SOLANA_NODE_RPC_ENDPOINT=...
|
SOLANA_NODE_RPC_ENDPOINT=...
|
||||||
SOLANA_NODE_WSS_ENDPOINT=...
|
SOLANA_NODE_WSS_ENDPOINT=...
|
||||||
|
GEYSER_ENDPOINT=...
|
||||||
|
GEYSER_API_TOKEN=...
|
||||||
SOLANA_PRIVATE_KEY=...
|
SOLANA_PRIVATE_KEY=...
|
||||||
+13
@@ -65,6 +65,9 @@ async def main() -> None:
|
|||||||
rpc_endpoint: str | None = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
|
rpc_endpoint: str | None = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
|
||||||
wss_endpoint: str | None = os.environ.get("SOLANA_NODE_WSS_ENDPOINT")
|
wss_endpoint: str | None = os.environ.get("SOLANA_NODE_WSS_ENDPOINT")
|
||||||
private_key: str | None = os.environ.get("SOLANA_PRIVATE_KEY")
|
private_key: str | None = os.environ.get("SOLANA_PRIVATE_KEY")
|
||||||
|
geyser_endpoint: str | None = os.environ.get("GEYSER_ENDPOINT")
|
||||||
|
geyser_api_token: str | None = os.environ.get("GEYSER_API_TOKEN")
|
||||||
|
|
||||||
|
|
||||||
# Validate configuration values
|
# Validate configuration values
|
||||||
if not rpc_endpoint or not rpc_endpoint.startswith(("http://", "https://")):
|
if not rpc_endpoint or not rpc_endpoint.startswith(("http://", "https://")):
|
||||||
@@ -79,6 +82,14 @@ async def main() -> None:
|
|||||||
logger.error("Invalid private key. Key appears to be missing or too short")
|
logger.error("Invalid private key. Key appears to be missing or too short")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
if config.LISTENER_TYPE.lower() == "geyser":
|
||||||
|
if not geyser_endpoint:
|
||||||
|
logger.error("GEYSER_ENDPOINT environment variable is not set")
|
||||||
|
sys.exit(1)
|
||||||
|
if not geyser_api_token:
|
||||||
|
logger.error("GEYSER_API_TOKEN environment variable is not set")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
# Get trading parameters
|
# Get trading parameters
|
||||||
buy_amount: float = args.amount if args.amount is not None else config.BUY_AMOUNT
|
buy_amount: float = args.amount if args.amount is not None else config.BUY_AMOUNT
|
||||||
buy_slippage: float = (
|
buy_slippage: float = (
|
||||||
@@ -97,6 +108,8 @@ async def main() -> None:
|
|||||||
sell_slippage=sell_slippage,
|
sell_slippage=sell_slippage,
|
||||||
max_retries=config.MAX_RETRIES,
|
max_retries=config.MAX_RETRIES,
|
||||||
listener_type=config.LISTENER_TYPE,
|
listener_type=config.LISTENER_TYPE,
|
||||||
|
geyser_endpoint=geyser_endpoint,
|
||||||
|
geyser_api_token=geyser_api_token,
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
+6
-5
@@ -15,7 +15,7 @@ SELL_SLIPPAGE: float = 0.4 # Consistent slippage tolerance to maintain trading
|
|||||||
# EXTREME FAST Mode configuration
|
# EXTREME FAST Mode configuration
|
||||||
# When enabled, skips waiting for the bonding curve to stabilize and RPC price check.
|
# When enabled, skips waiting for the bonding curve to stabilize and RPC price check.
|
||||||
# The bot buys the specified number of tokens directly, making the process faster but less precise.
|
# The bot buys the specified number of tokens directly, making the process faster but less precise.
|
||||||
EXTREME_FAST_MODE: bool = False
|
EXTREME_FAST_MODE: bool = True
|
||||||
# Amount of tokens to buy in EXTREME FAST mode. No price calculation is done; the bot buys exactly this amount.
|
# Amount of tokens to buy in EXTREME FAST mode. No price calculation is done; the bot buys exactly this amount.
|
||||||
EXTREME_FAST_TOKEN_AMOUNT: int = 30
|
EXTREME_FAST_TOKEN_AMOUNT: int = 30
|
||||||
|
|
||||||
@@ -24,15 +24,16 @@ EXTREME_FAST_TOKEN_AMOUNT: int = 30
|
|||||||
# Manage transaction speed and cost on the Solana network
|
# Manage transaction speed and cost on the Solana network
|
||||||
ENABLE_DYNAMIC_PRIORITY_FEE: bool = False # Adaptive fee calculation
|
ENABLE_DYNAMIC_PRIORITY_FEE: bool = False # Adaptive fee calculation
|
||||||
ENABLE_FIXED_PRIORITY_FEE: bool = True # Use consistent, predictable fee
|
ENABLE_FIXED_PRIORITY_FEE: bool = True # Use consistent, predictable fee
|
||||||
FIXED_PRIORITY_FEE: int = 2_000 # Base fee in microlamports
|
FIXED_PRIORITY_FEE: int = 500_000 # Base fee in microlamports
|
||||||
EXTRA_PRIORITY_FEE: float = 0.0 # Percentage increase on priority fee (0.1 = 10%)
|
EXTRA_PRIORITY_FEE: float = 0.0 # Percentage increase on priority fee (0.1 = 10%)
|
||||||
HARD_CAP_PRIOR_FEE: int = 200_000 # Maximum allowable fee to prevent excessive spending in microlamports
|
HARD_CAP_PRIOR_FEE: int = 500_000 # Maximum allowable fee to prevent excessive spending in microlamports
|
||||||
|
|
||||||
|
|
||||||
# Listener configuration
|
# Listener configuration
|
||||||
# Choose method for detecting new tokens on the network
|
# Choose method for detecting new tokens on the network
|
||||||
# "logs": Recommended for more stable token detection
|
# "logs": Recommended for more stable token detection
|
||||||
# "blocks": Unstable method, potentially less reliable
|
# "blocks": Unstable method, potentially less reliable
|
||||||
|
# "geyser": The fastest way for getting updates, requires Geyser endpoint
|
||||||
LISTENER_TYPE = "logs"
|
LISTENER_TYPE = "logs"
|
||||||
|
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ WAIT_TIME_BEFORE_NEW_TOKEN: int | float = 15 # Pause between token trades
|
|||||||
|
|
||||||
# Token and account management
|
# Token and account management
|
||||||
# Control token processing and account cleanup strategies
|
# Control token processing and account cleanup strategies
|
||||||
MAX_TOKEN_AGE: int | float = 0.1 # Maximum token age in seconds for processing
|
MAX_TOKEN_AGE: int | float = 0.001 # Maximum token age in seconds for processing
|
||||||
|
|
||||||
# Cleanup mode determines when to manage token accounts. Options:
|
# Cleanup mode determines when to manage token accounts. Options:
|
||||||
# "disabled": No cleanup will occur.
|
# "disabled": No cleanup will occur.
|
||||||
@@ -98,7 +99,7 @@ def validate_configuration() -> None:
|
|||||||
raise ValueError("Cannot enable both dynamic and fixed priority fees simultaneously")
|
raise ValueError("Cannot enable both dynamic and fixed priority fees simultaneously")
|
||||||
|
|
||||||
# Validate listener type
|
# Validate listener type
|
||||||
if LISTENER_TYPE not in ["logs", "blocks"]:
|
if LISTENER_TYPE not in ["logs", "blocks", "geyser"]:
|
||||||
raise ValueError("LISTENER_TYPE must be either 'logs' or 'blocks'")
|
raise ValueError("LISTENER_TYPE must be either 'logs' or 'blocks'")
|
||||||
|
|
||||||
# Validate cleanup mode
|
# Validate cleanup mode
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ from typing import Any
|
|||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from solana.rpc.async_api import AsyncClient
|
from solana.rpc.async_api import AsyncClient
|
||||||
from solana.rpc.commitment import Confirmed
|
from solana.rpc.commitment import Processed
|
||||||
from solana.rpc.types import TxOpts
|
from solana.rpc.types import TxOpts
|
||||||
from solders.compute_budget import set_compute_unit_limit, set_compute_unit_price
|
from solders.compute_budget import set_compute_unit_limit, set_compute_unit_price
|
||||||
from solders.hash import Hash
|
from solders.hash import Hash
|
||||||
@@ -163,7 +163,7 @@ class SolanaClient:
|
|||||||
for attempt in range(max_retries):
|
for attempt in range(max_retries):
|
||||||
try:
|
try:
|
||||||
tx_opts = TxOpts(
|
tx_opts = TxOpts(
|
||||||
skip_preflight=skip_preflight, preflight_commitment=Confirmed
|
skip_preflight=skip_preflight, preflight_commitment=Processed
|
||||||
)
|
)
|
||||||
response = await client.send_transaction(transaction, tx_opts)
|
response = await client.send_transaction(transaction, tx_opts)
|
||||||
return response.value
|
return response.value
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,485 @@
|
|||||||
|
import solana_storage_pb2 as _solana_storage_pb2
|
||||||
|
from google.protobuf.internal import containers as _containers
|
||||||
|
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
|
||||||
|
from google.protobuf import descriptor as _descriptor
|
||||||
|
from google.protobuf import message as _message
|
||||||
|
from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union
|
||||||
|
from solana_storage_pb2 import ConfirmedBlock as ConfirmedBlock
|
||||||
|
from solana_storage_pb2 import ConfirmedTransaction as ConfirmedTransaction
|
||||||
|
from solana_storage_pb2 import Transaction as Transaction
|
||||||
|
from solana_storage_pb2 import Message as Message
|
||||||
|
from solana_storage_pb2 import MessageHeader as MessageHeader
|
||||||
|
from solana_storage_pb2 import MessageAddressTableLookup as MessageAddressTableLookup
|
||||||
|
from solana_storage_pb2 import TransactionStatusMeta as TransactionStatusMeta
|
||||||
|
from solana_storage_pb2 import TransactionError as TransactionError
|
||||||
|
from solana_storage_pb2 import InnerInstructions as InnerInstructions
|
||||||
|
from solana_storage_pb2 import InnerInstruction as InnerInstruction
|
||||||
|
from solana_storage_pb2 import CompiledInstruction as CompiledInstruction
|
||||||
|
from solana_storage_pb2 import TokenBalance as TokenBalance
|
||||||
|
from solana_storage_pb2 import UiTokenAmount as UiTokenAmount
|
||||||
|
from solana_storage_pb2 import ReturnData as ReturnData
|
||||||
|
from solana_storage_pb2 import Reward as Reward
|
||||||
|
from solana_storage_pb2 import Rewards as Rewards
|
||||||
|
from solana_storage_pb2 import UnixTimestamp as UnixTimestamp
|
||||||
|
from solana_storage_pb2 import BlockHeight as BlockHeight
|
||||||
|
from solana_storage_pb2 import NumPartitions as NumPartitions
|
||||||
|
from solana_storage_pb2 import RewardType as RewardType
|
||||||
|
|
||||||
|
DESCRIPTOR: _descriptor.FileDescriptor
|
||||||
|
Unspecified: _solana_storage_pb2.RewardType
|
||||||
|
Fee: _solana_storage_pb2.RewardType
|
||||||
|
Rent: _solana_storage_pb2.RewardType
|
||||||
|
Staking: _solana_storage_pb2.RewardType
|
||||||
|
Voting: _solana_storage_pb2.RewardType
|
||||||
|
|
||||||
|
class CommitmentLevel(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||||
|
__slots__ = ()
|
||||||
|
PROCESSED: _ClassVar[CommitmentLevel]
|
||||||
|
CONFIRMED: _ClassVar[CommitmentLevel]
|
||||||
|
FINALIZED: _ClassVar[CommitmentLevel]
|
||||||
|
FIRST_SHRED_RECEIVED: _ClassVar[CommitmentLevel]
|
||||||
|
COMPLETED: _ClassVar[CommitmentLevel]
|
||||||
|
CREATED_BANK: _ClassVar[CommitmentLevel]
|
||||||
|
DEAD: _ClassVar[CommitmentLevel]
|
||||||
|
PROCESSED: CommitmentLevel
|
||||||
|
CONFIRMED: CommitmentLevel
|
||||||
|
FINALIZED: CommitmentLevel
|
||||||
|
FIRST_SHRED_RECEIVED: CommitmentLevel
|
||||||
|
COMPLETED: CommitmentLevel
|
||||||
|
CREATED_BANK: CommitmentLevel
|
||||||
|
DEAD: CommitmentLevel
|
||||||
|
|
||||||
|
class SubscribeRequest(_message.Message):
|
||||||
|
__slots__ = ("accounts", "slots", "transactions", "transactions_status", "blocks", "blocks_meta", "entry", "commitment", "accounts_data_slice", "ping")
|
||||||
|
class AccountsEntry(_message.Message):
|
||||||
|
__slots__ = ("key", "value")
|
||||||
|
KEY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
VALUE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
key: str
|
||||||
|
value: SubscribeRequestFilterAccounts
|
||||||
|
def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[SubscribeRequestFilterAccounts, _Mapping]] = ...) -> None: ...
|
||||||
|
class SlotsEntry(_message.Message):
|
||||||
|
__slots__ = ("key", "value")
|
||||||
|
KEY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
VALUE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
key: str
|
||||||
|
value: SubscribeRequestFilterSlots
|
||||||
|
def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[SubscribeRequestFilterSlots, _Mapping]] = ...) -> None: ...
|
||||||
|
class TransactionsEntry(_message.Message):
|
||||||
|
__slots__ = ("key", "value")
|
||||||
|
KEY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
VALUE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
key: str
|
||||||
|
value: SubscribeRequestFilterTransactions
|
||||||
|
def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[SubscribeRequestFilterTransactions, _Mapping]] = ...) -> None: ...
|
||||||
|
class TransactionsStatusEntry(_message.Message):
|
||||||
|
__slots__ = ("key", "value")
|
||||||
|
KEY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
VALUE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
key: str
|
||||||
|
value: SubscribeRequestFilterTransactions
|
||||||
|
def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[SubscribeRequestFilterTransactions, _Mapping]] = ...) -> None: ...
|
||||||
|
class BlocksEntry(_message.Message):
|
||||||
|
__slots__ = ("key", "value")
|
||||||
|
KEY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
VALUE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
key: str
|
||||||
|
value: SubscribeRequestFilterBlocks
|
||||||
|
def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[SubscribeRequestFilterBlocks, _Mapping]] = ...) -> None: ...
|
||||||
|
class BlocksMetaEntry(_message.Message):
|
||||||
|
__slots__ = ("key", "value")
|
||||||
|
KEY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
VALUE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
key: str
|
||||||
|
value: SubscribeRequestFilterBlocksMeta
|
||||||
|
def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[SubscribeRequestFilterBlocksMeta, _Mapping]] = ...) -> None: ...
|
||||||
|
class EntryEntry(_message.Message):
|
||||||
|
__slots__ = ("key", "value")
|
||||||
|
KEY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
VALUE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
key: str
|
||||||
|
value: SubscribeRequestFilterEntry
|
||||||
|
def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[SubscribeRequestFilterEntry, _Mapping]] = ...) -> None: ...
|
||||||
|
ACCOUNTS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SLOTS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
TRANSACTIONS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
TRANSACTIONS_STATUS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCKS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCKS_META_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ENTRY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
COMMITMENT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ACCOUNTS_DATA_SLICE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
PING_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
accounts: _containers.MessageMap[str, SubscribeRequestFilterAccounts]
|
||||||
|
slots: _containers.MessageMap[str, SubscribeRequestFilterSlots]
|
||||||
|
transactions: _containers.MessageMap[str, SubscribeRequestFilterTransactions]
|
||||||
|
transactions_status: _containers.MessageMap[str, SubscribeRequestFilterTransactions]
|
||||||
|
blocks: _containers.MessageMap[str, SubscribeRequestFilterBlocks]
|
||||||
|
blocks_meta: _containers.MessageMap[str, SubscribeRequestFilterBlocksMeta]
|
||||||
|
entry: _containers.MessageMap[str, SubscribeRequestFilterEntry]
|
||||||
|
commitment: CommitmentLevel
|
||||||
|
accounts_data_slice: _containers.RepeatedCompositeFieldContainer[SubscribeRequestAccountsDataSlice]
|
||||||
|
ping: SubscribeRequestPing
|
||||||
|
def __init__(self, accounts: _Optional[_Mapping[str, SubscribeRequestFilterAccounts]] = ..., slots: _Optional[_Mapping[str, SubscribeRequestFilterSlots]] = ..., transactions: _Optional[_Mapping[str, SubscribeRequestFilterTransactions]] = ..., transactions_status: _Optional[_Mapping[str, SubscribeRequestFilterTransactions]] = ..., blocks: _Optional[_Mapping[str, SubscribeRequestFilterBlocks]] = ..., blocks_meta: _Optional[_Mapping[str, SubscribeRequestFilterBlocksMeta]] = ..., entry: _Optional[_Mapping[str, SubscribeRequestFilterEntry]] = ..., commitment: _Optional[_Union[CommitmentLevel, str]] = ..., accounts_data_slice: _Optional[_Iterable[_Union[SubscribeRequestAccountsDataSlice, _Mapping]]] = ..., ping: _Optional[_Union[SubscribeRequestPing, _Mapping]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeRequestFilterAccounts(_message.Message):
|
||||||
|
__slots__ = ("account", "owner", "filters", "nonempty_txn_signature")
|
||||||
|
ACCOUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
OWNER_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
FILTERS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
NONEMPTY_TXN_SIGNATURE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
account: _containers.RepeatedScalarFieldContainer[str]
|
||||||
|
owner: _containers.RepeatedScalarFieldContainer[str]
|
||||||
|
filters: _containers.RepeatedCompositeFieldContainer[SubscribeRequestFilterAccountsFilter]
|
||||||
|
nonempty_txn_signature: bool
|
||||||
|
def __init__(self, account: _Optional[_Iterable[str]] = ..., owner: _Optional[_Iterable[str]] = ..., filters: _Optional[_Iterable[_Union[SubscribeRequestFilterAccountsFilter, _Mapping]]] = ..., nonempty_txn_signature: bool = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeRequestFilterAccountsFilter(_message.Message):
|
||||||
|
__slots__ = ("memcmp", "datasize", "token_account_state", "lamports")
|
||||||
|
MEMCMP_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DATASIZE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
TOKEN_ACCOUNT_STATE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
LAMPORTS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
memcmp: SubscribeRequestFilterAccountsFilterMemcmp
|
||||||
|
datasize: int
|
||||||
|
token_account_state: bool
|
||||||
|
lamports: SubscribeRequestFilterAccountsFilterLamports
|
||||||
|
def __init__(self, memcmp: _Optional[_Union[SubscribeRequestFilterAccountsFilterMemcmp, _Mapping]] = ..., datasize: _Optional[int] = ..., token_account_state: bool = ..., lamports: _Optional[_Union[SubscribeRequestFilterAccountsFilterLamports, _Mapping]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeRequestFilterAccountsFilterMemcmp(_message.Message):
|
||||||
|
__slots__ = ("offset", "bytes", "base58", "base64")
|
||||||
|
OFFSET_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BYTES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BASE58_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BASE64_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
offset: int
|
||||||
|
bytes: bytes
|
||||||
|
base58: str
|
||||||
|
base64: str
|
||||||
|
def __init__(self, offset: _Optional[int] = ..., bytes: _Optional[bytes] = ..., base58: _Optional[str] = ..., base64: _Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeRequestFilterAccountsFilterLamports(_message.Message):
|
||||||
|
__slots__ = ("eq", "ne", "lt", "gt")
|
||||||
|
EQ_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
NE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
LT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
GT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
eq: int
|
||||||
|
ne: int
|
||||||
|
lt: int
|
||||||
|
gt: int
|
||||||
|
def __init__(self, eq: _Optional[int] = ..., ne: _Optional[int] = ..., lt: _Optional[int] = ..., gt: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeRequestFilterSlots(_message.Message):
|
||||||
|
__slots__ = ("filter_by_commitment",)
|
||||||
|
FILTER_BY_COMMITMENT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
filter_by_commitment: bool
|
||||||
|
def __init__(self, filter_by_commitment: bool = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeRequestFilterTransactions(_message.Message):
|
||||||
|
__slots__ = ("vote", "failed", "signature", "account_include", "account_exclude", "account_required")
|
||||||
|
VOTE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
FAILED_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SIGNATURE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ACCOUNT_INCLUDE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ACCOUNT_EXCLUDE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ACCOUNT_REQUIRED_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
vote: bool
|
||||||
|
failed: bool
|
||||||
|
signature: str
|
||||||
|
account_include: _containers.RepeatedScalarFieldContainer[str]
|
||||||
|
account_exclude: _containers.RepeatedScalarFieldContainer[str]
|
||||||
|
account_required: _containers.RepeatedScalarFieldContainer[str]
|
||||||
|
def __init__(self, vote: bool = ..., failed: bool = ..., signature: _Optional[str] = ..., account_include: _Optional[_Iterable[str]] = ..., account_exclude: _Optional[_Iterable[str]] = ..., account_required: _Optional[_Iterable[str]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeRequestFilterBlocks(_message.Message):
|
||||||
|
__slots__ = ("account_include", "include_transactions", "include_accounts", "include_entries")
|
||||||
|
ACCOUNT_INCLUDE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
INCLUDE_TRANSACTIONS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
INCLUDE_ACCOUNTS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
INCLUDE_ENTRIES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
account_include: _containers.RepeatedScalarFieldContainer[str]
|
||||||
|
include_transactions: bool
|
||||||
|
include_accounts: bool
|
||||||
|
include_entries: bool
|
||||||
|
def __init__(self, account_include: _Optional[_Iterable[str]] = ..., include_transactions: bool = ..., include_accounts: bool = ..., include_entries: bool = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeRequestFilterBlocksMeta(_message.Message):
|
||||||
|
__slots__ = ()
|
||||||
|
def __init__(self) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeRequestFilterEntry(_message.Message):
|
||||||
|
__slots__ = ()
|
||||||
|
def __init__(self) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeRequestAccountsDataSlice(_message.Message):
|
||||||
|
__slots__ = ("offset", "length")
|
||||||
|
OFFSET_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
LENGTH_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
offset: int
|
||||||
|
length: int
|
||||||
|
def __init__(self, offset: _Optional[int] = ..., length: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeRequestPing(_message.Message):
|
||||||
|
__slots__ = ("id",)
|
||||||
|
ID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
id: int
|
||||||
|
def __init__(self, id: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeUpdate(_message.Message):
|
||||||
|
__slots__ = ("filters", "account", "slot", "transaction", "transaction_status", "block", "ping", "pong", "block_meta", "entry")
|
||||||
|
FILTERS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ACCOUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
TRANSACTION_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
TRANSACTION_STATUS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCK_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
PING_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
PONG_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCK_META_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ENTRY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
filters: _containers.RepeatedScalarFieldContainer[str]
|
||||||
|
account: SubscribeUpdateAccount
|
||||||
|
slot: SubscribeUpdateSlot
|
||||||
|
transaction: SubscribeUpdateTransaction
|
||||||
|
transaction_status: SubscribeUpdateTransactionStatus
|
||||||
|
block: SubscribeUpdateBlock
|
||||||
|
ping: SubscribeUpdatePing
|
||||||
|
pong: SubscribeUpdatePong
|
||||||
|
block_meta: SubscribeUpdateBlockMeta
|
||||||
|
entry: SubscribeUpdateEntry
|
||||||
|
def __init__(self, filters: _Optional[_Iterable[str]] = ..., account: _Optional[_Union[SubscribeUpdateAccount, _Mapping]] = ..., slot: _Optional[_Union[SubscribeUpdateSlot, _Mapping]] = ..., transaction: _Optional[_Union[SubscribeUpdateTransaction, _Mapping]] = ..., transaction_status: _Optional[_Union[SubscribeUpdateTransactionStatus, _Mapping]] = ..., block: _Optional[_Union[SubscribeUpdateBlock, _Mapping]] = ..., ping: _Optional[_Union[SubscribeUpdatePing, _Mapping]] = ..., pong: _Optional[_Union[SubscribeUpdatePong, _Mapping]] = ..., block_meta: _Optional[_Union[SubscribeUpdateBlockMeta, _Mapping]] = ..., entry: _Optional[_Union[SubscribeUpdateEntry, _Mapping]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeUpdateAccount(_message.Message):
|
||||||
|
__slots__ = ("account", "slot", "is_startup")
|
||||||
|
ACCOUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
IS_STARTUP_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
account: SubscribeUpdateAccountInfo
|
||||||
|
slot: int
|
||||||
|
is_startup: bool
|
||||||
|
def __init__(self, account: _Optional[_Union[SubscribeUpdateAccountInfo, _Mapping]] = ..., slot: _Optional[int] = ..., is_startup: bool = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeUpdateAccountInfo(_message.Message):
|
||||||
|
__slots__ = ("pubkey", "lamports", "owner", "executable", "rent_epoch", "data", "write_version", "txn_signature")
|
||||||
|
PUBKEY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
LAMPORTS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
OWNER_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
EXECUTABLE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
RENT_EPOCH_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DATA_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
WRITE_VERSION_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
TXN_SIGNATURE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
pubkey: bytes
|
||||||
|
lamports: int
|
||||||
|
owner: bytes
|
||||||
|
executable: bool
|
||||||
|
rent_epoch: int
|
||||||
|
data: bytes
|
||||||
|
write_version: int
|
||||||
|
txn_signature: bytes
|
||||||
|
def __init__(self, pubkey: _Optional[bytes] = ..., lamports: _Optional[int] = ..., owner: _Optional[bytes] = ..., executable: bool = ..., rent_epoch: _Optional[int] = ..., data: _Optional[bytes] = ..., write_version: _Optional[int] = ..., txn_signature: _Optional[bytes] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeUpdateSlot(_message.Message):
|
||||||
|
__slots__ = ("slot", "parent", "status", "dead_error")
|
||||||
|
SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
PARENT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
STATUS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DEAD_ERROR_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
slot: int
|
||||||
|
parent: int
|
||||||
|
status: CommitmentLevel
|
||||||
|
dead_error: str
|
||||||
|
def __init__(self, slot: _Optional[int] = ..., parent: _Optional[int] = ..., status: _Optional[_Union[CommitmentLevel, str]] = ..., dead_error: _Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeUpdateTransaction(_message.Message):
|
||||||
|
__slots__ = ("transaction", "slot")
|
||||||
|
TRANSACTION_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
transaction: SubscribeUpdateTransactionInfo
|
||||||
|
slot: int
|
||||||
|
def __init__(self, transaction: _Optional[_Union[SubscribeUpdateTransactionInfo, _Mapping]] = ..., slot: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeUpdateTransactionInfo(_message.Message):
|
||||||
|
__slots__ = ("signature", "is_vote", "transaction", "meta", "index")
|
||||||
|
SIGNATURE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
IS_VOTE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
TRANSACTION_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
META_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
INDEX_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
signature: bytes
|
||||||
|
is_vote: bool
|
||||||
|
transaction: _solana_storage_pb2.Transaction
|
||||||
|
meta: _solana_storage_pb2.TransactionStatusMeta
|
||||||
|
index: int
|
||||||
|
def __init__(self, signature: _Optional[bytes] = ..., is_vote: bool = ..., transaction: _Optional[_Union[_solana_storage_pb2.Transaction, _Mapping]] = ..., meta: _Optional[_Union[_solana_storage_pb2.TransactionStatusMeta, _Mapping]] = ..., index: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeUpdateTransactionStatus(_message.Message):
|
||||||
|
__slots__ = ("slot", "signature", "is_vote", "index", "err")
|
||||||
|
SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SIGNATURE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
IS_VOTE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
INDEX_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ERR_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
slot: int
|
||||||
|
signature: bytes
|
||||||
|
is_vote: bool
|
||||||
|
index: int
|
||||||
|
err: _solana_storage_pb2.TransactionError
|
||||||
|
def __init__(self, slot: _Optional[int] = ..., signature: _Optional[bytes] = ..., is_vote: bool = ..., index: _Optional[int] = ..., err: _Optional[_Union[_solana_storage_pb2.TransactionError, _Mapping]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeUpdateBlock(_message.Message):
|
||||||
|
__slots__ = ("slot", "blockhash", "rewards", "block_time", "block_height", "parent_slot", "parent_blockhash", "executed_transaction_count", "transactions", "updated_account_count", "accounts", "entries_count", "entries")
|
||||||
|
SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCKHASH_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
REWARDS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCK_TIME_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCK_HEIGHT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
PARENT_SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
PARENT_BLOCKHASH_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
EXECUTED_TRANSACTION_COUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
TRANSACTIONS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
UPDATED_ACCOUNT_COUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ACCOUNTS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ENTRIES_COUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ENTRIES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
slot: int
|
||||||
|
blockhash: str
|
||||||
|
rewards: _solana_storage_pb2.Rewards
|
||||||
|
block_time: _solana_storage_pb2.UnixTimestamp
|
||||||
|
block_height: _solana_storage_pb2.BlockHeight
|
||||||
|
parent_slot: int
|
||||||
|
parent_blockhash: str
|
||||||
|
executed_transaction_count: int
|
||||||
|
transactions: _containers.RepeatedCompositeFieldContainer[SubscribeUpdateTransactionInfo]
|
||||||
|
updated_account_count: int
|
||||||
|
accounts: _containers.RepeatedCompositeFieldContainer[SubscribeUpdateAccountInfo]
|
||||||
|
entries_count: int
|
||||||
|
entries: _containers.RepeatedCompositeFieldContainer[SubscribeUpdateEntry]
|
||||||
|
def __init__(self, slot: _Optional[int] = ..., blockhash: _Optional[str] = ..., rewards: _Optional[_Union[_solana_storage_pb2.Rewards, _Mapping]] = ..., block_time: _Optional[_Union[_solana_storage_pb2.UnixTimestamp, _Mapping]] = ..., block_height: _Optional[_Union[_solana_storage_pb2.BlockHeight, _Mapping]] = ..., parent_slot: _Optional[int] = ..., parent_blockhash: _Optional[str] = ..., executed_transaction_count: _Optional[int] = ..., transactions: _Optional[_Iterable[_Union[SubscribeUpdateTransactionInfo, _Mapping]]] = ..., updated_account_count: _Optional[int] = ..., accounts: _Optional[_Iterable[_Union[SubscribeUpdateAccountInfo, _Mapping]]] = ..., entries_count: _Optional[int] = ..., entries: _Optional[_Iterable[_Union[SubscribeUpdateEntry, _Mapping]]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeUpdateBlockMeta(_message.Message):
|
||||||
|
__slots__ = ("slot", "blockhash", "rewards", "block_time", "block_height", "parent_slot", "parent_blockhash", "executed_transaction_count", "entries_count")
|
||||||
|
SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCKHASH_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
REWARDS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCK_TIME_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCK_HEIGHT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
PARENT_SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
PARENT_BLOCKHASH_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
EXECUTED_TRANSACTION_COUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ENTRIES_COUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
slot: int
|
||||||
|
blockhash: str
|
||||||
|
rewards: _solana_storage_pb2.Rewards
|
||||||
|
block_time: _solana_storage_pb2.UnixTimestamp
|
||||||
|
block_height: _solana_storage_pb2.BlockHeight
|
||||||
|
parent_slot: int
|
||||||
|
parent_blockhash: str
|
||||||
|
executed_transaction_count: int
|
||||||
|
entries_count: int
|
||||||
|
def __init__(self, slot: _Optional[int] = ..., blockhash: _Optional[str] = ..., rewards: _Optional[_Union[_solana_storage_pb2.Rewards, _Mapping]] = ..., block_time: _Optional[_Union[_solana_storage_pb2.UnixTimestamp, _Mapping]] = ..., block_height: _Optional[_Union[_solana_storage_pb2.BlockHeight, _Mapping]] = ..., parent_slot: _Optional[int] = ..., parent_blockhash: _Optional[str] = ..., executed_transaction_count: _Optional[int] = ..., entries_count: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeUpdateEntry(_message.Message):
|
||||||
|
__slots__ = ("slot", "index", "num_hashes", "hash", "executed_transaction_count", "starting_transaction_index")
|
||||||
|
SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
INDEX_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
NUM_HASHES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
HASH_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
EXECUTED_TRANSACTION_COUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
STARTING_TRANSACTION_INDEX_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
slot: int
|
||||||
|
index: int
|
||||||
|
num_hashes: int
|
||||||
|
hash: bytes
|
||||||
|
executed_transaction_count: int
|
||||||
|
starting_transaction_index: int
|
||||||
|
def __init__(self, slot: _Optional[int] = ..., index: _Optional[int] = ..., num_hashes: _Optional[int] = ..., hash: _Optional[bytes] = ..., executed_transaction_count: _Optional[int] = ..., starting_transaction_index: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeUpdatePing(_message.Message):
|
||||||
|
__slots__ = ()
|
||||||
|
def __init__(self) -> None: ...
|
||||||
|
|
||||||
|
class SubscribeUpdatePong(_message.Message):
|
||||||
|
__slots__ = ("id",)
|
||||||
|
ID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
id: int
|
||||||
|
def __init__(self, id: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class PingRequest(_message.Message):
|
||||||
|
__slots__ = ("count",)
|
||||||
|
COUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
count: int
|
||||||
|
def __init__(self, count: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class PongResponse(_message.Message):
|
||||||
|
__slots__ = ("count",)
|
||||||
|
COUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
count: int
|
||||||
|
def __init__(self, count: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class GetLatestBlockhashRequest(_message.Message):
|
||||||
|
__slots__ = ("commitment",)
|
||||||
|
COMMITMENT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
commitment: CommitmentLevel
|
||||||
|
def __init__(self, commitment: _Optional[_Union[CommitmentLevel, str]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class GetLatestBlockhashResponse(_message.Message):
|
||||||
|
__slots__ = ("slot", "blockhash", "last_valid_block_height")
|
||||||
|
SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCKHASH_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
LAST_VALID_BLOCK_HEIGHT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
slot: int
|
||||||
|
blockhash: str
|
||||||
|
last_valid_block_height: int
|
||||||
|
def __init__(self, slot: _Optional[int] = ..., blockhash: _Optional[str] = ..., last_valid_block_height: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class GetBlockHeightRequest(_message.Message):
|
||||||
|
__slots__ = ("commitment",)
|
||||||
|
COMMITMENT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
commitment: CommitmentLevel
|
||||||
|
def __init__(self, commitment: _Optional[_Union[CommitmentLevel, str]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class GetBlockHeightResponse(_message.Message):
|
||||||
|
__slots__ = ("block_height",)
|
||||||
|
BLOCK_HEIGHT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
block_height: int
|
||||||
|
def __init__(self, block_height: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class GetSlotRequest(_message.Message):
|
||||||
|
__slots__ = ("commitment",)
|
||||||
|
COMMITMENT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
commitment: CommitmentLevel
|
||||||
|
def __init__(self, commitment: _Optional[_Union[CommitmentLevel, str]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class GetSlotResponse(_message.Message):
|
||||||
|
__slots__ = ("slot",)
|
||||||
|
SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
slot: int
|
||||||
|
def __init__(self, slot: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class GetVersionRequest(_message.Message):
|
||||||
|
__slots__ = ()
|
||||||
|
def __init__(self) -> None: ...
|
||||||
|
|
||||||
|
class GetVersionResponse(_message.Message):
|
||||||
|
__slots__ = ("version",)
|
||||||
|
VERSION_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
version: str
|
||||||
|
def __init__(self, version: _Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
class IsBlockhashValidRequest(_message.Message):
|
||||||
|
__slots__ = ("blockhash", "commitment")
|
||||||
|
BLOCKHASH_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
COMMITMENT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
blockhash: str
|
||||||
|
commitment: CommitmentLevel
|
||||||
|
def __init__(self, blockhash: _Optional[str] = ..., commitment: _Optional[_Union[CommitmentLevel, str]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class IsBlockhashValidResponse(_message.Message):
|
||||||
|
__slots__ = ("slot", "valid")
|
||||||
|
SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
VALID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
slot: int
|
||||||
|
valid: bool
|
||||||
|
def __init__(self, slot: _Optional[int] = ..., valid: bool = ...) -> None: ...
|
||||||
@@ -0,0 +1,355 @@
|
|||||||
|
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
||||||
|
"""Client and server classes corresponding to protobuf-defined services."""
|
||||||
|
|
||||||
|
import grpc
|
||||||
|
|
||||||
|
import geyser.generated.geyser_pb2 as geyser__pb2
|
||||||
|
|
||||||
|
GRPC_GENERATED_VERSION = '1.71.0'
|
||||||
|
GRPC_VERSION = grpc.__version__
|
||||||
|
_version_not_supported = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
from grpc._utilities import first_version_is_lower
|
||||||
|
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
|
||||||
|
except ImportError:
|
||||||
|
_version_not_supported = True
|
||||||
|
|
||||||
|
if _version_not_supported:
|
||||||
|
raise RuntimeError(
|
||||||
|
f'The grpc package installed is at version {GRPC_VERSION},'
|
||||||
|
+ ' but the generated code in geyser_pb2_grpc.py depends on'
|
||||||
|
+ f' grpcio>={GRPC_GENERATED_VERSION}.'
|
||||||
|
+ f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
|
||||||
|
+ f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GeyserStub:
|
||||||
|
"""Missing associated documentation comment in .proto file."""
|
||||||
|
|
||||||
|
def __init__(self, channel):
|
||||||
|
"""Constructor.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
channel: A grpc.Channel.
|
||||||
|
"""
|
||||||
|
self.Subscribe = channel.stream_stream(
|
||||||
|
'/geyser.Geyser/Subscribe',
|
||||||
|
request_serializer=geyser__pb2.SubscribeRequest.SerializeToString,
|
||||||
|
response_deserializer=geyser__pb2.SubscribeUpdate.FromString,
|
||||||
|
_registered_method=True)
|
||||||
|
self.Ping = channel.unary_unary(
|
||||||
|
'/geyser.Geyser/Ping',
|
||||||
|
request_serializer=geyser__pb2.PingRequest.SerializeToString,
|
||||||
|
response_deserializer=geyser__pb2.PongResponse.FromString,
|
||||||
|
_registered_method=True)
|
||||||
|
self.GetLatestBlockhash = channel.unary_unary(
|
||||||
|
'/geyser.Geyser/GetLatestBlockhash',
|
||||||
|
request_serializer=geyser__pb2.GetLatestBlockhashRequest.SerializeToString,
|
||||||
|
response_deserializer=geyser__pb2.GetLatestBlockhashResponse.FromString,
|
||||||
|
_registered_method=True)
|
||||||
|
self.GetBlockHeight = channel.unary_unary(
|
||||||
|
'/geyser.Geyser/GetBlockHeight',
|
||||||
|
request_serializer=geyser__pb2.GetBlockHeightRequest.SerializeToString,
|
||||||
|
response_deserializer=geyser__pb2.GetBlockHeightResponse.FromString,
|
||||||
|
_registered_method=True)
|
||||||
|
self.GetSlot = channel.unary_unary(
|
||||||
|
'/geyser.Geyser/GetSlot',
|
||||||
|
request_serializer=geyser__pb2.GetSlotRequest.SerializeToString,
|
||||||
|
response_deserializer=geyser__pb2.GetSlotResponse.FromString,
|
||||||
|
_registered_method=True)
|
||||||
|
self.IsBlockhashValid = channel.unary_unary(
|
||||||
|
'/geyser.Geyser/IsBlockhashValid',
|
||||||
|
request_serializer=geyser__pb2.IsBlockhashValidRequest.SerializeToString,
|
||||||
|
response_deserializer=geyser__pb2.IsBlockhashValidResponse.FromString,
|
||||||
|
_registered_method=True)
|
||||||
|
self.GetVersion = channel.unary_unary(
|
||||||
|
'/geyser.Geyser/GetVersion',
|
||||||
|
request_serializer=geyser__pb2.GetVersionRequest.SerializeToString,
|
||||||
|
response_deserializer=geyser__pb2.GetVersionResponse.FromString,
|
||||||
|
_registered_method=True)
|
||||||
|
|
||||||
|
|
||||||
|
class GeyserServicer:
|
||||||
|
"""Missing associated documentation comment in .proto file."""
|
||||||
|
|
||||||
|
def Subscribe(self, request_iterator, context):
|
||||||
|
"""Missing associated documentation comment in .proto file."""
|
||||||
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||||
|
context.set_details('Method not implemented!')
|
||||||
|
raise NotImplementedError('Method not implemented!')
|
||||||
|
|
||||||
|
def Ping(self, request, context):
|
||||||
|
"""Missing associated documentation comment in .proto file."""
|
||||||
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||||
|
context.set_details('Method not implemented!')
|
||||||
|
raise NotImplementedError('Method not implemented!')
|
||||||
|
|
||||||
|
def GetLatestBlockhash(self, request, context):
|
||||||
|
"""Missing associated documentation comment in .proto file."""
|
||||||
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||||
|
context.set_details('Method not implemented!')
|
||||||
|
raise NotImplementedError('Method not implemented!')
|
||||||
|
|
||||||
|
def GetBlockHeight(self, request, context):
|
||||||
|
"""Missing associated documentation comment in .proto file."""
|
||||||
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||||
|
context.set_details('Method not implemented!')
|
||||||
|
raise NotImplementedError('Method not implemented!')
|
||||||
|
|
||||||
|
def GetSlot(self, request, context):
|
||||||
|
"""Missing associated documentation comment in .proto file."""
|
||||||
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||||
|
context.set_details('Method not implemented!')
|
||||||
|
raise NotImplementedError('Method not implemented!')
|
||||||
|
|
||||||
|
def IsBlockhashValid(self, request, context):
|
||||||
|
"""Missing associated documentation comment in .proto file."""
|
||||||
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||||
|
context.set_details('Method not implemented!')
|
||||||
|
raise NotImplementedError('Method not implemented!')
|
||||||
|
|
||||||
|
def GetVersion(self, request, context):
|
||||||
|
"""Missing associated documentation comment in .proto file."""
|
||||||
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||||
|
context.set_details('Method not implemented!')
|
||||||
|
raise NotImplementedError('Method not implemented!')
|
||||||
|
|
||||||
|
|
||||||
|
def add_GeyserServicer_to_server(servicer, server):
|
||||||
|
rpc_method_handlers = {
|
||||||
|
'Subscribe': grpc.stream_stream_rpc_method_handler(
|
||||||
|
servicer.Subscribe,
|
||||||
|
request_deserializer=geyser__pb2.SubscribeRequest.FromString,
|
||||||
|
response_serializer=geyser__pb2.SubscribeUpdate.SerializeToString,
|
||||||
|
),
|
||||||
|
'Ping': grpc.unary_unary_rpc_method_handler(
|
||||||
|
servicer.Ping,
|
||||||
|
request_deserializer=geyser__pb2.PingRequest.FromString,
|
||||||
|
response_serializer=geyser__pb2.PongResponse.SerializeToString,
|
||||||
|
),
|
||||||
|
'GetLatestBlockhash': grpc.unary_unary_rpc_method_handler(
|
||||||
|
servicer.GetLatestBlockhash,
|
||||||
|
request_deserializer=geyser__pb2.GetLatestBlockhashRequest.FromString,
|
||||||
|
response_serializer=geyser__pb2.GetLatestBlockhashResponse.SerializeToString,
|
||||||
|
),
|
||||||
|
'GetBlockHeight': grpc.unary_unary_rpc_method_handler(
|
||||||
|
servicer.GetBlockHeight,
|
||||||
|
request_deserializer=geyser__pb2.GetBlockHeightRequest.FromString,
|
||||||
|
response_serializer=geyser__pb2.GetBlockHeightResponse.SerializeToString,
|
||||||
|
),
|
||||||
|
'GetSlot': grpc.unary_unary_rpc_method_handler(
|
||||||
|
servicer.GetSlot,
|
||||||
|
request_deserializer=geyser__pb2.GetSlotRequest.FromString,
|
||||||
|
response_serializer=geyser__pb2.GetSlotResponse.SerializeToString,
|
||||||
|
),
|
||||||
|
'IsBlockhashValid': grpc.unary_unary_rpc_method_handler(
|
||||||
|
servicer.IsBlockhashValid,
|
||||||
|
request_deserializer=geyser__pb2.IsBlockhashValidRequest.FromString,
|
||||||
|
response_serializer=geyser__pb2.IsBlockhashValidResponse.SerializeToString,
|
||||||
|
),
|
||||||
|
'GetVersion': grpc.unary_unary_rpc_method_handler(
|
||||||
|
servicer.GetVersion,
|
||||||
|
request_deserializer=geyser__pb2.GetVersionRequest.FromString,
|
||||||
|
response_serializer=geyser__pb2.GetVersionResponse.SerializeToString,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
generic_handler = grpc.method_handlers_generic_handler(
|
||||||
|
'geyser.Geyser', rpc_method_handlers)
|
||||||
|
server.add_generic_rpc_handlers((generic_handler,))
|
||||||
|
server.add_registered_method_handlers('geyser.Geyser', rpc_method_handlers)
|
||||||
|
|
||||||
|
|
||||||
|
# This class is part of an EXPERIMENTAL API.
|
||||||
|
class Geyser:
|
||||||
|
"""Missing associated documentation comment in .proto file."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def Subscribe(request_iterator,
|
||||||
|
target,
|
||||||
|
options=(),
|
||||||
|
channel_credentials=None,
|
||||||
|
call_credentials=None,
|
||||||
|
insecure=False,
|
||||||
|
compression=None,
|
||||||
|
wait_for_ready=None,
|
||||||
|
timeout=None,
|
||||||
|
metadata=None):
|
||||||
|
return grpc.experimental.stream_stream(
|
||||||
|
request_iterator,
|
||||||
|
target,
|
||||||
|
'/geyser.Geyser/Subscribe',
|
||||||
|
geyser__pb2.SubscribeRequest.SerializeToString,
|
||||||
|
geyser__pb2.SubscribeUpdate.FromString,
|
||||||
|
options,
|
||||||
|
channel_credentials,
|
||||||
|
insecure,
|
||||||
|
call_credentials,
|
||||||
|
compression,
|
||||||
|
wait_for_ready,
|
||||||
|
timeout,
|
||||||
|
metadata,
|
||||||
|
_registered_method=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def Ping(request,
|
||||||
|
target,
|
||||||
|
options=(),
|
||||||
|
channel_credentials=None,
|
||||||
|
call_credentials=None,
|
||||||
|
insecure=False,
|
||||||
|
compression=None,
|
||||||
|
wait_for_ready=None,
|
||||||
|
timeout=None,
|
||||||
|
metadata=None):
|
||||||
|
return grpc.experimental.unary_unary(
|
||||||
|
request,
|
||||||
|
target,
|
||||||
|
'/geyser.Geyser/Ping',
|
||||||
|
geyser__pb2.PingRequest.SerializeToString,
|
||||||
|
geyser__pb2.PongResponse.FromString,
|
||||||
|
options,
|
||||||
|
channel_credentials,
|
||||||
|
insecure,
|
||||||
|
call_credentials,
|
||||||
|
compression,
|
||||||
|
wait_for_ready,
|
||||||
|
timeout,
|
||||||
|
metadata,
|
||||||
|
_registered_method=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def GetLatestBlockhash(request,
|
||||||
|
target,
|
||||||
|
options=(),
|
||||||
|
channel_credentials=None,
|
||||||
|
call_credentials=None,
|
||||||
|
insecure=False,
|
||||||
|
compression=None,
|
||||||
|
wait_for_ready=None,
|
||||||
|
timeout=None,
|
||||||
|
metadata=None):
|
||||||
|
return grpc.experimental.unary_unary(
|
||||||
|
request,
|
||||||
|
target,
|
||||||
|
'/geyser.Geyser/GetLatestBlockhash',
|
||||||
|
geyser__pb2.GetLatestBlockhashRequest.SerializeToString,
|
||||||
|
geyser__pb2.GetLatestBlockhashResponse.FromString,
|
||||||
|
options,
|
||||||
|
channel_credentials,
|
||||||
|
insecure,
|
||||||
|
call_credentials,
|
||||||
|
compression,
|
||||||
|
wait_for_ready,
|
||||||
|
timeout,
|
||||||
|
metadata,
|
||||||
|
_registered_method=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def GetBlockHeight(request,
|
||||||
|
target,
|
||||||
|
options=(),
|
||||||
|
channel_credentials=None,
|
||||||
|
call_credentials=None,
|
||||||
|
insecure=False,
|
||||||
|
compression=None,
|
||||||
|
wait_for_ready=None,
|
||||||
|
timeout=None,
|
||||||
|
metadata=None):
|
||||||
|
return grpc.experimental.unary_unary(
|
||||||
|
request,
|
||||||
|
target,
|
||||||
|
'/geyser.Geyser/GetBlockHeight',
|
||||||
|
geyser__pb2.GetBlockHeightRequest.SerializeToString,
|
||||||
|
geyser__pb2.GetBlockHeightResponse.FromString,
|
||||||
|
options,
|
||||||
|
channel_credentials,
|
||||||
|
insecure,
|
||||||
|
call_credentials,
|
||||||
|
compression,
|
||||||
|
wait_for_ready,
|
||||||
|
timeout,
|
||||||
|
metadata,
|
||||||
|
_registered_method=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def GetSlot(request,
|
||||||
|
target,
|
||||||
|
options=(),
|
||||||
|
channel_credentials=None,
|
||||||
|
call_credentials=None,
|
||||||
|
insecure=False,
|
||||||
|
compression=None,
|
||||||
|
wait_for_ready=None,
|
||||||
|
timeout=None,
|
||||||
|
metadata=None):
|
||||||
|
return grpc.experimental.unary_unary(
|
||||||
|
request,
|
||||||
|
target,
|
||||||
|
'/geyser.Geyser/GetSlot',
|
||||||
|
geyser__pb2.GetSlotRequest.SerializeToString,
|
||||||
|
geyser__pb2.GetSlotResponse.FromString,
|
||||||
|
options,
|
||||||
|
channel_credentials,
|
||||||
|
insecure,
|
||||||
|
call_credentials,
|
||||||
|
compression,
|
||||||
|
wait_for_ready,
|
||||||
|
timeout,
|
||||||
|
metadata,
|
||||||
|
_registered_method=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def IsBlockhashValid(request,
|
||||||
|
target,
|
||||||
|
options=(),
|
||||||
|
channel_credentials=None,
|
||||||
|
call_credentials=None,
|
||||||
|
insecure=False,
|
||||||
|
compression=None,
|
||||||
|
wait_for_ready=None,
|
||||||
|
timeout=None,
|
||||||
|
metadata=None):
|
||||||
|
return grpc.experimental.unary_unary(
|
||||||
|
request,
|
||||||
|
target,
|
||||||
|
'/geyser.Geyser/IsBlockhashValid',
|
||||||
|
geyser__pb2.IsBlockhashValidRequest.SerializeToString,
|
||||||
|
geyser__pb2.IsBlockhashValidResponse.FromString,
|
||||||
|
options,
|
||||||
|
channel_credentials,
|
||||||
|
insecure,
|
||||||
|
call_credentials,
|
||||||
|
compression,
|
||||||
|
wait_for_ready,
|
||||||
|
timeout,
|
||||||
|
metadata,
|
||||||
|
_registered_method=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def GetVersion(request,
|
||||||
|
target,
|
||||||
|
options=(),
|
||||||
|
channel_credentials=None,
|
||||||
|
call_credentials=None,
|
||||||
|
insecure=False,
|
||||||
|
compression=None,
|
||||||
|
wait_for_ready=None,
|
||||||
|
timeout=None,
|
||||||
|
metadata=None):
|
||||||
|
return grpc.experimental.unary_unary(
|
||||||
|
request,
|
||||||
|
target,
|
||||||
|
'/geyser.Geyser/GetVersion',
|
||||||
|
geyser__pb2.GetVersionRequest.SerializeToString,
|
||||||
|
geyser__pb2.GetVersionResponse.FromString,
|
||||||
|
options,
|
||||||
|
channel_credentials,
|
||||||
|
insecure,
|
||||||
|
call_credentials,
|
||||||
|
compression,
|
||||||
|
wait_for_ready,
|
||||||
|
timeout,
|
||||||
|
metadata,
|
||||||
|
_registered_method=True)
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,238 @@
|
|||||||
|
from google.protobuf.internal import containers as _containers
|
||||||
|
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
|
||||||
|
from google.protobuf import descriptor as _descriptor
|
||||||
|
from google.protobuf import message as _message
|
||||||
|
from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union
|
||||||
|
|
||||||
|
DESCRIPTOR: _descriptor.FileDescriptor
|
||||||
|
|
||||||
|
class RewardType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||||
|
__slots__ = ()
|
||||||
|
Unspecified: _ClassVar[RewardType]
|
||||||
|
Fee: _ClassVar[RewardType]
|
||||||
|
Rent: _ClassVar[RewardType]
|
||||||
|
Staking: _ClassVar[RewardType]
|
||||||
|
Voting: _ClassVar[RewardType]
|
||||||
|
Unspecified: RewardType
|
||||||
|
Fee: RewardType
|
||||||
|
Rent: RewardType
|
||||||
|
Staking: RewardType
|
||||||
|
Voting: RewardType
|
||||||
|
|
||||||
|
class ConfirmedBlock(_message.Message):
|
||||||
|
__slots__ = ("previous_blockhash", "blockhash", "parent_slot", "transactions", "rewards", "block_time", "block_height", "num_partitions")
|
||||||
|
PREVIOUS_BLOCKHASH_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCKHASH_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
PARENT_SLOT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
TRANSACTIONS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
REWARDS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCK_TIME_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BLOCK_HEIGHT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
NUM_PARTITIONS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
previous_blockhash: str
|
||||||
|
blockhash: str
|
||||||
|
parent_slot: int
|
||||||
|
transactions: _containers.RepeatedCompositeFieldContainer[ConfirmedTransaction]
|
||||||
|
rewards: _containers.RepeatedCompositeFieldContainer[Reward]
|
||||||
|
block_time: UnixTimestamp
|
||||||
|
block_height: BlockHeight
|
||||||
|
num_partitions: NumPartitions
|
||||||
|
def __init__(self, previous_blockhash: _Optional[str] = ..., blockhash: _Optional[str] = ..., parent_slot: _Optional[int] = ..., transactions: _Optional[_Iterable[_Union[ConfirmedTransaction, _Mapping]]] = ..., rewards: _Optional[_Iterable[_Union[Reward, _Mapping]]] = ..., block_time: _Optional[_Union[UnixTimestamp, _Mapping]] = ..., block_height: _Optional[_Union[BlockHeight, _Mapping]] = ..., num_partitions: _Optional[_Union[NumPartitions, _Mapping]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class ConfirmedTransaction(_message.Message):
|
||||||
|
__slots__ = ("transaction", "meta")
|
||||||
|
TRANSACTION_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
META_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
transaction: Transaction
|
||||||
|
meta: TransactionStatusMeta
|
||||||
|
def __init__(self, transaction: _Optional[_Union[Transaction, _Mapping]] = ..., meta: _Optional[_Union[TransactionStatusMeta, _Mapping]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class Transaction(_message.Message):
|
||||||
|
__slots__ = ("signatures", "message")
|
||||||
|
SIGNATURES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
MESSAGE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
signatures: _containers.RepeatedScalarFieldContainer[bytes]
|
||||||
|
message: Message
|
||||||
|
def __init__(self, signatures: _Optional[_Iterable[bytes]] = ..., message: _Optional[_Union[Message, _Mapping]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class Message(_message.Message):
|
||||||
|
__slots__ = ("header", "account_keys", "recent_blockhash", "instructions", "versioned", "address_table_lookups")
|
||||||
|
HEADER_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ACCOUNT_KEYS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
RECENT_BLOCKHASH_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
INSTRUCTIONS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
VERSIONED_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ADDRESS_TABLE_LOOKUPS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
header: MessageHeader
|
||||||
|
account_keys: _containers.RepeatedScalarFieldContainer[bytes]
|
||||||
|
recent_blockhash: bytes
|
||||||
|
instructions: _containers.RepeatedCompositeFieldContainer[CompiledInstruction]
|
||||||
|
versioned: bool
|
||||||
|
address_table_lookups: _containers.RepeatedCompositeFieldContainer[MessageAddressTableLookup]
|
||||||
|
def __init__(self, header: _Optional[_Union[MessageHeader, _Mapping]] = ..., account_keys: _Optional[_Iterable[bytes]] = ..., recent_blockhash: _Optional[bytes] = ..., instructions: _Optional[_Iterable[_Union[CompiledInstruction, _Mapping]]] = ..., versioned: bool = ..., address_table_lookups: _Optional[_Iterable[_Union[MessageAddressTableLookup, _Mapping]]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class MessageHeader(_message.Message):
|
||||||
|
__slots__ = ("num_required_signatures", "num_readonly_signed_accounts", "num_readonly_unsigned_accounts")
|
||||||
|
NUM_REQUIRED_SIGNATURES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
NUM_READONLY_SIGNED_ACCOUNTS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
NUM_READONLY_UNSIGNED_ACCOUNTS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
num_required_signatures: int
|
||||||
|
num_readonly_signed_accounts: int
|
||||||
|
num_readonly_unsigned_accounts: int
|
||||||
|
def __init__(self, num_required_signatures: _Optional[int] = ..., num_readonly_signed_accounts: _Optional[int] = ..., num_readonly_unsigned_accounts: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class MessageAddressTableLookup(_message.Message):
|
||||||
|
__slots__ = ("account_key", "writable_indexes", "readonly_indexes")
|
||||||
|
ACCOUNT_KEY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
WRITABLE_INDEXES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
READONLY_INDEXES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
account_key: bytes
|
||||||
|
writable_indexes: bytes
|
||||||
|
readonly_indexes: bytes
|
||||||
|
def __init__(self, account_key: _Optional[bytes] = ..., writable_indexes: _Optional[bytes] = ..., readonly_indexes: _Optional[bytes] = ...) -> None: ...
|
||||||
|
|
||||||
|
class TransactionStatusMeta(_message.Message):
|
||||||
|
__slots__ = ("err", "fee", "pre_balances", "post_balances", "inner_instructions", "inner_instructions_none", "log_messages", "log_messages_none", "pre_token_balances", "post_token_balances", "rewards", "loaded_writable_addresses", "loaded_readonly_addresses", "return_data", "return_data_none", "compute_units_consumed")
|
||||||
|
ERR_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
FEE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
PRE_BALANCES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
POST_BALANCES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
INNER_INSTRUCTIONS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
INNER_INSTRUCTIONS_NONE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
LOG_MESSAGES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
LOG_MESSAGES_NONE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
PRE_TOKEN_BALANCES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
POST_TOKEN_BALANCES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
REWARDS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
LOADED_WRITABLE_ADDRESSES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
LOADED_READONLY_ADDRESSES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
RETURN_DATA_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
RETURN_DATA_NONE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
COMPUTE_UNITS_CONSUMED_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
err: TransactionError
|
||||||
|
fee: int
|
||||||
|
pre_balances: _containers.RepeatedScalarFieldContainer[int]
|
||||||
|
post_balances: _containers.RepeatedScalarFieldContainer[int]
|
||||||
|
inner_instructions: _containers.RepeatedCompositeFieldContainer[InnerInstructions]
|
||||||
|
inner_instructions_none: bool
|
||||||
|
log_messages: _containers.RepeatedScalarFieldContainer[str]
|
||||||
|
log_messages_none: bool
|
||||||
|
pre_token_balances: _containers.RepeatedCompositeFieldContainer[TokenBalance]
|
||||||
|
post_token_balances: _containers.RepeatedCompositeFieldContainer[TokenBalance]
|
||||||
|
rewards: _containers.RepeatedCompositeFieldContainer[Reward]
|
||||||
|
loaded_writable_addresses: _containers.RepeatedScalarFieldContainer[bytes]
|
||||||
|
loaded_readonly_addresses: _containers.RepeatedScalarFieldContainer[bytes]
|
||||||
|
return_data: ReturnData
|
||||||
|
return_data_none: bool
|
||||||
|
compute_units_consumed: int
|
||||||
|
def __init__(self, err: _Optional[_Union[TransactionError, _Mapping]] = ..., fee: _Optional[int] = ..., pre_balances: _Optional[_Iterable[int]] = ..., post_balances: _Optional[_Iterable[int]] = ..., inner_instructions: _Optional[_Iterable[_Union[InnerInstructions, _Mapping]]] = ..., inner_instructions_none: bool = ..., log_messages: _Optional[_Iterable[str]] = ..., log_messages_none: bool = ..., pre_token_balances: _Optional[_Iterable[_Union[TokenBalance, _Mapping]]] = ..., post_token_balances: _Optional[_Iterable[_Union[TokenBalance, _Mapping]]] = ..., rewards: _Optional[_Iterable[_Union[Reward, _Mapping]]] = ..., loaded_writable_addresses: _Optional[_Iterable[bytes]] = ..., loaded_readonly_addresses: _Optional[_Iterable[bytes]] = ..., return_data: _Optional[_Union[ReturnData, _Mapping]] = ..., return_data_none: bool = ..., compute_units_consumed: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class TransactionError(_message.Message):
|
||||||
|
__slots__ = ("err",)
|
||||||
|
ERR_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
err: bytes
|
||||||
|
def __init__(self, err: _Optional[bytes] = ...) -> None: ...
|
||||||
|
|
||||||
|
class InnerInstructions(_message.Message):
|
||||||
|
__slots__ = ("index", "instructions")
|
||||||
|
INDEX_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
INSTRUCTIONS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
index: int
|
||||||
|
instructions: _containers.RepeatedCompositeFieldContainer[InnerInstruction]
|
||||||
|
def __init__(self, index: _Optional[int] = ..., instructions: _Optional[_Iterable[_Union[InnerInstruction, _Mapping]]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class InnerInstruction(_message.Message):
|
||||||
|
__slots__ = ("program_id_index", "accounts", "data", "stack_height")
|
||||||
|
PROGRAM_ID_INDEX_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ACCOUNTS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DATA_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
STACK_HEIGHT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
program_id_index: int
|
||||||
|
accounts: bytes
|
||||||
|
data: bytes
|
||||||
|
stack_height: int
|
||||||
|
def __init__(self, program_id_index: _Optional[int] = ..., accounts: _Optional[bytes] = ..., data: _Optional[bytes] = ..., stack_height: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class CompiledInstruction(_message.Message):
|
||||||
|
__slots__ = ("program_id_index", "accounts", "data")
|
||||||
|
PROGRAM_ID_INDEX_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ACCOUNTS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DATA_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
program_id_index: int
|
||||||
|
accounts: bytes
|
||||||
|
data: bytes
|
||||||
|
def __init__(self, program_id_index: _Optional[int] = ..., accounts: _Optional[bytes] = ..., data: _Optional[bytes] = ...) -> None: ...
|
||||||
|
|
||||||
|
class TokenBalance(_message.Message):
|
||||||
|
__slots__ = ("account_index", "mint", "ui_token_amount", "owner", "program_id")
|
||||||
|
ACCOUNT_INDEX_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
MINT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
UI_TOKEN_AMOUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
OWNER_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
PROGRAM_ID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
account_index: int
|
||||||
|
mint: str
|
||||||
|
ui_token_amount: UiTokenAmount
|
||||||
|
owner: str
|
||||||
|
program_id: str
|
||||||
|
def __init__(self, account_index: _Optional[int] = ..., mint: _Optional[str] = ..., ui_token_amount: _Optional[_Union[UiTokenAmount, _Mapping]] = ..., owner: _Optional[str] = ..., program_id: _Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
class UiTokenAmount(_message.Message):
|
||||||
|
__slots__ = ("ui_amount", "decimals", "amount", "ui_amount_string")
|
||||||
|
UI_AMOUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DECIMALS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
AMOUNT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
UI_AMOUNT_STRING_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ui_amount: float
|
||||||
|
decimals: int
|
||||||
|
amount: str
|
||||||
|
ui_amount_string: str
|
||||||
|
def __init__(self, ui_amount: _Optional[float] = ..., decimals: _Optional[int] = ..., amount: _Optional[str] = ..., ui_amount_string: _Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
class ReturnData(_message.Message):
|
||||||
|
__slots__ = ("program_id", "data")
|
||||||
|
PROGRAM_ID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DATA_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
program_id: bytes
|
||||||
|
data: bytes
|
||||||
|
def __init__(self, program_id: _Optional[bytes] = ..., data: _Optional[bytes] = ...) -> None: ...
|
||||||
|
|
||||||
|
class Reward(_message.Message):
|
||||||
|
__slots__ = ("pubkey", "lamports", "post_balance", "reward_type", "commission")
|
||||||
|
PUBKEY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
LAMPORTS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
POST_BALANCE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
REWARD_TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
COMMISSION_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
pubkey: str
|
||||||
|
lamports: int
|
||||||
|
post_balance: int
|
||||||
|
reward_type: RewardType
|
||||||
|
commission: str
|
||||||
|
def __init__(self, pubkey: _Optional[str] = ..., lamports: _Optional[int] = ..., post_balance: _Optional[int] = ..., reward_type: _Optional[_Union[RewardType, str]] = ..., commission: _Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
class Rewards(_message.Message):
|
||||||
|
__slots__ = ("rewards", "num_partitions")
|
||||||
|
REWARDS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
NUM_PARTITIONS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
rewards: _containers.RepeatedCompositeFieldContainer[Reward]
|
||||||
|
num_partitions: NumPartitions
|
||||||
|
def __init__(self, rewards: _Optional[_Iterable[_Union[Reward, _Mapping]]] = ..., num_partitions: _Optional[_Union[NumPartitions, _Mapping]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class UnixTimestamp(_message.Message):
|
||||||
|
__slots__ = ("timestamp",)
|
||||||
|
TIMESTAMP_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
timestamp: int
|
||||||
|
def __init__(self, timestamp: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class BlockHeight(_message.Message):
|
||||||
|
__slots__ = ("block_height",)
|
||||||
|
BLOCK_HEIGHT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
block_height: int
|
||||||
|
def __init__(self, block_height: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class NumPartitions(_message.Message):
|
||||||
|
__slots__ = ("num_partitions",)
|
||||||
|
NUM_PARTITIONS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
num_partitions: int
|
||||||
|
def __init__(self, num_partitions: _Optional[int] = ...) -> None: ...
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
||||||
|
"""Client and server classes corresponding to protobuf-defined services."""
|
||||||
|
import grpc
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
|
GRPC_GENERATED_VERSION = '1.71.0'
|
||||||
|
GRPC_VERSION = grpc.__version__
|
||||||
|
_version_not_supported = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
from grpc._utilities import first_version_is_lower
|
||||||
|
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
|
||||||
|
except ImportError:
|
||||||
|
_version_not_supported = True
|
||||||
|
|
||||||
|
if _version_not_supported:
|
||||||
|
raise RuntimeError(
|
||||||
|
f'The grpc package installed is at version {GRPC_VERSION},'
|
||||||
|
+ f' but the generated code in solana_storage_pb2_grpc.py depends on'
|
||||||
|
+ f' grpcio>={GRPC_GENERATED_VERSION}.'
|
||||||
|
+ f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
|
||||||
|
+ f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
|
||||||
|
)
|
||||||
@@ -0,0 +1,262 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import public "solana-storage.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/rpcpool/yellowstone-grpc/examples/golang/proto";
|
||||||
|
|
||||||
|
package geyser;
|
||||||
|
|
||||||
|
service Geyser {
|
||||||
|
rpc Subscribe(stream SubscribeRequest) returns (stream SubscribeUpdate) {}
|
||||||
|
rpc Ping(PingRequest) returns (PongResponse) {}
|
||||||
|
rpc GetLatestBlockhash(GetLatestBlockhashRequest) returns (GetLatestBlockhashResponse) {}
|
||||||
|
rpc GetBlockHeight(GetBlockHeightRequest) returns (GetBlockHeightResponse) {}
|
||||||
|
rpc GetSlot(GetSlotRequest) returns (GetSlotResponse) {}
|
||||||
|
rpc IsBlockhashValid(IsBlockhashValidRequest) returns (IsBlockhashValidResponse) {}
|
||||||
|
rpc GetVersion(GetVersionRequest) returns (GetVersionResponse) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CommitmentLevel {
|
||||||
|
PROCESSED = 0;
|
||||||
|
CONFIRMED = 1;
|
||||||
|
FINALIZED = 2;
|
||||||
|
FIRST_SHRED_RECEIVED = 3;
|
||||||
|
COMPLETED = 4;
|
||||||
|
CREATED_BANK = 5;
|
||||||
|
DEAD = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequest {
|
||||||
|
map<string, SubscribeRequestFilterAccounts> accounts = 1;
|
||||||
|
map<string, SubscribeRequestFilterSlots> slots = 2;
|
||||||
|
map<string, SubscribeRequestFilterTransactions> transactions = 3;
|
||||||
|
map<string, SubscribeRequestFilterTransactions> transactions_status = 10;
|
||||||
|
map<string, SubscribeRequestFilterBlocks> blocks = 4;
|
||||||
|
map<string, SubscribeRequestFilterBlocksMeta> blocks_meta = 5;
|
||||||
|
map<string, SubscribeRequestFilterEntry> entry = 8;
|
||||||
|
optional CommitmentLevel commitment = 6;
|
||||||
|
repeated SubscribeRequestAccountsDataSlice accounts_data_slice = 7;
|
||||||
|
optional SubscribeRequestPing ping = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterAccounts {
|
||||||
|
repeated string account = 2;
|
||||||
|
repeated string owner = 3;
|
||||||
|
repeated SubscribeRequestFilterAccountsFilter filters = 4;
|
||||||
|
optional bool nonempty_txn_signature = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterAccountsFilter {
|
||||||
|
oneof filter {
|
||||||
|
SubscribeRequestFilterAccountsFilterMemcmp memcmp = 1;
|
||||||
|
uint64 datasize = 2;
|
||||||
|
bool token_account_state = 3;
|
||||||
|
SubscribeRequestFilterAccountsFilterLamports lamports = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterAccountsFilterMemcmp {
|
||||||
|
uint64 offset = 1;
|
||||||
|
oneof data {
|
||||||
|
bytes bytes = 2;
|
||||||
|
string base58 = 3;
|
||||||
|
string base64 = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterAccountsFilterLamports {
|
||||||
|
oneof cmp {
|
||||||
|
uint64 eq = 1;
|
||||||
|
uint64 ne = 2;
|
||||||
|
uint64 lt = 3;
|
||||||
|
uint64 gt = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterSlots {
|
||||||
|
optional bool filter_by_commitment = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterTransactions {
|
||||||
|
optional bool vote = 1;
|
||||||
|
optional bool failed = 2;
|
||||||
|
optional string signature = 5;
|
||||||
|
repeated string account_include = 3;
|
||||||
|
repeated string account_exclude = 4;
|
||||||
|
repeated string account_required = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterBlocks {
|
||||||
|
repeated string account_include = 1;
|
||||||
|
optional bool include_transactions = 2;
|
||||||
|
optional bool include_accounts = 3;
|
||||||
|
optional bool include_entries = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterBlocksMeta {}
|
||||||
|
|
||||||
|
message SubscribeRequestFilterEntry {}
|
||||||
|
|
||||||
|
message SubscribeRequestAccountsDataSlice {
|
||||||
|
uint64 offset = 1;
|
||||||
|
uint64 length = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeRequestPing {
|
||||||
|
int32 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdate {
|
||||||
|
repeated string filters = 1;
|
||||||
|
oneof update_oneof {
|
||||||
|
SubscribeUpdateAccount account = 2;
|
||||||
|
SubscribeUpdateSlot slot = 3;
|
||||||
|
SubscribeUpdateTransaction transaction = 4;
|
||||||
|
SubscribeUpdateTransactionStatus transaction_status = 10;
|
||||||
|
SubscribeUpdateBlock block = 5;
|
||||||
|
SubscribeUpdatePing ping = 6;
|
||||||
|
SubscribeUpdatePong pong = 9;
|
||||||
|
SubscribeUpdateBlockMeta block_meta = 7;
|
||||||
|
SubscribeUpdateEntry entry = 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateAccount {
|
||||||
|
SubscribeUpdateAccountInfo account = 1;
|
||||||
|
uint64 slot = 2;
|
||||||
|
bool is_startup = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateAccountInfo {
|
||||||
|
bytes pubkey = 1;
|
||||||
|
uint64 lamports = 2;
|
||||||
|
bytes owner = 3;
|
||||||
|
bool executable = 4;
|
||||||
|
uint64 rent_epoch = 5;
|
||||||
|
bytes data = 6;
|
||||||
|
uint64 write_version = 7;
|
||||||
|
optional bytes txn_signature = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateSlot {
|
||||||
|
uint64 slot = 1;
|
||||||
|
optional uint64 parent = 2;
|
||||||
|
CommitmentLevel status = 3;
|
||||||
|
optional string dead_error = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateTransaction {
|
||||||
|
SubscribeUpdateTransactionInfo transaction = 1;
|
||||||
|
uint64 slot = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateTransactionInfo {
|
||||||
|
bytes signature = 1;
|
||||||
|
bool is_vote = 2;
|
||||||
|
solana.storage.ConfirmedBlock.Transaction transaction = 3;
|
||||||
|
solana.storage.ConfirmedBlock.TransactionStatusMeta meta = 4;
|
||||||
|
uint64 index = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateTransactionStatus {
|
||||||
|
uint64 slot = 1;
|
||||||
|
bytes signature = 2;
|
||||||
|
bool is_vote = 3;
|
||||||
|
uint64 index = 4;
|
||||||
|
solana.storage.ConfirmedBlock.TransactionError err = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateBlock {
|
||||||
|
uint64 slot = 1;
|
||||||
|
string blockhash = 2;
|
||||||
|
solana.storage.ConfirmedBlock.Rewards rewards = 3;
|
||||||
|
solana.storage.ConfirmedBlock.UnixTimestamp block_time = 4;
|
||||||
|
solana.storage.ConfirmedBlock.BlockHeight block_height = 5;
|
||||||
|
uint64 parent_slot = 7;
|
||||||
|
string parent_blockhash = 8;
|
||||||
|
uint64 executed_transaction_count = 9;
|
||||||
|
repeated SubscribeUpdateTransactionInfo transactions = 6;
|
||||||
|
uint64 updated_account_count = 10;
|
||||||
|
repeated SubscribeUpdateAccountInfo accounts = 11;
|
||||||
|
uint64 entries_count = 12;
|
||||||
|
repeated SubscribeUpdateEntry entries = 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateBlockMeta {
|
||||||
|
uint64 slot = 1;
|
||||||
|
string blockhash = 2;
|
||||||
|
solana.storage.ConfirmedBlock.Rewards rewards = 3;
|
||||||
|
solana.storage.ConfirmedBlock.UnixTimestamp block_time = 4;
|
||||||
|
solana.storage.ConfirmedBlock.BlockHeight block_height = 5;
|
||||||
|
uint64 parent_slot = 6;
|
||||||
|
string parent_blockhash = 7;
|
||||||
|
uint64 executed_transaction_count = 8;
|
||||||
|
uint64 entries_count = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdateEntry {
|
||||||
|
uint64 slot = 1;
|
||||||
|
uint64 index = 2;
|
||||||
|
uint64 num_hashes = 3;
|
||||||
|
bytes hash = 4;
|
||||||
|
uint64 executed_transaction_count = 5;
|
||||||
|
uint64 starting_transaction_index = 6; // added in v1.18, for solana 1.17 value is always 0
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeUpdatePing {}
|
||||||
|
|
||||||
|
message SubscribeUpdatePong {
|
||||||
|
int32 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// non-streaming methods
|
||||||
|
|
||||||
|
message PingRequest {
|
||||||
|
int32 count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PongResponse {
|
||||||
|
int32 count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetLatestBlockhashRequest {
|
||||||
|
optional CommitmentLevel commitment = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetLatestBlockhashResponse {
|
||||||
|
uint64 slot = 1;
|
||||||
|
string blockhash = 2;
|
||||||
|
uint64 last_valid_block_height = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetBlockHeightRequest {
|
||||||
|
optional CommitmentLevel commitment = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetBlockHeightResponse {
|
||||||
|
uint64 block_height = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetSlotRequest {
|
||||||
|
optional CommitmentLevel commitment = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetSlotResponse {
|
||||||
|
uint64 slot = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetVersionRequest {}
|
||||||
|
|
||||||
|
message GetVersionResponse {
|
||||||
|
string version = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message IsBlockhashValidRequest {
|
||||||
|
string blockhash = 1;
|
||||||
|
optional CommitmentLevel commitment = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message IsBlockhashValidResponse {
|
||||||
|
uint64 slot = 1;
|
||||||
|
bool valid = 2;
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package solana.storage.ConfirmedBlock;
|
||||||
|
|
||||||
|
option go_package = "github.com/rpcpool/yellowstone-grpc/examples/golang/proto";
|
||||||
|
|
||||||
|
message ConfirmedBlock {
|
||||||
|
string previous_blockhash = 1;
|
||||||
|
string blockhash = 2;
|
||||||
|
uint64 parent_slot = 3;
|
||||||
|
repeated ConfirmedTransaction transactions = 4;
|
||||||
|
repeated Reward rewards = 5;
|
||||||
|
UnixTimestamp block_time = 6;
|
||||||
|
BlockHeight block_height = 7;
|
||||||
|
NumPartitions num_partitions = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConfirmedTransaction {
|
||||||
|
Transaction transaction = 1;
|
||||||
|
TransactionStatusMeta meta = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Transaction {
|
||||||
|
repeated bytes signatures = 1;
|
||||||
|
Message message = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Message {
|
||||||
|
MessageHeader header = 1;
|
||||||
|
repeated bytes account_keys = 2;
|
||||||
|
bytes recent_blockhash = 3;
|
||||||
|
repeated CompiledInstruction instructions = 4;
|
||||||
|
bool versioned = 5;
|
||||||
|
repeated MessageAddressTableLookup address_table_lookups = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageHeader {
|
||||||
|
uint32 num_required_signatures = 1;
|
||||||
|
uint32 num_readonly_signed_accounts = 2;
|
||||||
|
uint32 num_readonly_unsigned_accounts = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageAddressTableLookup {
|
||||||
|
bytes account_key = 1;
|
||||||
|
bytes writable_indexes = 2;
|
||||||
|
bytes readonly_indexes = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TransactionStatusMeta {
|
||||||
|
TransactionError err = 1;
|
||||||
|
uint64 fee = 2;
|
||||||
|
repeated uint64 pre_balances = 3;
|
||||||
|
repeated uint64 post_balances = 4;
|
||||||
|
repeated InnerInstructions inner_instructions = 5;
|
||||||
|
bool inner_instructions_none = 10;
|
||||||
|
repeated string log_messages = 6;
|
||||||
|
bool log_messages_none = 11;
|
||||||
|
repeated TokenBalance pre_token_balances = 7;
|
||||||
|
repeated TokenBalance post_token_balances = 8;
|
||||||
|
repeated Reward rewards = 9;
|
||||||
|
repeated bytes loaded_writable_addresses = 12;
|
||||||
|
repeated bytes loaded_readonly_addresses = 13;
|
||||||
|
ReturnData return_data = 14;
|
||||||
|
bool return_data_none = 15;
|
||||||
|
|
||||||
|
// Sum of compute units consumed by all instructions.
|
||||||
|
// Available since Solana v1.10.35 / v1.11.6.
|
||||||
|
// Set to `None` for txs executed on earlier versions.
|
||||||
|
optional uint64 compute_units_consumed = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TransactionError {
|
||||||
|
bytes err = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InnerInstructions {
|
||||||
|
uint32 index = 1;
|
||||||
|
repeated InnerInstruction instructions = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InnerInstruction {
|
||||||
|
uint32 program_id_index = 1;
|
||||||
|
bytes accounts = 2;
|
||||||
|
bytes data = 3;
|
||||||
|
|
||||||
|
// Invocation stack height of an inner instruction.
|
||||||
|
// Available since Solana v1.14.6
|
||||||
|
// Set to `None` for txs executed on earlier versions.
|
||||||
|
optional uint32 stack_height = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CompiledInstruction {
|
||||||
|
uint32 program_id_index = 1;
|
||||||
|
bytes accounts = 2;
|
||||||
|
bytes data = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TokenBalance {
|
||||||
|
uint32 account_index = 1;
|
||||||
|
string mint = 2;
|
||||||
|
UiTokenAmount ui_token_amount = 3;
|
||||||
|
string owner = 4;
|
||||||
|
string program_id = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UiTokenAmount {
|
||||||
|
double ui_amount = 1;
|
||||||
|
uint32 decimals = 2;
|
||||||
|
string amount = 3;
|
||||||
|
string ui_amount_string = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ReturnData {
|
||||||
|
bytes program_id = 1;
|
||||||
|
bytes data = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RewardType {
|
||||||
|
Unspecified = 0;
|
||||||
|
Fee = 1;
|
||||||
|
Rent = 2;
|
||||||
|
Staking = 3;
|
||||||
|
Voting = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Reward {
|
||||||
|
string pubkey = 1;
|
||||||
|
int64 lamports = 2;
|
||||||
|
uint64 post_balance = 3;
|
||||||
|
RewardType reward_type = 4;
|
||||||
|
string commission = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Rewards {
|
||||||
|
repeated Reward rewards = 1;
|
||||||
|
NumPartitions num_partitions = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UnixTimestamp {
|
||||||
|
int64 timestamp = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BlockHeight {
|
||||||
|
uint64 block_height = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NumPartitions {
|
||||||
|
uint64 num_partitions = 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
"""
|
||||||
|
Event processing for pump.fun tokens using Geyser data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import struct
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
from solders.pubkey import Pubkey
|
||||||
|
|
||||||
|
from trading.base import TokenInfo
|
||||||
|
from utils.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class GeyserEventProcessor:
|
||||||
|
"""Processes token creation events from Geyser stream."""
|
||||||
|
|
||||||
|
CREATE_DISCRIMINATOR: Final[bytes] = struct.pack("<Q", 8576854823835016728)
|
||||||
|
|
||||||
|
def __init__(self, pump_program: Pubkey):
|
||||||
|
"""Initialize event processor.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pump_program: Pump.fun program address
|
||||||
|
"""
|
||||||
|
self.pump_program = pump_program
|
||||||
|
|
||||||
|
def process_transaction_data(self, instruction_data: bytes, accounts: list, keys: list) -> TokenInfo | None:
|
||||||
|
"""Process transaction data and extract token creation info.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
instruction_data: Raw instruction data
|
||||||
|
accounts: List of account indices
|
||||||
|
keys: List of account public keys
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
TokenInfo if token creation found, None otherwise
|
||||||
|
"""
|
||||||
|
if not instruction_data.startswith(self.CREATE_DISCRIMINATOR):
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Skip past the 8-byte discriminator
|
||||||
|
offset = 8
|
||||||
|
|
||||||
|
# Helper to read strings (prefixed with length)
|
||||||
|
def read_string():
|
||||||
|
nonlocal offset
|
||||||
|
# Get string length (4-byte uint)
|
||||||
|
length = struct.unpack_from("<I", instruction_data, offset)[0]
|
||||||
|
offset += 4
|
||||||
|
# Extract and decode the string
|
||||||
|
value = instruction_data[offset:offset + length].decode("utf-8")
|
||||||
|
offset += length
|
||||||
|
return value
|
||||||
|
|
||||||
|
# Helper to get account key
|
||||||
|
def get_account_key(index):
|
||||||
|
if index >= len(accounts):
|
||||||
|
return None
|
||||||
|
account_index = accounts[index]
|
||||||
|
if account_index >= len(keys):
|
||||||
|
return None
|
||||||
|
return Pubkey.from_bytes(keys[account_index])
|
||||||
|
|
||||||
|
name = read_string()
|
||||||
|
symbol = read_string()
|
||||||
|
uri = read_string()
|
||||||
|
|
||||||
|
mint = get_account_key(0)
|
||||||
|
bonding_curve = get_account_key(2)
|
||||||
|
associated_bonding_curve = get_account_key(3)
|
||||||
|
user = get_account_key(7)
|
||||||
|
|
||||||
|
if not all([mint, bonding_curve, associated_bonding_curve, user]):
|
||||||
|
logger.warning("Missing required account keys in token creation")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return TokenInfo(
|
||||||
|
name=name,
|
||||||
|
symbol=symbol,
|
||||||
|
uri=uri,
|
||||||
|
mint=mint,
|
||||||
|
bonding_curve=bonding_curve,
|
||||||
|
associated_bonding_curve=associated_bonding_curve,
|
||||||
|
user=user,
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to process transaction data: {e}")
|
||||||
|
return None
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
"""
|
||||||
|
Geyser monitoring for pump.fun tokens.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from collections.abc import Awaitable, Callable
|
||||||
|
|
||||||
|
import grpc
|
||||||
|
from solders.pubkey import Pubkey
|
||||||
|
|
||||||
|
from geyser.generated import geyser_pb2, geyser_pb2_grpc
|
||||||
|
from monitoring.base_listener import BaseTokenListener
|
||||||
|
from monitoring.geyser_event_processor import GeyserEventProcessor
|
||||||
|
from trading.base import TokenInfo
|
||||||
|
from utils.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class GeyserListener(BaseTokenListener):
|
||||||
|
"""Geyser listener for pump.fun token creation events."""
|
||||||
|
|
||||||
|
def __init__(self, geyser_endpoint: str, geyser_api_token: str, pump_program: Pubkey):
|
||||||
|
"""Initialize token listener.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
geyser_endpoint: Geyser gRPC endpoint URL
|
||||||
|
geyser_api_token: API token for authentication
|
||||||
|
pump_program: Pump.fun program address
|
||||||
|
"""
|
||||||
|
self.geyser_endpoint = geyser_endpoint
|
||||||
|
self.geyser_api_token = geyser_api_token
|
||||||
|
self.pump_program = pump_program
|
||||||
|
self.event_processor = GeyserEventProcessor(pump_program)
|
||||||
|
|
||||||
|
async def _create_geyser_connection(self):
|
||||||
|
"""Establish a secure connection to the Geyser endpoint."""
|
||||||
|
auth = grpc.metadata_call_credentials(
|
||||||
|
lambda context, callback: callback(
|
||||||
|
(("authorization", f"Basic {self.geyser_api_token}"),), None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
creds = grpc.composite_channel_credentials(
|
||||||
|
grpc.ssl_channel_credentials(), auth
|
||||||
|
)
|
||||||
|
channel = grpc.aio.secure_channel(self.geyser_endpoint, creds)
|
||||||
|
return geyser_pb2_grpc.GeyserStub(channel), channel
|
||||||
|
|
||||||
|
def _create_subscription_request(self):
|
||||||
|
"""Create a subscription request for Pump.fun transactions."""
|
||||||
|
request = geyser_pb2.SubscribeRequest()
|
||||||
|
request.transactions["pump_filter"].account_include.append(str(self.pump_program))
|
||||||
|
request.transactions["pump_filter"].failed = False
|
||||||
|
request.commitment = geyser_pb2.CommitmentLevel.PROCESSED
|
||||||
|
return request
|
||||||
|
|
||||||
|
async def listen_for_tokens(
|
||||||
|
self,
|
||||||
|
token_callback: Callable[[TokenInfo], Awaitable[None]],
|
||||||
|
match_string: str | None = None,
|
||||||
|
creator_address: str | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Listen for new token creations using Geyser subscription.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
token_callback: Callback function for new tokens
|
||||||
|
match_string: Optional string to match in token name/symbol
|
||||||
|
creator_address: Optional creator address to filter by
|
||||||
|
"""
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
stub, channel = await self._create_geyser_connection()
|
||||||
|
request = self._create_subscription_request()
|
||||||
|
|
||||||
|
logger.info(f"Connected to Geyser endpoint: {self.geyser_endpoint}")
|
||||||
|
logger.info(f"Monitoring for transactions involving program: {self.pump_program}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
async for update in stub.Subscribe(iter([request])):
|
||||||
|
token_info = await self._process_update(update)
|
||||||
|
if not token_info:
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"New token detected: {token_info.name} ({token_info.symbol})"
|
||||||
|
)
|
||||||
|
|
||||||
|
if match_string and not (
|
||||||
|
match_string.lower() in token_info.name.lower()
|
||||||
|
or match_string.lower() in token_info.symbol.lower()
|
||||||
|
):
|
||||||
|
logger.info(
|
||||||
|
f"Token does not match filter '{match_string}'. Skipping..."
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (
|
||||||
|
creator_address
|
||||||
|
and str(token_info.user) != creator_address
|
||||||
|
):
|
||||||
|
logger.info(
|
||||||
|
f"Token not created by {creator_address}. Skipping..."
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
await token_callback(token_info)
|
||||||
|
|
||||||
|
except grpc.aio.AioRpcError as e:
|
||||||
|
logger.error(f"gRPC error: {e.details()}")
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
await channel.close()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Geyser connection error: {e}")
|
||||||
|
logger.info("Reconnecting in 10 seconds...")
|
||||||
|
await asyncio.sleep(10)
|
||||||
|
|
||||||
|
async def _process_update(self, update) -> TokenInfo | None:
|
||||||
|
"""Process a Geyser update and extract token creation info.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
update: Geyser update from the subscription
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
TokenInfo if a token creation is found, None otherwise
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not update.HasField("transaction"):
|
||||||
|
return None
|
||||||
|
|
||||||
|
tx = update.transaction.transaction.transaction
|
||||||
|
msg = getattr(tx, "message", None)
|
||||||
|
if msg is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
for ix in msg.instructions:
|
||||||
|
# Skip non-Pump.fun program instructions
|
||||||
|
program_idx = ix.program_id_index
|
||||||
|
if program_idx >= len(msg.account_keys):
|
||||||
|
continue
|
||||||
|
|
||||||
|
program_id = msg.account_keys[program_idx]
|
||||||
|
if bytes(program_id) != bytes(self.pump_program):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Process instruction data
|
||||||
|
token_info = self.event_processor.process_transaction_data(
|
||||||
|
ix.data, ix.accounts, msg.account_keys
|
||||||
|
)
|
||||||
|
if token_info:
|
||||||
|
return token_info
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error processing Geyser update: {e}")
|
||||||
|
return None
|
||||||
+19
-3
@@ -22,6 +22,7 @@ from core.priority_fee.manager import PriorityFeeManager
|
|||||||
from core.pubkeys import PumpAddresses
|
from core.pubkeys import PumpAddresses
|
||||||
from core.wallet import Wallet
|
from core.wallet import Wallet
|
||||||
from monitoring.block_listener import BlockListener
|
from monitoring.block_listener import BlockListener
|
||||||
|
from monitoring.geyser_listener import GeyserListener
|
||||||
from monitoring.logs_listener import LogsListener
|
from monitoring.logs_listener import LogsListener
|
||||||
from trading.base import TokenInfo, TradeResult
|
from trading.base import TokenInfo, TradeResult
|
||||||
from trading.buyer import TokenBuyer
|
from trading.buyer import TokenBuyer
|
||||||
@@ -43,7 +44,9 @@ class PumpTrader:
|
|||||||
buy_slippage: float,
|
buy_slippage: float,
|
||||||
sell_slippage: float,
|
sell_slippage: float,
|
||||||
max_retries: int = 5,
|
max_retries: int = 5,
|
||||||
listener_type: str = "block", # Add this parameter
|
listener_type: str = "logs",
|
||||||
|
geyser_endpoint: str | None = None,
|
||||||
|
geyser_api_token: str | None = None,
|
||||||
):
|
):
|
||||||
"""Initialize the pump trader.
|
"""Initialize the pump trader.
|
||||||
|
|
||||||
@@ -55,7 +58,9 @@ class PumpTrader:
|
|||||||
buy_slippage: Slippage tolerance for buys
|
buy_slippage: Slippage tolerance for buys
|
||||||
sell_slippage: Slippage tolerance for sells
|
sell_slippage: Slippage tolerance for sells
|
||||||
max_retries: Maximum number of retry attempts
|
max_retries: Maximum number of retry attempts
|
||||||
listener_type: Type of listener to use ('block' or 'logs')
|
listener_type: Type of listener to use ('logs', 'blocks', or 'geyser')
|
||||||
|
geyser_endpoint: Geyser endpoint URL (required for geyser listener)
|
||||||
|
geyser_api_token: Geyser API token (required for geyser listener)
|
||||||
"""
|
"""
|
||||||
self.solana_client = SolanaClient(rpc_endpoint)
|
self.solana_client = SolanaClient(rpc_endpoint)
|
||||||
self.wallet = Wallet(private_key)
|
self.wallet = Wallet(private_key)
|
||||||
@@ -92,7 +97,18 @@ class PumpTrader:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Initialize the appropriate listener type
|
# Initialize the appropriate listener type
|
||||||
if listener_type.lower() == "logs":
|
listener_type = listener_type.lower()
|
||||||
|
if listener_type == "geyser":
|
||||||
|
if not geyser_endpoint or not geyser_api_token:
|
||||||
|
raise ValueError("Geyser endpoint and API token are required for geyser listener")
|
||||||
|
|
||||||
|
self.token_listener = GeyserListener(
|
||||||
|
geyser_endpoint,
|
||||||
|
geyser_api_token,
|
||||||
|
PumpAddresses.PROGRAM
|
||||||
|
)
|
||||||
|
logger.info("Using Geyser listener for token monitoring")
|
||||||
|
elif listener_type == "logs":
|
||||||
self.token_listener = LogsListener(wss_endpoint, PumpAddresses.PROGRAM)
|
self.token_listener = LogsListener(wss_endpoint, PumpAddresses.PROGRAM)
|
||||||
logger.info("Using logsSubscribe listener for token monitoring")
|
logger.info("Using logsSubscribe listener for token monitoring")
|
||||||
else:
|
else:
|
||||||
|
|||||||
+123
-52
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
Test script to compare BlockListener and LogsListener
|
Test script to compare BlockListener, LogsListener, and GeyserListener
|
||||||
Runs both listeners simultaneously to compare their performance
|
Runs all listeners simultaneously to compare their performance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
@@ -10,13 +10,18 @@ import sys
|
|||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
sys.path.append(str(Path(__file__).parent.parent / "src"))
|
sys.path.append(str(Path(__file__).parent.parent / "src"))
|
||||||
|
|
||||||
from core.pubkeys import PumpAddresses
|
from core.pubkeys import PumpAddresses
|
||||||
from monitoring.block_listener import BlockListener
|
from monitoring.block_listener import BlockListener
|
||||||
|
from monitoring.geyser_listener import GeyserListener
|
||||||
from monitoring.logs_listener import LogsListener
|
from monitoring.logs_listener import LogsListener
|
||||||
from trading.base import TokenInfo
|
from trading.base import TokenInfo
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
||||||
)
|
)
|
||||||
@@ -46,9 +51,30 @@ class TimingTokenCallback:
|
|||||||
print(f"{'=' * 50}\n")
|
print(f"{'=' * 50}\n")
|
||||||
|
|
||||||
|
|
||||||
|
async def listen_with_timeout(listener, callback, timeout):
|
||||||
|
"""Run a listener for a specified duration"""
|
||||||
|
try:
|
||||||
|
listen_task = asyncio.create_task(
|
||||||
|
listener.listen_for_tokens(callback.on_token_created)
|
||||||
|
)
|
||||||
|
|
||||||
|
await asyncio.sleep(timeout)
|
||||||
|
|
||||||
|
listen_task.cancel()
|
||||||
|
try:
|
||||||
|
await listen_task
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in listener {callback.name}: {e}")
|
||||||
|
|
||||||
|
|
||||||
async def run_comparison(test_duration: int = 300):
|
async def run_comparison(test_duration: int = 300):
|
||||||
"""Run both listeners and compare their performance"""
|
"""Run all listeners and compare their performance"""
|
||||||
wss_endpoint = os.environ.get("SOLANA_NODE_WSS_ENDPOINT")
|
wss_endpoint = os.environ.get("SOLANA_NODE_WSS_ENDPOINT")
|
||||||
|
geyser_endpoint = os.environ.get("GEYSER_ENDPOINT")
|
||||||
|
geyser_api_token = os.environ.get("GEYSER_API_TOKEN")
|
||||||
|
|
||||||
if not wss_endpoint:
|
if not wss_endpoint:
|
||||||
logger.error("SOLANA_NODE_WSS_ENDPOINT environment variable is not set")
|
logger.error("SOLANA_NODE_WSS_ENDPOINT environment variable is not set")
|
||||||
return
|
return
|
||||||
@@ -57,70 +83,115 @@ async def run_comparison(test_duration: int = 300):
|
|||||||
|
|
||||||
block_listener = BlockListener(wss_endpoint, PumpAddresses.PROGRAM)
|
block_listener = BlockListener(wss_endpoint, PumpAddresses.PROGRAM)
|
||||||
logs_listener = LogsListener(wss_endpoint, PumpAddresses.PROGRAM)
|
logs_listener = LogsListener(wss_endpoint, PumpAddresses.PROGRAM)
|
||||||
|
|
||||||
block_callback = TimingTokenCallback("BlockListener")
|
block_callback = TimingTokenCallback("BlockListener")
|
||||||
logs_callback = TimingTokenCallback("LogsListener")
|
logs_callback = TimingTokenCallback("LogsListener")
|
||||||
|
|
||||||
logger.info("Starting both listeners...")
|
listener_tasks = [
|
||||||
block_task = asyncio.create_task(
|
listen_with_timeout(block_listener, block_callback, test_duration),
|
||||||
block_listener.listen_for_tokens(block_callback.on_token_created)
|
listen_with_timeout(logs_listener, logs_callback, test_duration)
|
||||||
)
|
]
|
||||||
logs_task = asyncio.create_task(
|
|
||||||
logs_listener.listen_for_tokens(logs_callback.on_token_created)
|
callbacks = [block_callback, logs_callback]
|
||||||
)
|
listener_names = ["BlockListener", "LogsListener"]
|
||||||
|
|
||||||
|
# Initialize Geyser listener if credentials are available
|
||||||
|
if geyser_endpoint and geyser_api_token:
|
||||||
|
logger.info(f"Connecting to Geyser API: {geyser_endpoint}")
|
||||||
|
geyser_listener = GeyserListener(geyser_endpoint, geyser_api_token, PumpAddresses.PROGRAM)
|
||||||
|
geyser_callback = TimingTokenCallback("GeyserListener")
|
||||||
|
|
||||||
|
listener_tasks.append(
|
||||||
|
listen_with_timeout(geyser_listener, geyser_callback, test_duration)
|
||||||
|
)
|
||||||
|
|
||||||
|
callbacks.append(geyser_callback)
|
||||||
|
listener_names.append("GeyserListener")
|
||||||
|
else:
|
||||||
|
logger.warning("Geyser API credentials not found. Running without Geyser listener.")
|
||||||
|
|
||||||
|
logger.info("Starting all listeners simultaneously...")
|
||||||
logger.info(f"Comparison running for {test_duration} seconds...")
|
logger.info(f"Comparison running for {test_duration} seconds...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await asyncio.sleep(test_duration)
|
# Start all listeners at the same time
|
||||||
|
start_time = time.time()
|
||||||
|
await asyncio.gather(*listener_tasks)
|
||||||
|
end_time = time.time()
|
||||||
|
|
||||||
|
logger.info(f"Test completed in {end_time - start_time:.2f} seconds")
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.info("Test interrupted by user")
|
logger.info("Test interrupted by user")
|
||||||
finally:
|
# No need for explicit cancellation as gather() will be interrupted
|
||||||
block_task.cancel()
|
|
||||||
logs_task.cancel()
|
|
||||||
try:
|
|
||||||
await asyncio.gather(block_task, logs_task, return_exceptions=True)
|
|
||||||
except asyncio.CancelledError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
logger.info(f"BlockListener detected {len(block_callback.detected_tokens)} tokens")
|
for i, callback in enumerate(callbacks):
|
||||||
logger.info(f"LogsListener detected {len(logs_callback.detected_tokens)} tokens")
|
logger.info(f"{listener_names[i]} detected {len(callback.detected_tokens)} tokens")
|
||||||
|
|
||||||
# Find tokens detected by both listeners
|
# Find tokens detected by multiple listeners
|
||||||
block_mints = {str(token.mint) for token in block_callback.detected_tokens}
|
all_mints = {}
|
||||||
logs_mints = {str(token.mint) for token in logs_callback.detected_tokens}
|
for i, callback in enumerate(callbacks):
|
||||||
common_mints = block_mints.intersection(logs_mints)
|
mints = {str(token.mint) for token in callback.detected_tokens}
|
||||||
|
all_mints[listener_names[i]] = mints
|
||||||
|
|
||||||
logger.info(f"Tokens detected by both listeners: {len(common_mints)}")
|
# Analyze common detections between all listeners
|
||||||
|
if len(callbacks) > 1:
|
||||||
|
logger.info("\nAnalyzing token detection across listeners:")
|
||||||
|
|
||||||
|
# Find tokens detected by all listeners
|
||||||
|
if len(callbacks) > 2: # If we have all 3 listeners
|
||||||
|
common_to_all = set.intersection(*all_mints.values())
|
||||||
|
logger.info(f"Tokens detected by all listeners: {len(common_to_all)}")
|
||||||
|
|
||||||
|
# Compare pairs of listeners
|
||||||
|
listeners = list(all_mints.keys())
|
||||||
|
for i in range(len(listeners)):
|
||||||
|
for j in range(i+1, len(listeners)):
|
||||||
|
listener1 = listeners[i]
|
||||||
|
listener2 = listeners[j]
|
||||||
|
common = all_mints[listener1].intersection(all_mints[listener2])
|
||||||
|
logger.info(f"Tokens detected by both {listener1} and {listener2}: {len(common)}")
|
||||||
|
|
||||||
|
unique1 = all_mints[listener1] - all_mints[listener2]
|
||||||
|
unique2 = all_mints[listener2] - all_mints[listener1]
|
||||||
|
logger.info(f"Tokens unique to {listener1}: {len(unique1)}")
|
||||||
|
logger.info(f"Tokens unique to {listener2}: {len(unique2)}")
|
||||||
|
|
||||||
|
# Find tokens detected by at least one listener
|
||||||
|
all_detected = set.union(*all_mints.values())
|
||||||
|
logger.info(f"Total unique tokens detected by any listener: {len(all_detected)}")
|
||||||
|
|
||||||
# Compare detection times for common tokens
|
logger.info("\nDetection speed comparison:")
|
||||||
if common_mints:
|
|
||||||
logger.info("\nPerformance comparison for tokens detected by both listeners:")
|
# Collect all tokens detected by at least two listeners
|
||||||
logger.info("Token Mint | BlockListener Time | LogsListener Time | Difference (ms)")
|
detection_comparisons = []
|
||||||
|
for mint in set.union(*all_mints.values()):
|
||||||
|
detections = {}
|
||||||
|
for i, callback in enumerate(callbacks):
|
||||||
|
if mint in callback.detection_times:
|
||||||
|
detections[listener_names[i]] = callback.detection_times[mint]
|
||||||
|
|
||||||
|
if len(detections) > 1: # Only consider tokens detected by multiple listeners
|
||||||
|
detection_comparisons.append((mint, detections))
|
||||||
|
|
||||||
|
if detection_comparisons:
|
||||||
|
logger.info("Token | " + " | ".join(listener_names) + " | Fastest")
|
||||||
logger.info("-" * 80)
|
logger.info("-" * 80)
|
||||||
|
|
||||||
for mint in common_mints:
|
for mint, detections in detection_comparisons:
|
||||||
block_time = block_callback.detection_times.get(mint)
|
# Create row with detection times or "N/A" if not detected
|
||||||
logs_time = logs_callback.detection_times.get(mint)
|
times = []
|
||||||
|
for name in listener_names:
|
||||||
|
time_str = f"{detections.get(name, 0):.6f}" if name in detections else "N/A"
|
||||||
|
times.append(time_str)
|
||||||
|
|
||||||
if block_time and logs_time:
|
# Determine fastest listener
|
||||||
diff_ms = abs(block_time - logs_time) * 1000 # Convert to milliseconds
|
valid_times = {name: time for name, time in detections.items() if time > 0}
|
||||||
faster = "BlockListener" if block_time < logs_time else "LogsListener"
|
fastest = min(valid_times.items(), key=lambda x: x[1])[0] if valid_times else "N/A"
|
||||||
|
|
||||||
logger.info(f"{mint[:10]}... | {block_time:.6f} | {logs_time:.6f} | {diff_ms:.2f}ms ({faster} faster)")
|
logger.info(f"{mint[:10]}... | " + " | ".join(times) + f" | {fastest}")
|
||||||
|
else:
|
||||||
# Report tokens only detected by one listener
|
logger.info("No tokens were detected by multiple listeners for timing comparison")
|
||||||
block_only = block_mints - logs_mints
|
|
||||||
logs_only = logs_mints - block_mints
|
|
||||||
|
|
||||||
if block_only:
|
|
||||||
logger.info(f"\nTokens only detected by BlockListener: {len(block_only)}")
|
|
||||||
for mint in block_only:
|
|
||||||
logger.info(f" - {mint}")
|
|
||||||
|
|
||||||
if logs_only:
|
|
||||||
logger.info(f"\nTokens only detected by LogsListener: {len(logs_only)}")
|
|
||||||
for mint in logs_only:
|
|
||||||
logger.info(f" - {mint}")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -9,12 +9,16 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
sys.path.append(str(Path(__file__).parent.parent / "src"))
|
sys.path.append(str(Path(__file__).parent.parent / "src"))
|
||||||
|
|
||||||
from core.pubkeys import PumpAddresses
|
from core.pubkeys import PumpAddresses
|
||||||
from monitoring.block_listener import BlockListener
|
from monitoring.block_listener import BlockListener
|
||||||
from trading.base import TokenInfo
|
from trading.base import TokenInfo
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
||||||
)
|
)
|
||||||
@@ -91,7 +95,7 @@ async def test_block_listener(
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
match_string = None # Update if you want to filter tokens by name/symbol
|
match_string = None # Update if you want to filter tokens by name/symbol
|
||||||
creator_address = None # Update if you want to filter tokens by creator address
|
creator_address = None # Update if you want to filter tokens by creator address
|
||||||
test_duration = 15
|
test_duration = 30
|
||||||
|
|
||||||
logger.info("Starting block listener test (using blockSubscribe)")
|
logger.info("Starting block listener test (using blockSubscribe)")
|
||||||
asyncio.run(test_block_listener(match_string, creator_address, test_duration))
|
asyncio.run(test_block_listener(match_string, creator_address, test_duration))
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
"""
|
||||||
|
Test script for GeyserListener
|
||||||
|
Tests gRPC monitoring for new pump.fun tokens using Geyser
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
sys.path.append(str(Path(__file__).parent.parent / "src"))
|
||||||
|
|
||||||
|
from core.pubkeys import PumpAddresses
|
||||||
|
from monitoring.geyser_listener import GeyserListener
|
||||||
|
from trading.base import TokenInfo
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
||||||
|
)
|
||||||
|
logger = logging.getLogger("geyser-listener-test")
|
||||||
|
|
||||||
|
|
||||||
|
class TestTokenCallback:
|
||||||
|
def __init__(self):
|
||||||
|
self.detected_tokens = []
|
||||||
|
|
||||||
|
async def on_token_created(self, token_info: TokenInfo) -> None:
|
||||||
|
"""Process detected token"""
|
||||||
|
logger.info(f"New token detected: {token_info.name} ({token_info.symbol})")
|
||||||
|
logger.info(f"Mint: {token_info.mint}")
|
||||||
|
self.detected_tokens.append(token_info)
|
||||||
|
print(f"\n{'=' * 50}")
|
||||||
|
print(f"NEW TOKEN: {token_info.name}")
|
||||||
|
print(f"Symbol: {token_info.symbol}")
|
||||||
|
print(f"Mint: {token_info.mint}")
|
||||||
|
print(f"URI: {token_info.uri}")
|
||||||
|
print(f"Creator: {token_info.user}")
|
||||||
|
print(f"Bonding Curve: {token_info.bonding_curve}")
|
||||||
|
print(f"Associated Bonding Curve: {token_info.associated_bonding_curve}")
|
||||||
|
print(f"{'=' * 50}\n")
|
||||||
|
|
||||||
|
|
||||||
|
async def test_geyser_listener(
|
||||||
|
match_string: str | None = None,
|
||||||
|
creator_address: str | None = None,
|
||||||
|
test_duration: int = 60,
|
||||||
|
):
|
||||||
|
"""Test the Geyser listener functionality"""
|
||||||
|
geyser_endpoint = os.environ.get("GEYSER_ENDPOINT")
|
||||||
|
geyser_api_token = os.environ.get("GEYSER_API_TOKEN")
|
||||||
|
|
||||||
|
if not geyser_endpoint:
|
||||||
|
logger.error("GEYSER_ENDPOINT environment variable is not set")
|
||||||
|
return []
|
||||||
|
|
||||||
|
if not geyser_api_token:
|
||||||
|
logger.error("GEYSER_API_TOKEN environment variable is not set")
|
||||||
|
return []
|
||||||
|
|
||||||
|
logger.info(f"Connecting to Geyser API: {geyser_endpoint}")
|
||||||
|
listener = GeyserListener(geyser_endpoint, geyser_api_token, PumpAddresses.PROGRAM)
|
||||||
|
callback = TestTokenCallback()
|
||||||
|
|
||||||
|
if match_string:
|
||||||
|
logger.info(f"Filtering tokens matching: {match_string}")
|
||||||
|
if creator_address:
|
||||||
|
logger.info(f"Filtering tokens by creator: {creator_address}")
|
||||||
|
|
||||||
|
listen_task = asyncio.create_task(
|
||||||
|
listener.listen_for_tokens(
|
||||||
|
callback.on_token_created,
|
||||||
|
match_string=match_string,
|
||||||
|
creator_address=creator_address,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"Listening for {test_duration} seconds...")
|
||||||
|
try:
|
||||||
|
await asyncio.sleep(test_duration)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
logger.info("Test interrupted by user")
|
||||||
|
finally:
|
||||||
|
listen_task.cancel()
|
||||||
|
try:
|
||||||
|
await listen_task
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
logger.info(f"Detected {len(callback.detected_tokens)} tokens")
|
||||||
|
for token in callback.detected_tokens:
|
||||||
|
logger.info(f" - {token.name} ({token.symbol}): {token.mint}")
|
||||||
|
|
||||||
|
return callback.detected_tokens
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
match_string = None # Update if you want to filter tokens by name/symbol
|
||||||
|
creator_address = None # Update if you want to filter tokens by creator address
|
||||||
|
test_duration = 30
|
||||||
|
|
||||||
|
logger.info("Starting Geyser listener test (using Geyser API)")
|
||||||
|
asyncio.run(test_geyser_listener(match_string, creator_address, test_duration))
|
||||||
@@ -9,12 +9,16 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
sys.path.append(str(Path(__file__).parent.parent / "src"))
|
sys.path.append(str(Path(__file__).parent.parent / "src"))
|
||||||
|
|
||||||
from core.pubkeys import PumpAddresses
|
from core.pubkeys import PumpAddresses
|
||||||
from monitoring.logs_listener import LogsListener
|
from monitoring.logs_listener import LogsListener
|
||||||
from trading.base import TokenInfo
|
from trading.base import TokenInfo
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
||||||
)
|
)
|
||||||
@@ -91,7 +95,7 @@ async def test_logs_listener(
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
match_string = None # Update if you want to filter tokens by name/symbol
|
match_string = None # Update if you want to filter tokens by name/symbol
|
||||||
creator_address = None # Update if you want to filter tokens by creator address
|
creator_address = None # Update if you want to filter tokens by creator address
|
||||||
test_duration = 15
|
test_duration = 30
|
||||||
|
|
||||||
logger.info("Starting logs listener test (using logsSubscribe)")
|
logger.info("Starting logs listener test (using logsSubscribe)")
|
||||||
asyncio.run(test_logs_listener(match_string, creator_address, test_duration))
|
asyncio.run(test_logs_listener(match_string, creator_address, test_duration))
|
||||||
|
|||||||
Reference in New Issue
Block a user