Source Code

;; Music Royalty Distribution Smart Contract
;; This contract manages music royalty distributions among artists, producers, and rights holders

;; Error codes
(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-DOES-NOT-EXIST (err u103))
(define-constant ERR-INSUFFICIENT-PAYMENT-FUNDS (err u104))
(define-constant ERR-INVALID-ROYALTY-RECIPIENT (err u105))
(define-constant ERR-PAYMENT-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-PRIMARY-ARTIST (err u110))
(define-constant ERR-INVALID-ADMINISTRATOR (err u111))

;; Data structures
(define-map RegisteredSongs
  { song-identifier: uint }
  {
    song-title: (string-ascii 50),
    primary-artist: principal,
    accumulated-revenue: uint,
    publication-date: uint,
    song-status-active: bool,
  }
)

(define-map RoyaltyDistribution
  {
    song-identifier: uint,
    royalty-recipient: principal,
  }
  {
    royalty-percentage: uint,
    participant-role: (string-ascii 20),
    accumulated-earnings: uint,
  }
)

;; Track total registered songs
(define-data-var registered-song-count uint u0)

;; Track contract administrator
(define-data-var contract-administrator principal tx-sender)

;; Read-only functions
(define-read-only (get-song-information (song-identifier uint))
  (map-get? RegisteredSongs { song-identifier: song-identifier })
)

(define-read-only (get-royalty-distribution
    (song-identifier uint)
    (royalty-recipient principal)
  )
  (map-get? RoyaltyDistribution {
    song-identifier: song-identifier,
    royalty-recipient: royalty-recipient,
  })
)

(define-read-only (get-total-registered-songs)
  (var-get registered-song-count)
)

;; Get royalty shares for a song
(define-read-only (get-royalty-shares-by-song (song-identifier uint))
  (let (
      (song-info (get-song-information song-identifier))
      (primary-artist (match song-info
        record (get primary-artist record)
        tx-sender
      ))
    )
    (let ((distribution (get-royalty-distribution song-identifier primary-artist)))
      (match distribution
        share (list {
          royalty-recipient: primary-artist,
          royalty-percentage: (get royalty-percentage share),
        })
        (list)
      )
    )
  )
)

;; Helper functions for input validation
(define-private (is-valid-royalty-share (share {
  royalty-percentage: uint,
  participant-role: (string-ascii 20),
  accumulated-earnings: uint,
}))
  (> (get royalty-percentage share) u0)
)

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

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

