Devnet · Pilot

Build with Ground Truth.

Signal is a price oracle for the physical economy. People photograph receipts, price tags and fuel pumps in Telegram; a vision model reads the prices; a Solana program decides which ones to trust and pays for them. The result is a set of per-item, per-city price feeds that any app or program can read.

Overview

How it works

1. Capture
A reporter sends a photo to @OfficialSignalOracleBot. The bot creates a Solana wallet for them on first use, so no crypto setup is needed.
2. Parse
A vision model (Qwen, via Groq) rejects screenshots and edited images, then reads each line item, the store, the city, the time and the total. Prices are converted to USD at live exchange rates.
3. File on-chain
Each priced item is submitted to the Signal program on Solana. The program locks the reward from its pool and records the price against a feed such as GROCERY:ng-port_harcourt:rice_kg.
4. Consensus
The program compares the price with the feed's recent median and with other reporters' prices for the same item (rules below).
5. Settle
Accepted: the program pays the reward from its vault and adds the price to the feed. Contradicted: the reward stays in the pool and the reporter's reputation drops. Results are mirrored to BOTChain as a public log.

Off-chain

Reading the photo, currency conversion and duplicate detection run in the Signal bot, which signs each submission as the attester.

On-chain

Who gets paid, how much and when; price bounds, peer agreement, slashing, reputation and feed medians are all enforced by the Solana program.

Verification

Consensus rules

Every report starts as pending with its reward locked. It settles as soon as there is enough evidence:

Local price bounds
Once a feed has 3 or more accepted prices, a new price within ±40% of the feed's median is accepted immediately. Outside that range it needs peer support or it is slashed.
Peer agreement
Reports for the same item in the same city within 72 hours are compared pairwise. Prices within ±25% of each other confirm one another; each pair counts once, and a reporter can't confirm their own report.
Contradiction
A report is slashed when it is outside local bounds with no peer support, or (on a new feed) when 2 or more peers disagree and none agree.
Timeout
With no evidence either way after 48 hours, reporters with reputation 40 or higher are accepted; others expire without a reward.

In practice: in a city where people report the same items, payouts arrive within minutes. The first report of a new item waits for a peer or the 48-hour timeout.

Incentives

Rewards & reputation

Rewards

Each accepted item pays $0.10 – $0.30, scaling with reputation ($0.20 at the starting reputation of 50). Rewards come from a USDC vault owned by the program, funded by the treasury and, later, by data buyers. Nothing is paid until consensus accepts the report.

Reputation (0–100)

Starts at 50. +3 per accepted report, −20 per slashed report, −10 for reusing someone else's photo, −15 for a faked image. Below 15, reporting is blocked.

Developer

Reading the data

Feeds are keyed CATEGORY:region:item, for example FUEL:ng-port_harcourt:petrol_litre. Prices are in USD. There are three ways to read them; all three work today.

1. REST API (simplest)

Terminal
# Network stats (reports, rewards paid, reward pool)
curl https://signal-production-d83e.up.railway.app/api/stats

# Every price feed with its median
curl https://signal-production-d83e.up.railway.app/api/feeds

2. Solana program (source of truth)

read-feed.ts
import * as anchor from "@coral-xyz/anchor";
import { Connection, PublicKey } from "@solana/web3.js";
import { createHash } from "crypto";
import idl from "./signal_oracle.json"; // from signal-bot/idl in the repo

const connection = new Connection("https://api.devnet.solana.com");
const program = new anchor.Program(idl as anchor.Idl, { connection });

// Feed accounts are PDAs: ["feed", sha256("CATEGORY:region:item")]
const key = "GROCERY:ng-port_harcourt:rice_kg";
const [feedPda] = PublicKey.findProgramAddressSync(
  [Buffer.from("feed"), createHash("sha256").update(key).digest()],
  program.programId,
);

const feed = await program.account.feed.fetch(feedPda);
const prices = feed.prices.slice(0, feed.len).map((p) => p.toNumber()).sort((a, b) => a - b);
const median = prices[Math.floor((prices.length - 1) / 2)] / 1_000_000; // USD, 6 decimals
console.log(key, "median $" + median, "from", feed.len, "recent prices");

3. BOTChain mirror (any EVM app)

read-botchain.ts
import { ethers } from "ethers";

const provider = new ethers.JsonRpcProvider("https://rpc.botchain.ai", 677);
const oracle = new ethers.Contract("0x9119b3D7e7DcC540F08D62CeBC5bC2f83e8793f2", [
  "function getFeed(string feedKey) view returns (uint256 medianPrice, uint256 samples, uint256 updatedAt)",
], provider);

const [cents, samples] = await oracle.getFeed("GROCERY:ng-port_harcourt:rice_kg");
console.log("median $" + Number(cents) / 100, "from", samples.toString(), "samples");

Live feeds

Read straight from the Signal API right now. Each row is a feed with at least one accepted price, and its current median.

Reference

Deployments

Security

Security model

Duplicate photos
Every photo is fingerprinted with SHA-256, and the fingerprint is part of the report's on-chain address, so the same photo can't be filed twice for the same item. Reusing someone else's photo costs 10 reputation; resending your own is simply ignored.
Faked images
The vision check rejects screenshots and flags edited or generated images, which costs 15 reputation.
The reward pool
Only the program can move funds out of the vault, and only for accepted reports, up to each report's locked reward. New reports are refused once the pool is fully committed.
Known limits
  • The program can't see a photo. It trusts the bot's attester key to vouch for the vision check.
  • One person with several Telegram accounts could confirm their own prices. Reputation is the only cost today.
  • A brand-new feed has no price bounds until 3 prices are accepted, so its first reports rely on peers.
Roadmap

Not built yet

TypeScript SDK
A published client with a one-line getFeed(key), built on the program's generated client.
Reporter staking
Reporters lock a small USDC stake that is slashed for fraud, in addition to reputation.
Paid data access
Per-query fees from data buyers flowing into the reward pool, so rewards are funded by usage.
Mainnet
Real USDC rewards after the devnet pilot.