Source Code

;; Progressive Achievement & Badge System
;; Award badges for milestones and track user progression

(define-constant CONTRACT_OWNER tx-sender)
(define-constant ERR_NOT_AUTHORIZED (err u401))
(define-constant ERR_BADGE_EXISTS (err u402))
(define-constant ERR_ALREADY_EARNED (err u403))
(define-constant ERR_REQUIREMENTS_NOT_MET (err u404))

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

(define-map badges
    uint ;; badge-id
    {
        name: (string-utf8 64),
        description: (string-utf8 256),
        requirement-type: (string-ascii 32),
        requirement-value: uint,
        total-awarded: uint,
        active: bool
    }
)

(define-map user-badges
    {user: principal, badge-id: uint}
    {earned-at: uint, progress: uint}
)

(define-map user-stats
    principal
    {
        total-badges: uint,
        total-points: uint,
        level: uint
    }
)

;; Badge types: "check-ins", "transactions", "volume", "referrals"
(define-public (create-badge 
    (name (string-utf8 64))
    (description (string-utf8 256))
    (requirement-type (string-ascii 32))
    (requirement-value uint))
    (let
        (
            (badge-id (var-get badge-nonce))
        )
        (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_NOT_AUTHORIZED)
        
        (map-set badges badge-id {
            name: name,
            description: description,
            requirement-type: requirement-type,
            requirement-value: requirement-value,
            total-awarded: u0,
            active: true
        })
        
        (var-set badge-nonce (+ badge-id u1))
        (ok badge-id)
    )
)

(define-public (award-badge (user principal) (badge-id uint))
    (let
        (
            (badge (unwrap! (map-get? badges badge-id) ERR_NOT_AUTHORIZED))
            (existing-badge (map-get? user-badges {user: user, badge-id: badge-id}))
            (user-stat (default-to {total-badges: u0, total-points: u0, level: u1} 
                        (map-get? user-stats user)))
        )
        (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_NOT_AUTHORIZED)
        (asserts! (is-none existing-badge) ERR_ALREADY_EARNED)
        (asserts! (get active badge) ERR_NOT_AUTHORIZED)
        
        (map-set user-badges {user: user, badge-id: badge-id} {
            earned-at: stacks-block-height,
            progress: u100
        })
        
        (map-set badges badge-id (merge badge {
            total-awarded: (+ (get total-awarded badge) u1)
        }))
        
        (map-set user-stats user {
            total-badges: (+ (get total-badges user-stat) u1),
            total-points: (+ (get total-points user-stat) u10),
            level: (calculate-level (+ (get total-points user-stat) u10))
        })
        
        (ok true)
    )
)

(define-public (update-progress (user principal) (badge-id uint) (progress uint))
    (begin
        (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_NOT_AUTHORIZED)
        
        (map-set user-badges {user: user, badge-id: badge-id} {
            earned-at: u0,
            progress: progress
        })
        (ok true)
    )
)

(define-private (calculate-level (points uint))
    (if (>= points u100) u10
        (if (>= points u80) u9
            (if (>= points u60) u8
                (if (>= points u50) u7
                    (if (>= points u40) u6
                        (if (>= points u30) u5
                            (if (>= points u20) u4
                                (if (>= points u10) u3
                                    (if (>= points u5) u2
                                        u1
                                    )
                                )
                            )
                        )
                    )
                )
            )
        )
    )
)

(define-read-only (get-badge (badge-id uint))
    (ok (map-get? badges badge-id))
)

(define-read-only (has-badge (user principal) (badge-id uint))
    (ok (map-get? user-badges {user: user, badge-id: badge-id}))
)

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

(define-read-only (get-user-level (user principal))
    (ok (get level (default-to {total-badges: u0, total-points: u0, level: u1} 
                    (map-get? user-stats user))))
)

Functions (8)

FunctionAccessArgs
create-badgepublicname: (string-utf8 64
award-badgepublicuser: principal, badge-id: uint
update-progresspublicuser: principal, badge-id: uint, progress: uint
calculate-levelprivatepoints: uint
get-badgeread-onlybadge-id: uint
has-badgeread-onlyuser: principal, badge-id: uint
get-user-statsread-onlyuser: principal
get-user-levelread-onlyuser: principal