Source Code

;; Content License Contract
;; License digital content with on-chain rights management
;; Halal - intellectual property trade
;; Clarity 4 compatible

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

(define-data-var content-count uint u0)
(define-data-var license-count uint u0)

(define-map contents uint { creator: principal, title: (string-utf8 100), license-fee: uint, total-licenses: uint, created: uint })
(define-map licenses uint { content-id: uint, licensee: principal, granted-at: uint, expires-at: uint })
(define-map user-licenses { user: principal, content: uint } uint)

(define-public (register-content (title (string-utf8 100)) (fee uint))
  (let ((id (+ (var-get content-count) u1)))
    (map-set contents id { creator: tx-sender, title: title, license-fee: fee, total-licenses: u0, created: stacks-block-height })
    (var-set content-count id) (ok id)))

(define-public (purchase-license (content-id uint) (duration uint))
  (let (
    (content (unwrap! (map-get? contents content-id) ERR-NOT-FOUND))
    (lid (+ (var-get license-count) u1))
  )
    (asserts! (is-none (map-get? user-licenses { user: tx-sender, content: content-id })) ERR-ALREADY-LICENSED)
    (try! (stx-transfer? (get license-fee content) tx-sender (get creator content)))
    (map-set licenses lid { content-id: content-id, licensee: tx-sender, granted-at: stacks-block-height, expires-at: (+ stacks-block-height duration) })
    (map-set user-licenses { user: tx-sender, content: content-id } lid)
    (map-set contents content-id (merge content { total-licenses: (+ (get total-licenses content) u1) }))
    (var-set license-count lid) (ok lid)))

(define-public (update-fee (content-id uint) (new-fee uint))
  (let ((content (unwrap! (map-get? contents content-id) ERR-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get creator content)) ERR-NOT-AUTHORIZED)
    (map-set contents content-id (merge content { license-fee: new-fee })) (ok true)))

(define-read-only (get-content (id uint)) (map-get? contents id))
(define-read-only (get-license (id uint)) (map-get? licenses id))
(define-read-only (get-content-count) (ok (var-get content-count)))
(define-read-only (get-license-count) (ok (var-get license-count)))
(define-read-only (has-license (user principal) (content uint))
  (match (map-get? user-licenses { user: user, content: content })
    lid (match (map-get? licenses lid) l (ok (>= (get expires-at l) stacks-block-height)) (ok false))
    (ok false)))

Functions (8)

FunctionAccessArgs
register-contentpublictitle: (string-utf8 100
purchase-licensepubliccontent-id: uint, duration: uint
update-feepubliccontent-id: uint, new-fee: uint
get-contentread-onlyid: uint
get-licenseread-onlyid: uint
get-content-countread-only
get-license-countread-only
has-licenseread-onlyuser: principal, content: uint