Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
legacy-peer-deps=true
auto-install-peers=true
production=false
registry=https://registry.npmjs.org/
57 changes: 34 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
[![Smart Contract](https://img.shields.io/badge/Solidity-0.8.20_Verified-f04b2f?style=for-the-badge&logo=solidity&logoColor=white)](contracts/ProofCastAnchor.sol)
[![DreamDEX](https://img.shields.io/badge/DEX-DreamDEX_Event_Contracts-38bdf8?style=for-the-badge&logo=polkadot&logoColor=white)](https://docs.dreamdex.io)
[![EIP-712](https://img.shields.io/badge/Cryptography-EIP--712_%2B_SHA--256-blue?style=for-the-badge&logo=letsencrypt&logoColor=white)](shared/eip712.ts)
[![Tests](https://img.shields.io/badge/Tests-42%2F42_Passed-emerald?style=for-the-badge&logo=vitest&logoColor=white)](server/receipts.test.ts)
[![Tests](https://img.shields.io/badge/Tests-60%2F60_Passed-emerald?style=for-the-badge&logo=vitest&logoColor=white)](server/receipts.test.ts)
[![TypeScript](https://img.shields.io/badge/TypeScript-Strict_0_Errors-3178c6?style=for-the-badge&logo=typescript&logoColor=white)](tsconfig.json)
[![License](https://img.shields.io/badge/License-MIT-white?style=for-the-badge)](LICENSE)

Expand All @@ -37,7 +37,7 @@ Prediction markets reflect what the crowd prices. However, crowd consensus is fr
1. **Live DreamDEX Ingestion**: Direct integration with Somnia binary event pools and order-book depth via `@somnia-chain/markets-sdk`.
2. **EventForge Dual-Layer AI**: Deterministic order-book microstructure calculations coupled with real-time multi-model AI reasoning (Claude, Gemini, DeepSeek, Meta-Ensemble).
3. **EIP-712 Decision Receipts**: Forecasters freeze their probability, confidence, thesis, and counter-thesis into a signed SHA-256 cryptographic digest before outcome settlement.
4. **Somnia Smart Contract Anchoring & Staking**: Immutable on-chain anchoring via `ProofCastAnchor.sol` on Somnia Shannon Testnet, with optional native `$SOM` conviction staking.
4. **Somnia Smart Contract Anchoring & Staking**: Immutable on-chain anchoring via `ProofCastAnchor.sol` on Somnia Shannon Testnet, with optional native `STT` conviction staking, verified server-side against the mined transaction.
5. **Automated On-Chain Resolution & Brier Calibration**: Background daemons monitor on-chain settlements, auto-resolve receipts, and calculate empirical Brier calibration scores ($BS \in [0, 1]$), registering verified **Soulbound Reputation Badges** on-chain.

---
Expand Down Expand Up @@ -76,7 +76,7 @@ flowchart LR
| **02. UNDERSTAND** | Multi-Model AI | Deterministic microstructure fair value + LLM Bull/Bear reasoning. | EventForge Multi-Model Engine |
| **03. CHALLENGE** | Stress-Test Edge | Force forecasters to articulate counter-theses and calculate net executable edge. | True Executable Edge Algorithm |
| **04. COMMIT** | Sign Receipt | User cryptographically signs forecast probability, thesis, and evidence before settlement. | EIP-712 Typed Data + SHA-256 Digest |
| **05. ANCHOR** | Somnia On-Chain | Anchor hash on Somnia Shannon Testnet; optional payable native `$SOM` staking. | `ProofCastAnchor.sol` Smart Contract |
| **05. ANCHOR** | Somnia On-Chain | Anchor hash on Somnia Shannon Testnet; optional payable native `STT` staking. | `ProofCastAnchor.sol` Smart Contract |
| **06. RESOLVE** | Auto-Settlement | Background daemon detects DreamDEX contract settlement & oracle webhooks. | `resolutionWorker.ts` / Oracle Webhook |
| **07. PROVE** | Reputation & Calibration | Mathematical Brier calibration, lead-time bonuses, and on-chain Soulbound Badges. | Brier Scoring Engine + SBT Badges |

Expand All @@ -94,7 +94,7 @@ ProofCast is engineered natively around the technical architecture of the **Somn
│ • High-Throughput Binary Event Markets │ • Multi-Model AI Decision Engine │
│ • On-Chain CLOB Order Book Depth │ • Cryptographic EIP-712 Decision Receipts │
│ • ERC-6909 Multi-Token Outcome Shares │ • Immutable Somnia Smart Contract Anchor │
│ • Automated Contract Payout Vectors │ • Native $SOM Staking & Conviction Pool │
│ • Automated Contract Payout Vectors │ • Native STT Staking & Conviction Pool
│ • Official @somnia-chain/markets-sdk │ • Soulbound On-Chain Reputation Badges │
└──────────────────────────────────────────┴─────────────────────────────────────────────┘
```
Expand All @@ -114,7 +114,7 @@ ProofCast is engineered natively around the technical architecture of the **Somn

### 4.2 EventForge Dual-Layer AI & SSE Streaming
* **Layer A (Deterministic Microstructure Engine)**: Pure mathematical model computing fair value from order-book depth imbalance, spread penalty, and time decay. Output is 100% deterministic: identical market inputs yield identical probability outputs.
* **Layer B (Multi-Model AI Reasoning)**: Real-time structured reasoning generating Bull Case, Bear Case, Key Risks, and Disagreement Analysis across Claude, Gemini, DeepSeek, and Meta-Ensemble.
* **Layer B (Multi-Model AI Reasoning)**: Structured reasoning generating Bull Case, Bear Case, Key Risks, and Disagreement Analysis across Claude, Gemini, DeepSeek, and Meta-Ensemble. Live provider calls require the corresponding API keys; without them each model falls back to a built-in analytical engine and reports itself as `BUILTIN_ANALYTICAL` rather than `REAL_LLM`, which the UI surfaces per model.
* **Server-Sent Events (SSE)**: Mounted at `GET /api/eventforge/stream` for live token streaming.
* **Strict Invariant**: AI models cannot alter numerical probabilities, fabricate order books, or mutate market prices.

Expand All @@ -125,18 +125,21 @@ ProofCast is engineered natively around the technical architecture of the **Somn

### 4.4 Cryptographic EIP-712 Receipts & Evidence Hashing
* **EIP-712 Typed Signing**: Standardized typed data verification (`domain: { name: "ProofCast", chainId: 50312 }`, primary type: `ForecastCommitment`) verified via `viem`.
* **SHA-256 Digest**: Freezes market snapshot, probability, direction, thesis, counter-thesis, and timestamp into an immutable 32-byte hash.
* **Parent-Linked Revisions**: Historical forecasts are never overwritten; updates create parent-linked `forecast_revisions`.
* **SHA-256 Commitment Digest**: At commit time the server freezes the market evidence (mid, best bid, best ask, snapshot timestamp) together with the probability, direction, confidence, thesis, counter-thesis, and signer into an immutable 32-byte digest, stored as `decision_receipts.commitmentHash`.
* **Pre-Settlement Anchoring**: The commitment digest — not the post-resolution evidence hash — is the value anchored on Somnia. Because it is computed before the outcome is known, the anchor proves what was believed *prior* to settlement, which is the entire claim the product rests on.
* **Parent-Linked Revisions**: Historical forecasts are never overwritten; updates create parent-linked `forecast_revisions`. The anchored digest continues to represent the original commitment.

### 4.5 Somnia Smart Contract (`ProofCastAnchor.sol`) & Staking
* **Non-Custodial Anchoring**: Stores `(receiptHash, marketId, timestamp, owner, stakeAmount)`.
* **Payable $SOM Staking**: `anchorReceiptWithStake()` allows forecasters to stake native Somnia tokens behind high-conviction predictions.
* **Payable STT Staking**: `anchorReceiptWithStake()` lets forecasters stake native Somnia tokens behind high-conviction predictions. The stake amount is chosen when the forecast is committed and is transferred on-chain at anchor time.
* **Server-Verified Stakes**: A client-reported transaction hash is only a claim. The server re-reads the mined transaction from a Somnia RPC and confirms it succeeded, targeted the anchor contract, and came from the claiming address. The recorded stake is always the value observed on-chain, never the amount requested — an intended-but-unpaid stake stays at `NONE` and is never settled.
* **Soulbound Forecaster Badges**: Registers verified reputation tiers (`GOLD_MASTER`, `SILVER`, `BRONZE`, `UNRANKED`) directly on-chain.

### 4.6 Automated Resolution Worker & Oracle Webhooks
* **Automated Daemon Worker**: `server/resolutionWorker.ts` polls on-chain DreamDEX settlement status every 60 seconds without manual bottlenecks.
* **Oracle Webhook**: `POST /api/oracle/resolve` enables sub-second settlement for UMA / Chainlink oracles with `ORACLE_WEBHOOK_SECRET` protection.
* **Automated Stake Resolution**: Automatically transitions stakes to `WON`, `LOST`, or `REFUNDED`.
* **Oracle Webhook**: `POST /api/oracle/resolve` enables sub-second settlement for UMA / Chainlink oracles. The endpoint **fails closed**: without `ORACLE_WEBHOOK_SECRET` configured it returns `503` rather than accepting anonymous settlement.
* **Conclusive Settlements Only**: A binary pool settles at `>= 99%` (YES) or `<= 1%` (NO). Any price in between means the market expired without a conclusive print and is recorded `VOID`, which is excluded from calibration scoring. Outcomes are never guessed.
* **Automated Stake Resolution**: Transitions on-chain-verified stakes to `WON`, `LOST`, or `REFUNDED`.

### 4.7 Brier Calibration & Soulbound Reputation Tiers
* **Strict Brier Scoring**: Dimensionless $BS = (f - o)^2 \in [0, 1]$, where $0.000$ represents perfection.
Expand Down Expand Up @@ -217,23 +220,28 @@ graph TD
* **Deployment Tx**: [`0x9f85fdde97a1149000f0ae4230daea2908c1d123701ff20c393d85a6b1031e46`](https://shannon-explorer.somnia.network/tx/0x9f85fdde97a1149000f0ae4230daea2908c1d123701ff20c393d85a6b1031e46)
* **Solidity Version**: `^0.8.20`
* **Target Network**: Somnia Shannon Testnet (`Chain ID: 50312`)
* **Currency**: Native `STT` / `$SOM`
* **Currency**: Native `STT`

### Core Smart Contract Interfaces

```solidity
// Anchor a cryptographic decision receipt hash
function anchorReceipt(bytes32 receiptHash, string calldata marketId) external;

// Anchor a decision receipt with payable native $SOM stake
// Anchor a decision receipt with a payable native STT stake
function anchorReceiptWithStake(bytes32 receiptHash, string calldata marketId) external payable;

// Record a verified forecaster soulbound reputation badge
function recordForecasterBadge(address forecaster, string calldata tier, uint256 brierScoreBps) external;
// Record a verified forecaster soulbound reputation badge (admin only)
function recordForecasterBadge(
address forecaster,
uint8 tier,
uint256 brierScoreBps,
uint256 verifiedCount
) external;

// Retrieve an anchored receipt
function getReceipt(bytes32 receiptHash) external view returns (
bytes32 hash,
// Verify whether a receipt hash has been anchored
function verifyAnchor(bytes32 receiptHash) external view returns (
bool isAnchored,
string memory marketId,
uint256 timestamp,
address owner,
Expand All @@ -242,10 +250,10 @@ function getReceipt(bytes32 receiptHash) external view returns (

// Retrieve a forecaster's soulbound badge
function getForecasterBadge(address forecaster) external view returns (
string memory tier,
uint8 tier,
uint256 brierScoreBps,
uint256 totalAnchored,
uint256 lastUpdated
uint256 verifiedCount,
uint256 updatedAt
);
```

Expand Down Expand Up @@ -307,14 +315,15 @@ pnpm build
✓ server/dreamdex.test.ts (3 tests)
✓ server/eventforge.multimodel.test.ts (5 tests)
✓ server/eventforge.test.ts (7 tests)
✓ server/integrity.test.ts (18 tests)
✓ server/oracle.staking.test.ts (5 tests)
✓ server/receipts.test.ts (16 tests)
✓ server/scoring.api.test.ts (1 test)
✓ server/scoring.integration.test.ts (1 test)
✓ client/src/lib/comparisonMotion.test.ts (3 tests)

Test Files 9 passed (9)
Tests 42 passed (42)
Test Files 10 passed (10)
Tests 60 passed (60)
TypeScript 0 diagnostics (tsc --noEmit clean)
Production Clean bundle build successful
```
Expand All @@ -328,7 +337,9 @@ pnpm build
| **Zero Private Key Custody** | All transactions and EIP-712 hashes are signed in the browser wallet via RainbowKit / Viem. The server never handles private keys. |
| **Model Invariance** | Layer A deterministic calculations are mathematically isolated from LLM output. AI cannot modify probabilities or prices. |
| **Cryptographic Immutability** | Commitments produce a SHA-256 digest anchored permanently to [ProofCastAnchor.sol](contracts/ProofCastAnchor.sol) on Somnia. |
| **Webhook Authentication** | `POST /api/oracle/resolve` requires valid `ORACLE_WEBHOOK_SECRET` authentication to prevent unauthorized resolution spoofing. |
| **Webhook Authentication** | `POST /api/oracle/resolve` requires a valid `ORACLE_WEBHOOK_SECRET`. The endpoint **fails closed** — if no secret is configured it returns `503` rather than accepting anonymous settlement. |
| **Verified On-Chain Anchors** | A client-reported anchor transaction is re-read from a Somnia RPC before anything is recorded: it must have succeeded, targeted the anchor contract, been sent by the claiming address, and its calldata must contain exactly this receipt's commitment digest. Stakes are credited from the value observed on-chain, never from the client's request. |
| **No Fabricated Outcomes** | Receipts resolve only on a conclusive binary settlement print (`>= 99%` YES / `<= 1%` NO). Inconclusive expiries are recorded `VOID` and excluded from calibration scoring. |
| **Duplicate Prevention** | `ProofCastAnchor.sol` reverts with `AlreadyAnchored` on replay attempts for previously anchored receipt hashes. |

---
Expand Down
29 changes: 25 additions & 4 deletions client/src/lib/web3/somnia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ export const PROOFCAST_ANCHOR_ABI = [
outputs: [],
stateMutability: "nonpayable",
},
{
type: "function",
name: "anchorReceiptWithStake",
inputs: [
{ name: "receiptHash", type: "bytes32" },
{ name: "marketId", type: "string" },
],
outputs: [],
stateMutability: "payable",
},
{
type: "function",
name: "verifyAnchor",
Expand Down Expand Up @@ -102,10 +112,18 @@ export async function switchOrAddSomniaShannon(): Promise<void> {
}
}

/**
* Anchors a receipt hash on Somnia. When `stakeWei` is greater than zero the
* payable `anchorReceiptWithStake` entrypoint is used and the stake is actually
* transferred with the transaction; the server independently re-reads the mined
* transaction and only credits a stake it can confirm on-chain.
*/
export async function anchorReceiptToSomniaChain(
receiptHash: string,
marketId: string,
): Promise<{ txHash: string; callerAddress: string }> {
stakeWei: bigint = 0n,
): Promise<{ txHash: string; callerAddress: string; stakeWei: string }> {
if (stakeWei < 0n) throw new Error("Stake amount cannot be negative.");
const address = await connectBrowserWallet();
await switchOrAddSomniaShannon();

Expand All @@ -118,10 +136,12 @@ export async function anchorReceiptToSomniaChain(
throw new Error(`Invalid 32-byte receipt hash format (expected 66 characters with 0x prefix, got ${formattedHash.length})`);
}

// Encode anchorReceipt(bytes32,string) function call data
const isStaking = stakeWei > 0n;

// Encode anchorReceipt(bytes32,string) / anchorReceiptWithStake(bytes32,string)
const calldata = encodeFunctionData({
abi: PROOFCAST_ANCHOR_ABI,
functionName: "anchorReceipt",
functionName: isStaking ? "anchorReceiptWithStake" : "anchorReceipt",
args: [formattedHash as `0x${string}`, marketId || "SOMNIA_EVENT_MARKET"],
});

Expand All @@ -134,11 +154,12 @@ export async function anchorReceiptToSomniaChain(
from: address,
to: PROOFCAST_ANCHOR_CONTRACT,
data: calldata,
...(isStaking ? { value: `0x${stakeWei.toString(16)}` } : {}),
},
],
})) as string;

return { txHash, callerAddress: address };
return { txHash, callerAddress: address, stakeWei: stakeWei.toString() };
} catch (err: any) {
throw new Error(translateWeb3Error(err));
}
Expand Down
29 changes: 21 additions & 8 deletions client/src/pages/Leaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ import { ModelArenaCard } from "@/components/ModelArenaCard";
import { trpc } from "@/lib/trpc";
import type { LeaderboardBadge } from "../../../server/receipts";

// Mirrors SYNTHETIC_BENCHMARK_NOTICE in server/eventforge/benchmark.ts. Declared
// here rather than imported so the arena fixture module (and the model providers
// it pulls in) never reaches the client bundle. Keep the two strings in sync.
const SYNTHETIC_BENCHMARK_NOTICE =
"Demonstration fixture — illustrative scenarios, not resolved DreamDEX contracts. These rankings show how the scoring mechanism works, not measured model performance.";

function renderBadge(badge: LeaderboardBadge) {
switch (badge) {
case "SHANNON_ANCHORED":
Expand Down Expand Up @@ -152,13 +158,17 @@ export default function Leaderboard() {
<div className="pi-hero-instrument-inner">
<div className="flex items-center justify-between border-b border-white/10 pb-3">
<span className="font-mono text-xs font-bold text-white uppercase tracking-wider">
{activeTab === "ARENA" ? "AI Arena Invariant" : "Integrity Invariant"}
{activeTab === "ARENA" ? "AI Arena Scoring Method" : "Integrity Invariant"}
</span>
<StatusChip tone="live">VERIFIED</StatusChip>
{activeTab === "ARENA" ? (
<StatusChip tone="watch">DEMO DATA</StatusChip>
) : (
<StatusChip tone="live">VERIFIED</StatusChip>
)}
</div>
<p className="mt-3 text-xs leading-relaxed text-[#8b96a8]">
{activeTab === "ARENA"
? "Every AI model forecast is frozen prior to event settlement. Brier scores cannot be retroactively smoothed."
? "Arena rankings run on a demonstration fixture, not resolved DreamDEX contracts. They illustrate how Brier scoring separates models once live forecasts accumulate."
: "Every score reflects immutable SHA-256 Decision Receipts anchored on Somnia. Forecast history cannot be retroactively altered."}
</p>
</div>
Expand Down Expand Up @@ -191,8 +201,8 @@ export default function Leaderboard() {
>
<Cpu size={16} />
AI Model Arena (5 Models)
<span className="rounded bg-[#38bdf8]/20 px-1.5 py-0.5 text-[10px] font-bold text-[#38bdf8]">
LIVE
<span className="rounded bg-[#f5a524]/20 px-1.5 py-0.5 text-[10px] font-bold text-[#f5a524]">
DEMO
</span>
</button>
</section>
Expand Down Expand Up @@ -400,14 +410,17 @@ export default function Leaderboard() {
<div className="rounded-2xl border border-[#38bdf8]/30 bg-gradient-to-r from-[#38bdf8]/10 via-[#0d121c] to-[#080b10] p-6 shadow-xl">
<div className="flex flex-wrap items-center justify-between gap-4">
<div>
<div className="flex items-center gap-2 font-mono text-xs font-bold uppercase tracking-wider text-[#38bdf8]">
<BrainCircuit size={16} /> Empirical Model Evaluation
<div className="flex items-center gap-2 font-mono text-xs font-bold uppercase tracking-wider text-[#f5a524]">
<BrainCircuit size={16} /> Scoring Method Demonstration
</div>
<h2 className="mt-1 text-xl font-bold text-white sm:text-2xl">
EventForge AI Model Leaderboard
</h2>
<p className="mt-1 text-xs text-[#8b96a8] max-w-2xl">
Models are ranked strictly by mathematical Brier score calibration ($BS = (f - o)^2$) across resolved Somnia DreamDEX binary contracts. Lower Brier score reflects superior probability calibration.
Models are ranked by Brier score calibration ($BS = (f - o)^2$); a lower score reflects superior probability calibration.
</p>
<p className="mt-2 rounded-lg border border-[#f5a524]/30 bg-[#f5a524]/10 px-3 py-2 text-xs text-[#f5a524] max-w-2xl">
{SYNTHETIC_BENCHMARK_NOTICE}
</p>
</div>

Expand Down
Loading
Loading