Source Code

;; title: community-scholar
;; version:
;; summary:
;; description:

;; Community Scholarship Fund Smart Contract
;; Handles scholarship applications, funding, and disbursement

;; Constants
(define-constant CONTRACT_ADMINISTRATOR tx-sender)
(define-constant ERROR_UNAUTHORIZED_ACCESS (err u1))
(define-constant ERROR_APPLICATION_EXISTS (err u2))
(define-constant ERROR_DONATION_BELOW_MINIMUM (err u3))
(define-constant ERROR_INSUFFICIENT_BALANCE (err u4))
(define-constant ERROR_RECIPIENT_NOT_ELIGIBLE (err u5))
(define-constant ERROR_APPLICATION_PERIOD_CLOSED (err u6))
(define-constant ERROR_INVALID_INPUT (err u7))
(define-constant MINIMUM_DONATION_AMOUNT u1000000) ;; in microSTX (1 STX)
(define-constant MAXIMUM_SCHOLARSHIP_AMOUNT u1000000000) ;; 1000 STX
(define-constant MAXIMUM_GPA u400) ;; 4.00 GPA * 100
(define-constant MAXIMUM_ACADEMIC_YEAR u8)

;; Data Variables
(define-data-var scholarship-pool-balance uint u0)
(define-data-var application-submission-deadline uint u0)
(define-data-var scholarship-disbursement-period uint u0)

;; Data Maps
(define-map DonorRegistry
  principal
  {
    cumulative-donation-amount: uint,
    most-recent-donation-block: uint,
  }
)

(define-map ScholarshipRecipients
  principal
  {
    recipient-status: (string-ascii 20),
    scholarship-amount: uint,
    academic-performance: uint,
    field-of-study: (string-ascii 50),
    academic-year: uint,
  }
)

(define-map ScholarshipApplications
  principal
  {
    applicant-full-name: (string-ascii 50),
    academic-performance: uint,
    field-of-study: (string-ascii 50),
    academic-year: uint,
    requested-scholarship-amount: uint,
    application-status: (string-ascii 20),
  }
)

;; Private Functions
(define-private (is-administrator)
  (is-eq tx-sender CONTRACT_ADMINISTRATOR)
)

(define-private (validate-donation-amount (donation-amount uint))
  (>= donation-amount MINIMUM_DONATION_AMOUNT)
)

(define-private (update-donor-records
    (donor-address principal)
    (donation-amount uint)
  )
  (let ((current-donor-info (default-to {
      cumulative-donation-amount: u0,
      most-recent-donation-block: u0,
    }
      (map-get? DonorRegistry donor-address)
    )))
    (map-set DonorRegistry donor-address {
      cumulative-donation-amount: (+ (get cumulative-donation-amount current-donor-info) donation-amount),
      most-recent-donation-block: stacks-block-height,
    })
  )
)

(define-private (validate-applicant-name (name (string-ascii 50)))
  (and (>= (len name) u1) (<= (len name) u50))
)

(define-private (validate-grade-point-average (gpa uint))
  (<= gpa MAXIMUM_GPA)
)

(define-private (validate-selected-major (major (string-ascii 50)))
  (and (>= (len major) u1) (<= (len major) u50))
)

(define-private (validate-current-year (year uint))
  (and (>= year u1) (<= year MAXIMUM_ACADEMIC_YEAR))
)

(define-private (validate-requested-amount (amount uint))
  (<= amount MAXIMUM_SCHOLARSHIP_AMOUNT)
)

(define-private (validate-and-sanitize-application
    (applicant-name (string-ascii 50))
    (grade-point-average uint)
    (selected-major (string-ascii 50))
    (current-year uint)
    (requested-amount uint)
  )
  (begin
    ;; Perform all validations first
    (asserts! (validate-applicant-name applicant-name) ERROR_INVALID_INPUT)
    (asserts! (validate-grade-point-average grade-point-average)
      ERROR_INVALID_INPUT
    )
    (asserts! (validate-selected-major selected-major) ERROR_INVALID_INPUT)
    (asserts! (validate-current-year current-year) ERROR_INVALID_INPUT)
    (asserts! (validate-requested-amount requested-amount) ERROR_INVALID_INPUT)

    ;; Return validated data structure
    (ok {
      applicant-name: applicant-name,
      grade-point-average: grade-point-average,
      selected-major: selected-major,
      current-year: current-year,
      requested-amount: requested-amount,
    })
  )
)

