Source Code

;; enhanced-post-message.clar - Clarity 4
;; Extended version with frontend-friendly read functions
;; Compatible with all Clarity environments

(define-constant MAX_LEN u280)
(define-constant BLOCKS_PER_DAY u144)
(define-constant MAX_BATCH_SIZE u20)

(define-data-var total-messages uint u0)
(define-data-var total-messages-today uint u0)
(define-data-var last-message-day uint u0)

(define-map messages uint {
  sender: principal,
  block: uint,
  timestamp: uint,
  content: (string-ascii 280)
})

;; Track messages by author for easier filtering
(define-map user-message-count principal uint)
(define-map user-messages {user: principal, index: uint} uint)

;; Error constants
(define-constant err-invalid-length (err u100))
(define-constant err-invalid-range (err u101))
(define-constant err-batch-too-large (err u102))
(define-constant err-message-not-found (err u103))

;; Helper: get current day number
(define-read-only (current-day)
  (ok (/ burn-block-height BLOCKS_PER_DAY))
)

;; Public: send a message
(define-public (post-message (msg (string-ascii 280)))
  (let
    (
      (msg-length (len msg))
      (curr-day (/ burn-block-height BLOCKS_PER_DAY))
      (last-day (var-get last-message-day))
      (msg-id (+ (var-get total-messages) u1))
      (user-msg-count (default-to u0 (map-get? user-message-count tx-sender)))
      ;; Clarity 4: Use real timestamp
      (current-timestamp stacks-block-time)
    )
    ;; Validate message length
    (asserts! (and (> msg-length u0) (<= msg-length MAX_LEN)) err-invalid-length)
    
    ;; Update daily counter
    (if (is-eq curr-day last-day)
      (var-set total-messages-today (+ (var-get total-messages-today) u1))
      (begin
        (var-set total-messages-today u1)
        (var-set last-message-day curr-day)
      )
    )
    
    ;; Save message with timestamp
    (map-set messages msg-id {
      sender: tx-sender,
      block: burn-block-height,
      timestamp: current-timestamp,
      content: msg
    })
    
    ;; Track user's messages
    (map-set user-messages {user: tx-sender, index: user-msg-count} msg-id)
    (map-set user-message-count tx-sender (+ user-msg-count u1))
    
    (var-set total-messages msg-id)
    (ok msg-id)
  )
)

;; Read-only: get message by ID
(define-read-only (get-message (id uint))
  (ok (map-get? messages id))
)

;; Read-only: get messages in range (batch)
(define-read-only (get-messages-range (start-id uint) (end-id uint))
  (let
    (
      (total (var-get total-messages))
    )
    (asserts! (<= start-id end-id) err-invalid-range)
    (asserts! (<= (- end-id start-id) MAX_BATCH_SIZE) err-batch-too-large)
    (asserts! (<= end-id total) err-invalid-range)
    
    (ok {
      start: start-id,
      end: end-id,
      total: total,
      messages: (list
        (map-get? messages start-id)
        (map-get? messages (+ start-id u1))
        (map-get? messages (+ start-id u2))
        (map-get? messages (+ start-id u3))
        (map-get? messages (+ start-id u4))
        (map-get? messages (+ start-id u5))
        (map-get? messages (+ start-id u6))
        (map-get? messages (+ start-id u7))
        (map-get? messages (+ start-id u8))
        (map-get? messages (+ start-id u9))
        (map-get? messages (+ start-id u10))
        (map-get? messages (+ start-id u11))
        (map-get? messages (+ start-id u12))
        (map-get? messages (+ start-id u13))
        (map-get? messages (+ start-id u14))
        (map-get? messages (+ start-id u15))
        (map-get? messages (+ start-id u16))
        (map-get? messages (+ start-id u17))
        (map-get? messages (+ start-id u18))
        (map-get? messages (+ start-id u19))
      )
    })
  )
)

;; Read-only: get latest N messages
(define-read-only (get-latest-messages (count uint))
  (let
    (
      (total (var-get total-messages))
      (start-id (if (> total count) (+ (- total count) u1) u1))
      (end-id total)
    )
    (asserts! (<= count MAX_BATCH_SIZE) err-batch-too-large)
    (asserts! (> total u0) err-message-not-found)
    (get-messages-range start-id end-id)
  )
)

;; Read-only: get user's message count
(define-read-only (get-user-message-count (user principal))
  (ok (default-to u0 (map-get? user-message-count user)))
)

;; Read-only: get user's message ID by index
(define-read-only (get-user-message-id (user principal) (index uint))
  (ok (map-get? user-messages {user: user, index: index}))
)

