From b9a55a2eabd6282fd51d938baaa23d713d216a78 Mon Sep 17 00:00:00 2001 From: blessychoco Date: Mon, 23 Sep 2024 17:06:11 +0100 Subject: [PATCH 1/8] modified distribute-to-recipient function --- contracts/clearcut.clar | 61 ++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/contracts/clearcut.clar b/contracts/clearcut.clar index e9f2add..746082c 100644 --- a/contracts/clearcut.clar +++ b/contracts/clearcut.clar @@ -3,40 +3,49 @@ ;; 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 data maps (define-map royalty-recipients principal uint) +(define-map recipient-list uint principal) (define-map total-distributed uint uint) +;; Define a variable to keep track of the number of recipients +(define-data-var num-recipients uint u0) + ;; Public function to set royalty percentage for a recipient (define-public (set-royalty-percentage (recipient principal) (percentage uint)) (begin (asserts! (is-eq tx-sender contract-owner) err-owner-only) (asserts! (<= percentage u100) err-invalid-percentage) + (if (is-none (map-get? royalty-recipients recipient)) + (let ((new-index (var-get num-recipients))) + (map-set recipient-list new-index recipient) + (var-set num-recipients (+ new-index u1))) + true) (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) - (begin - (try! (as-contract (stx-transfer? payment tx-sender recipient-principal))) - (- remaining payment)) - remaining))) +;; Public function to distribute royalties to a single recipient +(define-public (distribute-to-recipient (recipient-index uint) (amount uint)) + (let ((num-recip (var-get num-recipients))) + (if (>= recipient-index num-recip) + (err err-invalid-recipient) + (match (map-get? recipient-list recipient-index) + recipient + (let ((percentage (default-to u0 (map-get? royalty-recipients recipient))) + (payment (/ (* amount percentage) u100))) + (if (> payment u0) + (begin + (map-set total-distributed block-height + (+ (default-to u0 (map-get? total-distributed block-height)) payment)) + (match (as-contract (stx-transfer? payment tx-sender recipient)) + success (ok payment) + error (err err-transfer-failed))) + (ok u0))) + (err err-invalid-recipient))))) ;; Read-only function to get royalty percentage for a recipient (define-read-only (get-royalty-percentage (recipient principal)) @@ -45,3 +54,11 @@ ;; Read-only function to get total distributed amount (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 index +(define-read-only (get-recipient-by-index (index uint)) + (ok (map-get? recipient-list index))) \ No newline at end of file From ce66b96c05a70c59897dc16fbf6976357865c8cd Mon Sep 17 00:00:00 2001 From: blessychoco Date: Mon, 23 Sep 2024 22:01:12 +0100 Subject: [PATCH 2/8] remove-recipient function --- README.md | 84 +++++++++++++++++++++++++++++++++++++++++ contracts/clearcut.clar | 36 ++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..232989d --- /dev/null +++ b/README.md @@ -0,0 +1,84 @@ +# 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-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 + +## 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 distribute royalties, call `distribute-to-recipient` for each recipient with the total amount to be distributed. +4. Use the read-only functions to query the contract's state at any time. + +## 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. + +Always review and test thoroughly before deploying to mainnet. \ No newline at end of file diff --git a/contracts/clearcut.clar b/contracts/clearcut.clar index 746082c..fa60632 100644 --- a/contracts/clearcut.clar +++ b/contracts/clearcut.clar @@ -7,10 +7,12 @@ (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 data maps (define-map royalty-recipients principal uint) (define-map recipient-list uint principal) +(define-map recipient-indices principal uint) (define-map total-distributed uint uint) ;; Define a variable to keep track of the number of recipients @@ -24,10 +26,44 @@ (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) (ok (map-set royalty-recipients recipient percentage)))) +;; Public function to remove a recipient +(define-public (remove-recipient (recipient principal)) + (if (is-eq tx-sender contract-owner) + (match (map-get? recipient-indices recipient) + index (begin + (map-delete royalty-recipients recipient) + (map-delete recipient-indices recipient) + (shift-recipients index) + (var-set num-recipients (- (var-get num-recipients) u1)) + (ok true)) + (err err-recipient-not-found)) + (err err-owner-only))) + +;; Private function to shift recipients after removal +(define-private (shift-recipients (removed-index uint)) + (let ((num-recip (var-get num-recipients))) + (map shift-single-recipient + (map-to-list removed-index (- num-recip u1))))) + +;; Helper function to shift a single recipient +(define-private (shift-single-recipient (index uint)) + (match (map-get? recipient-list (+ index u1)) + next-recipient (begin + (map-set recipient-list index next-recipient) + (map-set recipient-indices next-recipient index) + true) + false)) + +;; Helper function to create a list of indices to shift +(define-private (map-to-list (start uint) (end uint)) + (list start (+ start u1) (+ start u2) (+ start u3) (+ start u4) + (+ start u5) (+ start u6) (+ start u7) (+ start u8) (+ start u9))) + ;; Public function to distribute royalties to a single recipient (define-public (distribute-to-recipient (recipient-index uint) (amount uint)) (let ((num-recip (var-get num-recipients))) From 431cd5b7f7da7617079e8283692986fe3c7d6c23 Mon Sep 17 00:00:00 2001 From: blessychoco Date: Mon, 23 Sep 2024 23:29:23 +0100 Subject: [PATCH 3/8] contract fix --- contracts/clearcut.clar | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/contracts/clearcut.clar b/contracts/clearcut.clar index fa60632..3cbf63d 100644 --- a/contracts/clearcut.clar +++ b/contracts/clearcut.clar @@ -33,7 +33,8 @@ ;; Public function to remove a recipient (define-public (remove-recipient (recipient principal)) - (if (is-eq tx-sender contract-owner) + (begin + (asserts! (is-eq tx-sender contract-owner) err-owner-only) (match (map-get? recipient-indices recipient) index (begin (map-delete royalty-recipients recipient) @@ -41,14 +42,13 @@ (shift-recipients index) (var-set num-recipients (- (var-get num-recipients) u1)) (ok true)) - (err err-recipient-not-found)) - (err err-owner-only))) + (ok false)))) ;; Private function to shift recipients after removal (define-private (shift-recipients (removed-index uint)) (let ((num-recip (var-get num-recipients))) (map shift-single-recipient - (map-to-list removed-index (- num-recip u1))))) + (unwrap-panic (slice? (map-to-list) removed-index (- num-recip u1)))))) ;; Helper function to shift a single recipient (define-private (shift-single-recipient (index uint)) @@ -60,9 +60,8 @@ false)) ;; Helper function to create a list of indices to shift -(define-private (map-to-list (start uint) (end uint)) - (list start (+ start u1) (+ start u2) (+ start u3) (+ start u4) - (+ start u5) (+ start u6) (+ start u7) (+ start u8) (+ start u9))) +(define-private (map-to-list) + (list u0 u1 u2 u3 u4 u5 u6 u7 u8 u9)) ;; Public function to distribute royalties to a single recipient (define-public (distribute-to-recipient (recipient-index uint) (amount uint)) @@ -74,14 +73,14 @@ (let ((percentage (default-to u0 (map-get? royalty-recipients recipient))) (payment (/ (* amount percentage) u100))) (if (> payment u0) - (begin - (map-set total-distributed block-height - (+ (default-to u0 (map-get? total-distributed block-height)) payment)) - (match (as-contract (stx-transfer? payment tx-sender recipient)) - success (ok payment) - error (err err-transfer-failed))) + (match (as-contract (stx-transfer? payment tx-sender recipient)) + success (begin + (map-set total-distributed block-height + (+ (default-to u0 (map-get? total-distributed block-height)) payment)) + (ok payment)) + error (err err-transfer-failed)) (ok u0))) - (err err-invalid-recipient))))) + (err err-recipient-not-found))))) ;; Read-only function to get royalty percentage for a recipient (define-read-only (get-royalty-percentage (recipient principal)) From 4c3ae16f9f756d502a9c0c15cad045e818062bd6 Mon Sep 17 00:00:00 2001 From: blessychoco Date: Mon, 23 Sep 2024 23:43:14 +0100 Subject: [PATCH 4/8] updated README.md file --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 232989d..9fb170a 100644 --- a/README.md +++ b/README.md @@ -81,4 +81,6 @@ In this example, we set a 30% royalty for a recipient and then distribute 1,000, - 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. -Always review and test thoroughly before deploying to mainnet. \ No newline at end of file +## Author + +Blessing Eze \ No newline at end of file From 15b0e1723bc8b7d691e267a3782a0ec4509e38a6 Mon Sep 17 00:00:00 2001 From: blessychoco Date: Mon, 23 Sep 2024 23:54:29 +0100 Subject: [PATCH 5/8] batch royalty distribution --- contracts/clearcut.clar | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/contracts/clearcut.clar b/contracts/clearcut.clar index 3cbf63d..6263f5b 100644 --- a/contracts/clearcut.clar +++ b/contracts/clearcut.clar @@ -8,6 +8,7 @@ (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 data maps (define-map royalty-recipients principal uint) @@ -47,21 +48,18 @@ ;; Private function to shift recipients after removal (define-private (shift-recipients (removed-index uint)) (let ((num-recip (var-get num-recipients))) - (map shift-single-recipient - (unwrap-panic (slice? (map-to-list) removed-index (- num-recip u1)))))) + (fold shift-single-recipient + (list removed-index (- num-recip u1)) + true))) ;; Helper function to shift a single recipient -(define-private (shift-single-recipient (index uint)) +(define-private (shift-single-recipient (index uint) (last bool)) (match (map-get? recipient-list (+ index u1)) next-recipient (begin (map-set recipient-list index next-recipient) (map-set recipient-indices next-recipient index) - true) - false)) - -;; Helper function to create a list of indices to shift -(define-private (map-to-list) - (list u0 u1 u2 u3 u4 u5 u6 u7 u8 u9)) + last) + last)) ;; Public function to distribute royalties to a single recipient (define-public (distribute-to-recipient (recipient-index uint) (amount uint)) @@ -82,6 +80,28 @@ (ok u0))) (err err-recipient-not-found))))) +;; New function to distribute royalties to all recipients in a single transaction +(define-public (batch-distribute-royalties (total-amount uint)) + (let ((num-recip (var-get num-recipients))) + (if (is-eq num-recip u0) + (err err-no-recipients) + (let ((result (fold distribute-to-recipient-fold + (list u0 (- num-recip u1)) + (tuple (amount total-amount) (total-distributed u0) (success true))))) + (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 to distribute to a single recipient within the fold +(define-private (distribute-to-recipient-fold (index uint) (state (tuple (amount uint) (total-distributed uint) (success bool)))) + (if (get success state) + (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)) + ;; Read-only function to get royalty percentage for a recipient (define-read-only (get-royalty-percentage (recipient principal)) (ok (default-to u0 (map-get? royalty-recipients recipient)))) From f19a173fd7d268607cee3d31caa4ec9b2f60962a Mon Sep 17 00:00:00 2001 From: blessychoco Date: Tue, 24 Sep 2024 20:11:13 +0100 Subject: [PATCH 6/8] Updated Contract with Automated Recurring Distributions --- contracts/clearcut.clar | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/contracts/clearcut.clar b/contracts/clearcut.clar index 6263f5b..754b7b1 100644 --- a/contracts/clearcut.clar +++ b/contracts/clearcut.clar @@ -1,4 +1,4 @@ -;; Royalty Distribution Smart Contract +;; Royalty Distribution Smart Contract with Recurring Distributions ;; Define constants (define-constant contract-owner tx-sender) @@ -9,6 +9,7 @@ (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) @@ -16,8 +17,10 @@ (define-map recipient-indices principal uint) (define-map total-distributed uint uint) -;; Define a variable to keep track of the number of recipients +;; Define variables to keep track of the number of recipients and distribution timing (define-data-var num-recipients uint u0) +(define-data-var distribution-interval uint u1440) ;; e.g., once every 1440 blocks (~1 day) +(define-data-var last-distribution-block uint u0) ;; Public function to set royalty percentage for a recipient (define-public (set-royalty-percentage (recipient principal) (percentage uint)) @@ -102,6 +105,32 @@ error (merge state { success: false })) state)) +;; Automated recurring distribution function +(define-public (automated-distribute (total-amount uint)) + (let ((current-block block-height) + (last-distribution (var-get last-distribution-block)) + (interval (var-get distribution-interval))) + (if (>= (- current-block last-distribution) interval) + (begin + (var-set last-distribution-block current-block) + (batch-distribute-royalties total-amount)) + (err err-too-soon)))) + +;; Public function to adjust the distribution interval +(define-public (set-distribution-interval (new-interval uint)) + (begin + (asserts! (is-eq tx-sender contract-owner) err-owner-only) + (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 the last distribution block +(define-read-only (get-last-distribution-block) + (ok (var-get last-distribution-block))) + ;; Read-only function to get royalty percentage for a recipient (define-read-only (get-royalty-percentage (recipient principal)) (ok (default-to u0 (map-get? royalty-recipients recipient)))) @@ -116,4 +145,4 @@ ;; Read-only function to get a recipient by index (define-read-only (get-recipient-by-index (index uint)) - (ok (map-get? recipient-list index))) \ No newline at end of file + (ok (map-get? recipient-list index))) From 7ee00ea08941c45f67ad55d229307b118c3a7647 Mon Sep 17 00:00:00 2001 From: blessychoco Date: Tue, 24 Sep 2024 20:39:07 +0100 Subject: [PATCH 7/8] updated the distribute-to-recipient function to handle the unchecked data warnings --- contracts/clearcut.clar | 137 +++++++++++++++++++++------------------- 1 file changed, 72 insertions(+), 65 deletions(-) diff --git a/contracts/clearcut.clar b/contracts/clearcut.clar index 754b7b1..1e60db5 100644 --- a/contracts/clearcut.clar +++ b/contracts/clearcut.clar @@ -1,4 +1,4 @@ -;; Royalty Distribution Smart Contract with Recurring Distributions +;; Royalty Distribution Smart Contract with Simplified "Remove" Functionality ;; Define constants (define-constant contract-owner tx-sender) @@ -11,138 +11,145 @@ (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 recipient-list uint principal) -(define-map recipient-indices 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 -;; Define variables to keep track of the number of recipients and distribution timing -(define-data-var num-recipients uint u0) -(define-data-var distribution-interval uint u1440) ;; e.g., once every 1440 blocks (~1 day) -(define-data-var last-distribution-block uint u0) +;; 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 +;; 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 remove a recipient -(define-public (remove-recipient (recipient principal)) - (begin - (asserts! (is-eq tx-sender contract-owner) err-owner-only) - (match (map-get? recipient-indices recipient) - index (begin - (map-delete royalty-recipients recipient) - (map-delete recipient-indices recipient) - (shift-recipients index) - (var-set num-recipients (- (var-get num-recipients) u1)) - (ok true)) - (ok false)))) - -;; Private function to shift recipients after removal -(define-private (shift-recipients (removed-index uint)) - (let ((num-recip (var-get num-recipients))) - (fold shift-single-recipient - (list removed-index (- num-recip u1)) - true))) - -;; Helper function to shift a single recipient -(define-private (shift-single-recipient (index uint) (last bool)) - (match (map-get? recipient-list (+ index u1)) - next-recipient (begin - (map-set recipient-list index next-recipient) - (map-set recipient-indices next-recipient index) - last) - last)) - -;; Public function to distribute royalties to a single recipient +;; 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) - (match (map-get? recipient-list recipient-index) - recipient - (let ((percentage (default-to u0 (map-get? royalty-recipients recipient))) - (payment (/ (* amount percentage) u100))) - (if (> payment u0) - (match (as-contract (stx-transfer? payment tx-sender recipient)) - success (begin - (map-set total-distributed block-height - (+ (default-to u0 (map-get? total-distributed block-height)) payment)) - (ok payment)) - error (err err-transfer-failed)) - (ok u0))) - (err err-recipient-not-found))))) - -;; New function to distribute royalties to all recipients in a single transaction + ;; 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 to distribute to a single recipient within the fold +;; 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)) -;; Automated recurring distribution function +;; 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 + ;; 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 adjust the distribution interval +;; 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 +;; 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 the last distribution block +;; 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 royalty percentage for a recipient +;; 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 +;; 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 index +;; 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))) From 7e6a4a5aac66bdde8294ea3a1b8c0b1c98acdead Mon Sep 17 00:00:00 2001 From: blessychoco Date: Tue, 24 Sep 2024 21:21:09 +0100 Subject: [PATCH 8/8] updated README.md file --- README.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9fb170a..467e99b 100644 --- a/README.md +++ b/README.md @@ -46,17 +46,22 @@ This Clarity smart contract manages the distribution of royalties to multiple re ## Error Codes - `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-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 distribute royalties, call `distribute-to-recipient` for each recipient with the total amount to be distributed. -4. Use the read-only functions to query the contract's state at any time. +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