Source Code

;; title: wrapped-stx
;; version:
;; summary:
;; description:

;; Music Royalty Distribution Smart Contract

(define-constant ERR-UNAUTHORIZED-ACCESS (err u100))
(define-constant ERR-INVALID-ROYALTY-PERCENTAGE (err u101))
(define-constant ERR-DUPLICATE-SONG-ENTRY (err u102))
(define-constant ERR-SONG-NOT-FOUND (err u103))
(define-constant ERR-INSUFFICIENT-PAYMENT (err u104))
(define-constant ERR-INVALID-RECIPIENT (err u105))
(define-constant ERR-PAYMENT-DISTRIBUTION-FAILED (err u106))
(define-constant ERR-INVALID-STRING-LENGTH (err u107))
(define-constant ERR-INVALID-SONG-TITLE (err u108))
(define-constant ERR-INVALID-PARTICIPANT-ROLE (err u109))
(define-constant ERR-INVALID-ARTIST (err u110))
(define-constant ERR-INVALID-ADMIN (err u111))

;; Data Structures for Music Rights Management
(define-map music-catalog
  { song-id: uint }
  {
    title: (string-ascii 50),
    primary-artist: principal,
    total-revenue: uint,
    release-date: uint,
    is-active: bool,
  }
)

(define-map royalty-allocations
  {
    song-id: uint,
    rights-holder: principal,
  }
  {
    percentage: uint,
    role: (string-ascii 20),
    earned-royalties: uint,
  }
)

;; Contract State Variables
(define-data-var total-registered-tracks uint u0)
(define-data-var contract-owner principal tx-sender)

;; Validation Helpers
(define-private (is-valid-royalty-share (share {
  percentage: uint,
  role: (string-ascii 20),
  earned-royalties: uint,
}))
  (> (get percentage share) u0)
)

(define-private (is-contract-owner)
  (is-eq tx-sender (var-get contract-owner))
)

(define-private (validate-royalty-percentage (percentage uint))
  (and (>= percentage u0) (<= percentage u100))
)

(define-private (validate-ascii-string (input (string-ascii 50)))
  (let ((length (len input)))
    (and (> length u0) (<= length u50))
  )
)

(define-private (validate-participant-role (role (string-ascii 20)))
  (let ((length (len role)))
    (and (> length u0) (<= length u20))
  )
)

(define-private (validate-rights-holder (holder principal))
  (and
    (not (is-eq holder tx-sender))
    (not (is-eq holder (var-get contract-owner)))
  )
)

;; Read-Only Query Functions
(define-read-only (get-track-details (song-id uint))
  (map-get? music-catalog { song-id: song-id })
)

(define-read-only (get-royalty-details
    (song-id uint)
    (rights-holder principal)
  )
  (map-get? royalty-allocations {
    song-id: song-id,
    rights-holder: rights-holder,
  })
)

(define-read-only (get-total-tracks)
  (var-get total-registered-tracks)
)

(define-read-only (get-track-royalty-shares (song-id uint))
  (let (
      (track-info (get-track-details song-id))
      (primary-artist (match track-info
        record (get primary-artist record)
        tx-sender
      ))
    )
    (let ((distribution (get-royalty-details song-id primary-artist)))
      (match distribution
        share (list {
          rights-holder: primary-artist,
          percentage: (get percentage share),
        })
        (list)
      )
    )
  )
)

;; Royalty Distribution Mechanism
(define-private (calculate-rights-holder-payment
    (rights-share {
      rights-holder: principal,
      percentage: uint,
    })
    (total-payment uint)
  )
  (let ((holder-payment (/ (* total-payment (get percentage rights-share)) u100)))
    (if (> holder-payment u0)
      (match (stx-transfer? holder-payment tx-sender (get rights-holder rights-share))
        success total-payment
        error u0
      )
      u0
    )
  )
)

(define-private (distribute-royalties
    (song-id uint)
    (total-payment uint)
  )
  (let (
      (royalty-distribution-list (get-track-royalty-shares song-id))
      (total-distributed (fold calculate-rights-holder-payment royalty-distribution-list
        total-payment
      ))
    )
    (begin
      (asserts! (> (len royalty-distribution-list) u0) ERR-SONG-NOT-FOUND)
      (asserts! (> total-distributed u0) ERR-PAYMENT-DISTRIBUTION-FAILED)
      (ok total-distributed)
    )
  )
)

