ONCHAINEMISSARY — FINAL GRANT REPORT
Grant: $42,000
Milestones: 6 of 6 delivered
Period: April – August 2026
Repository: GitHub - TanawatTH/OnchainEmissary · GitHub
SUMMARY
OnchainEmissary set out to remove the setup cost from onchain monitoring. Existing options ask a user to read an ABI, know which event carries the signal they want, run their own indexer, or pay for a monitoring seat. Emissary asks for a contract address.
All six milestones are delivered. The system catalogs any verified contract on Arbitrum One — resolving proxies automatically — surfaces its events, and turns a selection into live subscriptions delivered across four channels, with a watcher that guarantees at-most-once delivery. The final milestone added a library of ten pre-configured protocol templates and an integration toolkit so a protocol can offer alerts from its own frontend.
Both operational KPIs were met over the validation period: uptime held above the committed threshold, and the alert-configuration target was reached across the template library. One note on how the second is counted: the system collects no identity, so what is measured is alert configurations and distinct delivery destinations, not identified people. That is a consequence of the permissionless design this grant funded, not a gap in the reporting.
DELIVERY LEDGER
M1 Event indexer & contract cataloger $8,000 3 Apr 2026 Complete
M2 Alert discovery interface $6,000 28 May 2026 Complete
M3 Multi-channel messenger $6,000 28 May 2026 Complete
M4 Backend logic, persistence, watcher $9,000 28 May 2026 Complete
M5 Deployment & end-to-end integration tests $8,000 23 Jun 2026 Complete
M6 Protocol templates & integration toolkit $5,000 17 Aug 2026 Complete
First milestone landed 3 April 2026, the last on 17 August 2026 — 19 weeks against a 13-week plan, inside the four-and-a-half-month contingency the application allowed for. Every date above is the commit that landed the work in the public repository and is independently checkable there.
WHERE TO CHECK THE WORK
- Repository — GitHub - TanawatTH/OnchainEmissary · GitHub , complete history, six milestone commits
- Integration toolkit — examples/: a dependency-free Node client, a drop-in browser widget, and the full API reference
- Deployment guide — DEPLOY.md: container, persistence, health probes, and the single-instance constraint
- Test suite — 68 tests passing, plus two opt-in tests that run against live Arbiscan and RPC
MILESTONE 1 — EVENT INDEXER & CONTRACT CATALOGER ($8,000)
The foundation everything else reads from. Given an address, it validates that the address holds bytecode, detects whether it is a proxy, resolves to the implementation, fetches the verified ABI, and computes the topic0 hash for every event the contract can emit.
Delivered:
- Proxy resolution across four patterns — EIP-1967, EIP-1967 beacon, EIP-1822 (UUPS), and legacy OpenZeppelin — read from their well-known storage slots, EIP-1967 first as it covers the majority on Arbitrum
- ABI retrieval through Etherscan’s V2 unified API with the chain id as a request parameter
- Event extraction with canonical signatures and derived topic hashes; anonymous events are surfaced but marked non-subscribable, since they emit no hash as topic0
- Structured JSON catalog per contract, and a CLI that prints a readable summary
- Verified against 15 live Arbitrum contracts, whose catalogs are committed in output/
MILESTONE 2 — ALERT DISCOVERY INTERFACE ($6,000)
The web surface where a non-technical user does what previously required reading an ABI: paste an address, see what the contract actually emits in plain language, tick the events worth knowing about.
Delivered:
- Contract search rendering proxy resolution and the full event list
- Event multi-select showing the topic0, indexed parameters, and data layout for each
- Anonymous events shown distinctly and excluded from selection rather than silently dropped
- Active subscriptions panel with periodic refresh and delete
- Resolved implementation address surfaced even when the ABI fetch fails, so a user can still inspect what was found
MILESTONE 3 — MULTI-CHANNEL MESSENGER ($6,000)
Four delivery channels behind one dispatcher, each with the retry and rate behaviour its provider actually requires.
Delivered:
- Telegram — MarkdownV2 formatting, per-chat lock, bounded retry on flood-wait
- Discord — per-webhook lock, retry_after-aware, webhook URL validated and masked on read endpoints
- Email — SMTP via nodemailer with connect and socket timeouts and sender-shape validation
- X — single-account serialisation, repeat-firing dedup, no client cache so rotated credentials take effect immediately
- Unified dispatcher reporting per-channel configured/missing state, so a partial failure is recorded per channel rather than collapsing the delivery
MILESTONE 4 — BACKEND LOGIC, PERSISTENCE & WATCHER ($9,000)
The part that has to be right when nobody is watching: the poller that finds matching logs, and the delivery accounting that decides whether a user gets one message, none, or five.
Delivered:
- Polling watcher with configurable confirmation depth, per-group log caps that preserve block boundaries, and a pre-claim → dispatch → update sequence giving at-most-once delivery
- SQLite persistence in WAL mode with crash-safe migration, surviving restarts and redeploys
- Subscription CRUD with caps — 50 events per request, 10 channels per subscription, 5,000 active subscriptions — and head-anchored start blocks so a new subscription never replays history
- Per-IP, per-route rate limiting on every write path
- Nine JSON API routes under a unified {ok, data} / {ok, error} envelope, including /api/health covering database, watcher, channels and environment in one probe
- Graceful shutdown: SIGTERM/SIGINT handlers, WAL truncate, and a close flag preventing in-flight workers from reopening the database
MILESTONE 5 — DEPLOYMENT & END-TO-END INTEGRATION TESTS ($8,000)
Packaging and proof. No new application logic — this milestone connected the four preceding ones into a single deployable unit and demonstrated the whole path end to end.
Delivered:
- Two-stage container image on node:20-bookworm-slim, running unprivileged, with SQLite on a volume so subscriptions and delivery history survive redeploys
- HEALTHCHECK against /api/health, and a compose file setting a stop grace period long enough for the watcher’s WAL checkpoint on SIGTERM
- Integration suite proving subscribe → detect → dispatch → record with the chain and Telegram mocked, covering claim dedup, failed-delivery accounting, and confirmation-buffer suppression
- CI installing both dependency trees, running the suite, and guarding types and routes with a production build
- DEPLOY.md covering hosts, persistence and backup, health probes, the uptime monitor, and the single-instance constraint — the watcher has no distributed lock, so exactly one replica may run
ON THE UPTIME KPI — MET
The application commits to >90% uptime, and the system held above that threshold across the test and production validation period.
The measurement path is built in rather than bolted on: /api/health is a single probe covering database, watcher, channel configuration and environment, the container wires it to a Docker HEALTHCHECK, and DEPLOY.md specifies the external monitor and the single-instance constraint the watcher requires. Restarts are non-destructive — SQLite sits on a volume and shuts down through a WAL checkpoint on SIGTERM — so subscriptions and delivery history survive a redeploy without a gap in coverage.
MILESTONE 6 — PROTOCOL TEMPLATES & INTEGRATION TOOLKIT ($5,000)
Pasting an address is still work. Templates remove it: ten well-known Arbitrum contracts, each pinned to the handful of events actually worth watching, with a line explaining what each one means and how often it really fires.
Delivered:
- Ten protocol templates spanning lending, DEX, perps, governance, token and bridge — Aave v3, Uniswap v3 (factory and positions), Balancer v2, GMX v1, Circle USDC, ARB token, Arbitrum Core Governor, the L2 gateway router, and Curve — covering 45 measured events
- topic0 never written by hand; derived from the canonical signature so a typo becomes a hash that never matches rather than a plausible subscription that silently never fires
- Applying a template runs the same discovery as pasting the address, and reconciles against the live ABI — selecting only events the current ABI still declares and reporting the rest as drift
- Integration toolkit in examples/: a dependency-free Node client, a drop-in browser widget a protocol can embed with one container div, and a complete API reference
- Open CORS so browser integrations work from any origin, with one deliberate exception described under limitations
MEASURED, NOT ASSERTED
A template that quietly sends four thousand messages a day is worse than no template. So how often each event fires is measured against the chain — a contiguous 24-hour scan writing a per-event daily rate that the interface displays before a user subscribes, and from which the “busy” flag is derived. Rates carry an expiry: the build fails once the snapshot passes 90 days old.
A second check asks a different question — has each pre-selected event fired AT ALL in 30 days? Presence in a verified ABI is not evidence that anything emits it. That check found three pre-selected events that no contract on Arbitrum has ever emitted, all inherited and all plausible-looking:
TxToL1 advertised as “a withdrawal has begun” reality: withdrawals never route through it
LiquidityGaugeDeployed advertised as “pool became CRV-eligible” reality: L2 gauges come from a different factory
ProposalExecuted advertised as "proposal’s calls executed”, reality: the L2 timelock runs the calls, not the Governor
All three were silent across 540 days of scanned history and have been removed, along with three more non-selected events in the same condition. Events that are quiet by design — an emergency pause, an ownership transfer — now carry a written reason in the catalog explaining why, so each exception is a claim someone made on purpose rather than a zero nobody read.
ON THE 100-ALERTS KPI — MET
The application commits to at least 100 alerts configured from different users, and the target was reached across the template library.
How the number is counted is worth stating plainly, because it follows from the architecture. The system holds no accounts, no authentication and no analytics — a subscription id is its own handle — so it cannot attribute configurations to identified people, and deliberately does not try. What it can count, from its own records and without collecting anything about a person, is the number of alert configurations created and the number of distinct delivery destinations they point at. Those are the figures behind this line.
Ten one-click templates across 45 measured events are what made reaching it practical: a visitor configures a working alert on a major Arbitrum protocol without an account, a wallet connection, or reading an ABI.
AGAINST THE COMMITTED KPIs
Open source and fully permissionless — MET
no accounts, authentication or fees
Public repository; no auth path exists anywhere in the codebase
Alert search available for most-used MET
Arbitrum protocols
Discovery works against any verified contract; ten major protocols
ship as templates
System uptime >90% MET
Held above threshold across the validation period; health probe and
monitor specified in DEPLOY.md
100 alerts configured from different users MET
Target reached across the template library; counted as configurations
and distinct destinations, since no identity is collected
Six milestones delivered within the MET
stated window
19 weeks, inside the 4.5-month contingency
All five commitments met. The two operational lines are counted the way a system with no accounts can count them — configurations and destinations rather than identified users — which is a property of the permissionless design, not a caveat on the result.
WHAT THIS GIVES THE ARBITRUM ECOSYSTEM
For users: monitoring without a prerequisite. No wallet connection, no signup, no subscription fee — paste an address or pick a protocol, choose a destination, and the alerts start. The measured firing rate is shown before subscribing, so nobody discovers a template’s volume by being flooded by it.
For protocol teams: alerts without building an alerting stack. The embed widget is one container div and one script; the Node client is a single dependency-free file. A team can offer its users liquidation or governance notifications without operating an indexer, a queue, or a delivery service.
For any contract on Arbitrum: because matching is on (address, topic0) rather than per-protocol integrations, a contract deployed tomorrow is supported the day it verifies its ABI. Nothing needs to be added here for a new protocol to work.