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 (16)

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-royalty-detailsread-onlysong-id: uint, rights-holder: principal
get-total-tracksread-only
get-track-royalty-sharesread-onlysong-id: uint
calculate-rights-holder-paymentprivaterights-share: { rights-holder: principal, percentage: uint }, total-payment: uint
distribute-royaltiesprivatesong-id: uint, total-payment: uint
register-trackpublictrack-title: (string-ascii 50
set-royalty-allocationpublicsong-id: uint, rights-holder: principal, percentage: uint, participant-role: (string-ascii 20
process-royalty-paymentpublicsong-id: uint, payment-amount: uint
update-track-statuspublicsong-id: uint, is-active: bool
transfer-ownershippublicnew-owner: principal