Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PulseGrid

Real-time DePIN telemetry and verifiable settlement on BOT Chain.

Devices stream signed telemetry that is anchored on-chain every interval. An autonomous Settlement Agent then pays or slashes each operator per interval in stablecoin, automatically, continuously, and gaslessly for the devices. On-chain policy is the sole authority: an operator cannot be paid for data they did not deliver, and the agent cannot stiff an honest operator. Built for the BOT Chain Builder Challenge #1 (DePIN / Real World track, AI-Agent cross-eligible).

▶ Demo video: https://www.youtube.com/watch?v=zK4Gjhi_khY · Live app: https://pulsegrid-nine.vercel.app

Status: deployed and verified on BOT Chain mainnet (chainId 677). The hosted app runs on mainnet with real PAY/SLASH settlements on chain; all four PulseGrid contracts are verified on scan.botchain.ai (see Mainnet deployment). Gasless blob-anchored telemetry, on-chain per-epoch pay/slash with value-layer data-quality SLAs, a permissionless keeper set, an autonomous Go agent, and a Next.js operator app wired to real chain and agent data. Every headline number is a live chain read with an explorer link.

Seamless, no local setup. A tester connects a wallet, deploys a node, and clicks Go live to stream signed telemetry directly from the browser tab (gaslessly, the device key never leaves the client). The agent then pays it every satisfied interval and slashes it if the tab closes, all on-chain. The registry is permissionless, so anyone is an operator of the nodes they deploy. An optional DeepSeek layer adds advisory device-health narratives, kept strictly non-authoritative.

Reading the dashboard. It is always live against chain state, so it is never blank: the settlement ledger, its full settled history, and every proof transaction render immediately. BOT Chain has a gas floor, so the streaming fleet runs in short funded windows (for a recording or a judging session), not 24/7. When it is not streaming, devices honestly read "idle, not streaming" or "awaiting telemetry". That is truthful live state, not mock data. See What you'll see to bring it fully live.


The guarantee

Bilateral machine-trustlessness. SettlementVault.settleEpoch(deviceId, epoch) takes only (deviceId, epoch). The pay/slash verdict and the amount are re-derived on-chain from device-signed facts and the SLA. A buggy or malicious agent (or keeper) cannot overpay, wrongly slash, pay an un-anchored or out-of-range epoch, or double-settle. This is proven by an adversarial Foundry suite (test_Adversarial_*), part of 81 passing tests.

Two-layer guarantee. The device signs its reading as a first-class EIP-712 field (int256 value, milliunits). With a value-layer SLA, an interval pays only when at least minAnchors readings were both delivered and in range [minValue, maxValue]; a device that streams its full quota out of range is slashed. The guarantee thus extends from liveness (did you deliver?) to data quality (was the data good?), with the agent still unable to assert either. Proven live on mainnet.

Permissionless settlement. settleEpoch pays an equal tip to whoever calls it from a dedicated keeper pool (never skimmed from operator pay or the slash, so amounts are unchanged), turning the single Settlement Agent into an incentivized, permissionless keeper set.

Why BOT Chain (native integrations)

Native primitive How PulseGrid uses it
Sub-second blocks (0.74s, measured) per-interval real-time settlement driven by an autonomous agent
Near-zero fees (base fee 0, measured) continuous micro-settlement is economically viable
EOA Paymaster / gasless devices sign EIP-712 telemetry and spend zero gas (plain EOAs, no 4337) via a self-hosted MegaFuel-shaped sponsor relayer
Blob API (EIP-4844) raw telemetry rides in type-3 blobs; a compact blobhash(0) anchor is committed to state
On-chain policy authority the agent only drives; the vault re-derives every verdict

Autonomous + advisory AI (AI-Agent track). The Settlement Agent is an autonomous Go service that drives settlement each epoch; the contract decides. An optional DeepSeek layer reads the on-chain facts and the statistical reputation to produce advisory device-health analysis for the dashboard. It is display-only: there is no code path from any AI output to settleEpoch, so the guarantee is untouched, and the app runs identically without a key.

See docs/ARCHITECTURE.md for the design and docs/WRITEUP.md for the submission narrative.

Architecture

