Source Code

(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-found (err u101))
(define-constant err-unauthorized (err u102))
(define-constant err-already-exists (err u103))
(define-constant err-invalid-amount (err u104))
(define-constant err-market-closed (err u105))
(define-constant err-market-not-resolved (err u106))

(define-data-var market-nonce uint u0)

(define-map prediction-markets
  uint
  {
    creator: principal,
    question: (string-ascii 100),
    description-hash: (buff 32),
    resolution-source-hash: (buff 32),
    total-yes-stakes: uint,
    total-no-stakes: uint,
    resolution-deadline: uint,
    resolved: bool,
    outcome: (optional bool),
    resolved-at: (optional uint)
  }
)

(define-map user-positions
  {market-id: uint, user: principal}
  {
    yes-stake: uint,
    no-stake: uint,
    claimed: bool
  }
)

(define-map creator-markets principal (list 50 uint))
(define-map market-participants uint (list 500 uint))

(define-public (create-market (question (string-ascii 100)) (description-hash (buff 32)) (resolution-source-hash (buff 32)) (resolution-deadline uint))
  (let
    (
      (market-id (+ (var-get market-nonce) u1))
    )
    (asserts! (> resolution-deadline stacks-block-height) err-invalid-amount)
    (map-set prediction-markets market-id
      {
        creator: tx-sender,
        question: question,
        description-hash: description-hash,
        resolution-source-hash: resolution-source-hash,
        total-yes-stakes: u0,
        total-no-stakes: u0,
        resolution-deadline: resolution-deadline,
        resolved: false,
        outcome: none,
        resolved-at: none
      }
    )
    (map-set creator-markets tx-sender
      (unwrap-panic (as-max-len? (append (default-to (list) (map-get? creator-markets tx-sender)) market-id) u50)))
    (var-set market-nonce market-id)
    (ok market-id)
  )
)

(define-public (stake-yes (market-id uint) (amount uint))
  (let
    (
      (market (unwrap! (map-get? prediction-markets market-id) err-not-found))
      (position (default-to {yes-stake: u0, no-stake: u0, claimed: false} (map-get? user-positions {market-id: market-id, user: tx-sender})))
    )
    (asserts! (not (get resolved market)) err-market-closed)
    (asserts! (< stacks-block-height (get resolution-deadline market)) err-market-closed)
    (asserts! (> amount u0) err-invalid-amount)
    (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
    (map-set user-positions {market-id: market-id, user: tx-sender}
      (merge position {yes-stake: (+ (get yes-stake position) amount)}))
    (map-set prediction-markets market-id (merge market {
      total-yes-stakes: (+ (get total-yes-stakes market) amount)
    }))
    (ok true)
  )
)

(define-public (stake-no (market-id uint) (amount uint))
  (let
    (
      (market (unwrap! (map-get? prediction-markets market-id) err-not-found))
      (position (default-to {yes-stake: u0, no-stake: u0, claimed: false} (map-get? user-positions {market-id: market-id, user: tx-sender})))
    )
    (asserts! (not (get resolved market)) err-market-closed)
    (asserts! (< stacks-block-height (get resolution-deadline market)) err-market-closed)
    (asserts! (> amount u0) err-invalid-amount)
    (try! (stx-transfer? amount tx-sender (as-contract tx-sender)))
    (map-set user-positions {market-id: market-id, user: tx-sender}
      (merge position {no-stake: (+ (get no-stake position) amount)}))
    (map-set prediction-markets market-id (merge market {
      total-no-stakes: (+ (get total-no-stakes market) amount)
    }))
    (ok true)
  )
)

(define-public (resolve-market (market-id uint) (outcome bool))
  (let
    (
      (market (unwrap! (map-get? prediction-markets market-id) err-not-found))
    )
    (asserts! (is-eq tx-sender (get creator market)) err-unauthorized)
    (asserts! (not (get resolved market)) err-already-exists)
    (asserts! (>= stacks-block-height (get resolution-deadline market)) err-market-closed)
    (map-set prediction-markets market-id (merge market {
      resolved: true,
      outcome: (some outcome),
      resolved-at: (some stacks-block-height)
    }))
    (ok true)
  )
)

(define-public (claim-winnings (market-id uint))
  (let
    (
      (market (unwrap! (map-get? prediction-markets market-id) err-not-found))
      (position (unwrap! (map-get? user-positions {market-id: market-id, user: tx-sender}) err-not-found))
      (outcome (unwrap! (get outcome market) err-market-not-resolved))
    )
    (asserts! (get resolved market) err-market-not-resolved)
    (asserts! (not (get claimed position)) err-already-exists)
    (let
      (
        (winning-stake (if outcome (get yes-stake position) (get no-stake position)))
        (total-winning-stakes (if outcome (get total-yes-stakes market) (get total-no-stakes market)))
        (total-pool (+ (get total-yes-stakes market) (get total-no-stakes market)))
        (payout (if (> total-winning-stakes u0) (/ (* winning-stake total-pool) total-winning-stakes) u0))
      )
      (asserts! (> payout u0) err-invalid-amount)
      (try! (as-contract (stx-transfer? payout tx-sender tx-sender)))
      (map-set user-positions {market-id: market-id, user: tx-sender} (merge position {claimed: true}))
      (ok payout)
    )
  )
)

(define-read-only (get-market (market-id uint))
  (ok (map-get? prediction-markets market-id))
)

(define-read-only (get-position (market-id uint) (user principal))
  (ok (map-get? user-positions {market-id: market-id, user: user}))
)

(define-read-only (get-creator-markets (creator principal))
  (ok (map-get? creator-markets creator))
)

(define-read-only (get-market-odds (market-id uint))
  (let
    (
      (market (unwrap! (map-get? prediction-markets market-id) err-not-found))
      (total-pool (+ (get total-yes-stakes market) (get total-no-stakes market)))
    )
    (ok {
      yes-probability: (if (> total-pool u0) (/ (* (get total-yes-stakes market) u10000) total-pool) u0),
      no-probability: (if (> total-pool u0) (/ (* (get total-no-stakes market) u10000) total-pool) u0)
    })
  )
)

Functions (9)

FunctionAccessArgs
create-marketpublicquestion: (string-ascii 100
stake-yespublicmarket-id: uint, amount: uint
stake-nopublicmarket-id: uint, amount: uint
resolve-marketpublicmarket-id: uint, outcome: bool
claim-winningspublicmarket-id: uint
get-marketread-onlymarket-id: uint
get-positionread-onlymarket-id: uint, user: principal
get-creator-marketsread-onlycreator: principal
get-market-oddsread-onlymarket-id: uint