Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

101 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NullCast — Confidential Prediction Markets

Bet without revealing. Verify without exposing. Markets without manipulation.

NullCast is the first fully confidential prediction market protocol built on Fully Homomorphic Encryption (FHE). Using Zama's FHEVM on Ethereum Sepolia, NullCast enables users to place bets on real-world and crypto outcomes while keeping individual position sizes completely private — even from the protocol itself.

Built for: Zama Developer Program Season 2 — Builder Track


How It Works

User enters bet amount (plaintext)
        │
        ▼
FHEVM SDK encrypts amount client-side
        │
        ▼
Encrypted bet submitted on-chain (euint64)
        │
        ▼
FHE arithmetic updates encrypted pool totals
        │
        ▼
Aggregate odds publicly decryptable ──► UI shows live odds
Individual positions stay private ──► Only owner can decrypt

Key Properties

Property Description
Position Privacy Individual bet amounts are encrypted euint64 values, never publicly visible
Live Odds Aggregate pool totals are publicly decryptable — odds update in real-time
No Front-Running Position sizes hidden, preventing MEV and whale manipulation
Composable Built on ERC-7984 cUSDT, compatible with any FHEVM-native protocol
Permissionless Anyone can create a market via the factory contract
Reputation-Gated Optional encrypted reputation score for market participation

Architecture

nullcast/
├── contracts/              # Hardhat project — Solidity + FHEVM
│   ├── contracts/
│   │   ├── NullCastMarket.sol      # Core FHE prediction market
│   │   ├── NullCastFactory.sol     # Permissionless market creation
│   │   ├── LiquidityPool.sol       # LP deposits + fee distribution
│   │   ├── OracleMock.sol          # Sepolia demo resolution
│   │   ├── ReputationGate.sol      # Encrypted reputation scores
│   │   ├── interfaces/
│   │   └── mocks/
│   ├── test/                       # 79 tests (unit + integration)
│   ├── scripts/                    # Deploy + seed scripts
│   └── deployments/sepolia.json    # Deployed addresses
│
├── client/                 # Next.js 14 frontend
│   └── src/
│       ├── app/                    # App Router pages
│       ├── components/             # UI components
│       ├── hooks/                  # wagmi contract hooks
│       ├── lib/                    # Config, contracts, store
│       ├── constants/              # ABIs + addresses
│       └── styles/                 # Design system CSS
│
└── SPEC.md                 # Full technical specification

Deployed Contracts (Sepolia)

Contract Address Etherscan
MockcUSDT 0x904793F739dA238686078dDC477CeFD2a071F9F9 View
OracleMock 0xF01fa4F99146A938633E06DC8C9B3CE72778a846 View
ReputationGate 0xfC448A571c0bEBf9B7f5AfA2bac89137F460DeA8 View
NullCastFactory 0x5BEe5fae827Cebd188A44C55d89323693888c059 View
VaultFactory 0xe3675c64a72eFc3e89105B826720Eb7E9f956f8a View

Each market gets a paired LiquidityPool. Each vault is deployed by VaultFactory.

Demo Markets

Market Address Type Category
BTC above $90k on Apr 30? 0xF539719aDf6646D0842aCECbBB1190EeecAE8F55 Binary CRYPTO
ETH above $2k on May 5? 0xA04f5885f979FC925487327aA027C7781BEec5BC Binary CRYPTO
BTC price range May 10 0xf2848303d93149dF0113fE8c13404A07b99924C4 Scalar (3 buckets) CRYPTO

Quick Start

Prerequisites

  • Node.js >= 20
  • npm >= 7
  • Sepolia ETH (for contract interaction)

Contracts

cd contracts
npm install
cp .env.example .env    # Fill in your Sepolia RPC, private key, Etherscan key

# Run tests (79 passing)
npx hardhat test

# Deploy to Sepolia
npx hardhat run scripts/deploy.ts --network sepolia

# Seed demo markets
npx hardhat run scripts/createDemoMarkets.ts --network sepolia

# Keeper: update odds (run periodically)
npx hardhat run scripts/oddsKeeper.ts --network sepolia

# Keeper: compute reputation scores (run per epoch)
npx hardhat run scripts/computeScores.ts --network sepolia

Frontend

cd client
npm install

# Create .env.local with:
# NEXT_PUBLIC_WALLET_CONNECT_ID=your_walletconnect_project_id
# NEXT_PUBLIC_SEPOLIA_RPC_URL=your_sepolia_rpc_url

npm run dev    # http://localhost:3000

Smart Contract Design

FHE Privacy Model

ENCRYPTED (never publicly readable):
├── userPositions[address]     — individual bet amounts (euint64)
├── userWinnings[address]      — individual payout amounts (euint64)
├── lpShares[address]          — individual LP positions (euint64)
└── reputationScore[address]   — individual reputation (euint8)

