Source Code

;; Royalty Distributor Contract
;; Handles automatic royalty distribution for NFT sales

;; Constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u300))
(define-constant err-invalid-royalty (err u301))
(define-constant err-invalid-amount (err u302))
(define-constant err-transfer-failed (err u303))
(define-constant err-royalty-not-set (err u304))
(define-constant err-invalid-recipient (err u305))
(define-constant err-calculation-error (err u306))

;; Maximum royalty: 10% (1000 basis points)
(define-constant max-royalty-basis-points u1000)
(define-constant basis-points-divisor u10000)

;; Default platform fee: 2.5% (250 basis points)
(define-constant default-platform-fee u250)

;; Data Variables
(define-data-var platform-fee-recipient principal contract-owner)
(define-data-var platform-fee-basis-points uint default-platform-fee)
(define-data-var royalty-enabled bool true)

;; Data Maps
(define-map token-royalties 
    {nft-contract: principal, token-id: uint}
    {
        creator: principal,
        royalty-percent: uint
    }
)

(define-map collection-default-royalties
    principal
    {
        creator: principal,
        royalty-percent: uint
    }
)

(define-map creator-earnings principal uint)
(define-map platform-earnings principal uint)

;; Read-only functions

(define-read-only (get-token-royalty (nft-contract principal) (token-id uint))
    (ok (map-get? token-royalties {nft-contract: nft-contract, token-id: token-id}))
)

(define-read-only (get-collection-default-royalty (nft-contract principal))
    (ok (map-get? collection-default-royalties nft-contract))
)

(define-read-only (calculate-royalty (sale-price uint) (royalty-percent uint))
    (if (<= royalty-percent max-royalty-basis-points)
        (ok (/ (* sale-price royalty-percent) basis-points-divisor))
        err-invalid-royalty
    )
)

(define-read-only (calculate-platform-fee (sale-price uint))
    (ok (/ (* sale-price (var-get platform-fee-basis-points)) basis-points-divisor))
)

(define-read-only (calculate-distribution (sale-price uint) (royalty-percent uint))
    (let 
        (
            (platform-fee (unwrap! (calculate-platform-fee sale-price) err-calculation-error))
            (royalty-amount (unwrap! (calculate-royalty sale-price royalty-percent) err-calculation-error))
            (seller-amount (- (- sale-price platform-fee) royalty-amount))
        )
        (ok {
            platform-fee: platform-fee,
            royalty-amount: royalty-amount,
            seller-amount: seller-amount,
            total: sale-price
        })
    )
)

(define-read-only (get-creator-earnings (creator principal))
    (ok (default-to u0 (map-get? creator-earnings creator)))
)

(define-read-only (get-platform-earnings)
    (ok (default-to u0 (map-get? platform-earnings (var-get platform-fee-recipient))))
)

(define-read-only (get-platform-fee-info)
    (ok {
        recipient: (var-get platform-fee-recipient),
        fee-basis-points: (var-get platform-fee-basis-points),
        fee-percentage: (/ (* (var-get platform-fee-basis-points) u100) basis-points-divisor)
    })
)

(define-read-only (is-royalty-enabled)
    (ok (var-get royalty-enabled))
)

;; Private functions

(define-private (transfer-stx-safe (amount uint) (sender principal) (recipient principal))
    (if (> amount u0)
        (stx-transfer? amount sender recipient)
        (ok true)
    )
)

(define-private (update-creator-earnings (creator principal) (amount uint))
    (let ((current-earnings (default-to u0 (map-get? creator-earnings creator))))
        (map-set creator-earnings creator (+ current-earnings amount))
        true
    )
)

(define-private (update-platform-earnings (amount uint))
    (let 
        (
            (recipient (var-get platform-fee-recipient))
            (current-earnings (default-to u0 (map-get? platform-earnings recipient)))
        )
        (map-set platform-earnings recipient (+ current-earnings amount))
        true
    )
)

;; Public functions

