Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Royalty Distribution Smart Contract

This Clarity smart contract manages the distribution of royalties to multiple recipients based on predefined percentages. It's designed to run on the Stacks blockchain.

## Features

- Set royalty percentages for recipients
- Distribute royalties to individual recipients
- Track total distributed amount
- Query contract state (recipient percentages, total distributed, etc.)

## Functions

### Public Functions

1. `set-royalty-percentage`
- Sets the royalty percentage for a recipient
- Parameters:
- `recipient`: `principal`
- `percentage`: `uint`
- Only the contract owner can call this function

2. `distribute-to-recipient`
- Distributes royalties to a single recipient
- Parameters:
- `recipient-index`: `uint`
- `amount`: `uint`
- Returns the amount transferred or an error

### Read-Only Functions

1. `get-royalty-percentage`
- Gets the royalty percentage for a recipient
- Parameter: `recipient`: `principal`

2. `get-total-distributed`
- Gets the total amount distributed so far

3. `get-num-recipients`
- Gets the number of recipients

4. `get-recipient-by-index`
- Gets a recipient by their index
- Parameter: `index`: `uint`

## Error Codes

- `err-owner-only (u100)`: Only the contract owner can perform this action
- err-owner-only (u100): Only the contract owner can perform this action
- err-invalid-percentage (u101): The percentage must be between 0 and 100
- err-no-recipients (u102): There are no recipients set
- err-invalid-recipient (u103): The recipient index is invalid
- err-transfer-failed (u104): The STX transfer failed
- err-distribution-failed (u105): The batch distribution failed
- err-invalid-interval (u106): The interval must be a positive value

## Usage

1. Deploy the contract to the Stacks blockchain.
2. As the contract owner, use set-royalty-percentage to set percentages for each recipient.
3. To remove a recipient, call remove-recipient.
4. To distribute royalties, call distribute-to-recipient for individual recipients or batch-distribute-royalties for all recipients with the total amount to be distributed.
5. Use the read-only functions to query the contract's state at any time.
6. To set automated recurring distributions, use set-distribution-interval with the desired interval.

## Example

```clarity
;; Set royalty percentage
(contract-call? .royalty-distribution set-royalty-percentage 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM 30)

;; Distribute royalties
(contract-call? .royalty-distribution distribute-to-recipient u0 u1000000)
```

In this example, we set a 30% royalty for a recipient and then distribute 1,000,000 microSTX (1 STX) according to the set percentages.

## Notes

- All percentages and amounts are in micro-units (e.g., micro-percentages, microSTX).
- The contract uses block height to track distributions, so multiple distributions in the same block will overwrite each other in the `total-distributed` map.

## Security Considerations

- Only the contract owner can set royalty percentages.
- The contract uses `as-contract` when transferring STX to ensure it's using its own balance.
- Input validation is performed to prevent invalid percentages or recipient indices.

## Author

Blessing Eze
164 changes: 136 additions & 28 deletions contracts/clearcut.clar
Original file line number Diff line number Diff line change
@@ -1,47 +1,155 @@
;; Royalty Distribution Smart Contract
;; Royalty Distribution Smart Contract with Simplified "Remove" Functionality

;; Define constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-found (err u101))
(define-constant err-invalid-percentage (err u102))
(define-constant err-invalid-percentage (err u101))
(define-constant err-no-recipients (err u102))
(define-constant err-invalid-recipient (err u103))
(define-constant err-transfer-failed (err u104))
(define-constant err-recipient-not-found (err u105))
(define-constant err-distribution-failed (err u106))
(define-constant err-too-soon (err u107))

;; Define data maps
(define-map royalty-recipients principal uint)
(define-map total-distributed uint uint)
;; Define data maps to store recipients, indices, and distributed amounts
(define-map royalty-recipients principal uint) ;; Stores recipient percentages
(define-map recipient-list uint principal) ;; Maps index to recipient
(define-map recipient-indices principal uint) ;; Maps recipient to index
(define-map total-distributed uint uint) ;; Tracks total distributed per block

;; Public function to set royalty percentage for a recipient
;; Variables to track the number of recipients and distribution timing
(define-data-var num-recipients uint u0) ;; Total recipients count
(define-data-var distribution-interval uint u1440) ;; Time interval for recurring distribution (e.g., 1440 blocks ~ 1 day)
(define-data-var last-distribution-block uint u0) ;; Last block where distribution occurred

;; Public function to set royalty percentage for a recipient.
;; This also adds a new recipient if they do not exist, or updates their percentage.
(define-public (set-royalty-percentage (recipient principal) (percentage uint))
(begin
;; Ensure only the contract owner can set the percentage
(asserts! (is-eq tx-sender contract-owner) err-owner-only)
;; Ensure the percentage is between 0 and 100
(asserts! (<= percentage u100) err-invalid-percentage)
;; If the recipient does not exist, add them to the recipient list
(if (is-none (map-get? royalty-recipients recipient))
(let ((new-index (var-get num-recipients)))
(map-set recipient-list new-index recipient)
(map-set recipient-indices recipient new-index)
(var-set num-recipients (+ new-index u1)))
true)
;; Set or update the recipient's percentage
(ok (map-set royalty-recipients recipient percentage))))