PUBLICLY DECRYPTABLE (aggregate, revealed via makePubliclyDecryptable):
├── totalYesPool               — sum of all YES bets (euint64)
├── totalNoPool                — sum of all NO bets (euint64)
└── totalLiquidity             — sum of all LP deposits (euint64)

ACL Permission Pattern

Every encrypted value follows strict access control:

// User places a bet:
FHE.allowThis(userPositions[msg.sender]);     // contract can compute
FHE.allow(userPositions[msg.sender], msg.sender); // user can decrypt

// Pool totals — anyone can request decryption:
FHE.makePubliclyDecryptable(totalYesPool);

Market Types & Categories

  • Binary — YES/NO outcome, two pools
  • Scalar — Multiple buckets (e.g., price ranges), N pools
  • Categories — Markets tagged with bytes32 category (CRYPTO, MACRO, EQUITY, SPORTS, TECH). Filterable on the frontend.

Market Lifecycle

createMarket() → OPEN → placeBet() → EXPIRED → resolveMarket() → RESOLVED → claimWinnings()

Reputation & Tiers

Reputation scores are encrypted on-chain (euint8, 0-100), computed by a protocol keeper from on-chain signals:

Input Weight Source
Wallet age 40pts max Block history
Transaction count 40pts max On-chain activity
NullCast participation 20pts max Auto-tracked per bet

Scores decay at 5 points per 7-day epoch of inactivity.

Tier system — derived from threshold checks (meetsThreshold), not the raw score:

Tier Threshold Access
Oracle ≥ 80 Top-tier markets, max position sizes
Strategist ≥ 60 High-stakes markets
Analyst ≥ 40 Standard markets
Explorer ≥ 20 Basic markets

Anyone can verify a user's tier via meetsThreshold(user, threshold) → returns encrypted boolean. The actual score is never revealed publicly — only the user can decrypt it.

Participation is tracked automatically: NullCastMarket.placeBet() calls ReputationGate.recordParticipation() on every bet.

Keeper script: npx hardhat run scripts/computeScores.ts --network sepolia computes scores for active users per epoch.

Dispute Resolution

Markets have a 24-hour dispute window (~7200 blocks) after resolution:

  1. Oracle resolves the market
  2. 7200-block window opens — anyone can call raiseDispute() with a bond
  3. If disputed, claims are frozen pending owner review
  4. Owner calls resolveDispute(upheld, newOutcome) — if upheld, outcome is reversed and a new window opens; if rejected, original outcome stands
  5. After window closes with no dispute, winners can claim

Strategy Vaults (Copy-Trading)

Managers create vaults, followers deposit cUSDT:

  • Manager creates a vault with a name, required reputation tier, and performance fee
  • Followers deposit encrypted cUSDT into the vault
  • Manager places bets from vault funds across markets
  • Individual allocations are encrypted — followers see the vault's aggregate performance
  • Vault closure: manager closes the vault, followers withdraw

Contracts: StrategyVault.sol + VaultFactory.sol


Test Coverage

122 tests passing across 9 test files:

  NullCastMarket:  39 tests — placeBet, odds, resolve, claim, dispute, categories, admin
  NullCastFactory: 17 tests — creation, LP pool deployment, validation, admin
  LiquidityPool:    8 tests — deposits, withdrawal, LP tracking
  OracleMock:       7 tests — registration, resolution
  ReputationGate:  13 tests — scoring, decay, threshold
  StrategyVault:   17 tests — deposit, withdraw, close, access control
  VaultFactory:    10 tests — create, registry, views
  Integration:      2 tests — full YES/NO lifecycle with factory + oracle
  Scalar:           4 tests — bucket betting, validation

Tech Stack

Smart Contracts

Tool Purpose
Solidity ^0.8.24 Contract language
Hardhat 2.x Development framework
@fhevm/solidity 0.11 FHE types + operations
@fhevm/hardhat-plugin Local mock FHEVM for testing
OpenZeppelin 5.x Pausable, Ownable, ReentrancyGuard

Frontend

Tool Purpose
Next.js 14 React framework (App Router)
TypeScript Type safety
Tailwind CSS Utility styling
RainbowKit Wallet connection
wagmi v2 + viem Ethereum interaction
@zama-fhe/sdk Client-side FHE encryption + user decryption
Zustand State management (persisted to localStorage)

Known Limitations

  • Mock oracle — single EOA resolution for demo (production: Chainlink/UMA)
  • Gas costs — FHE operations are expensive (~500k-2M gas per bet)
  • Async odds — 5-15 second delay between bet and odds update
  • No dispute mechanism — resolution is final once submitted

See SPEC.md for the full technical specification including future work and production oracle design.


License

MIT


NullCast — Confidential Finance Track | Builder Track Deployed on Ethereum Sepolia | Powered by Zama FHEVM

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages