Source Code


;; Quest Registry v6 (`quest-registry-v6`)
;; Manages available quests and verifies completion with Knowledge Proofs.
;; Integrates with Soulbound Badge v3 for curriculum-specific rewards.

(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-QUEST-NOT-FOUND (err u101))
(define-constant ERR-ALREADY-COMPLETED (err u102))
(define-constant ERR-REQUIREMENT-NOT-MET (err u104))
(define-constant ERR-INVALID-PROOF (err u105))
(define-constant ERR-NOT-BNS-OWNER (err u106))

;; Mainnet BNS Contract
(define-constant BNS-CONTRACT 'SP000000000000000000002Q6VF78.bns)

;; Data Maps
(define-map quests
    uint ;; Quest ID
    {
        title: (string-ascii 64),
        xp-reward: uint,
        fee: uint,
        active: bool,
        proof-hash: (buff 32) ;; SHA-256 hash of the knowledge secret
    }
)

(define-map user-progress
    { user: principal, quest-id: uint }
    bool ;; Completed?
)

(define-map user-xp principal uint)

;; Variables
(define-data-var last-quest-id uint u0)
(define-data-var protocol-wallet principal tx-sender)

;; Read-Only Functions
(define-read-only (get-quest (id uint))
    (map-get? quests id)
)

(define-read-only (get-last-quest-id)
    (ok (var-get last-quest-id))
)

(define-read-only (has-completed (user principal) (quest-id uint))
    (default-to false (map-get? user-progress { user: user, quest-id: quest-id }))
)

(define-read-only (get-user-xp (user principal))
    (default-to u0 (map-get? user-xp user))
)

;; Admin Functions
(define-public (create-quest (title (string-ascii 64)) (xp uint) (fee uint) (hash (buff 32)))
    (begin
        (asserts! (is-eq tx-sender (var-get protocol-wallet)) ERR-NOT-AUTHORIZED)
        (let
            (
                (quest-id (+ (var-get last-quest-id) u1))
            )
            (map-set quests quest-id { 
                title: title, 
                xp-reward: xp, 
                fee: fee, 
                active: true,
                proof-hash: hash
            })
            (var-set last-quest-id quest-id)
            (ok quest-id)
        )
    )
)

;; User Functions
(define-public (complete-quest (quest-id uint) (secret (buff 256)))
    (let
        (
            (quest (unwrap! (map-get? quests quest-id) ERR-QUEST-NOT-FOUND))
            (fee (get fee quest))
            (xp (get xp-reward quest))
            (expected-hash (get proof-hash quest))
            (user tx-sender)
            (current-xp (get-user-xp user))
        )
        ;; 1. Check if already completed
        (asserts! (is-none (map-get? user-progress { user: user, quest-id: quest-id })) ERR-ALREADY-COMPLETED)
        
        ;; 2. Verify Knowledge Proof
        (asserts! (is-eq (sha256 secret) expected-hash) ERR-INVALID-PROOF)

        ;; 3. Verify Prerequisites (Modular checks)
        (if (> quest-id u1)
            (asserts! (has-completed user (- quest-id u1)) ERR-REQUIREMENT-NOT-MET)
            true
        )

        ;; Special XP check for high council modules (Quest 4+)
        (if (>= quest-id u4)
            (asserts! (>= current-xp u350) ERR-REQUIREMENT-NOT-MET)
            true
        )

        ;; 4. Pay Protocol Fee
        (if (and (> fee u0) (not (is-eq user (var-get protocol-wallet))))
            (try! (stx-transfer? fee user (var-get protocol-wallet)))
            true
        )

        ;; 5. Mark as complete & Add XP
        (map-set user-progress { user: user, quest-id: quest-id } true)
        (map-set user-xp user (+ current-xp xp))
        
        ;; 6. Mint Badge V3 with specific Quest ID
        (contract-call? .soulbound-badge-v3 mint user quest-id)
    )
)

;; BNS Special Quest
(define-public (claim-bns-quest (quest-id uint) (name (buff 48)) (namespace (buff 20)))
    (let
        (
            (quest (unwrap! (map-get? quests quest-id) ERR-QUEST-NOT-FOUND))
            (xp (get xp-reward quest))
            (user tx-sender)
            (current-xp (get-user-xp user))
            ;; Verify ownership via BNS contract
            (owner-info (unwrap! (contract-call? BNS-CONTRACT name-resolve namespace name) ERR-NOT-BNS-OWNER))
        )
        ;; 1. Check if already completed
        (asserts! (is-none (map-get? user-progress { user: user, quest-id: quest-id })) ERR-ALREADY-COMPLETED)
        
        ;; 2. Verify tx-sender is the owner
        (asserts! (is-eq (get owner owner-info) user) ERR-NOT-BNS-OWNER)

        ;; 3. Mark as complete & Add XP
        (map-set user-progress { user: user, quest-id: quest-id } true)
        (map-set user-xp user (+ current-xp xp))
        
        ;; 4. Mint Badge V3 with specific Quest ID
        (contract-call? .soulbound-badge-v3 mint user quest-id)
    )
)

Functions (7)

FunctionAccessArgs
get-questread-onlyid: uint
get-last-quest-idread-only
has-completedread-onlyuser: principal, quest-id: uint
get-user-xpread-onlyuser: principal
create-questpublictitle: (string-ascii 64
complete-questpublicquest-id: uint, secret: (buff 256
claim-bns-questpublicquest-id: uint, name: (buff 48