Source Code

;; Knowledge Rewards Contract
;; Reward knowledge contributions
;; Halal - spreading knowledge
;; Clarity 4 compatible

(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-NOT-AUTHORIZED (err u401))
(define-constant ERR-NOT-FOUND (err u404))

(define-data-var content-count uint u0)
(define-data-var total-rewards uint u0)

(define-map knowledge-entries uint {
  author: principal, title: (string-utf8 100), category: (string-ascii 20),
  content-hash: (buff 32), upvotes: uint, reward-earned: uint, created: uint
})
(define-map author-stats principal { entries: uint, total-upvotes: uint, total-earned: uint })
(define-map has-upvoted { content-id: uint, voter: principal } bool)
(define-data-var reward-per-upvote uint u1000) ;; 0.001 STX

(define-public (publish-entry (title (string-utf8 100)) (category (string-ascii 20)) (content-hash (buff 32)))
  (let (
    (id (+ (var-get content-count) u1))
    (stats (default-to { entries: u0, total-upvotes: u0, total-earned: u0 } (map-get? author-stats tx-sender)))
  )
    (map-set knowledge-entries id { author: tx-sender, title: title, category: category, content-hash: content-hash, upvotes: u0, reward-earned: u0, created: stacks-block-height })
    (map-set author-stats tx-sender (merge stats { entries: (+ (get entries stats) u1) }))
    (var-set content-count id) (ok id)))

(define-public (upvote (content-id uint))
  (let (
    (entry (unwrap! (map-get? knowledge-entries content-id) ERR-NOT-FOUND))
    (reward (var-get reward-per-upvote))
    (stats (default-to { entries: u0, total-upvotes: u0, total-earned: u0 } (map-get? author-stats (get author entry))))
  )
    (asserts! (not (is-eq tx-sender (get author entry))) ERR-NOT-AUTHORIZED)
    (asserts! (not (default-to false (map-get? has-upvoted { content-id: content-id, voter: tx-sender }))) ERR-NOT-AUTHORIZED)
    (map-set has-upvoted { content-id: content-id, voter: tx-sender } true)
    (map-set knowledge-entries content-id (merge entry { upvotes: (+ (get upvotes entry) u1), reward-earned: (+ (get reward-earned entry) reward) }))
    (map-set author-stats (get author entry) (merge stats { total-upvotes: (+ (get total-upvotes stats) u1), total-earned: (+ (get total-earned stats) reward) }))
    (var-set total-rewards (+ (var-get total-rewards) reward))
    (ok true)))

(define-public (claim-rewards (content-id uint))
  (let ((entry (unwrap! (map-get? knowledge-entries content-id) ERR-NOT-FOUND)))
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (asserts! (> (get reward-earned entry) u0) ERR-NOT-FOUND)
    (try! (stx-transfer? (get reward-earned entry) tx-sender (get author entry)))
    (map-set knowledge-entries content-id (merge entry { reward-earned: u0 })) (ok true)))

(define-public (set-reward-rate (rate uint))
  (begin (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED) (var-set reward-per-upvote rate) (ok rate)))

(define-read-only (get-entry (id uint)) (map-get? knowledge-entries id))
(define-read-only (get-author-stats (who principal)) (map-get? author-stats who))
(define-read-only (get-content-count) (ok (var-get content-count)))
(define-read-only (get-total-rewards) (ok (var-get total-rewards)))

Functions (8)

FunctionAccessArgs
publish-entrypublictitle: (string-utf8 100
upvotepubliccontent-id: uint
claim-rewardspubliccontent-id: uint
set-reward-ratepublicrate: uint
get-entryread-onlyid: uint
get-author-statsread-onlywho: principal
get-content-countread-only
get-total-rewardsread-only