Skip to content

Repository files navigation

Predict-IO Logo

Predict-IO 🔮

🎯 The problem this project solves

Traditional prediction markets often suffer from a lack of transparency, high fees, and centralized resolutions that are open to manipulation. Predict-IO addresses this by being a genuinely trustless, decentralized platform built on the Stellar (Soroban) network. It lets anyone create, trade, and settle prediction markets automatically and securely, using an on-chain oracle (Reflector Network) so that outcomes cannot be tampered with by a central party. All prices are handled as fixed-point integers (i128) rather than floating-point numbers, so micro-fluctuations in asset prices are compared without rounding loss.

🛠 Technologies used

📦 Repository layout

contracts/    Soroban smart contracts (Cargo workspace rooted at ./Cargo.toml)
  prediction_market/             operator-settled markets, N outcomes, parimutuel
  reflector_prediction_market/   oracle-settled binary markets (Up/Down/Draw)
  reflector_mock/                mock SEP-40 oracle for local and testnet runs
backend/      Fastify API + Prisma indexer + settlement cron worker
frontend/     Next.js app (market discovery, trading, admin dashboard)

🚀 How to run or access the project

Prerequisites

  • Node.js 20+ and pnpm
  • Rust 1.80+ with the wasm32v1-none target (rustup target add wasm32v1-none)
  • stellar-cli (cargo install --locked stellar-cli)
  • A PostgreSQL database — either a Supabase project or a local instance (Docker works)

Local installation and setup

  1. Clone the repository:

    git clone https://github.com/jorgesoares2997/predict_io
    cd predict_io
  2. Build and deploy the smart contracts: Run the tests, then build and deploy to the Stellar Testnet.

    cargo test                                    # from the repo root
    cd contracts/prediction_market
    stellar contract build --optimize
    stellar contract deploy \
      --wasm ../../target/wasm32v1-none/release/prediction_market.wasm \
      --source admin --network testnet

    Save the returned contract ID — both the backend and the frontend need it. Deploy reflector_prediction_market the same way if you want oracle-settled markets, and call its init function once after deploying. See contracts/prediction_market/DEPLOYMENT_GUIDE.md for the full walkthrough, including identity creation and CLI invocation examples.

    Note: the two market contracts are not interchangeable — they expose different create_market and place_bet signatures. Whichever WASM you deploy at the address you configure as MARKET_CONTRACT_ADDRESS determines which market type the platform can create.

  3. Set up the backend:

    cd backend
    pnpm install          # runs `prisma generate` automatically
    cp .env.example .env  # then fill in the values described below

    Point DATABASE_URL at your database, set JWT_SECRET, and fill in the Stellar settings (STELLAR_HORIZON_URL, STELLAR_NETWORK_PASSPHRASE, SOROBAN_RPC_URL), the contract addresses (MARKET_CONTRACT_ADDRESS, USDC_CONTRACT_ADDRESS, REFLECTOR_CONTRACT_ID), and the operator keypair (OPERATOR_PUBLIC_KEY, OPERATOR_SECRET_KEY). .env.example documents every variable, including the testnet and mainnet values for each network setting. Set ENABLE_KYC=false in development to skip identity verification.

    If you are using Supabase, link the project first:

    npx supabase login
    npx supabase link --project-ref <YOUR_PROJECT_REF>

    Apply the schema, seed it, and start the server:

    npx prisma db push
    pnpm seed
    pnpm dev
  4. Set up the frontend:

    cd frontend
    pnpm install
    pnpm dev

    The frontend reads its configuration from frontend/.env.local (no template is committed, so create the file yourself):

    NEXT_PUBLIC_API_URL=http://localhost:8080/api
    NEXT_PUBLIC_HORIZON_URL=https://horizon-testnet.stellar.org
    NEXT_PUBLIC_SOROBAN_RPC_URL=https://soroban-testnet.stellar.org
    NEXT_PUBLIC_NETWORK_PASSPHRASE=Test SDF Network ; September 2015
    NEXT_PUBLIC_MARKET_CONTRACT_ID=<your market contract id>
    NEXT_PUBLIC_USDC_CONTRACT_ID=<USDC SAC address>
    NEXT_PUBLIC_REFLECTOR_CONTRACT_ID=<Reflector oracle contract id>
    # Optional — only needed to exercise the KYC flow
    NEXT_PUBLIC_DIDIT_VERIFICATION_URL=

    Make sure NEXT_PUBLIC_API_URL matches the port the backend actually listens on (PORT in backend/.env), and that the network passphrase and contract IDs match the backend's — otherwise transactions will be simulated against the wrong network. Then open http://localhost:3000.

    workthought at the repo root is a step-by-step manual test script covering every major UI flow end to end.

