Source Code

;; Define constants
(define-constant PROTOCOL_ADMINISTRATOR tx-sender)
(define-constant ERR_UNAUTHORIZED_ACCESS (err u100))
(define-constant ERR_INVALID_SUBMISSION (err u101))
(define-constant ERR_DUPLICATE_ENTRY (err u102))
(define-constant ERR_NONEXISTENT_ITEM (err u103))
(define-constant ERR_INADEQUATE_BALANCE (err u104))
(define-constant ERR_INVALID_TOPIC (err u105))
(define-constant ERR_INVALID_FLAG (err u106))
(define-constant ERR_OVERFLOW (err u107))
(define-constant ERR_INVALID_APPRAISAL (err u108))
(define-constant ERR_INVALID_ITEM_ID (err u109))
(define-constant MIN_HYPERLINK_LENGTH u10)
(define-constant MAX_UINT u340282366920938463463374607431768211455)

;; Define data variables
(define-data-var submission-charge uint u10)
(define-data-var aggregate-submissions uint u0)
(define-data-var content-topics (list 10 (string-ascii 20)) (list "Technology" "Science" "Art" "Politics" "Sports"))

;; Define data maps
(define-map curated-items
  { item-identifier: uint }
  {
    originator: principal,
    headline: (string-ascii 100),
    hyperlink: (string-ascii 200),
    topic: (string-ascii 20),
    publication-epoch: uint,
    appraisals: int,
    gratuities: uint,
    flags: uint,
  }
)

(define-map participant-appraisals
  {
    participant: principal,
    item-identifier: uint,
  }
  { appraisal: int }
)

(define-map participant-credibility
  { participant: principal }
  { metric: int }
)

;; Helper function to check if an item exists
(define-private (item-exists (item-identifier uint))
  (is-some (map-get? curated-items { item-identifier: item-identifier }))
)

;; Public functions

;; Submit new content for curation
(define-public (contribute-item
    (headline (string-ascii 100))
    (hyperlink (string-ascii 200))
    (topic (string-ascii 20))
  )
  (let ((item-identifier (+ (var-get aggregate-submissions) u1)))
    (asserts!
      (and
        (>= (len headline) u1)
        (>= (len hyperlink) MIN_HYPERLINK_LENGTH)
        (>= (len topic) u1)
      )
      ERR_INVALID_SUBMISSION
    )
    (asserts! (> item-identifier (var-get aggregate-submissions)) ERR_OVERFLOW)
    (asserts! (is-some (index-of (var-get content-topics) topic))
      ERR_INVALID_TOPIC
    )
    (asserts! (>= (stx-get-balance tx-sender) (var-get submission-charge))
      ERR_INADEQUATE_BALANCE
    )
    (try! (stx-transfer? (var-get submission-charge) tx-sender PROTOCOL_ADMINISTRATOR))
    (map-set curated-items { item-identifier: item-identifier } {
      originator: tx-sender,
      headline: headline,
      hyperlink: hyperlink,
      topic: topic,
      publication-epoch: stacks-block-height,
      appraisals: 0,
      gratuities: u0,
      flags: u0,
    })
    (var-set aggregate-submissions item-identifier)
    (print {
      type: "new-item",
      item-identifier: item-identifier,
      originator: tx-sender,
    })
    (ok item-identifier)
  )
)

;; Vote on curated content
(define-public (appraise-item
    (item-identifier uint)
    (appraisal int)
  )
  (let (
      (previous-appraisal (default-to 0
        (get appraisal
          (map-get? participant-appraisals {
            participant: tx-sender,
            item-identifier: item-identifier,
          })
        )))
      (target-item (unwrap! (map-get? curated-items { item-identifier: item-identifier })
        ERR_NONEXISTENT_ITEM
      ))
      (appraiser-standing (default-to { metric: 0 }
        (map-get? participant-credibility { participant: tx-sender })
      ))
    )
    (asserts! (item-exists item-identifier) ERR_NONEXISTENT_ITEM)
    (asserts! (or (is-eq appraisal 1) (is-eq appraisal -1)) ERR_INVALID_APPRAISAL)
    (map-set participant-appraisals {
      participant: tx-sender,
      item-identifier: item-identifier,
    } { appraisal: appraisal }
    )
    (map-set curated-items { item-identifier: item-identifier }
      (merge target-item { appraisals: (+ (get appraisals target-item) (- appraisal previous-appraisal)) })
    )
    (map-set participant-credibility { participant: tx-sender } { metric: (+ (get metric appraiser-standing) appraisal) })
    (print {
      type: "appraisal",
      item-identifier: item-identifier,
      appraiser: tx-sender,
      appraisal: appraisal,
    })
    (ok true)
  )
)

;; Tip content creator
(define-public (reward-originator
    (item-identifier uint)
    (gratuity-amount uint)
  )
  (let ((target-item (unwrap! (map-get? curated-items { item-identifier: item-identifier })
      ERR_NONEXISTENT_ITEM
    )))
    (asserts! (item-exists item-identifier) ERR_NONEXISTENT_ITEM)
    (asserts! (>= (stx-get-balance tx-sender) gratuity-amount)
      ERR_INADEQUATE_BALANCE
    )
    ;; Update state before transfer
    (map-set curated-items { item-identifier: item-identifier }
      (merge target-item { gratuities: (+ (get gratuities target-item) gratuity-amount) })
    )
    ;; Perform transfer last
    (try! (stx-transfer? gratuity-amount tx-sender (get originator target-item)))
    (print {
      type: "reward",
      item-identifier: item-identifier,
      from: tx-sender,
      to: (get originator target-item),
      amount: gratuity-amount,
    })
    (ok true)
  )
)