flowchart TD
  subgraph OFF["Off-chain"]
    DEV["Device (EOA, 0 gas)<br/>signs EIP-712 telemetry"]
    REL["Sponsor Relayer<br/>MegaFuel-shaped · pays gas"]
    AGENT["Settlement Agent (Go)<br/>autonomous · predicts, never decides"]
    APP["Next.js App<br/>wagmi + viem"]
    AI["Advisory AI (DeepSeek)<br/>non-authoritative"]
  end

  subgraph ON["On-chain · BOT Chain mainnet (677)"]
    TA["TelemetryAnchor<br/>verifies sig · buckets anchorsInEpoch"]
    PM["PolicyModule<br/>pure pay/slash verdict"]
    SV["SettlementVault<br/>dual escrow"]
    OPW(["Operator wallet"])
    TRE(["Treasury"])
  end

  DEV -->|"signed telemetry"| REL
  REL -->|"type-3 blob tx"| TA
  AGENT -->|"settleEpoch(deviceId, epoch)"| SV
  TA -.->|"anchorsInEpoch (the only settlement fact)"| SV
  PM -.->|"re-derives verdict"| SV
  SV -->|"PAY · payEscrow"| OPW
  SV -->|"SLASH · bond"| TRE
  APP -->|"viem reads"| TA
  APP ==>|"wagmi wallet writes"| SV
  AGENT -.-> AI -.-> APP
Loading

The Settlement Agent supplies only (deviceId, epoch); the vault re-derives pay-or-slash on-chain from the device-signed anchorsInEpoch and the epoch's SLA. The agent (and the advisory AI) never decide a payment.

  • contracts/ Foundry: DeviceRegistry, TelemetryAnchor (epoch buckets), ServiceContract (versioned SLA), PolicyModule (pure verdict library), SettlementVault (dual escrow), and MockUSD (local dev only; mainnet settles in canonical USDT).
  • agent/ Go: settler (autonomous agent), paymaster (sponsor relayer), devicesim (gasless fleet), internal/{telemetry,chain,policy,reputation,ai} (ai = optional DeepSeek advisory layer). Container/host config in agent/Dockerfile + agent/railway.json.
  • web/ Next.js (App Router, wagmi/viem): landing plus the operator app, live-wired. A deployed node can stream from the browser tab (same EIP-712 signing) through the hosted relayer, no install.

Prerequisites

Foundry (forge/cast), Go 1.25+, Node 20+.

Reproduce it

A. View the live instance (no keys, read-only, works immediately)

The app defaults to the deployed demo instance, so it runs against real chain state out of the box.

git clone <this repo> && cd pulseGrid
make test                      # Foundry: 81/81 green
cp web/.env.local.example web/.env.local
make web-install
make web                       # http://localhost:3000  (landing) and /app (operator flow)

The dashboard reads the live contracts: the fleet table, the settlement ledger (PAY green / SLASH red, each deep-linked to scan.botchain.ai), per-device detail, the on-chain SLA, treasury balances, and the agent's reputation snapshot. No mock data anywhere on the app path.

Connect a wallet on chainId 677 and you can deploy your own node in /app/deploy, then click Go live to stream it straight from the browser (gasless, no local process). It appears under Operators and is settled automatically. /app/operators groups every device by operator wallet with per-operator earnings, slashes, and reputation.

What you'll see (and why devices may read "idle")

The dashboard is always live against chain state, so it is never blank: the settlement ledger, the full settled history, dev-001/003's paid history, and dev-002's slash are permanent on-chain facts and render immediately, each deep-linked to scan.botchain.ai. (The ledger is cumulative and grows with every demo window, so read current totals from the live app, not a fixed number here.)

BOT Chain has a gas floor, so gasless anchoring and per-epoch settlement cost real BOT. We therefore do not run the streaming fleet 24/7; it runs in short funded windows. When it is not in a window, devices honestly read "idle, not streaming" or "awaiting telemetry", and recent intervals read "idle, no telemetry". That is truthful live state, not mock data and not a bug: a device that is not currently delivering is shown as such (and would be slashed for it).

To see it fully live (devices streaming, new PAY/SLASH rows appearing on camera):

  • Fastest, one device: deploy a node in /app/deploy and click Go live to stream it from your browser. Needs the hosted relayer funded (below).
  • Full fleet: fund the relayer and agent keys, then start the hosted services (see Host it). A ~10 BOT budget covers roughly 25 to 45 minutes of live operation; stop the services when done. Every settlement is a real on-chain tx.

Deploy and stream your own node (2 minutes, no install)