✨ Main features

  • Dual market types: standard markets, settled by the operator from an external resolution_source, and oracle markets, settled entirely on-chain against the Reflector Network.
  • Trustless oracle settlement: settle_market on the oracle contract takes only a market ID — no winning-outcome argument and no admin authorization. The contract queries Reflector itself, prefers the historical price at the market's end_time, rejects stale data, and derives the winner from the market's stored target price and comparison operator. The backend cron can only trigger settlement; it cannot influence the result.
  • Precise price handling: oracle prices are stored and compared as fixed-point i128 integers with per-asset decimal precision, and never pass through JavaScript floating-point numbers, so tiny price differences don't collapse into false ties.
  • Refunds on draws: if an oracle market closes exactly at its reference price, the contract marks the outcome as a draw and claim returns each participant's full stake from both sides of the pool.
  • Custody stays with the contract: the backend never holds user keys. Trades and claims use a prepare → sign → execute flow — the API returns an unsigned transaction, the user's wallet signs it, and the API verifies that the returned transaction hash matches the one it prepared before submitting.
  • Parimutuel payouts: winnings are computed on-chain as stake × total_pool / winning_pool using integer arithmetic, with an optional protocol fee in basis points on oracle markets.
  • Workers and indexing: a cron worker locks markets whose betting window has closed, resolves those past their liquidation time, and retries on-chain settlement until it succeeds. The database acts as a fast read cache over on-chain state.
  • Admin dashboard and KYC: an admin area for creating and managing markets and categories, plus optional identity verification through DIDIT, gated behind the ENABLE_KYC flag.

🧠 Technical decisions

  • Clean architecture (DDD): the backend is organized into presentation, application, and infrastructure layers, with use cases depending on port interfaces rather than concrete implementations. Everything is wired explicitly in src/server.ts, which keeps the API testable and makes it straightforward to swap out the Stellar or persistence layer.
  • Real decentralization with Reflector: in production the system reads consensus prices from the official Reflector network through the SEP-40 interface (Asset::Stellar(Address) / Asset::Other(Symbol)), so market data is not sourced from a single trusted party. A mock oracle contract is included for local development.
  • Resolution that degrades safely: if on-chain settlement fails, the database still records the resolution so the UI stays accurate, and the worker keeps retrying settlement on every tick — both settle_market and create_market are idempotent. If the price fetch fails, the market deliberately stays locked instead of resolving on incomplete data.
  • Separation of responsibilities: the frontend handles discovery and transaction signing, the backend acts as a fast gateway and indexer, and the blockchain holds all funds and business logic in immutable contracts.
  • Wallet-signature authentication: login proves key ownership instead of relying on a password. Because wallets sign transactions rather than arbitrary bytes, the challenge is embedded as a hash in a manageData operation, verified server-side, and exchanged for a JWT.

📸 Screenshots, deploy, and usage examples

Application screenshots

Home page

Home page

Admin dashboard

Admin dashboard

Market creation form

Market creation form

🔮 Next steps and improvements

  • Multiple oracle integrations: extend support to other oracle networks in the Stellar ecosystem to cover non-crypto markets.
  • User analytics dashboard: build a richer dashboard so users can track their betting history, PnL, and statistics.
  • Liquidity provision: incentivize liquidity providers to seed markets with more capital, reducing slippage for bettors.
  • Decentralized governance (DAO): introduce governance tokens so the community can decide platform fees and new market types.
  • Automated backend tests: the contracts have a Rust test suite, but the API currently has no test runner configured.

📄 License

Released under the MIT License — you are free to use, copy, modify, and distribute this project, including commercially, as long as the copyright notice is preserved.

The software is provided "as is", without warranty of any kind. Prediction markets that handle real funds are regulated differently across jurisdictions; if you deploy this to mainnet, that compliance is your responsibility.

About

Decentralized prediction market on Stellar (Soroban), settled on-chain by the Reflector oracle. Rust contracts + Fastify API + Next.js.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages