ownerwatch
Self-hosted monitor that alerts you the moment the admin surface of your EVM contracts changes.
Most protocol losses in 2025-2026 started with a key, a role or a proxy changing hands, not with a clever exploit. ownerwatch watches the events that mean "someone just gained control" and pushes them to Discord, Slack, Telegram or any webhook within seconds. No account, no SaaS, no third party holding your contract list. One YAML file and a Docker container.
What it watches
Every address in your config gets the full catalog:
| Standard | Events | Severity |
|---|---|---|
| Ownable / Ownable2Step | OwnershipTransferred, OwnershipTransferStarted | critical / high |
| AccessControl | RoleGranted, RoleRevoked, RoleAdminChanged | high / critical |
| ERC-1967 proxies | Upgraded, AdminChanged, BeaconUpgraded | critical |
| Pausable | Paused, Unpaused | medium |
| TimelockController | CallScheduled, CallExecuted, Cancelled, MinDelayChange | high / critical |
| Legacy Timelock (Compound, Uniswap and forks) | NewAdmin, NewPendingAdmin, NewDelay, QueueTransaction, ExecuteTransaction, CancelTransaction | medium to critical |
| Governor (OpenZeppelin and GovernorBravo) | ProposalCreated, ProposalQueued, ProposalExecuted, ProposalCanceled, VotingDelaySet, VotingPeriodSet, ProposalThresholdSet, QuorumNumeratorUpdated, TimelockChange, NewImplementation | medium to critical |
| Aragon OS (Lido and others) | SetApp, SetPermission, ChangePermissionManager, StartVote, ExecuteVote | medium to critical |
| Safe | AddedOwner, RemovedOwner, ChangedThreshold, EnabledModule, DisabledModule, ChangedGuard, ChangedFallbackHandler, ExecutionFromModuleSuccess, ExecutionSuccess, ExecutionFailure | info to critical |
| ERC-20 treasuries | Transfer out above a threshold you set | high |
| NFT treasuries | Any ERC-721 or ERC-1155 transfer out | high |
| Native balance | ETH balance of a treasury falls by more than a threshold between polls | high |
Safe queues. Add a Safe under safes: and ownerwatch polls the Safe Transaction Service: you get an alert when a transaction is proposed, and a critical one when it has collected enough signatures to execute. That is the moment to act; the on-chain events above are the record of what already happened.
Timelock and Safe-queue alerts show the call being made: full function and arguments when the target is verified on Sourcify, otherwise the signature from the openchain and 4byte databases, otherwise the raw selector. Set explorer_url and every alert carries a clickable transaction link.
Run ownerwatch events to print the catalog.
Quick start
pip install git+https://github.com/imal1ice/ownerwatch
cp config.example.yaml config.yaml # add your contracts and a Discord/Telegram target
ownerwatch run --config config.yaml
Or with Docker:
docker compose up -d # uses ./config.yaml, keeps state in a named volume
A hardened systemd unit is in deploy/ownerwatch.service.
Several chains in one process: put shared settings at the top level and a chains: list underneath, see examples/multichain.yaml.
Don't know what to watch? Point probe at your main contract and it walks the owner/admin/proxy chain, tells you who can upgrade or pause it, flags EOAs and 1-of-N Safes, and prints a config:
ownerwatch probe --rpc https://eth.drpc.org --safe-chain eth --label Acme 0xYourProxy 0xYourSafe
ownerwatch probe --rpc https://eth.drpc.org --roles-from-logs 0xAccessControlContract # every current role holder, replayed from logs since deployment
ownerwatch probe --config config.yaml --markdown --label Acme > admin-surface.md # monthly report of everything you watch; set probe_rpc_url per chain if rpc_url rejects eth_call
--roles-from-logs finds the creation block on Sourcify and reads the history from a free public log gateway on Ethereum, Optimism, Arbitrum, Gnosis and Polygon; on Base (and for any chain without one) pass --logs-rpc with an RPC that serves wide eth_getLogs ranges, or --from-block to bound the scan.
Want that check in CI? examples/github-action-drift.yml re-probes weekly and fails when a signer, delay or proxy admin changes from the committed report.
</code></pre>
<p>Other commands:</p>
<pre><code class="language-bash">ownerwatch validate # check the config, the RPC and that every address has code
ownerwatch test-alert # push a test message through your sinks
ownerwatch run --once # single scan, for cron
ownerwatch backfill --from-block N --to-block M # replay history, prints what you would have seen
ownerwatch events # list the catalog
See docs/what-to-watch.md for the control-graph model behind the catalog, and docs/sample-alerts.md for real alerts replayed from mainnet history.
Examples
examples/ has verified configs for Uniswap, Compound, Arbitrum, Aave and the Optimism Foundation Safe on mainnet. Backfill one to see what alerts look like on real history:
ownerwatch backfill --config examples/aave-mainnet.yaml --from-block 22000000 --to-block 22050000
How it works
- Polls
eth_getLogsfor the catalog's topic hashes on your addresses, in chunks that fit public RPCs. - Waits
confirmationsblocks behind the head so small reorgs don't produce ghost alerts. - Persists a block cursor to
state/, so a restart resumes where it left off with no duplicates. - Alert delivery never stops the poller: a dead webhook is logged and skipped.
- Set
heartbeat_urlto a dead-man's-switch (healthchecks.io or similar) and you get paged when ownerwatch itself stops polling. Quiet and dead must not look the same. - Works on any EVM chain, one process can watch several.
Hosted tier
Don't want to run it? I run it for you, per chain, paid in USDC on-chain through contracts/src/Subscriptions.sol: approve, call subscribe(periods, chains, maxCost, configRef), send me the config. No invoices, no accounts, no KYC either way. Details in docs/hosted.md. Deployment address will be listed here once live; until then open an issue. The operator side is ownerwatch hosted --rpc ... --subscriptions 0x... --configs configs/: it starts a watcher for every address with an active subscription and stops it when the subscription lapses.
cd contracts && forge install foundry-rs/forge-std --no-git && forge test
Roadmap
- Native balance and NFT alerts for Safe queues (currently on-chain only)
Support the project
ownerwatch is free and MIT licensed. If it saves you a 3am incident, fund the work:
- Drips: https://www.drips.network/app/projects/github/imal1ice/ownerwatch
- Giveth: https://giveth.io/project/ownerwatch
- Direct:
0x874D018623EeBb398B898AC7AB0c97506244141B(Ethereum and all major L2s)
Need it deployed, extended or integrated with your alerting stack? Open an issue or reach out on GitHub.
Development
python3 -m venv .venv && .venv/bin/pip install -e . pytest
.venv/bin/pytest # end-to-end test against a local anvil node; needs foundry
Services
ownerwatch is free and self-hosted. If you would rather have someone do the work:
- Admin-surface review: every owner, proxy admin, role holder and Safe signer in your deployment, replayed from logs, with a written report and a ready-to-run ownerwatch config. Flat $400 in USDC, delivered within 48 hours.
- Hosted monitoring: I run ownerwatch for you on a dedicated server with a heartbeat check, and alert your Discord, Slack or Telegram. $300 per month per chain, paid in USDC on-chain. Offered to teams already running the self-hosted version.
Contact: imal1ice@proton.me. Wallet for payments: 0x874D018623EeBb398B898AC7AB0c97506244141B.
Metadata
Release files for ownerwatch 0.7.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| ownerwatch-0.7.3.tar.gz | 43.5 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ownerwatch-0.7.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 79.7 kB
Release files / ownerwatch-0.7.3.tar.gz
| Download URL | ownerwatch-0.7.3.tar.gz |
|---|---|
| Size | 43.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
464690f7db25b4db61b22917553cd89e326d06235be7425b546a23b480535491
|
|
BLAKE2b-256 checksum How to use checksums |
79e8b1ad42660ac1bd768cdaec8a7cd42728d47a774ddb85d533c5b418a411ad
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / ownerwatch-0.7.3-py3-none-any.whl
| Download URL | ownerwatch-0.7.3-py3-none-any.whl |
|---|---|
| Size | 36.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0686f432cbc477ca3aeb55d369ec1164706b02357af053b3c91f45576b45109c
|
|
BLAKE2b-256 checksum How to use checksums |
1913a61695a4bde82cbba6c76efde303de0be205d80bf8ecb8c79c8b70f80a5a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for ownerwatch 0.7.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for ownerwatch-0.7.3.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.