;; Public function to distribute royalties
(define-public (distribute-royalties (amount uint))
(let ((total-percentage u0)
(remaining amount))
(map-set total-distributed block-height
(+ (default-to u0 (map-get? total-distributed block-height)) amount))
(ok (fold distribute-to-recipient
(map-to-list royalty-recipients)
remaining))))

;; Private function to distribute to a single recipient
(define-private (distribute-to-recipient (recipient (tuple (key principal) (value uint))) (remaining uint))
(let ((recipient-principal (get key recipient))
(percentage (get value recipient))
(payment (/ (* remaining percentage) u100)))
(if (> payment u0)
;; Public function to distribute royalties to a single recipient.
;; This checks if the recipient is valid and skips those with 0% royalty.
;; Public function to distribute royalties to a single recipient with input validation
(define-public (distribute-to-recipient (recipient-index uint) (amount uint))
(let ((num-recip (var-get num-recipients)))
;; Ensure the recipient index is valid
(if (>= recipient-index num-recip)
(err err-invalid-recipient)
;; Validate the amount is positive
(if (<= amount u0)
(err err-invalid-percentage) ;; Using this error for invalid amounts as well
;; Proceed with distribution if the index and amount are valid
(match (map-get? recipient-list recipient-index)
recipient
(let ((percentage (default-to u0 (map-get? royalty-recipients recipient)))
(payment (/ (* amount percentage) u100)))
;; Skip the recipient if their percentage is 0
(if (> percentage u0)
;; Ensure payment is valid before transfer
(if (> payment u0)
(match (as-contract (stx-transfer? payment tx-sender recipient))
success (begin
;; Record the total amount distributed in the current block
(map-set total-distributed block-height
(+ (default-to u0 (map-get? total-distributed block-height)) payment))
(ok payment))
error (err err-transfer-failed))
;; Handle case where payment is 0
(err err-invalid-percentage))
;; If the percentage is 0, no payment is made
(ok u0)))
;; Handle case where recipient is not found
(err err-recipient-not-found))))))



;; Public function to distribute royalties to all recipients in a single transaction.
;; It skips over recipients with 0% royalty.
(define-public (batch-distribute-royalties (total-amount uint))
(let ((num-recip (var-get num-recipients)))
;; Check if there are any recipients
(if (is-eq num-recip u0)
(err err-no-recipients)
;; Distribute to all recipients via a fold operation
(let ((result (fold distribute-to-recipient-fold
(list u0 (- num-recip u1))
(tuple (amount total-amount) (total-distributed u0) (success true)))))
;; If the distribution is successful, record the total distributed
(if (get success result)
(begin
(map-set total-distributed block-height (get total-distributed result))
(ok (get total-distributed result)))
(err err-distribution-failed))))))

;; Helper function for batch distribution (used in the fold).
;; It ensures the recipient's royalty is distributed only if their percentage is > 0.
(define-private (distribute-to-recipient-fold (index uint) (state (tuple (amount uint) (total-distributed uint) (success bool))))
(if (get success state)
;; Distribute to the recipient at the current index
(match (distribute-to-recipient index (get amount state))
distributed-amount (merge state { total-distributed: (+ (get total-distributed state) distributed-amount) })
error (merge state { success: false }))
state))

;; Public function to trigger automated recurring distributions.
;; This checks if enough time (block intervals) has passed since the last distribution.
(define-public (automated-distribute (total-amount uint))
(let ((current-block block-height)
(last-distribution (var-get last-distribution-block))
(interval (var-get distribution-interval)))
;; Check if the required number of blocks has passed for the next distribution
(if (>= (- current-block last-distribution) interval)
(begin
(try! (as-contract (stx-transfer? payment tx-sender recipient-principal)))
(- remaining payment))
remaining)))
;; Update the last distribution block and trigger batch distribution
(var-set last-distribution-block current-block)
(batch-distribute-royalties total-amount))
(err err-too-soon))))

;; Public function to set the distribution interval (in blocks).
;; This allows the contract owner to adjust how frequently distributions occur.
(define-public (set-distribution-interval (new-interval uint))
(begin
;; Only the contract owner can set the interval
(asserts! (is-eq tx-sender contract-owner) err-owner-only)
;; Ensure the interval is greater than 0
(asserts! (> new-interval u0) (err u108)) ;; Define a new error for invalid intervals
;; Set the new interval
(var-set distribution-interval new-interval)
(ok new-interval)))

;; Read-only function to get the current distribution interval.
(define-read-only (get-distribution-interval)
(ok (var-get distribution-interval)))

;; Read-only function to get royalty percentage for a recipient
;; Read-only function to get the last distribution block.
(define-read-only (get-last-distribution-block)
(ok (var-get last-distribution-block)))

;; Read-only function to get the royalty percentage for a recipient.
(define-read-only (get-royalty-percentage (recipient principal))
(ok (default-to u0 (map-get? royalty-recipients recipient))))

;; Read-only function to get total distributed amount
;; Read-only function to get the total distributed amount in the current block.
(define-read-only (get-total-distributed)
(ok (default-to u0 (map-get? total-distributed block-height))))

;; Read-only function to get the number of recipients.
(define-read-only (get-num-recipients)
(ok (var-get num-recipients)))

;; Read-only function to get a recipient by their index in the recipient list.
(define-read-only (get-recipient-by-index (index uint))
(ok (map-get? recipient-list index)))