Source Code

;; ------------------------------------------------------------
;; ONE WORD STORY v2 - Clarity Contract
;; - Unlimited number of words (no small fixed list cap)
;; - Each entry is:
;;     - id        : auto-incrementing uint
;;     - word      : (string-ascii 32)
;;     - sender    : principal
;;     - timestamp : uint
;;     - category  : (string-ascii 32), e.g. "general", "tech"
;; ------------------------------------------------------------
;; Example usage (frontend):
;; 1. Call (add-word "hello" "general")
;; 2. Get total count with (get-word-count)
;; 3. Iterate ids [0..count-1] and call (get-word id)
;; 4. Use timestamps per sender to compute client-side streaks
;; ------------------------------------------------------------

;; Error codes
(define-constant ERR-WORD-NOT-FOUND (err u300))
(define-constant ERR-INVALID-WORD (err u301))
(define-constant ERR-INVALID-CATEGORY (err u302))

;; Default category used when caller does not provide one
;; or provides an empty string.
(define-constant DEFAULT-CATEGORY "general")

;; ------------------------------------------------------------
;; INTERNAL: Get current timestamp
;; For now this is a simple placeholder (u0). It is factored out
;; so it can be easily updated later if block time usage changes.
;; ------------------------------------------------------------
(define-private (get-now)
  u0
)

;; ------------------------------------------------------------
;; INTERNAL: Normalize category
;; - If the provided category is empty (zero-length), use DEFAULT-CATEGORY.
;; - Otherwise return the provided category unchanged.
;; ------------------------------------------------------------
(define-private (normalize-category (maybe-category (string-ascii 32)))
  (if (is-eq (len maybe-category) u0)
      DEFAULT-CATEGORY
      maybe-category
  )
)

;; ------------------------------------------------------------
;; PUBLIC: Add a new word to the story
;; Intended usage:
;; - Called directly by users from the frontend.
;; - Frontend should pass the selected category or an empty string ("")
;;   to fall back to DEFAULT-CATEGORY.
;; Parameters:
;; - word      : the one-word text (string-ascii 32)
;; - category  : optional category name; if empty, DEFAULT-CATEGORY is used
;; Returns:
;; - { id, word, category, timestamp, sender }
;; ------------------------------------------------------------
(define-public (add-word (word (string-ascii 32)) (category (string-ascii 32)))
  (let
    (
      (id (var-get next-word-id))
      (sender tx-sender)
      (time (get-now))
      (final-category (normalize-category category))
    )
    (begin
      ;; ensure the word is not empty
      (asserts! (> (len word) u0) ERR-INVALID-WORD)
      ;; basic validation: category (after normalization) must not be empty
      (asserts! (> (len final-category) u0) ERR-INVALID-CATEGORY)
      ;; store the new word
      (map-set words
        { id: id }
        {
          word: word,
          sender: sender,
          timestamp: time,
          category: final-category
        }
      )
      ;; increment the next-word-id counter
      (var-set next-word-id (+ id u1))
      ;; return basic info about the new entry
      (ok
        {
          id: id,
          word: word,
          category: final-category,
          timestamp: time,
          sender: sender
        }
      )
    )
  )
)

;; ------------------------------------------------------------
;; READ-ONLY: Get a word by its id
;; Intended usage:
;; - Frontend uses this to inspect a specific word, e.g. when iterating
;;   from 0 up to (get-word-count - 1).
;; Behavior:
;; - Returns (ok { word, sender, timestamp, category }) when found.
;; - Returns ERR-WORD-NOT-FOUND when no entry exists for that id.
;; ------------------------------------------------------------
(define-read-only (get-word (id uint))
  (let
    (
      (stored (map-get? words { id: id }))
    )
    (if (is-none stored)
        ERR-WORD-NOT-FOUND
        (ok (unwrap-panic stored))
    )
  )
)

;; ------------------------------------------------------------
;; READ-ONLY: Get the total number of words added so far
;; Intended usage:
;; - Frontend calls this once, then iterates ids [0..count-1]
;;   and calls (get-word id) for each.
;; Notes:
;; - This is equal to the current value of next-word-id,
;;   since ids start at 0 and increment by 1 for each new word.
;; ------------------------------------------------------------
(define-read-only (get-word-count)
  (ok (var-get next-word-id))
)

;; ------------------------------------------------------------
;; READ-ONLY: Get the latest word id (if any)
;; - Returns (ok some-id) when at least one word exists.
;; - Returns ERR-WORD-NOT-FOUND when there are no words yet.
;; ------------------------------------------------------------
(define-read-only (get-latest-id)
  (let
    (
      (count (var-get next-word-id))
    )
    (if (is-eq count u0)
        ERR-WORD-NOT-FOUND
        (ok (- count u1))
    )
  )
)

;; ------------------------------------------------------------
;; READ-ONLY: Get the latest word entry (if any)
;; - Uses get-latest-id to find the most recent id.
;; - Returns ERR-WORD-NOT-FOUND when there are no words yet.
;; ------------------------------------------------------------
(define-read-only (get-latest-word)
  (let
    (
      (latest-id-result (get-latest-id))
    )
    (match latest-id-result
      latest-id (get-word latest-id)
      err ERR-WORD-NOT-FOUND
    )
  )
)







;; ------------------------------------------------------------
;; DATA: Auto-incrementing ID counter for words
;; Starts at 0; each new word uses the current value, then increments.
;; ------------------------------------------------------------
(define-data-var next-word-id uint u0)

;; ------------------------------------------------------------
;; DATA: Storage for all words
;; Keyed by auto-incrementing id.
;; ------------------------------------------------------------
(define-map words
  {
    id: uint
  }
  {
    word: (string-ascii 32),     ;; the word itself
    sender: principal,           ;; who added it
    timestamp: uint,             ;; when it was added
    category: (string-ascii 32)  ;; e.g. "general", "tech"
  }
)


Functions (7)

FunctionAccessArgs
get-nowprivate
normalize-categoryprivatemaybe-category: (string-ascii 32
add-wordpublicword: (string-ascii 32
get-wordread-onlyid: uint
get-word-countread-only
get-latest-idread-only
get-latest-wordread-only