(define-public (set-token-royalty 
    (nft-contract principal) 
    (token-id uint) 
    (creator principal) 
    (royalty-percent uint))
    (begin
        ;; Validations
        (asserts! (<= royalty-percent max-royalty-basis-points) err-invalid-royalty)
        (asserts! (not (is-eq creator (var-get platform-fee-recipient))) err-invalid-recipient)
        
        ;; Set royalty info
        (map-set token-royalties 
            {nft-contract: nft-contract, token-id: token-id}
            {creator: creator, royalty-percent: royalty-percent}
        )
        
        (print {
            event: "royalty-set",
            nft-contract: nft-contract,
            token-id: token-id,
            creator: creator,
            royalty-percent: royalty-percent
        })
        
        (ok true)
    )
)

(define-public (set-collection-default-royalty 
    (nft-contract principal) 
    (creator principal) 
    (royalty-percent uint))
    (begin
        ;; Validations
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (asserts! (<= royalty-percent max-royalty-basis-points) err-invalid-royalty)
        
        ;; Set collection default
        (map-set collection-default-royalties 
            nft-contract
            {creator: creator, royalty-percent: royalty-percent}
        )
        
        (print {
            event: "collection-royalty-set",
            nft-contract: nft-contract,
            creator: creator,
            royalty-percent: royalty-percent
        })
        
        (ok true)
    )
)

(define-public (distribute-payment 
    (nft-contract principal) 
    (token-id uint) 
    (sale-price uint) 
    (seller principal) 
    (buyer principal))
    (let 
        (
            (royalty-info (default-to 
                (default-to {creator: seller, royalty-percent: u0} 
                    (map-get? collection-default-royalties nft-contract))
                (map-get? token-royalties {nft-contract: nft-contract, token-id: token-id})))
            (creator (get creator royalty-info))
            (royalty-percent (get royalty-percent royalty-info))
            (distribution (unwrap! (calculate-distribution sale-price royalty-percent) err-calculation-error))
            (platform-fee (get platform-fee distribution))
            (royalty-amount (get royalty-amount distribution))
            (seller-amount (get seller-amount distribution))
        )
        ;; Validations
        (asserts! (var-get royalty-enabled) err-owner-only)
        (asserts! (> sale-price u0) err-invalid-amount)
        
        ;; Transfer platform fee
        (if (> platform-fee u0)
            (begin
                (try! (transfer-stx-safe platform-fee buyer (var-get platform-fee-recipient)))
                (asserts! (update-platform-earnings platform-fee) err-transfer-failed)
            )
            true
        )
        
        ;; Transfer royalty to creator
        (if (and (> royalty-amount u0) (not (is-eq creator seller)))
            (begin
                (try! (transfer-stx-safe royalty-amount buyer creator))
                (asserts! (update-creator-earnings creator royalty-amount) err-transfer-failed)
            )
            true
        )
        
        ;; Transfer remaining amount to seller
        (try! (transfer-stx-safe seller-amount buyer seller))
        
        (print {
            event: "payment-distributed",
            nft-contract: nft-contract,
            token-id: token-id,
            sale-price: sale-price,
            buyer: buyer,
            seller: seller,
            creator: creator,
            platform-fee: platform-fee,
            royalty-amount: royalty-amount,
            seller-amount: seller-amount
        })
        
        (ok {
            platform-fee: platform-fee,
            royalty-amount: royalty-amount,
            seller-amount: seller-amount
        })
    )
)

(define-public (distribute-payment-simple
    (sale-price uint)
    (seller principal)
    (creator principal)
    (royalty-percent uint))
    (let 
        (
            (distribution (unwrap! (calculate-distribution sale-price royalty-percent) err-calculation-error))
            (platform-fee (get platform-fee distribution))
            (royalty-amount (get royalty-amount distribution))
            (seller-amount (get seller-amount distribution))
        )
        ;; Validations
        (asserts! (var-get royalty-enabled) err-owner-only)
        (asserts! (> sale-price u0) err-invalid-amount)
        (asserts! (<= royalty-percent max-royalty-basis-points) err-invalid-royalty)
        
        ;; Transfer platform fee
        (if (> platform-fee u0)
            (begin
                (try! (transfer-stx-safe platform-fee tx-sender (var-get platform-fee-recipient)))
                (asserts! (update-platform-earnings platform-fee) err-transfer-failed)
            )
            true
        )
        
        ;; Transfer royalty to creator
        (if (and (> royalty-amount u0) (not (is-eq creator seller)))
            (begin
                (try! (transfer-stx-safe royalty-amount tx-sender creator))
                (asserts! (update-creator-earnings creator royalty-amount) err-transfer-failed)
            )
            true
        )
        
        ;; Transfer remaining amount to seller
        (try! (transfer-stx-safe seller-amount tx-sender seller))
        
        (print {
            event: "simple-payment-distributed",
            sale-price: sale-price,
            buyer: tx-sender,
            seller: seller,
            creator: creator,
            platform-fee: platform-fee,
            royalty-amount: royalty-amount,
            seller-amount: seller-amount
        })
        
        (ok distribution)
    )
)