This is the seamless product flow: you become an operator and watch your own device get paid, entirely from the browser. It uses two tabs on purpose (explained below).

  1. Connect a wallet on chainId 677 with a little BOT for gas, on the live mainnet app.
  2. Get USDT + approve. In /app/treasury, use Get USDT (bridge) to bridge canonical USDT to your wallet, then Approve vault (a one-time allowance, no visible change is expected).
  3. Deploy in /app/deploy: give it a label, keep minAnchors 3, click 1 Register device, then 2 Bind policy. A device signing key is generated in this tab and never leaves it.
  4. Go live (button appears after step 3). The tab now signs a reading every ~10s and the sponsor relayer anchors it on-chain, gaslessly. You will see anchored ✓ tx ticks within ~10 seconds. Keep this tab open: it is the device; closing it stops the stream and the next interval slashes.
  5. Fund escrow, in a second tab. Your node streams and settles immediately, but each PAY is $0 until its pay-escrow is funded. Open /app/treasury in a new tab (leave the streaming tab running), find your node under Your devices, and Deposit escrow (e.g. 5 USDT). Its next PAY is now real USDT.

Why two tabs? The in-browser device key lives only in the deploy tab's memory. Navigating that tab away (to Treasury, or via a redirect) regenerates the key, i.e. a different device that is no longer the one streaming. So you fund escrow in a separate tab and let the first keep streaming. All in-app links from the Go-live panel open in new tabs for exactly this reason.

Within one or two intervals your node shows online on /app, appears under your wallet on /app/operators, and earns real USDT, all with no local process. The autonomous agent settles it like any other device; on-chain policy decides.

B. Deploy your own stack and run the full loop

cp .env.example .env           # then set PRIVATE_KEY to a funded key on your target BOT Chain RPC

# 1. Deploy the settlement stack (prints the 5 addresses + deploy block)
cd contracts && forge script script/DeploySettlement.s.sol:DeploySettlement \
  --rpc-url $BOT_RPC --broadcast --legacy --with-gas-price 50000000000 \
  --private-key $PRIVATE_KEY

# 2. Register a device fleet (operator key), then bond + fund escrow + set each SLA
#    (fund the settlement token to operator + funder; setSLA takes an SLA active from a future epoch).
cd ../agent && PRIVATE_KEY=$OPERATOR_KEY go run ./cmd/devicesim -register -n 3 -registry $REGISTRY

# 3. Run the three processes; point web at your addresses via NEXT_PUBLIC_* in web/.env.local
PRIVATE_KEY=$RELAYER go run ./cmd/paymaster -addr :8789 -anchor $ANCHOR              # sponsor (devices pay 0 gas)
go run ./cmd/devicesim -anchor $ANCHOR -relayer http://localhost:8789 \
  -interval 2s -drop dev-002 -drop-after 240s                                        # gasless fleet; drop one to force a SLASH
AGENT_PRIVATE_KEY=$AGENT go run ./cmd/settler -registry $REGISTRY -anchor $ANCHOR \
  -service $SERVICE -vault $VAULT -from-block $DEPLOY_BLOCK -tick 15s -rep-snapshot reputation.json

The agent logs, per epoch, its off-chain predict and then the on-chain settled result: the two always match, because the contract is the authority. A live device is paid; the dropped device, whose epochs anchor below the SLA threshold, is slashed and its bond flows to the treasury.

Run a real hardware device (encrypted key, single binary)

The browser tab and devicesim fleet are on-ramps; real hardware runs cmd/device - one device, its own key held in an encrypted keystore (not a shared plaintext fleet.json), signing each reading and streaming it gaslessly. The signing is byte-identical to the contract, so a device built on the importable SDK (github.com/Manuel-dev01/pulseGrid/agent/device) is a first-class node. On-chain key rotation is supported (DeviceRegistry.rotateDeviceKey).

cd agent
# 1. Provision: encrypt a fresh or browser-exported key into a keystore file.
KEYPASS=hunter2 RAWKEY=0x<privkey> go run ./cmd/device -import-key \
  -key-env RAWKEY -keystore-out ./keys -keystore-pass-env KEYPASS
# (register that device address on-chain once: DeviceRegistry.registerDevice)