(define-private (validate-principal (address principal))
  (match (principal-destruct? address)
    success true
    error false
  )
)

(define-public (submit-scholarship-application
    (applicant-name (string-ascii 50))
    (grade-point-average uint)
    (selected-major (string-ascii 50))
    (current-year uint)
    (requested-amount uint)
  )
  (let ((existing-application (map-get? ScholarshipApplications tx-sender)))
    (if (is-some existing-application)
      ERROR_APPLICATION_EXISTS
      (if (> stacks-block-height (var-get application-submission-deadline))
        ERROR_APPLICATION_PERIOD_CLOSED
        (match (validate-and-sanitize-application applicant-name grade-point-average
          selected-major current-year requested-amount
        )
          validated-data (begin
            (map-set ScholarshipApplications tx-sender {
              applicant-full-name: (get applicant-name validated-data),
              academic-performance: (get grade-point-average validated-data),
              field-of-study: (get selected-major validated-data),
              academic-year: (get current-year validated-data),
              requested-scholarship-amount: (get requested-amount validated-data),
              application-status: "PENDING",
            })
            (ok true)
          )
          error ERROR_INVALID_INPUT
        )
      )
    )
  )
)

(define-public (evaluate-scholarship-application
    (applicant-address principal)
    (is-approved bool)
  )
  (begin
    (asserts! (is-administrator) ERROR_UNAUTHORIZED_ACCESS)
    (asserts! (validate-principal applicant-address) ERROR_INVALID_INPUT)
    (match (map-get? ScholarshipApplications applicant-address)
      current-application (begin
        (map-set ScholarshipApplications applicant-address
          (merge current-application { application-status: (if is-approved
            "APPROVED"
            "REJECTED"
          ) }
          ))
        (if is-approved
          (map-set ScholarshipRecipients applicant-address {
            recipient-status: "ACTIVE",
            scholarship-amount: (get requested-scholarship-amount current-application),
            academic-performance: (get academic-performance current-application),
            field-of-study: (get field-of-study current-application),
            academic-year: (get academic-year current-application),
          })
          true
        )
        (ok true)
      )
      (err u404)
    )
  )
)

;; Read-Only Functions
(define-read-only (get-scholarship-fund-balance)
  (ok (var-get scholarship-pool-balance))
)

(define-read-only (get-donor-details (donor-address principal))
  (ok (map-get? DonorRegistry donor-address))
)

(define-read-only (get-application-details (applicant-address principal))
  (ok (map-get? ScholarshipApplications applicant-address))
)

(define-read-only (get-recipient-details (recipient-address principal))
  (ok (map-get? ScholarshipRecipients recipient-address))
)

;; Administrative Functions
(define-public (update-application-deadline (new-deadline-block uint))
  (begin
    (asserts! (is-administrator) ERROR_UNAUTHORIZED_ACCESS)
    (asserts! (> new-deadline-block stacks-block-height) ERROR_INVALID_INPUT)
    (var-set application-submission-deadline new-deadline-block)
    (ok true)
  )
)

(define-public (update-disbursement-period (new-disbursement-block uint))
  (begin
    (asserts! (is-administrator) ERROR_UNAUTHORIZED_ACCESS)
    (asserts! (> new-disbursement-block stacks-block-height) ERROR_INVALID_INPUT)
    (var-set scholarship-disbursement-period new-disbursement-block)
    (ok true)
  )
)

Functions (16)

FunctionAccessArgs
is-administratorprivate
validate-donation-amountprivatedonation-amount: uint
validate-applicant-nameprivatename: (string-ascii 50
validate-grade-point-averageprivategpa: uint
validate-selected-majorprivatemajor: (string-ascii 50
validate-current-yearprivateyear: uint
validate-requested-amountprivateamount: uint
validate-and-sanitize-applicationprivateapplicant-name: (string-ascii 50
validate-principalprivateaddress: principal
submit-scholarship-applicationpublicapplicant-name: (string-ascii 50
get-scholarship-fund-balanceread-only
get-donor-detailsread-onlydonor-address: principal
get-application-detailsread-onlyapplicant-address: principal
get-recipient-detailsread-onlyrecipient-address: principal
update-application-deadlinepublicnew-deadline-block: uint
update-disbursement-periodpublicnew-disbursement-block: uint