message-board-with-clarity-v4

SP1V95DB4JK47QVPJBXCEN6MT35JK84CQ4CWS15DQ

Source Code

;; Simple Message Board Contract with Clarity v4
;; This contract allows users to read and post messages for a fee in sBTC.

;; Define contract owner
(define-constant CONTRACT_OWNER tx-sender)

;; Define error codes
(define-constant ERR_NOT_ENOUGH_SBTC (err u1004))
(define-constant ERR_NOT_CONTRACT_OWNER (err u1005))
(define-constant ERR_BLOCK_NOT_FOUND (err u1003))
(define-constant ERR_INVALID_SIGNATURE (err u1006))
(define-constant ERR_ASSET_RESTRICTION (err u1007))
(define-constant ERR_CONVERSION_FAILED (err u1008))

;; Define asset restriction settings
(define-data-var assets-restricted bool false)

;; Define a map to store messages
;; Each message has an ID, content, author, Bitcoin block height timestamp, and Stacks block time
(define-map messages
  uint
  {
    message: (string-utf8 280),
    author: principal,
    burn-block-time: uint,
    stacks-block-time: uint,
    signature-verified: bool,
  }
)

;; Map to store verified signatures for messages
(define-map message-signatures
  uint
  {
    signature: (buff 64),
    public-key: (buff 33),
  }
)

;; Counter for total messages
(define-data-var message-count uint u0)

;; Public function to add a new message for 1 satoshi of sBTC
(define-public (add-message (content (string-utf8 280)))
  (let ((id (+ (var-get message-count) u1)))
    ;; Check if assets are restricted
    (asserts! (not (var-get assets-restricted)) ERR_ASSET_RESTRICTION)
    ;; Charge 1 satoshi of sBTC from the caller (user pays the fee but it goes to contract owner)
    (try! (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
      transfer u1 contract-caller CONTRACT_OWNER none
    ))
    ;; Store the message with current Bitcoin block height and Stacks block time
    (map-set messages id {
      message: content,
      author: contract-caller,
      burn-block-time: burn-block-height,
      stacks-block-time: stacks-block-time,
      signature-verified: false,
    })
    ;; Update message count
    (var-set message-count id)
    ;; Emit event for the new message
    (print {
      event: "[Stacks Dev Quickstart] New Message",
      message: content,
      id: id,
      author: contract-caller,
      burn-block-time: burn-block-height,
      stacks-block-time: stacks-block-time,
    })
    ;; Return the message ID
    (ok id)
  )
)

;; Withdraw function for contract owner to withdraw accumulated sBTC
(define-public (withdraw-funds)
  (begin
    (asserts! (is-eq tx-sender CONTRACT_OWNER) (err u1005))
    ;; For now, just return true since funds go directly to owner
    ;; In a real implementation, you might want to track accumulated fees separately
    (ok true)
  )
)

;; Read-only function to get a message by ID
(define-read-only (get-message (id uint))
  (map-get? messages id)
)

;; Read-only function to get message author
(define-read-only (get-message-author (id uint))
  (get author (map-get? messages id))
)

;; Read-only function to get message count at a specific Stacks block height
(define-read-only (get-message-count-at-block (block uint))
  (ok (at-block
    (unwrap! (get-stacks-block-info? id-header-hash block) ERR_BLOCK_NOT_FOUND)
    (var-get message-count)
  ))
)

;; Clarity v4 Functions

;; Function to get the hash of this contract using contract-hash?
(define-read-only (get-contract-hash)
  (contract-hash? tx-sender)
)

;; Function to toggle asset restrictions
(define-public (toggle-asset-restrictions (restricted bool))
  (begin
    (asserts! (is-eq tx-sender CONTRACT_OWNER) ERR_NOT_CONTRACT_OWNER)
    (var-set assets-restricted restricted)
    ;; Note: restrict-assets? function may not be available in all environments
    ;; For this demo, we're just setting the internal state
    (ok restricted)
  )
)

;; Function to convert message content to ASCII using to-ascii?
(define-read-only (get-message-ascii (id uint))
  (match (get-message id)
    message-data (ok (unwrap-panic (to-ascii? (get message message-data))))
    (err u404)
  )
)

;; Function to get current Stacks block time
(define-read-only (get-current-stacks-time)
  stacks-block-time
)

;; Function to add a message with secp256r1 signature verification
(define-public (add-message-with-signature 
    (content (string-utf8 280))
    (signature (buff 64))
    (public-key (buff 33))
    (message-hash (buff 32))
  )
  (let ((id (+ (var-get message-count) u1)))
    ;; Check if assets are restricted
    (asserts! (not (var-get assets-restricted)) ERR_ASSET_RESTRICTION)
    ;; Verify secp256r1 signature
    (asserts! (secp256r1-verify message-hash signature public-key) ERR_INVALID_SIGNATURE)
    ;; Charge 1 satoshi of sBTC from the caller
    (try! (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
      transfer u1 contract-caller CONTRACT_OWNER none
    ))
    ;; Store the message with signature verification
    (map-set messages id {
      message: content,
      author: contract-caller,
      burn-block-time: burn-block-height,
      stacks-block-time: stacks-block-time,
      signature-verified: true,
    })
    ;; Store signature data
    (map-set message-signatures id {
      signature: signature,
      public-key: public-key,
    })
    ;; Update message count
    (var-set message-count id)
    ;; Emit event for the new verified message
    (print {
      event: "[Stacks Dev Quickstart] New Verified Message",
      message: content,
      id: id,
      author: contract-caller,
      burn-block-time: burn-block-height,
      stacks-block-time: stacks-block-time,
      signature-verified: true,
    })
    ;; Return the message ID
    (ok id)
  )
)

;; Function to get signature data for a message
(define-read-only (get-message-signature (id uint))
  (map-get? message-signatures id)
)

;; Function to verify if a message has a valid signature
(define-read-only (is-message-signature-valid (id uint) (message-hash (buff 32)))
  (match (map-get? message-signatures id)
    signature-data 
      (secp256r1-verify 
        message-hash 
        (get signature signature-data) 
        (get public-key signature-data)
      )
    false
  )
)

;; Function to get contract information including hash
(define-read-only (get-contract-info)
  {
    hash: (contract-hash? tx-sender),
    owner: CONTRACT_OWNER,
    assets-restricted: (var-get assets-restricted),
    message-count: (var-get message-count),
    current-stacks-time: stacks-block-time,
  }
)

Functions (13)

FunctionAccessArgs
get-contract-hashread-only
add-messagepubliccontent: (string-utf8 280
withdraw-fundspublic
get-messageread-onlyid: uint
get-message-authorread-onlyid: uint
get-message-count-at-blockread-onlyblock: uint
toggle-asset-restrictionspublicrestricted: bool
get-message-asciiread-onlyid: uint
get-current-stacks-timeread-only
add-message-with-signaturepubliccontent: (string-utf8 280
get-message-signatureread-onlyid: uint
is-message-signature-validread-onlyid: uint, message-hash: (buff 32
get-contract-inforead-only