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.
How it works
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.
Consensus rules
Every report starts as pending with its reward locked. It settles as soon as there is enough evidence:
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.
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.
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)
# 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)
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)
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.
Deployments
Security model
- 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.