;; Withdrawal functions

(define-public (withdraw-creator-earnings)
    (let 
        (
            (earnings (default-to u0 (map-get? creator-earnings tx-sender)))
        )
        (asserts! (> earnings u0) err-invalid-amount)
        
        ;; Reset earnings
        (map-set creator-earnings tx-sender u0)
        
        (print {
            event: "creator-withdrawal",
            creator: tx-sender,
            amount: earnings,
            note: "Earnings tracked but withdrawal must be processed separately"
        })
        
        (ok earnings)
    )
)

(define-public (withdraw-platform-earnings)
    (let 
        (
            (recipient (var-get platform-fee-recipient))
            (earnings (default-to u0 (map-get? platform-earnings recipient)))
        )
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (asserts! (> earnings u0) err-invalid-amount)
        
        ;; Reset earnings
        (map-set platform-earnings recipient u0)
        
        (print {
            event: "platform-withdrawal",
            recipient: recipient,
            amount: earnings,
            note: "Earnings tracked but withdrawal must be processed separately"
        })
        
        (ok earnings)
    )
)

;; Admin functions

(define-public (set-platform-fee-recipient (new-recipient principal))
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (var-set platform-fee-recipient new-recipient)
        
        (print {
            event: "platform-fee-recipient-updated",
            new-recipient: new-recipient
        })
        
        (ok true)
    )
)

(define-public (set-platform-fee (new-fee-basis-points uint))
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (asserts! (<= new-fee-basis-points max-royalty-basis-points) err-invalid-royalty)
        (var-set platform-fee-basis-points new-fee-basis-points)
        
        (print {
            event: "platform-fee-updated",
            new-fee-basis-points: new-fee-basis-points
        })
        
        (ok true)
    )
)

(define-public (toggle-royalty-system)
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)
        (var-set royalty-enabled (not (var-get royalty-enabled)))
        
        (print {
            event: "royalty-system-toggled",
            enabled: (var-get royalty-enabled)
        })
        
        (ok (var-get royalty-enabled))
    )
)

Functions (21)

FunctionAccessArgs
get-token-royaltyread-onlynft-contract: principal, token-id: uint
get-collection-default-royaltyread-onlynft-contract: principal
calculate-royaltyread-onlysale-price: uint, royalty-percent: uint
calculate-platform-feeread-onlysale-price: uint
calculate-distributionread-onlysale-price: uint, royalty-percent: uint
get-creator-earningsread-onlycreator: principal
get-platform-earningsread-only
get-platform-fee-inforead-only
is-royalty-enabledread-only
transfer-stx-safeprivateamount: uint, sender: principal, recipient: principal
update-creator-earningsprivatecreator: principal, amount: uint
update-platform-earningsprivateamount: uint
set-token-royaltypublicnft-contract: principal, token-id: uint, creator: principal, royalty-percent: uint
set-collection-default-royaltypublicnft-contract: principal, creator: principal, royalty-percent: uint
distribute-paymentpublicnft-contract: principal, token-id: uint, sale-price: uint, seller: principal, buyer: principal
distribute-payment-simplepublicsale-price: uint, seller: principal, creator: principal, royalty-percent: uint
withdraw-creator-earningspublic
withdraw-platform-earningspublic
set-platform-fee-recipientpublicnew-recipient: principal
set-platform-feepublicnew-fee-basis-points: uint
toggle-royalty-systempublic