# 2. Run the device (device wallet spends ZERO gas; the relayer sponsors).
KEYPASS=hunter2 go run ./cmd/device -id NODE-1 -anchor $ANCHOR -relayer $RELAYER_URL \
  -rpc https://rpc.botchain.ai -keystore ./keys/UTC--... -keystore-pass-env KEYPASS \
  -metric power_kw -unit kW -min 38 -max 46 -interval 10s

The device logs each anchored reading's tx hash; it never sends a transaction itself. Value-layer SLAs (below) pay it only when its readings fall in the configured [min, max] band.

Host it (self-driving, browser streaming)

To keep the network live and let browser-deployed nodes stream, host the three Go services with the provided agent/Dockerfile + agent/railway.json. It is one image, the role selected by PG_SERVICE=relayer|settler|fleet:

  • relayer (paymaster) - public HTTPS, sponsors gas; needs PRIVATE_KEY, ANCHOR_ADDR. Point the app at it with NEXT_PUBLIC_RELAYER_URL (Vercel env or web/.env.local).
  • settler - needs AGENT_PRIVATE_KEY + the contract addresses; auto-discovers every on-chain device. Serves its reputation at /reputation (set REPUTATION_URL on the app to surface it and the advisory AI). Optional DEEPSEEK_KEY enables the narratives; BACKFILL=N stops a freshly started agent from re-settling a long idle gap.
  • fleet (devicesim) - streams the canonical devices; needs the device keys (FLEET_JSON).

Economics (why funded windows, not 24/7). BOT Chain has a ~50 gwei gas floor, so each gasless blob anchor (~0.04 BOT) and each settlement (~0.03 BOT) costs real BOT. Run the fleet and settler in short funded windows: fund the relayer and agent keys, railway up the services, record or judge, then railway down. A ~10 BOT budget covers roughly 25 to 45 minutes of live operation. Browser Go live streams a single device and needs only a funded relayer, so it is much cheaper. When nothing is streaming, the app shows honest idle state; the settled ledger stays on-chain permanently.

Mainnet deployment (BOT Chain, chainId 677) - verified

Deployed and Blockscout-verified on BOT Chain mainnet. The hosted DApp https://pulsegrid-nine.vercel.app runs against these contracts.

Contract Address (verified)
DeviceRegistry 0x3697113dEe694BeEA5FD354384D03ec9198DE7aF
TelemetryAnchor 0x6377BC3B08d81e33Fa91927D71CC7f806Fc45662
ServiceContract 0x908302Eb8810ef6E40f02dC5Dd63E62bc1cC00Eb
SettlementVault 0x6380ebC2EbF72fC6deA07028e500943fb876C546
USDT (settlement token, 6dp) 0xaBabc7Ddc03e501d190C676BF3d92ef0e6e87a3C

Deploy block 18822148, 600s (cost-tuned) epochs. The four PulseGrid contracts read Verified on https://scan.botchain.ai/address/<addr>; the settlement token is BOT Chain's canonical bridge-backed USDT (6 decimals), so the network settles operators in real USDT, no mock asset. Proven live on mainnet: a device streamed gaslessly and the autonomous agent settled real USDT, PAY 0x6b70ff3cd3402ade72ef8701099c394fcdc7684a7484b806ced8c97ad22623a2 (escrow to operator) and SLASH 0xd3263742c27c83bec60f441199e7983e22ea077fd206ccd4cdc7003377f69c75 (bond to treasury), each viewable on scan.botchain.ai.

Settlement asset. The production vault settles in canonical USDT (0xaBabc7...), acquired via the free bridge at bridge.botchain.ai. Operators are paid and slashed in real USDT: a device streams telemetry gaslessly (its wallet spends 0 BOT) and, once its escrow and bond are funded, the autonomous agent settles real USDT PAY/SLASH per interval. The vault is asset-agnostic (the token is a constructor argument).

Network

  • Mainnet (live deployment): chainId 677, RPC https://rpc.botchain.ai, explorer https://scan.botchain.ai
  • Run the app against any BOT Chain endpoint via NEXT_PUBLIC_CHAIN_ID plus the matching NEXT_PUBLIC_*_ADDR / BOT_RPC; the shipped web/.env.local.example targets the live mainnet instance above.

About

Real-time DePIN settlement on BOT Chain: devices sign telemetry and anchor it on-chain gaslessly, and an autonomous agent pays or slashes each operator per interval in stablecoin, with on-chain policy as the sole authority.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages