Verifying on-chain
You don't have to trust our servers about who won. Every settled battle puts its result and a hash of its full metric history on Solana, and the history itself is public.
The program
| Account | Address |
|---|---|
| Program | Diy3raLiExkBHky4e2WaPvDfXjCF44q89fYFduY7pck3 |
| Config PDA | 5Xqrcav4LM1Ai4rpioWR8gmGJgb6TWozaKP78JyB5QQZ |
| Treasury PDA | A9inq8QiFDEWxetvddWtWqHuWzgWFursRhXovN4RRBDZ |
Each battle has a Market account at the PDA ["market", battle_id as u64 little-endian]. The pot is held in that account's own balance. Once settled, the Market stores the winner, the score in basis points, the scoring version, the raw metrics as [orig_S0, orig_S1, reply_S0, reply_S1] × [likes, reposts, quotes, replies, views], and snapshot_hash.
import { PublicKey } from "@solana/web3.js";
const id = 1234n; // battle id from the URL: /c/1234
const seed = Buffer.alloc(8);
seed.writeBigUInt64LE(id);
const [market] = PublicKey.findProgramAddressSync(
[Buffer.from("market"), seed],
new PublicKey(PROGRAM_ID),
);snapshot.json
The full history behind a result is served at /c/<id>/snapshot.json as soon as the battle settles. It contains the scoring version (currently v1), the initial and final metrics for both posts, every interval poll in between, and the score breakdown. The response also carries an x-snapshot-sha256 header.
snapshot_hash = sha256(canonical JSON of snapshot.json), where canonical means object keys sorted alphabetically at every level and no whitespace. The file as served may list keys in a different order, so sort before hashing.
How to recompute
- Download
/c/<id>/snapshot.json, canonicalize it and take the sha256 (script below). - Read the Market account for that battle and hex-encode
snapshot_hash. It must equal your hash. If it does, the snapshot is exactly what the oracle committed to at settlement. - Check the metrics: the
initialandfinalvalues in the snapshot must match the metrics array on the Market account. - Re-run the scoring formula on
final − initial. You should get the Market'sscore_bpsand winner.
// verify.mjs — node verify.mjs https://<site> <battle-id>
import { createHash } from "node:crypto";
const [base, id] = process.argv.slice(2);
const res = await fetch(`${base}/c/${id}/snapshot.json`);
const snapshot = await res.json();
// Canonical form: object keys sorted (recursively), no whitespace.
const canon = (v) =>
Array.isArray(v) ? v.map(canon)
: v && typeof v === "object"
? Object.fromEntries(Object.keys(v).sort().map((k) => [k, canon(v[k])]))
: v;
const hash = createHash("sha256").update(JSON.stringify(canon(snapshot)), "utf8").digest("hex");
console.log("recomputed ", hash);
console.log("site header ", res.headers.get("x-snapshot-sha256"));
// Compare both with Market.snapshot_hash (32 bytes, hex-encode it) read from chain.JSON.stringify output, so canonicalize with JavaScript (or anything that prints numbers the same way) to get a matching hash.What this does and doesn't prove
The hash proves the published snapshot is the one used on-chain, and the formula proves the winner follows from it. It can't prove the metrics themselves are what X reported: those come from X's API via our oracle. To limit that trust, the oracle key can only settle a market after its deadline and only once, and the full poll history is public so anyone can compare it with what they saw on X.