Skip to main content

A synchronous callback facade for Ethereum websocket subscriptions backed by asyncio.

Project description

eth-listener

eth-listener is a tiny helper library that wraps Ethereum JSON-RPC websocket subscriptions with a synchronous, callback-based interface. Under the hood it drives a lean asyncio websocket client (built on top of websockets) and keeps your callbacks running on dedicated worker threads, letting you interact with Ethereum events using a simple .on(event, callback) pattern that feels familiar to JavaScript developers.

Features

  • Connect to any Ethereum node that exposes a websocket JSON-RPC endpoint
  • Strongly typed callback payloads for common subscriptions such as newHeads and newPendingTransactions
  • Automatic reconnection with stored subscription state
  • Callbacks run in a dedicated worker pool so they never block the websocket
  • Simple synchronous API: no need to manage event loops yourself
  • Works with any JSON-RPC node that speaks standard websocket subscriptions

Installation

pip install eth-listener

Python 3.9 or newer is required.

Quickstart

import argparse
import json
import logging
import time

from eth_listener import EthListener, NewHeadEvent

# Replace with your node's websocket endpoint
WS_URL = "wss://mainnet.infura.io/ws/v3/<your-project-id>"

def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--dump-raw", action="store_true")
    parser.add_argument("--dump-messages", action="store_true")
    parser.add_argument("--transport-debug", action="store_true")
    parser.add_argument("--debug", action="store_true")
    args = parser.parse_args()

    logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s")
    logging.getLogger("websockets").setLevel(
        logging.DEBUG if args.transport_debug else logging.WARNING
    )
    if args.debug or args.dump_messages:
        logging.getLogger("eth_listener").setLevel(logging.DEBUG)

    listener = EthListener(WS_URL, start_timeout=0)

    def handle_new_head(event: NewHeadEvent) -> None:
        print(f"New block {event.number} with hash {event.hash}")
        if args.dump_raw:
            print(json.dumps(event.raw, indent=2))

    def print_message(raw: str) -> None:
        try:
            parsed = json.loads(raw)
        except json.JSONDecodeError:
            print(raw)
        else:
            print(json.dumps(parsed, indent=2))

    if args.dump_messages:
        listener.add_raw_message_listener(print_message)

    with listener:
        listener.start(timeout=0)
        listener.on("newHeads", handle_new_head)

        # Keep your application alive while callbacks run on background threads
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            pass
        finally:
            if args.dump_messages:
                listener.remove_raw_message_listener(print_message)


if __name__ == "__main__":
    main()

Unsubscribing

Every call to :meth:EthListener.on returns a :class:~eth_listener.SubscriptionHandle. Use it to stop listening when you no longer need the events:

handle = listener.on("newHeads", handle_new_head)
...
handle.unsubscribe()  # Removes the callback and issues eth_unsubscribe

Stopping the listener (via :meth:stop, the context manager, or object finalisation) also unsubscribes from any remaining topics.

Event payloads

The library ships with typed payloads for common subscriptions:

  • :class:~eth_listener.NewHeadEvent for newHeads
  • :class:~eth_listener.NewPendingTransactionEvent for newPendingTransactions

Each payload preserves the original JSON data in the raw attribute while also exposing more convenient Pythonic fields (for example block numbers are converted to integers).

Unknown subscriptions are delivered as the base :class:~eth_listener.BaseEthereumEvent, letting you inspect the raw payload and build your own helper classes when needed.

Development

pip install -e .[dev]
pytest

Pull requests and issues are very welcome!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

eth_listener-0.1.0.tar.gz (9.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

eth_listener-0.1.0-py3-none-any.whl (11.0 kB view details)

Uploaded Python 3

File details

Details for the file eth_listener-0.1.0.tar.gz.

File metadata

  • Download URL: eth_listener-0.1.0.tar.gz
  • Upload date:
  • Size: 9.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for eth_listener-0.1.0.tar.gz
Algorithm Hash digest
SHA256 302fdd94bf80665bd5710a3820b0f0da2b0647322ff7d8042563eb8d899985da
MD5 80fe9fb880d8665136af0b36b355b533
BLAKE2b-256 153486ca278cf9f8b737b99452c40aa87f4ec095e0e89b66beab1a8ec1f46d55

See more details on using hashes here.

File details

Details for the file eth_listener-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: eth_listener-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 11.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for eth_listener-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 922e3cc410a7b5ffd7216ad62bd403014060b0911c70cf33a1f13f9858b379e1
MD5 ebc1a4d159190afedc21d3e689c280ea
BLAKE2b-256 3b5175b5f0b654491a270d796d2c5bfe125dabc4713d67478b52da5e769c99c2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page