(define-private (validate-string-ascii (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-principal (principal-to-check principal))
  (and
    (not (is-eq principal-to-check tx-sender)) ;; Can't be the sender
    (not (is-eq principal-to-check (var-get contract-administrator))) ;; Can't be the admin
  )
)

;; Fixed process-royalty-share function
(define-private (process-royalty-share
    (share {
      royalty-recipient: principal,
      royalty-percentage: uint,
    })
    (payment-amount uint)
  )
  (let ((recipient-payment-amount (/ (* payment-amount (get royalty-percentage share)) u100)))
    (if (> recipient-payment-amount u0)
      (match (stx-transfer? recipient-payment-amount tx-sender
        (get royalty-recipient share)
      )
        success payment-amount
        error u0
      )
      u0
    )
  )
)

;; Updated distribute-royalty-payment
(define-private (distribute-royalty-payment
    (song-identifier uint)
    (payment-amount uint)
  )
  (let (
      (royalty-distribution-list (get-royalty-shares-by-song song-identifier))
      (total-distributed (fold process-royalty-share royalty-distribution-list payment-amount))
    )
    (begin
      (asserts! (> (len royalty-distribution-list) u0) ERR-SONG-DOES-NOT-EXIST)
      (asserts! (> total-distributed u0) ERR-PAYMENT-FAILED)
      (ok total-distributed)
    )
  )
)

;; Public functions with added input validation
(define-public (register-new-song
    (song-title (string-ascii 50))
    (primary-artist principal)
  )
  (let ((new-song-identifier (+ (var-get registered-song-count) u1)))
    (begin
      (asserts! (verify-contract-administrator) ERR-UNAUTHORIZED-ACCESS)
      (asserts! (validate-string-ascii song-title) ERR-INVALID-SONG-TITLE)
      (asserts! (validate-principal primary-artist) ERR-INVALID-PRIMARY-ARTIST)

      (map-set RegisteredSongs { song-identifier: new-song-identifier } {
        song-title: song-title,
        primary-artist: primary-artist,
        accumulated-revenue: u0,
        publication-date: stacks-block-height,
        song-status-active: true,
      })
      (var-set registered-song-count new-song-identifier)
      (ok new-song-identifier)
    )
  )
)

(define-public (set-royalty-distribution
    (song-identifier uint)
    (royalty-recipient principal)
    (royalty-percentage uint)
    (participant-role (string-ascii 20))
  )
  (let ((song-record (get-song-information song-identifier)))
    (begin
      (asserts! (is-some song-record) ERR-SONG-DOES-NOT-EXIST)
      (asserts! (validate-royalty-percentage royalty-percentage)
        ERR-INVALID-ROYALTY-PERCENTAGE
      )
      (asserts! (validate-participant-role participant-role)
        ERR-INVALID-PARTICIPANT-ROLE
      )
      (asserts! (validate-principal royalty-recipient)
        ERR-INVALID-ROYALTY-RECIPIENT
      )

      (map-set RoyaltyDistribution {
        song-identifier: song-identifier,
        royalty-recipient: royalty-recipient,
      } {
        royalty-percentage: royalty-percentage,
        participant-role: participant-role,
        accumulated-earnings: u0,
      })
      (ok true)
    )
  )
)

(define-public (process-royalty-payment
    (song-identifier uint)
    (royalty-payment-amount uint)
  )
  (let ((song-record (get-song-information song-identifier)))
    (begin
      (asserts! (is-some song-record) ERR-SONG-DOES-NOT-EXIST)
      (asserts! (>= (stx-get-balance tx-sender) royalty-payment-amount)
        ERR-INSUFFICIENT-PAYMENT-FUNDS
      )

      (try! (distribute-royalty-payment song-identifier royalty-payment-amount))
      (map-set RegisteredSongs { song-identifier: song-identifier }
        (merge (unwrap-panic song-record) { accumulated-revenue: (+ (get accumulated-revenue (unwrap-panic song-record))
          royalty-payment-amount
        ) }
        ))
      (ok true)
    )
  )
)

(define-public (update-song-active-status
    (song-identifier uint)
    (new-active-status bool)
  )
  (let ((song-record (get-song-information song-identifier)))
    (begin
      (asserts! (verify-contract-administrator) ERR-UNAUTHORIZED-ACCESS)
      (asserts! (is-some song-record) ERR-SONG-DOES-NOT-EXIST)

      (map-set RegisteredSongs { song-identifier: song-identifier }
        (merge (unwrap-panic song-record) { song-status-active: new-active-status })
      )
      (ok true)
    )
  )
)

(define-public (transfer-administrator-rights (new-administrator principal))
  (begin
    (asserts! (verify-contract-administrator) ERR-UNAUTHORIZED-ACCESS)
    (asserts! (validate-principal new-administrator) ERR-INVALID-ADMINISTRATOR)

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

;; Contract initialization
(begin
  (var-set registered-song-count u0)
)

Functions (11)

FunctionAccessArgs
get-song-informationread-onlysong-identifier: uint
get-total-registered-songsread-only
get-royalty-shares-by-songread-onlysong-identifier: uint
verify-contract-administratorprivate
validate-royalty-percentageprivateroyalty-percentage: uint
validate-string-asciiprivateinput: (string-ascii 50
validate-participant-roleprivaterole: (string-ascii 20
validate-principalprivateprincipal-to-check: principal
register-new-songpublicsong-title: (string-ascii 50
set-royalty-distributionpublicsong-identifier: uint, royalty-recipient: principal, royalty-percentage: uint, participant-role: (string-ascii 20
transfer-administrator-rightspublicnew-administrator: principal