Source Code

;; Quadratic Voting Contract
;; Fair voting where cost increases quadratically
;; Halal - equitable governance
;; 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-ENDED (err u405))
(define-constant ERR-INSUFFICIENT-CREDITS (err u406))
(define-constant CREDITS-PER-STX u100)

(define-data-var proposal-count uint u0)
(define-data-var total-credits-purchased uint u0)

(define-map voter-credits principal uint)
(define-map proposals uint { title: (string-utf8 100), creator: principal, votes-for: uint, votes-against: uint, end-block: uint })
(define-map voter-allocations { proposal-id: uint, voter: principal } { votes-for: uint, votes-against: uint })

(define-public (buy-credits (stx-amount uint))
  (let ((credits (* stx-amount CREDITS-PER-STX)))
    (try! (stx-transfer? stx-amount tx-sender CONTRACT-OWNER))
    (map-set voter-credits tx-sender (+ (default-to u0 (map-get? voter-credits tx-sender)) credits))
    (var-set total-credits-purchased (+ (var-get total-credits-purchased) credits))
    (ok credits)))

(define-public (create-proposal (title (string-utf8 100)) (duration uint))
  (let ((id (+ (var-get proposal-count) u1)))
    (map-set proposals id { title: title, creator: tx-sender, votes-for: u0, votes-against: u0, end-block: (+ stacks-block-height duration) })
    (var-set proposal-count id) (ok id)))

(define-public (vote (proposal-id uint) (num-votes uint) (support bool))
  (let (
    (proposal (unwrap! (map-get? proposals proposal-id) ERR-NOT-FOUND))
    (cost (* num-votes num-votes))
    (balance (default-to u0 (map-get? voter-credits tx-sender)))
    (prev (default-to { votes-for: u0, votes-against: u0 } (map-get? voter-allocations { proposal-id: proposal-id, voter: tx-sender })))
  )
    (asserts! (< stacks-block-height (get end-block proposal)) ERR-ENDED)
    (asserts! (>= balance cost) ERR-INSUFFICIENT-CREDITS)
    (map-set voter-credits tx-sender (- balance cost))
    (if support
      (begin
        (map-set proposals proposal-id (merge proposal { votes-for: (+ (get votes-for proposal) num-votes) }))
        (map-set voter-allocations { proposal-id: proposal-id, voter: tx-sender } (merge prev { votes-for: (+ (get votes-for prev) num-votes) })))
      (begin
        (map-set proposals proposal-id (merge proposal { votes-against: (+ (get votes-against proposal) num-votes) }))
        (map-set voter-allocations { proposal-id: proposal-id, voter: tx-sender } (merge prev { votes-against: (+ (get votes-against prev) num-votes) }))))
    (ok cost)))

(define-read-only (get-proposal (id uint)) (map-get? proposals id))
(define-read-only (get-credits (who principal)) (ok (default-to u0 (map-get? voter-credits who))))
(define-read-only (get-vote-cost (num-votes uint)) (ok (* num-votes num-votes)))
(define-read-only (get-voter-allocation (proposal-id uint) (voter principal)) (map-get? voter-allocations { proposal-id: proposal-id, voter: voter }))
(define-read-only (get-proposal-count) (ok (var-get proposal-count)))

Functions (8)

FunctionAccessArgs
buy-creditspublicstx-amount: uint
create-proposalpublictitle: (string-utf8 100
votepublicproposal-id: uint, num-votes: uint, support: bool
get-proposalread-onlyid: uint
get-creditsread-onlywho: principal
get-vote-costread-onlynum-votes: uint
get-voter-allocationread-onlyproposal-id: uint, voter: principal
get-proposal-countread-only