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-already-forecasted (err u102))
(define-constant err-invalid-probability (err u103))

(define-map forecasters
  principal
  {
    total-forecasts: uint,
    correct-forecasts: uint,
    accuracy-score: uint,
    reputation: uint
  })

(define-map forecast-questions
  uint
  {
    question: (string-ascii 256),
    resolution-block: uint,
    resolved: bool,
    actual-outcome: bool,
    total-forecasts: uint
  })

(define-map user-forecasts
  {question-id: uint, forecaster: principal}
  {predicted-probability: uint, confidence: uint, timestamp: uint})

(define-data-var next-question-id uint u0)

(define-read-only (get-forecaster (user principal))
  (ok (map-get? forecasters user)))

(define-read-only (get-question (question-id uint))
  (ok (map-get? forecast-questions question-id)))

(define-read-only (get-forecast (question-id uint) (forecaster principal))
  (ok (map-get? user-forecasts {question-id: question-id, forecaster: forecaster})))

(define-public (create-question (question (string-ascii 256)) (resolution-time uint))
  (let ((question-id (var-get next-question-id)))
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (map-set forecast-questions question-id
      {question: question, resolution-block: (+ stacks-block-height resolution-time),
       resolved: false, actual-outcome: false, total-forecasts: u0})
    (var-set next-question-id (+ question-id u1))
    (ok question-id)))

(define-public (submit-forecast (question-id uint) (probability uint) (confidence uint))
  (let ((question (unwrap! (map-get? forecast-questions question-id) err-not-found)))
    (asserts! (<= probability u100) err-invalid-probability)
    (asserts! (< stacks-block-height (get resolution-block question)) err-not-found)
    (asserts! (is-none (map-get? user-forecasts {question-id: question-id, forecaster: tx-sender})) err-already-forecasted)
    (map-set user-forecasts {question-id: question-id, forecaster: tx-sender}
      {predicted-probability: probability, confidence: confidence, timestamp: stacks-block-height})
    (map-set forecast-questions question-id
      (merge question {total-forecasts: (+ (get total-forecasts question) u1)}))
    (ok true)))

(define-public (resolve-question (question-id uint) (outcome bool))
  (let ((question (unwrap! (map-get? forecast-questions question-id) err-not-found)))
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (asserts! (>= stacks-block-height (get resolution-block question)) err-not-found)
    (ok (map-set forecast-questions question-id
      (merge question {resolved: true, actual-outcome: outcome})))))

(define-public (update-forecaster-score (forecaster principal) (was-correct bool))
  (let ((info (default-to {total-forecasts: u0, correct-forecasts: u0, accuracy-score: u0, reputation: u0}
                          (map-get? forecasters forecaster)))
        (new-correct (if was-correct (+ (get correct-forecasts info) u1) (get correct-forecasts info)))
        (new-total (+ (get total-forecasts info) u1))
        (new-accuracy (/ (* new-correct u100) new-total)))
    (asserts! (is-eq tx-sender contract-owner) err-owner-only)
    (ok (map-set forecasters forecaster
      {total-forecasts: new-total, correct-forecasts: new-correct,
       accuracy-score: new-accuracy, reputation: new-accuracy}))))

Functions (7)

FunctionAccessArgs
get-forecasterread-onlyuser: principal
get-questionread-onlyquestion-id: uint
get-forecastread-onlyquestion-id: uint, forecaster: principal
create-questionpublicquestion: (string-ascii 256
submit-forecastpublicquestion-id: uint, probability: uint, confidence: uint
resolve-questionpublicquestion-id: uint, outcome: bool
update-forecaster-scorepublicforecaster: principal, was-correct: bool