Skip to main content

Minimal Python framework for building crypto bots

Project description

tinybot

Minimal Python framework for building crypto bots.

Installation

pip install tinybot-eth

Environment Variables

Variable Required Description
BOT_ACCESS_TOKEN Yes Telegram bot token
GROUP_CHAT_ID Yes Telegram group for notifications
DEV_GROUP_CHAT_ID Yes Telegram group for errors and startup
PRIVATE_KEY No Private key for onchain execution

Quick Start

bot = TinyBot(rpc_url, name="my bot")

bot.listen(event="AuctionKicked", handler=on_kick, ...)
bot.every(180, check_expired)

await bot.run()
import asyncio
import os
from tinybot import TinyBot, multicall, notify_group_chat

ERC20_ABI = [...]
STRATEGY_ABI = [...]

async def on_transfer(bot, log):
    print(f"{log.args.sender} -> {log.args.receiver}: {log.args.value}")
    await notify_group_chat(f"Transfer from {log.args.sender}")

async def check_and_tend(bot):
    strategy = bot.w3.eth.contract(address="0x...", abi=STRATEGY_ABI)
    needs_tend, _ = strategy.functions.tendTrigger().call()
    if needs_tend:
        tx_hash = bot.executor.execute(
            strategy.functions.tend(),
            gas_limit=5_000_000,
        )
        await notify_group_chat(f"Tend submitted: {tx_hash}")

async def main():
    bot = TinyBot(
        rpc_url=os.environ["RPC_URL"],
        name="my bot",
        private_key=os.environ.get("PRIVATE_KEY", ""),
    )

    bot.listen(
        event="Transfer",
        addresses=["0x..."],
        abi=ERC20_ABI,
        handler=on_transfer,
        poll_interval=180,
    )

    bot.every(3600, check_and_tend)

    await bot.run()

asyncio.run(main())

API

TinyBot(rpc_url, name="tinybot", private_key="")

Creates a bot instance.

  • bot.w3web3.Web3 instance
  • bot.stateState instance (see below)
  • bot.executorExecutor instance if private_key is provided, else None
  • bot.name — used in logs and Telegram startup message

On run(), sends a startup message to DEV_GROUP_CHAT_ID and prints a polling heartbeat every tick.


bot.listen(...) -> EventListener

Register an event listener.

bot.listen(
    event="AuctionKicked",   # event name (must exist in ABI)
    addresses=["0x..."],     # contracts to monitor
    abi=[...],               # ABI containing the event
    handler=on_kick,         # async fn(bot, log)
    name="kicks",            # defaults to handler.__name__ (optional)
    poll_interval=180,       # seconds between polls (default: 180)
    block_buffer=5,          # re-scan buffer in blocks (default: 5)
    notify_errors=True,      # send errors to Telegram (default: True)
)

The event signature is derived from the ABI at registration time. Raises ValueError if:

  • Event not found in ABI
  • Duplicate listener name
  • Empty addresses

bot.every(interval, handler, name="", notify_errors=True) -> PeriodicTask

Register a periodic task.

bot.every(3600, check_expired)

Handler signature: async fn(bot)


bot.get_listener(name) -> EventListener

Get a registered listener by name. Raises ValueError if not found.


bot.replay(name, from_block, to_block)

Replay historical events through a listener's handler. Useful for testing with real chain data.

await bot.replay("kicks", from_block=21000000, to_block=21000500)

bot.run(tick=10)

Start the polling loop. tick (default: 10s) is the inner loop sleep. Each listener and task fires at its own interval.


EventListener

Returned by bot.listen().

  • listener.add_address(address) — add a contract address at runtime
  • listener.remove_address(address) — remove a contract address at runtime

Both handle checksumming and dedup.


Executor

Available via bot.executor when private_key is provided.

bot = TinyBot(rpc_url, name="my bot", private_key=os.environ["PRIVATE_KEY"])

tx_hash = bot.executor.execute(
    contract.functions.tend(strategy_addr),
    gas_limit=0,              # default: 0 — auto-estimates with 1.5x buffer
    max_fee_gwei=100,         # default: 100
    max_priority_fee_gwei=0,  # default: 0
    simulate=True,            # default: True — dry-run via eth_call before sending
    wait=120,                 # default: 120 — seconds to wait for mining (0 = fire and forget)
)
  • executor.address — signer address
  • executor.balance — signer ETH balance in wei
  • executor.execute(call, ...) — sign and broadcast a transaction, returns tx hash hex string
  • gas_limit=0 (default) — auto-estimates gas with 1.5x buffer; pass a value to override
  • max_fee_gwei / max_priority_fee_gwei — accept floats (e.g. 0.1)
  • simulate=True (default) — runs call.call() first; reverts raise before the tx is sent
  • wait=120 (default) — wait up to N seconds for the tx to be mined; 0 for fire and forget

State

In-memory state, available via bot.state.

  • state.last_blockdict[str, int] mapping names to last processed block
  • state.active_itemslist of tracked items (e.g. address pairs)
  • state.add_item(*addrs) — add an item (deduped)
  • state.remove_item(item) — remove an item
  • state.is_processed(event_id) — check if event was processed
  • state.mark_processed(event_id) — mark event as processed (handled automatically for listeners)

multicall(w3, calls) -> list

Batch contract reads via Multicall3.

symbol, decimals = multicall(bot.w3, [
    token.functions.symbol(),
    token.functions.decimals(),
])

notify_group_chat(text, parse_mode="HTML", chat_id=GROUP_CHAT_ID)

Send a Telegram message. HTML parse mode by default.


event_id(log) -> str

Unique ID from a log (txHash:logIndex). Used internally for dedup, also available for custom event processing in periodic tasks.

Handler Signatures

# Event handler
async def on_event(bot: TinyBot, log) -> None: ...

# Task handler
async def my_task(bot: TinyBot) -> None: ...

Access bot.w3, bot.state, bot.executor, and bot.get_listener() from any handler.

Error Handling

Enabled by default. Exceptions are caught and sent to DEV_GROUP_CHAT_ID as [name] error message. The bot continues running. Set notify_errors=False to disable.

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

tinybot_eth-0.5.0.tar.gz (10.1 kB view details)

Uploaded Source

Built Distribution

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

tinybot_eth-0.5.0-py3-none-any.whl (9.3 kB view details)

Uploaded Python 3

File details

Details for the file tinybot_eth-0.5.0.tar.gz.

File metadata

  • Download URL: tinybot_eth-0.5.0.tar.gz
  • Upload date:
  • Size: 10.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for tinybot_eth-0.5.0.tar.gz
Algorithm Hash digest
SHA256 e5c0767fc13d9ab854a06bcb323acdaf5cf2966c8476e555d9cf44e7d3cf9f8d
MD5 f9abf2d9e573f0bda196016801f95fd6
BLAKE2b-256 6ba5a6ed781e53fb332d5fefa116aa39c100423aa0501c8e95ed857016ccae25

See more details on using hashes here.

File details

Details for the file tinybot_eth-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: tinybot_eth-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 9.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for tinybot_eth-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 469c2e3994541ecfb04669b9ee31078fa7672aabc41e4d86e70ae0e555bf7933
MD5 16b5f8939b8beda660974aa9f8df43c7
BLAKE2b-256 a6f011a369e118edd3008a94bfe5f6418b7ae32d90dc2af9eda4658ee849891b

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