Source Code

;; Impact Bond Contract
;; Social impact bond - pay for outcomes
;; Halal - ethical investment
;; Clarity 4 compatible

(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-NOT-AUTHORIZED (err u401))
(define-constant ERR-NOT-FOUND (err u404))
(define-constant ERR-NOT-VERIFIED (err u405))

(define-data-var bond-count uint u0)
(define-data-var total-invested uint u0)

(define-map bonds uint {
  issuer: principal, title: (string-utf8 100), target-outcome: (string-utf8 200),
  total-investment: uint, investor-count: uint, outcome-achieved: bool,
  return-rate: uint, verifier: principal, status: (string-ascii 20), created: uint
})
(define-map investors { bond-id: uint, investor: principal } { amount: uint, returned: bool })

(define-public (issue-bond (title (string-utf8 100)) (outcome (string-utf8 200)) (return-rate uint) (verifier principal))
  (let ((id (+ (var-get bond-count) u1)))
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (map-set bonds id { issuer: tx-sender, title: title, target-outcome: outcome, total-investment: u0, investor-count: u0, outcome-achieved: false, return-rate: return-rate, verifier: verifier, status: "open", created: stacks-block-height })
    (var-set bond-count id) (ok id)))

(define-public (invest (bond-id uint) (amount uint))
  (let ((bond (unwrap! (map-get? bonds bond-id) ERR-NOT-FOUND)))
    (asserts! (is-eq (get status bond) "open") ERR-NOT-FOUND)
    (try! (stx-transfer? amount tx-sender CONTRACT-OWNER))
    (map-set investors { bond-id: bond-id, investor: tx-sender } { amount: amount, returned: false })
    (map-set bonds bond-id (merge bond { total-investment: (+ (get total-investment bond) amount), investor-count: (+ (get investor-count bond) u1) }))
    (var-set total-invested (+ (var-get total-invested) amount))
    (ok amount)))

(define-public (verify-outcome (bond-id uint) (achieved bool))
  (let ((bond (unwrap! (map-get? bonds bond-id) ERR-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get verifier bond)) ERR-NOT-AUTHORIZED)
    (map-set bonds bond-id (merge bond { outcome-achieved: achieved, status: (if achieved "success" "failed") }))
    (ok achieved)))

(define-public (return-investment (bond-id uint) (investor principal))
  (let (
    (bond (unwrap! (map-get? bonds bond-id) ERR-NOT-FOUND))
    (inv (unwrap! (map-get? investors { bond-id: bond-id, investor: investor }) ERR-NOT-FOUND))
    (return-amount (if (get outcome-achieved bond) (+ (get amount inv) (/ (* (get amount inv) (get return-rate bond)) u100)) (get amount inv)))
  )
    (asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-NOT-AUTHORIZED)
    (asserts! (not (get returned inv)) ERR-NOT-FOUND)
    (try! (stx-transfer? return-amount tx-sender investor))
    (map-set investors { bond-id: bond-id, investor: investor } (merge inv { returned: true }))
    (ok return-amount)))

(define-read-only (get-bond (id uint)) (map-get? bonds id))
(define-read-only (get-investor (bond-id uint) (investor principal)) (map-get? investors { bond-id: bond-id, investor: investor }))
(define-read-only (get-bond-count) (ok (var-get bond-count)))
(define-read-only (get-total-invested) (ok (var-get total-invested)))

Functions (8)

FunctionAccessArgs
return-investmentpublicbond-id: uint, investor: principal
issue-bondpublictitle: (string-utf8 100
investpublicbond-id: uint, amount: uint
verify-outcomepublicbond-id: uint, achieved: bool
get-bondread-onlyid: uint
get-investorread-onlybond-id: uint, investor: principal
get-bond-countread-only
get-total-investedread-only