Source Code

;; Simple Reputation Contract - Working Version
;; Tracks user reputation with daily check-ins and contributions

;; Error codes
(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-ALREADY-REGISTERED (err u101))
(define-constant ERR-NOT-REGISTERED (err u102))
(define-constant ERR-ALREADY-CHECKED-IN (err u103))
(define-constant ERR-INVALID-AMOUNT (err u104))

;; Data variables
(define-data-var total-users uint u0)
(define-data-var total-check-ins uint u0)

;; User reputation data
(define-map users
  principal
  {
    score: uint,
    streak: uint,
    last-check-in: uint,
    contributions: uint,
    registered-at: uint
  }
)

;; Contribution tracking
(define-map contributions
  { user: principal, contribution-id: uint }
  {
    description: (string-ascii 256),
    points: uint,
    timestamp: uint
  }
)

;; Read-only functions
(define-read-only (get-user-info (user principal))
  (map-get? users user)
)

(define-read-only (get-total-users)
  (ok (var-get total-users))
)

(define-read-only (get-leaderboard-stats)
  (ok {
    total-users: (var-get total-users),
    total-check-ins: (var-get total-check-ins)
  })
)

;; Public functions
(define-public (register-user)
  (let
    (
      (caller tx-sender)
      (existing-user (map-get? users caller))
    )
    (asserts! (is-none existing-user) ERR-ALREADY-REGISTERED)
    
    (map-set users caller {
      score: u0,
      streak: u0,
      last-check-in: u0,
      contributions: u0,
      registered-at: stacks-block-height
    })
    
    (var-set total-users (+ (var-get total-users) u1))
    (ok true)
  )
)

(define-public (daily-check-in)
  (let
    (
      (caller tx-sender)
      (user-data (unwrap! (map-get? users caller) ERR-NOT-REGISTERED))
      (last-check (get last-check-in user-data))
      (current-streak (get streak user-data))
      (current-score (get score user-data))
    )
    ;; Check if already checked in today (within last 144 blocks ~24h)
    (asserts! (> (- stacks-block-height last-check) u144) ERR-ALREADY-CHECKED-IN)
    
    (let
      (
        ;; Calculate new streak
        (new-streak (if (<= (- stacks-block-height last-check) u288)
                      (+ current-streak u1)  ;; Continue streak
                      u1))                    ;; Reset streak
        ;; Calculate points: 10 base + streak bonus
        (points (+ u10 new-streak))
      )
      
      (map-set users caller (merge user-data {
        score: (+ current-score points),
        streak: new-streak,
        last-check-in: stacks-block-height
      }))
      
      (var-set total-check-ins (+ (var-get total-check-ins) u1))
      (ok points)
    )
  )
)

(define-public (add-contribution (description (string-ascii 256)) (points uint))
  (let
    (
      (caller tx-sender)
      (user-data (unwrap! (map-get? users caller) ERR-NOT-REGISTERED))
      (contribution-id (get contributions user-data))
    )
    (asserts! (> points u0) ERR-INVALID-AMOUNT)
    (asserts! (<= points u100) ERR-INVALID-AMOUNT)
    
    ;; Record contribution
    (map-set contributions 
      { user: caller, contribution-id: contribution-id }
      {
        description: description,
        points: points,
        timestamp: stacks-block-height
      }
    )
    
    ;; Update user stats
    (map-set users caller (merge user-data {
      score: (+ (get score user-data) points),
      contributions: (+ contribution-id u1)
    }))
    
    (ok true)
  )
)

;; Initialize contract
(begin
  (var-set total-users u0)
  (var-set total-check-ins u0)
)

Functions (6)

FunctionAccessArgs
get-user-inforead-onlyuser: principal
get-total-usersread-only
get-leaderboard-statsread-only
register-userpublic
daily-check-inpublic
add-contributionpublicdescription: (string-ascii 256