;; Read-only: get user's latest messages
(define-read-only (get-user-latest-messages (user principal) (count uint))
  (let
    (
      (user-total (default-to u0 (map-get? user-message-count user)))
      (start-index (if (> user-total count) (- user-total count) u0))
    )
    (asserts! (<= count MAX_BATCH_SIZE) err-batch-too-large)
    (asserts! (> user-total u0) err-message-not-found)
    
    (ok {
      user: user,
      total-count: user-total,
      start-index: start-index,
      count: count,
      messages: (list
        (get-user-msg-at user start-index)
        (get-user-msg-at user (+ start-index u1))
        (get-user-msg-at user (+ start-index u2))
        (get-user-msg-at user (+ start-index u3))
        (get-user-msg-at user (+ start-index u4))
        (get-user-msg-at user (+ start-index u5))
        (get-user-msg-at user (+ start-index u6))
        (get-user-msg-at user (+ start-index u7))
        (get-user-msg-at user (+ start-index u8))
        (get-user-msg-at user (+ start-index u9))
        (get-user-msg-at user (+ start-index u10))
        (get-user-msg-at user (+ start-index u11))
        (get-user-msg-at user (+ start-index u12))
        (get-user-msg-at user (+ start-index u13))
        (get-user-msg-at user (+ start-index u14))
        (get-user-msg-at user (+ start-index u15))
        (get-user-msg-at user (+ start-index u16))
        (get-user-msg-at user (+ start-index u17))
        (get-user-msg-at user (+ start-index u18))
        (get-user-msg-at user (+ start-index u19))
      )
    })
  )
)

;; Private: get user message at index
(define-private (get-user-msg-at (user principal) (index uint))
  (match (map-get? user-messages {user: user, index: index})
    msg-id (map-get? messages msg-id)
    none
  )
)

;; Read-only: get total messages
(define-read-only (get-total-messages)
  (ok (var-get total-messages))
)

;; Read-only: get today's messages count
(define-read-only (get-today-messages)
  (ok (var-get total-messages-today))
)

;; Read-only: get contract stats (all info in one call)
(define-read-only (get-stats)
  (ok {
    total-messages: (var-get total-messages),
    today-messages: (var-get total-messages-today),
    current-day: (/ burn-block-height BLOCKS_PER_DAY),
    current-block: burn-block-height,
    last-message-day: (var-get last-message-day)
  })
)

;; Read-only: check if message exists
(define-read-only (message-exists (id uint))
  (ok (is-some (map-get? messages id)))
)

;; Read-only: get message with metadata
(define-read-only (get-message-with-meta (id uint))
  (match (map-get? messages id)
    message (ok (some {
      id: id,
      sender: (get sender message),
      content: (get content message),
      block: (get block message),
      timestamp: (get timestamp message),
      blocks-ago: (- burn-block-height (get block message)),
      message-number: id,
      total-messages: (var-get total-messages)
    }))
    (ok none)
  )
)

;; Read-only: get paginated feed (page number based)
(define-read-only (get-messages-page (page uint) (per-page uint))
  (let
    (
      (total (var-get total-messages))
      (start-id (if (> page u0) (+ (* (- page u1) per-page) u1) u1))
      (end-id (+ start-id per-page))
      (actual-end (if (> end-id total) total end-id))
    )
    (asserts! (> page u0) err-invalid-range)
    (asserts! (<= per-page MAX_BATCH_SIZE) err-batch-too-large)
    (asserts! (> total u0) err-message-not-found)
    
    (get-messages-range start-id actual-end)
  )
)

;; Read-only: get message count by user
(define-read-only (get-user-stats (user principal))
  (let
    (
      (msg-count (default-to u0 (map-get? user-message-count user)))
    )
    (ok {
      user: user,
      message-count: msg-count,
      has-messages: (> msg-count u0)
    })
  )
)

Functions (16)

FunctionAccessArgs
current-dayread-only
post-messagepublicmsg: (string-ascii 280
get-messageread-onlyid: uint
get-messages-rangeread-onlystart-id: uint, end-id: uint
get-latest-messagesread-onlycount: uint
get-user-message-countread-onlyuser: principal
get-user-message-idread-onlyuser: principal, index: uint
get-user-latest-messagesread-onlyuser: principal, count: uint
get-user-msg-atprivateuser: principal, index: uint
get-total-messagesread-only
get-today-messagesread-only
get-statsread-only
message-existsread-onlyid: uint
get-message-with-metaread-onlyid: uint
get-messages-pageread-onlypage: uint, per-page: uint
get-user-statsread-onlyuser: principal