Source Code

;; title: Flow-Drop
;; version:
;; summary:
;; description:



;; Define the contract
(define-data-var contract-owner principal tx-sender)

;; Define constants for errors
(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-ALREADY-REGISTERED (err u101))
(define-constant ERR-NOT-FOUND (err u102))
(define-constant ERR-INSUFFICIENT-FUNDS (err u103))
(define-constant ERR-BENEFICIARY-NOT-FOUND (err u104))
(define-constant ERR-UTILIZATION-NOT-FOUND (err u105))
(define-constant ERR-INVALID-INPUT (err u106))

;; Define constants for roles
(define-constant ROLE-ADMIN u1)
(define-constant ROLE-MODERATOR u2)
(define-constant ROLE-BENEFICIARY u3)

;; Define data maps
(define-map roles { user: principal } { role: uint })

(define-map beneficiaries
  { id: uint }
  { 
    name: (string-utf8 50), 
    description: (string-utf8 255), 
    target-amount: uint, 
    received-amount: uint, 
    status: (string-ascii 20)
  })

(define-map donations
  { id: uint }
  { donor: principal, beneficiary-id: uint, amount: uint, timestamp: uint })

(define-map utilization
  { id: uint }
  { 
    beneficiary-id: uint, 
    milestone: uint, 
    description: (string-utf8 255), 
    amount: uint, 
    status: (string-ascii 20)
  })

;; Define data variables
(define-data-var beneficiary-count uint u0)
(define-data-var donation-count uint u0)
(define-data-var utilization-count uint u0)

;; Helper functions
(define-private (is-authorized (user principal) (required-role uint))
  (let ((role-data (default-to { role: u0 } (map-get? roles { user: user }))))
    (>= (get role role-data) required-role)))

(define-private (get-last-milestone (beneficiary-id uint))
  (var-get utilization-count))

;; Role management functions
(define-public (set-role (user principal) (new-role uint))
  (let ((existing-role (default-to u0 (get role (map-get? roles { user: user })))))
    (if (and 
          (is-eq tx-sender (var-get contract-owner))
          (<= new-role ROLE-BENEFICIARY)
          (not (is-eq user tx-sender))  ;; Ensure user is not setting their own role
          (or (is-eq new-role ROLE-ADMIN)
              (is-eq new-role ROLE-MODERATOR)
              (is-eq new-role ROLE-BENEFICIARY)))
        (ok (map-set roles { user: user } { role: new-role }))
        ERR-NOT-AUTHORIZED)))

(define-public (remove-role (user principal))
  (if (and 
        (is-eq tx-sender (var-get contract-owner))
        (is-some (map-get? roles { user: user }))
        (not (is-eq user tx-sender)))  ;; Ensure user is not removing their own role
      (ok (map-delete roles { user: user }))
      ERR-NOT-AUTHORIZED))

;; Main functions
(define-public (register-beneficiary (name (string-utf8 50)) (description (string-utf8 255)) (target-amount uint))
  (let 
    ((beneficiary-id (+ (var-get beneficiary-count) u1)))
    (if (and (is-authorized tx-sender ROLE-MODERATOR)
             (> (len name) u0)
             (> (len description) u0)
             (> target-amount u0))
        (begin
          (map-set beneficiaries
            { id: beneficiary-id }
            { 
              name: name, 
              description: description, 
              target-amount: target-amount, 
              received-amount: u0, 
              status: "active" 
            })
          (var-set beneficiary-count beneficiary-id)
          (ok beneficiary-id))
        ERR-INVALID-INPUT)))

(define-read-only (get-beneficiary (id uint))
  (match (map-get? beneficiaries { id: id })
    beneficiary (ok beneficiary)
    ERR-BENEFICIARY-NOT-FOUND))


(define-public (add-utilization (beneficiary-id uint) (description (string-utf8 255)) (amount uint))
  (let 
    ((beneficiary (unwrap! (get-beneficiary beneficiary-id) ERR-BENEFICIARY-NOT-FOUND)))
    (if (and (is-authorized tx-sender ROLE-ADMIN)
             (> (len description) u0)
             (> amount u0)
             (< beneficiary-id (var-get beneficiary-count)))  ;; Check if beneficiary-id is valid
        (let
          ((milestone (+ (get-last-milestone beneficiary-id) u1))
           (utilization-id (+ (var-get utilization-count) u1)))
          (begin
            (map-set utilization
              { id: utilization-id }
              { 
                beneficiary-id: beneficiary-id, 
                milestone: milestone, 
                description: description, 
                amount: amount, 
                status: "pending" 
              })
            (var-set utilization-count utilization-id)
            (ok milestone)))
        ERR-INVALID-INPUT)))

(define-public (approve-utilization (beneficiary-id uint) (milestone uint))
  (let 
    ((utilization-entry (unwrap! (map-get? utilization { id: milestone }) ERR-UTILIZATION-NOT-FOUND))
     (beneficiary (unwrap! (get-beneficiary beneficiary-id) ERR-BENEFICIARY-NOT-FOUND)))
    (if (and (is-authorized tx-sender ROLE-ADMIN)
             (is-eq (get beneficiary-id utilization-entry) beneficiary-id)
             (< beneficiary-id (var-get beneficiary-count))  ;; Check if beneficiary-id is valid
             (< milestone (var-get utilization-count)))  ;; Check if milestone is valid
        (if (<= (get amount utilization-entry) (get received-amount beneficiary))
            (begin
              (map-set utilization
                { id: milestone }
                (merge utilization-entry { status: "approved" }))
              (ok true))
            ERR-INSUFFICIENT-FUNDS)
        ERR-NOT-AUTHORIZED)))

;; Get a single donation by ID
(define-read-only (get-donation-by-id (donation-id uint))
  (match (map-get? donations { id: donation-id })
    donation (ok donation)
    ERR-NOT-FOUND))

;; Get a single utilization entry by ID
(define-read-only (get-utilization-by-id (utilization-id uint))
  (match (map-get? utilization { id: utilization-id })
    util (ok util)
    ERR-NOT-FOUND))

;; Get the total number of donations
(define-read-only (get-donation-count)
  (ok (var-get donation-count)))

;; Get the total number of utilization entries
(define-read-only (get-utilization-count)
  (ok (var-get utilization-count)))

;; Contract initialization
(define-private (initialize-contract)
  (begin
    (map-set roles { user: tx-sender } { role: ROLE-ADMIN })
    (var-set contract-owner tx-sender)))

(initialize-contract)

Functions (13)

FunctionAccessArgs
is-authorizedprivateuser: principal, required-role: uint
get-last-milestoneprivatebeneficiary-id: uint
set-rolepublicuser: principal, new-role: uint
remove-rolepublicuser: principal
register-beneficiarypublicname: (string-utf8 50
get-beneficiaryread-onlyid: uint
add-utilizationpublicbeneficiary-id: uint, description: (string-utf8 255
approve-utilizationpublicbeneficiary-id: uint, milestone: uint
get-donation-by-idread-onlydonation-id: uint
get-utilization-by-idread-onlyutilization-id: uint
get-donation-countread-only
get-utilization-countread-only
initialize-contractprivate