Search projects, hackathons, sponsors and tech

    ← All projects

    Onchain Heartbeat

    Live blockchain pulse narrated by an AI agent that pays for its own data over Hedera x402

    Demo

    About

    Onchain Heartbeat turns blockchain activity into a vital sign, and gives the AI narrating it a wallet and a name. A circle beats in the centre of the screen. Its rhythm, amplitude and colour are driven by live Uniswap v3 swap flow on Base a slow cyan swell when the chain is quiet, fast crimson thumping when it's busy. You read the state of the chain from across the room, before anyone tells you what the numbers mean. Every 18 seconds an AI agent buys a fresh reading and calls the play in one line of commentary. It doesn't authenticate with an API key it pays. The reading sits behind an x402 paywall: the agent gets a 402, signs an HBAR transfer, Blocky402 settles it on Hedera testnet, and only then does the data come back. One payment per narration, never batched, each individually auditable. And it signs what it says. The narrator owns onchain-heartbeat.eth on the ETHOnline ENSv2 deployment, with avatar, description and url as text records an identity, not a label. When the chain's mood shifts, that call is published as its own subname, carrying the comment, the reading behind it, and the Hedera transaction that paid for it. Resolve any narration and you can check what the agent claimed, what it was looking at, and what that claim cost. It addresses two gaps. Chain data is a wall of numbers you have to read to understand. And AI agents can't sign up for API keys, can't hold subscriptions, and can't prove they said anything their output is unattributable by default. This is a small, complete demonstration of the alternative, running live rather than described. Verifiable settlement: 0.0.7162784@1788690223.769989855 confirmed on the Hedera mirror node.

    How it's made

    Stack. Next.js 16 (App Router), TypeScript, Tailwind 4, OpenAI gpt-4o-mini, viem, and the x402 v2 packages (@x402/hedera, @x402/next, @x402/fetch). Nine runtime dependencies total. The seam. Every data source returns the same { activityLevel, label }, so the visual never knows where its number came from. That contract absorbed three swaps during the build mock to RPC, RPC to The Graph, and one ENS deployment to another and PulseVisual.tsx never changed once. The pulse itself maps 0–100 onto three CSS custom properties (--beat, --amp, --hue) and lets CSS keyframes animate, so the heartbeat runs on the compositor rather than in JS, with @property registrations easing amplitude and hue between readings. The Graph. A Messari standardized subgraph for Uniswap v3 on Base, queried server-side so the API key never reaches the browser. Swaps-per-minute is derived from the timestamp span of the last 500 swaps 500 rather than 100 because over a handful of integer seconds the rate quantises into coarse jumps (429, 600, 750, 1500). Thresholds were calibrated by sampling, not guessed: 341–652/min in the daytime, but 121 overnight, so the scale is logarithmic rather than linear. A linear window pinned half the day at zero and flatlined the pulse. Hedera x402. /api/chain-data is a real metered resource behind @x402/next, and the thing it sells is the subgraph reading so the payment buys indexed data, not a receipt. The client sets a hard 0.01 HBAR per-payment ceiling, because an agent paying on a timer should have one. scripts/buy-reading.mjs lets any third party with a funded testnet account buy a reading, so the endpoint isn't a private arrangement. ENSv2 and the hacky part. The hackathon's ENS app couldn't complete a registration, so we went straight at the contracts. None of the deployment is verified on Etherscan or Sourcify, so the ABIs were recovered by scanning PUSH4 selectors out of deployed bytecode and resolving them against a signature database, then cross-checked against a registration that had already succeeded on-chain argument order copied from a working call rather than guessed. That also isolated the app's defect: it encodes initialize's first argument as address,uint256 where the implementation expects (address,uint256)[], producing a selector that matches no function. Reported with a keyless repro (docs/ens-app-bug-report.md). Registration is commit-reveal paid in ERC-20, not ETH. Records live on a per-name Permissioned Resolver keyed by DNS-encoded name the shared PublicResolverV2 rejects the owner for every node derivation. The per-narration subnames need no registration at all: writing records on the parent's own resolver is enough for ENSIP-10 wildcard resolution to reach them, so no CCIP-Read gateway. Verification discipline. Every on-chain write is simulated before it's sent, and multi-step flows are checked together with eth_simulateV1 against one projected post-state which is the only way to verify a call against a resolver proxy that doesn't exist yet. Each dependency degrades instead of breaking: kill the subgraph, the facilitator, the LLM or the resolver and the pulse holds its last rate and keeps beating.