;; title: vaccine-batch-monitor
;; version:
;; summary:
;; description:
;; Vaccine Tracking Smart Contract
;; Contract Owner Management
(define-data-var immunization-system-controller principal tx-sender)
;; Error Codes
(define-constant AUTHORIZATION-ERROR (err u100))
(define-constant INVALID-VACCINE-SHIPMENT (err u101))
(define-constant DUPLICATE-SHIPMENT-ERROR (err u102))
(define-constant SHIPMENT-NOT-FOUND-ERROR (err u103))
(define-constant VACCINE-SUPPLY-DEPLETED (err u104))
(define-constant INVALID-RECIPIENT-ID (err u105))
(define-constant DUPLICATE-IMMUNIZATION (err u106))
(define-constant COLD-CHAIN-BREACH (err u107))
(define-constant EXPIRED-VACCINE-ERROR (err u108))
(define-constant INVALID-CLINIC-LOCATION (err u109))
(define-constant IMMUNIZATION-LIMIT-REACHED (err u110))
(define-constant MINIMUM-INTERVAL-VIOLATION (err u111))
(define-constant ADMIN-PRIVILEGES-REQUIRED (err u112))
(define-constant DATA-VALIDATION-ERROR (err u113))
(define-constant EXPIRY-DATE-ERROR (err u114))
(define-constant STORAGE-CAPACITY-ERROR (err u115))
(define-constant CLINICIAN-ALREADY-REGISTERED (err u116))
;; Constants
(define-constant COLD-CHAIN-MINIMUM-TEMP (- 70))
(define-constant COLD-CHAIN-MAXIMUM-TEMP 8)
(define-constant DOSE-INTERVAL-REQUIREMENT u21) ;; 21 days minimum between doses
(define-constant MAXIMUM-IMMUNIZATION-SERIES u4)
(define-constant MINIMUM-ENTRY-LENGTH u1)
(define-constant CURRENT-CHAIN-HEIGHT stacks-block-height)
;; Data Maps
(define-map vaccine-shipment-registry
{ shipment-id: (string-ascii 32) }
{
manufacturer-details: (string-ascii 50),
vaccine-brand-name: (string-ascii 50),
production-timestamp: uint,
expiration-timestamp: uint,
available-unit-count: uint,
storage-temp-celsius: int,
shipment-status: (string-ascii 20),
temp-violation-incidents: uint,
storage-facility-id: (string-ascii 100),
shipment-notes: (string-ascii 500),
}
)
(define-map immunization-registry
{ recipient-id: (string-ascii 32) }
{
immunization-sequence: (list
10
{
shipment-reference: (string-ascii 32),
administration-timestamp: uint,
vaccine-formulation: (string-ascii 50),
sequence-number: uint,
administering-clinician: principal,
clinic-location: (string-ascii 100),
next-dose-due-date: (optional uint),
}
),
completed-immunizations: uint,
adverse-event-reports: (list 5 (string-ascii 200)),
medical-exemption: (optional (string-ascii 200)),
}
)
(define-map authorized-clinicians
principal
{
clinical-role: (string-ascii 20),
clinic-name: (string-ascii 100),
credentials-valid-until: uint,
}
)
(define-map cold-chain-facilities
(string-ascii 100)
{
facility-location: (string-ascii 200),
max-storage-units: uint,
current-unit-count: uint,
temperature-monitoring: (list 100 {
monitoring-timestamp: uint,
recorded-temp-celsius: int,
}),
}
)
;; Private Functions
(define-private (is-system-controller)
(is-eq tx-sender (var-get immunization-system-controller))
)
;; String validation functions
(define-private (validate-identifier (input (string-ascii 32)))
(> (len input) MINIMUM-ENTRY-LENGTH)
)
(define-private (validate-name-field (input (string-ascii 50)))
(> (len input) MINIMUM-ENTRY-LENGTH)
)
(define-private (validate-location-field (input (string-ascii 100)))
(> (len input) MINIMUM-ENTRY-LENGTH)
)
(define-private (validate-description-field (input (string-ascii 200)))
(> (len input) MINIMUM-ENTRY-LENGTH)
)
(define-private (validate-future-date (input-date uint))
(> input-date CURRENT-CHAIN-HEIGHT)
)
(define-private (validate-storage-limit (proposed-limit uint))
(> proposed-limit u0)
)
;; Read-only Functions
(define-read-only (get-system-controller)
(ok (var-get immunization-system-controller))
)
(define-read-only (verify-clinician-status (clinician-address principal))
(match (map-get? authorized-clinicians clinician-address)
clinician-data (>= (get credentials-valid-until clinician-data) CURRENT-CHAIN-HEIGHT)
false
)
)
;; Public Functions
(define-public (transfer-system-control (new-controller principal))
(begin
(asserts! (is-system-controller) ADMIN-PRIVILEGES-REQUIRED)
(asserts! (is-some (map-get? authorized-clinicians new-controller))
AUTHORIZATION-ERROR
)
(ok (var-set immunization-system-controller new-controller))
)
)
(define-public (register-clinician
(clinician-address principal)
(role (string-ascii 20))
(facility-name (string-ascii 100))
(credential-expiry uint)
)
(begin
(asserts! (is-system-controller) AUTHORIZATION-ERROR)
(asserts! (is-none (map-get? authorized-clinicians clinician-address))
CLINICIAN-ALREADY-REGISTERED
)
(asserts! (validate-identifier role) DATA-VALIDATION-ERROR)
(asserts! (validate-location-field facility-name) DATA-VALIDATION-ERROR)
(asserts! (validate-future-date credential-expiry) EXPIRY-DATE-ERROR)
(ok (map-set authorized-clinicians clinician-address {
clinical-role: role,
clinic-name: facility-name,
credentials-valid-until: credential-expiry,
}))
)
)
(define-public (register-storage-facility
(facility-id (string-ascii 100))
(location-address (string-ascii 200))
(storage-capacity uint)
)
(begin
(asserts! (is-system-controller) AUTHORIZATION-ERROR)
(asserts! (validate-location-field facility-id) DATA-VALIDATION-ERROR)
(asserts! (validate-description-field location-address) DATA-VALIDATION-ERROR)
(asserts! (validate-storage-limit storage-capacity) STORAGE-CAPACITY-ERROR)
(ok (map-set cold-chain-facilities facility-id {
facility-location: location-address,
max-storage-units: storage-capacity,
current-unit-count: u0,
temperature-monitoring: (list),
}))
)
)
(define-public (register-vaccine-shipment
(shipment-id (string-ascii 32))
(manufacturer (string-ascii 50))
(brand-name (string-ascii 50))
(production-date uint)
(expiry-date uint)
(dose-quantity uint)
(storage-temp int)
(facility-id (string-ascii 100))
)
(begin
(asserts! (verify-clinician-status tx-sender) AUTHORIZATION-ERROR)
(asserts! (validate-identifier shipment-id) DATA-VALIDATION-ERROR)
(asserts! (validate-name-field manufacturer) DATA-VALIDATION-ERROR)
(asserts! (validate-name-field brand-name) DATA-VALIDATION-ERROR)
(asserts! (validate-location-field facility-id) DATA-VALIDATION-ERROR)
(asserts!
(is-none (map-get? vaccine-shipment-registry { shipment-id: shipment-id }))
DUPLICATE-SHIPMENT-ERROR
)
(asserts! (validate-storage-limit dose-quantity) INVALID-VACCINE-SHIPMENT)
(asserts! (validate-future-date expiry-date) EXPIRY-DATE-ERROR)
(asserts! (> expiry-date production-date) INVALID-VACCINE-SHIPMENT)
(asserts!
(and
(>= storage-temp COLD-CHAIN-MINIMUM-TEMP)
(<= storage-temp COLD-CHAIN-MAXIMUM-TEMP)
)
COLD-CHAIN-BREACH
)
(ok (map-set vaccine-shipment-registry { shipment-id: shipment-id } {
manufacturer-details: manufacturer,
vaccine-brand-name: brand-name,
production-timestamp: production-date,
expiration-timestamp: expiry-date,
available-unit-count: dose-quantity,
storage-temp-celsius: storage-temp,
shipment-status: "active",
temp-violation-incidents: u0,
storage-facility-id: facility-id,
shipment-notes: "",
}))
)
)
(define-public (update-shipment-status
(shipment-id (string-ascii 32))
(new-status (string-ascii 20))
)
(begin
(asserts! (verify-clinician-status tx-sender) AUTHORIZATION-ERROR)
(asserts! (validate-identifier shipment-id) DATA-VALIDATION-ERROR)
(asserts! (validate-identifier new-status) DATA-VALIDATION-ERROR)
(match (map-get? vaccine-shipment-registry { shipment-id: shipment-id })
shipment-data (ok (map-set vaccine-shipment-registry { shipment-id: shipment-id }
(merge shipment-data { shipment-status: new-status })
))
SHIPMENT-NOT-FOUND-ERROR
)
)
)
(define-public (record-temperature-violation
(shipment-id (string-ascii 32))
(violation-temp int)
)
(begin
(asserts! (verify-clinician-status tx-sender) AUTHORIZATION-ERROR)
(asserts! (validate-identifier shipment-id) DATA-VALIDATION-ERROR)
(match (map-get? vaccine-shipment-registry { shipment-id: shipment-id })
shipment-data (ok (map-set vaccine-shipment-registry { shipment-id: shipment-id }
(merge shipment-data {
temp-violation-incidents: (+ (get temp-violation-incidents shipment-data) u1),
shipment-status: (if (> (get temp-violation-incidents shipment-data) u2)
"compromised"
(get shipment-status shipment-data)
),
})
))
SHIPMENT-NOT-FOUND-ERROR
)
)
)
(define-public (record-immunization
(recipient-id (string-ascii 32))
(shipment-id (string-ascii 32))
(clinic-location (string-ascii 100))
)
(begin
(asserts! (verify-clinician-status tx-sender) AUTHORIZATION-ERROR)
(asserts! (validate-identifier recipient-id) INVALID-RECIPIENT-ID)
(asserts! (validate-identifier shipment-id) DATA-VALIDATION-ERROR)
(asserts! (validate-location-field clinic-location) INVALID-CLINIC-LOCATION)
(match (map-get? vaccine-shipment-registry { shipment-id: shipment-id })
shipment-data (begin
(asserts! (> (get available-unit-count shipment-data) u0)
VACCINE-SUPPLY-DEPLETED
)
(asserts! (is-eq (get shipment-status shipment-data) "active")
INVALID-VACCINE-SHIPMENT
)
(asserts!
(<= CURRENT-CHAIN-HEIGHT (get expiration-timestamp shipment-data))
EXPIRED-VACCINE-ERROR
)
(match (map-get? immunization-registry { recipient-id: recipient-id })
recipient-record
(begin
(asserts!
(< (get completed-immunizations recipient-record)
MAXIMUM-IMMUNIZATION-SERIES
)
IMMUNIZATION-LIMIT-REACHED
)
(let ((current-dose (+ (get completed-immunizations recipient-record) u1)))
(if (> current-dose u1)
(asserts!
(>=
(- CURRENT-CHAIN-HEIGHT
(get administration-timestamp
(unwrap-panic (element-at (get immunization-sequence recipient-record)
(- current-dose u2)
))
))
DOSE-INTERVAL-REQUIREMENT
)
MINIMUM-INTERVAL-VIOLATION
)
true
)
(ok (map-set immunization-registry { recipient-id: recipient-id } {
immunization-sequence: (unwrap-panic (as-max-len?
(append (get immunization-sequence recipient-record) {
shipment-reference: shipment-id,
administration-timestamp: CURRENT-CHAIN-HEIGHT,
vaccine-formulation: (get vaccine-brand-name shipment-data),
sequence-number: current-dose,
administering-clinician: tx-sender,
clinic-location: clinic-location,
next-dose-due-date: (some (+ CURRENT-CHAIN-HEIGHT DOSE-INTERVAL-REQUIREMENT)),
})
u10
)),
completed-immunizations: current-dose,
adverse-event-reports: (get adverse-event-reports recipient-record),
medical-exemption: (get medical-exemption recipient-record),
}))
)
)
;; First immunization for recipient
(ok (map-set immunization-registry { recipient-id: recipient-id } {
immunization-sequence: (list
{
shipment-reference: shipment-id,
administration-timestamp: CURRENT-CHAIN-HEIGHT,
vaccine-formulation: (get vaccine-brand-name shipment-data),
sequence-number: u1,
administering-clinician: tx-sender,
clinic-location: clinic-location,
next-dose-due-date: (some (+ CURRENT-CHAIN-HEIGHT DOSE-INTERVAL-REQUIREMENT)),
}
),
completed-immunizations: u1,
adverse-event-reports: (list),
medical-exemption: none,
}))
)
)
SHIPMENT-NOT-FOUND-ERROR
)
)
)