;; Administrative Functions
(define-public (register-track
    (track-title (string-ascii 50))
    (primary-artist principal)
  )
  (let ((new-track-id (+ (var-get total-registered-tracks) u1)))
    (begin
      (asserts! (is-contract-owner) ERR-UNAUTHORIZED-ACCESS)
      (asserts! (validate-ascii-string track-title) ERR-INVALID-SONG-TITLE)
      (asserts! (validate-rights-holder primary-artist) ERR-INVALID-ARTIST)

      (map-set music-catalog { song-id: new-track-id } {
        title: track-title,
        primary-artist: primary-artist,
        total-revenue: u0,
        release-date: stacks-block-height,
        is-active: true,
      })
      (var-set total-registered-tracks new-track-id)
      (ok new-track-id)
    )
  )
)

(define-public (set-royalty-allocation
    (song-id uint)
    (rights-holder principal)
    (percentage uint)
    (participant-role (string-ascii 20))
  )
  (let ((track-record (get-track-details song-id)))
    (begin
      (asserts! (is-some track-record) ERR-SONG-NOT-FOUND)
      (asserts! (validate-royalty-percentage percentage)
        ERR-INVALID-ROYALTY-PERCENTAGE
      )
      (asserts! (validate-participant-role participant-role)
        ERR-INVALID-PARTICIPANT-ROLE
      )
      (asserts! (validate-rights-holder rights-holder) ERR-INVALID-RECIPIENT)

      (map-set royalty-allocations {
        song-id: song-id,
        rights-holder: rights-holder,
      } {
        percentage: percentage,
        role: participant-role,
        earned-royalties: u0,
      })
      (ok true)
    )
  )
)

(define-public (process-royalty-payment
    (song-id uint)
    (payment-amount uint)
  )
  (let ((track-record (get-track-details song-id)))
    (begin
      (asserts! (is-some track-record) ERR-SONG-NOT-FOUND)
      (asserts! (>= (stx-get-balance tx-sender) payment-amount)
        ERR-INSUFFICIENT-PAYMENT
      )

      (try! (distribute-royalties song-id payment-amount))
      (map-set music-catalog { song-id: song-id }
        (merge (unwrap-panic track-record) { total-revenue: (+ (get total-revenue (unwrap-panic track-record)) payment-amount) })
      )
      (ok true)
    )
  )
)

(define-public (update-track-status
    (song-id uint)
    (is-active bool)
  )
  (let ((track-record (get-track-details song-id)))
    (begin
      (asserts! (is-contract-owner) ERR-UNAUTHORIZED-ACCESS)
      (asserts! (is-some track-record) ERR-SONG-NOT-FOUND)

      (map-set music-catalog { song-id: song-id }
        (merge (unwrap-panic track-record) { is-active: is-active })
      )
      (ok true)
    )
  )
)

(define-public (transfer-ownership (new-owner principal))
  (begin
    (asserts! (is-contract-owner) ERR-UNAUTHORIZED-ACCESS)
    (asserts! (validate-rights-holder new-owner) ERR-INVALID-ADMIN)

    (var-set contract-owner new-owner)
    (ok true)
  )
)

;; Contract Initialization
(begin
  (var-set total-registered-tracks u0)
)

Functions (11)

FunctionAccessArgs
is-contract-ownerprivate
validate-royalty-percentageprivatepercentage: uint
validate-ascii-stringprivateinput: (string-ascii 50
validate-participant-roleprivaterole: (string-ascii 20
validate-rights-holderprivateholder: principal
get-track-detailsread-onlysong-id: uint
get-total-tracksread-only
get-track-royalty-sharesread-onlysong-id: uint
register-trackpublictrack-title: (string-ascii 50
set-royalty-allocationpublicsong-id: uint, rights-holder: principal, percentage: uint, participant-role: (string-ascii 20
transfer-ownershippublicnew-owner: principal