;; Report content
(define-public (flag-item (item-identifier uint))
  (let ((target-item (unwrap! (map-get? curated-items { item-identifier: item-identifier })
      ERR_NONEXISTENT_ITEM
    )))
    (asserts! (item-exists item-identifier) ERR_NONEXISTENT_ITEM)
    (asserts! (not (is-eq (get originator target-item) tx-sender))
      ERR_INVALID_FLAG
    )
    (map-set curated-items { item-identifier: item-identifier }
      (merge target-item { flags: (+ (get flags target-item) u1) })
    )
    (print {
      type: "flag",
      item-identifier: item-identifier,
      flagger: tx-sender,
    })
    (ok true)
  )
)

;; Get content details
(define-read-only (retrieve-item-details (item-identifier uint))
  (map-get? curated-items { item-identifier: item-identifier })
)

;; Get user's vote on a specific content
(define-read-only (retrieve-participant-appraisal
    (participant principal)
    (item-identifier uint)
  )
  (get appraisal
    (map-get? participant-appraisals {
      participant: participant,
      item-identifier: item-identifier,
    })
  )
)

;; Get total number of curated content
(define-read-only (retrieve-aggregate-submissions)
  (var-get aggregate-submissions)
)

;; Get user reputation
(define-read-only (retrieve-participant-credibility (participant principal))
  (default-to { metric: 0 }
    (map-get? participant-credibility { participant: participant })
  )
)

;; Get top content (limited by number of items)
(define-read-only (retrieve-top-items (limit uint))
  (let (
      (item-count (var-get aggregate-submissions))
      (actual-limit (if (> limit item-count)
        item-count
        limit
      ))
    )
    (filter not-none (map retrieve-item-if-valid (get-item-ids actual-limit)))
  )
)

(define-private (not-none (item (optional {
  originator: principal,
  headline: (string-ascii 100),
  hyperlink: (string-ascii 200),
  topic: (string-ascii 20),
  publication-epoch: uint,
  appraisals: int,
  gratuities: uint,
  flags: uint,
})))
  (is-some item)
)

(define-private (retrieve-item-if-valid (id uint))
  (match (map-get? curated-items { item-identifier: id })
    item (if (>= (get appraisals item) 0)
      (some item)
      none
    )
    none
  )
)

;; Updated get-item-ids function
(define-read-only (get-item-ids (count uint))
  (filter is-non-zero (enumerate count))
)

;; Updated enumerate function
(define-private (enumerate (n uint))
  (let ((limit (if (> n u10)
      u10
      n
    )))
    (list
      (if (>= limit u1)
        u1
        u0
      )
      (if (>= limit u2)
        u2
        u0
      )
      (if (>= limit u3)
        u3
        u0
      )
      (if (>= limit u4)
        u4
        u0
      )
      (if (>= limit u5)
        u5
        u0
      )
      (if (>= limit u6)
        u6
        u0
      )
      (if (>= limit u7)
        u7
        u0
      )
      (if (>= limit u8)
        u8
        u0
      )
      (if (>= limit u9)
        u9
        u0
      )
      (if (>= limit u10)
        u10
        u0
      )
    )
  )
)

;; Helper function to filter out zero values
(define-private (is-non-zero (n uint))
  (not (is-eq n u0))
)

;; Admin functions

;; Set curation fee
(define-public (adjust-submission-charge (new-charge uint))
  (begin
    (asserts! (is-eq tx-sender PROTOCOL_ADMINISTRATOR) ERR_UNAUTHORIZED_ACCESS)
    (asserts! (<= new-charge MAX_UINT) ERR_OVERFLOW)
    (var-set submission-charge new-charge)
    (print {
      type: "fee-change",
      new-charge: new-charge,
    })
    (ok true)
  )
)

;; Remove content (only by contract owner)
(define-public (expunge-item (item-identifier uint))
  (begin
    (asserts! (is-eq tx-sender PROTOCOL_ADMINISTRATOR) ERR_UNAUTHORIZED_ACCESS)
    (asserts! (item-exists item-identifier) ERR_NONEXISTENT_ITEM)
    (map-delete curated-items { item-identifier: item-identifier })
    (print {
      type: "item-expunged",
      item-identifier: item-identifier,
    })
    (ok true)
  )
)

;; Add new category
(define-public (introduce-topic (new-topic (string-ascii 20)))
  (begin
    (asserts! (is-eq tx-sender PROTOCOL_ADMINISTRATOR) ERR_UNAUTHORIZED_ACCESS)
    (asserts! (< (len (var-get content-topics)) u10) ERR_INVALID_TOPIC)
    (asserts! (>= (len new-topic) u1) ERR_INVALID_TOPIC)
    (var-set content-topics
      (unwrap-panic (as-max-len? (append (var-get content-topics) new-topic) u10))
    )
    (print {
      type: "new-topic",
      topic: new-topic,
    })
    (ok true)
  )
)

Functions (14)

FunctionAccessArgs
item-existsprivateitem-identifier: uint
contribute-itempublicheadline: (string-ascii 100
flag-itempublicitem-identifier: uint
retrieve-item-detailsread-onlyitem-identifier: uint
retrieve-aggregate-submissionsread-only
retrieve-participant-credibilityread-onlyparticipant: principal
retrieve-top-itemsread-onlylimit: uint
retrieve-item-if-validprivateid: uint
get-item-idsread-onlycount: uint
enumerateprivaten: uint
is-non-zeroprivaten: uint
adjust-submission-chargepublicnew-charge: uint
expunge-itempublicitem-identifier: uint
introduce-topicpublicnew-topic: (string-ascii 20