Bittensor and EVM chain monitoring for AI agents.
Project description
Blockmachine Chainwake
Set a hook on chain state. Put your agent to sleep. Wake it the moment the chain moves.
Chain monitoring for AI agents across Bittensor, Ethereum, Base, and BNB Smart Chain. Chainwake is a watcher CLI and MCP server. One invocation watches one condition — a price threshold, a transaction reaching finality, an on-chain event — then exits with a single structured JSON payload. It is observation-only: no keys, no signing, no transaction submission.
Why
An agent that needs to react to chain state has two bad options today:
- Poll in a loop. Every empty check burns model tokens and context, and the agent still reacts late.
- Stay out of it. A human watches a dashboard and re-prompts the agent.
Chainwake is the third option. The agent starts a watcher and ends its turn. No model runs while the watcher waits — the hook lives in a cheap subprocess holding a WebSocket subscription. When the condition fires, the watcher exits with JSON on stdout and the host wakes the agent with the result. Tokens are spent only on the turns where something actually happened.
agent turn ──▶ chainwake watcher (no tokens) ──▶ condition fires ──▶ agent woken with JSON
What you can watch
| Chain | Examples |
|---|---|
bt (Bittensor) |
TAO/USD and subnet prices, validator/neuron state, account balance and transfers, hyperparameter changes, chain events, epoch boundaries |
eth (Ethereum) |
Transaction receipt, confirmations, finality; EIP-1559 base fee; ERC-20 USD prices |
base (Base) |
Transaction finality (safe/finalized), L2 and L1 fee inputs; ERC-20 USD prices |
bsc (BNB Smart Chain) |
Transaction confirmations and finality, gas price, BEP-20 USD prices |
The full observable catalogue is in the CLI reference.
Install
uv tool install chainwake # isolated install (recommended)
pip install chainwake # or into the active environment
To try unreleased code: uv tool install git+https://github.com/taostat/chainwake.git.
Enable shell completion with chainwake --install-completion.
Quickstart
Every command below blocks until its condition fires, then exits with one JSON payload. All defaults are anonymous public endpoints — no configuration, no API key needed for a first watcher.
Wait for a Bittensor subnet price to cross a threshold, or move 5% within an hour:
chainwake bt subnet 19 price --below 0.05
chainwake bt subnet 19 price --drop-pct 5 --window-time 1h
Watch TAO/USD directly:
chainwake bt network tao-price --below 180
chainwake bt network tao-price --move-pct 5 --window-time 1h
TAO/USD is a CoinGecko aggregate quote sampled every 60 seconds. It supports thresholds and percentage moves with a time window or watcher-start baseline.
The window controls which samples are compared; it does not impose a runtime limit.
Omit it to keep the first successful observation as an unexpiring baseline.
--max-runtime alone controls when an unmatched watcher exits with timeout.
Wait for an on-chain event:
chainwake bt event --type subnet-registered --max-runtime 24h
Wait for an Ethereum transaction, or for the EIP-1559 base fee (in gwei) to get cheap:
chainwake eth tx 0x0123...abcd --finality finalized
chainwake eth network base-fee --below 10
Base and BSC use the same grammar with chain-specific finality and fees:
chainwake base tx 0x0123...abcd --finality safe
chainwake base network l1-blob-base-fee --below 2
chainwake bsc tx 0x0123...abcd --confirmations 12
Watch DAI by symbol on any supported EVM chain, or GRAM on Ethereum/BSC:
chainwake eth token DAI price --below 0.995
chainwake base token DAI price --above 1.005
chainwake bsc token GRAM price --rise-pct 10 --window-time 1h
Token symbols are resolved within the selected chain. If a symbol is ambiguous,
pass its contract address instead. Prices are CoinGecko aggregate USD quotes,
sampled every 60 seconds by default; set CHAINWAKE_COINGECKO_API_KEY only if
anonymous requests hit CoinGecko's rate limit.
Fee and transaction watchers subscribe to newHeads and pin reads to the
notified block.
Transaction watches report success or reverted, gas used, and effective
gas price; a missing receipt stays pending — Chainwake never guesses that a
transaction was dropped or replaced.
The payload
A matched watcher exits 0 with stdout:
{
"status": "matched",
"watcher": { "chain": "bt", "resource": "subnet", "resource_id": "19",
"sub_resource": "pool.price", "name": null,
"primitive": "threshold",
"invocation": ["chainwake", "bt", "subnet", "19", "price",
"--below", "0.05"] },
"condition": { "operator": "below", "target": 0.05 },
"observed": { "path": "subnet.19.pool.price", "value": 0.0487,
"block": 4291820, "block_hash": "0xabc...",
"timestamp": "2026-05-06T10:00:00Z" },
"budget": { "runtime_ms": 3210, "rpc_calls": 3, "estimated_ru_consumed": 3 },
"process": { "pid": 12345, "started_at": "2026-05-06T09:59:57Z" }
}
The payload contract is
schemas/output.json
— the sole current output contract, regenerated from the Pydantic models and
CI-checked. Consumers should validate against it and reject unknown fields.
Waking your agent
MCP (Hermes, OpenClaw, Claude Desktop, Cursor)
The built-in MCP server exposes every watcher as an MCP tool:
chainwake mcp serve --stdio
chainwake mcp config hermes
chainwake mcp config openclaw
Hermes users install taostat/chainwake from the dashboard's Plugins page;
OpenClaw users run openclaw skills install @blockmachine/chainwake --global.
Both use native background-process completion notifications — no polling, no
long-held MCP request. See the
MCP guide.
Subprocess
Any agent framework that can spawn a process can set a hook:
import json, subprocess
proc = subprocess.run(
["chainwake", "bt", "subnet", "19", "price",
"--below", "0.05", "--max-runtime", "5m", "--json"],
capture_output=True, text=True,
)
payload = json.loads(proc.stdout)
if proc.returncode == 0:
print(f"Matched at block {payload['observed']['block']}")
elif proc.returncode == 1:
print(f"No match: {payload['reason']}") # timeout or budget_exhausted
See the agent integration guide for parallel wakes, detached processes, and restart durability.
Durable jobs
Add --durable when the watcher must outlive the shell or agent turn that
created it. Chainwake persists the watcher, starts a local supervisor, prints
the job id, and exits immediately:
chainwake --json --durable \
--context "Tell me the observed price and block." \
bt subnet 19 price --below 0.05
chainwake --json jobs wait <job-id> # blocks without polling the chain
The completion carries your context plus the normal watcher result. Manage
jobs with jobs list, jobs show, jobs cancel. Because job arguments are
stored locally, durable mode rejects literal --api-key/--rpc-url values —
use CHAINWAKE_BT_API_KEY / CHAINWAKE_BT_RPC_URL in the supervisor
environment.
Notifications (--out)
Route results anywhere apprise can deliver (~100 destinations: Telegram, Discord, Slack, email, webhooks):
chainwake bt subnet 19 price --below 0.05 --out "tgram://bottoken/chatid"
chainwake bt subnet 19 price --below 0.05 --out stream # NDJSON, keep running
chainwake bt subnet 19 price --below 0.05 --out file:///tmp/w.ndjson
--out is repeatable. See the
apprise URI reference.
Exit codes
Agents parse JSON for detail; exit codes drive shell-level control flow.
| Code | status field |
Meaning |
|---|---|---|
0 |
matched |
Condition fired |
1 |
stopped / timeout / budget_exhausted |
Finished without a match |
2 |
user_error |
Invalid args, unknown resource |
3 |
provider_error / auth_error |
RPC unavailable or credentials required |
4 |
internal_error |
Bug in chainwake |
For a watcher invocation, automation should always pass --json; every
watcher exit then emits one JSON envelope. Piped stdout also selects JSON, but
do not depend on TTY detection. Without --json, an interactive TTY uses
human-readable output. Help, version, and MCP configuration helpers are
outside the watcher-envelope contract. Stderr carries diagnostics and is not
part of the stable contract.
Configuration
| Method | Precedence | Example |
|---|---|---|
--rpc-url flag |
Highest | --rpc-url wss://my-node:9944 |
| Env var | Middle | CHAINWAKE_BT_RPC_URL, CHAINWAKE_ETH_RPC_URL |
| Anonymous default | Lowest | wss://rpc.blockmachine.io (bt), wss://ethereum-rpc.publicnode.com (eth) |
Blockmachine is the default Bittensor RPC provider;
the anonymous free tier needs no API key. Any Substrate-compatible WebSocket
endpoint works. No API key is required for a first watcher. Pass --api-key
(or CHAINWAKE_BT_API_KEY) for higher-limit access. Base and BSC default to
wss://base-rpc.publicnode.com and
wss://bsc-rpc.publicnode.com.
--max-runtime accepts 30s, 10m, 2h, 1d. Default is unbounded.
Agent and automation calls should always set a bounded runtime.
--max-ru and budget.estimated_ru_consumed form a registry-estimated
observation budget, not a provider billing cap.
Documentation
- README
- Quickstart
- Concepts — primitives, registry, polling model
- CLI reference
- Agent integration
- MCP guide
- Notification adapters
- JSON output schema
- Historical design proposal (archived)
Status
Chainwake is in active development. See
PROGRESS.md
for in-flight work. Issues and ideas:
GitHub Issues.
License
MIT
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file chainwake-0.5.0.tar.gz.
File metadata
- Download URL: chainwake-0.5.0.tar.gz
- Upload date:
- Size: 656.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3965675def2f53b5a8baf6aaf0469dfd3aa550972c2b01ce63778286436b969a
|
|
| MD5 |
dc2dbb1851fb3797df9e4aa070fa896e
|
|
| BLAKE2b-256 |
73f17804189c75732ba82e49d047eddba3689853cd67c1affe52e8e4e72ace16
|
Provenance
The following attestation bundles were made for chainwake-0.5.0.tar.gz:
Publisher:
publish.yml on taostat/chainwake
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chainwake-0.5.0.tar.gz -
Subject digest:
3965675def2f53b5a8baf6aaf0469dfd3aa550972c2b01ce63778286436b969a - Sigstore transparency entry: 2281517436
- Sigstore integration time:
-
Permalink:
taostat/chainwake@93de6a9c6f28acdfcd7befac7def6ef80c99606e -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/taostat
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@93de6a9c6f28acdfcd7befac7def6ef80c99606e -
Trigger Event:
push
-
Statement type:
File details
Details for the file chainwake-0.5.0-py3-none-any.whl.
File metadata
- Download URL: chainwake-0.5.0-py3-none-any.whl
- Upload date:
- Size: 176.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5098b0f785f3eb77aa2d14ea33410674406c5c814fbe2ab38fbce1138a180f11
|
|
| MD5 |
2c812399f15e17fb3beda2b23e7e6259
|
|
| BLAKE2b-256 |
eed14a55a667c8f6ba3cb120fee6fd7db089247047fa1da4a3df411147910e1f
|
Provenance
The following attestation bundles were made for chainwake-0.5.0-py3-none-any.whl:
Publisher:
publish.yml on taostat/chainwake
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chainwake-0.5.0-py3-none-any.whl -
Subject digest:
5098b0f785f3eb77aa2d14ea33410674406c5c814fbe2ab38fbce1138a180f11 - Sigstore transparency entry: 2281517453
- Sigstore integration time:
-
Permalink:
taostat/chainwake@93de6a9c6f28acdfcd7befac7def6ef80c99606e -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/taostat
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@93de6a9c6f28acdfcd7befac7def6ef80c99606e -
Trigger Event:
push
-
Statement type: